diff --git a/snowpack/src/build/import-resolver.ts b/snowpack/src/build/import-resolver.ts index 8add72cfeb..299570cd3a 100644 --- a/snowpack/src/build/import-resolver.ts +++ b/snowpack/src/build/import-resolver.ts @@ -3,7 +3,7 @@ import path from 'path'; import url from 'url'; import {ImportMap, SnowpackConfig} from '../types/snowpack'; import {findMatchingAliasEntry, getExt, relativeURL, replaceExt} from '../util'; -import {defaultFileExtensionMapping} from './file-urls'; +import {defaultFileExtensionMapping, getUrlForFile} from './file-urls'; const cwd = process.cwd(); @@ -14,8 +14,7 @@ interface ImportResolverOptions { } /** Perform a file disk lookup for the requested import specifier. */ -export function getImportStats(dirLoc: string, spec: string): fs.Stats | false { - const importedFileOnDisk = path.resolve(dirLoc, spec); +export function getImportStats(importedFileOnDisk: string): fs.Stats | false { try { return fs.statSync(importedFileOnDisk); } catch (err) { @@ -66,18 +65,20 @@ export function createImportResolver({ } if (spec.startsWith('/')) { - const importStats = getImportStats(cwd, spec.substr(1)); + const importStats = getImportStats(path.resolve(cwd, spec.substr(1))); return resolveSourceSpecifier(spec, importStats, config); } if (spec.startsWith('./') || spec.startsWith('../')) { - const importStats = getImportStats(path.dirname(fileLoc), spec); - return resolveSourceSpecifier(spec, importStats, config); + const importedFileLoc = path.resolve(path.dirname(fileLoc), spec); + const importStats = getImportStats(importedFileLoc); + const newSpec = getUrlForFile(importedFileLoc, config) || spec; + return resolveSourceSpecifier(newSpec, importStats, config); } const aliasEntry = findMatchingAliasEntry(config, spec); if (aliasEntry && aliasEntry.type === 'path') { const {from, to} = aliasEntry; let result = spec.replace(from, to); - const importStats = getImportStats(cwd, result); + const importStats = getImportStats(path.resolve(cwd, result)); result = resolveSourceSpecifier(result, importStats, config); // replace Windows backslashes at the end, after resolution result = relativeURL(path.dirname(fileLoc), result); diff --git a/snowpack/src/commands/dev.ts b/snowpack/src/commands/dev.ts index 4425c1fd2f..6cd4e5e2ad 100644 --- a/snowpack/src/commands/dev.ts +++ b/snowpack/src/commands/dev.ts @@ -75,6 +75,7 @@ import { openInBrowser, parsePackageImportSpecifier, readFile, + relativeURL, replaceExt, resolveDependencyManifest, updateLockfileHash, @@ -795,26 +796,40 @@ export async function startServer(commandOptions: CommandOptions): Promise { // Try to resolve the specifier to a known URL in the project - const resolvedImportUrl = resolveImportSpecifier(spec); - if (resolvedImportUrl) { - // Ignore "http://*" imports - if (url.parse(resolvedImportUrl).protocol) { - return resolvedImportUrl; - } - // Support proxy file imports - const extName = path.extname(resolvedImportUrl); - if ( - extName && - (responseExt === '.js' || responseExt === '.html') && - extName !== '.js' - ) { - return resolvedImportUrl + '.proxy.js'; - } - return resolvedImportUrl; + let resolvedImportUrl = resolveImportSpecifier(spec); + // Handle an import that couldn't be resolved + if (!resolvedImportUrl) { + missingPackages.push(spec); + return spec; + } + // Ignore "http://*" imports + if (url.parse(resolvedImportUrl).protocol) { + return spec; + } + // Ignore packages marked as external + if (config.installOptions.externalPackage?.includes(resolvedImportUrl)) { + return spec; + } + // Handle normal "./" & "../" import specifiers + const importExtName = path.posix.extname(resolvedImportUrl); + const isProxyImport = + importExtName && + (responseExt === '.js' || responseExt === '.html') && + importExtName !== '.js'; + const isAbsoluteUrlPath = path.posix.isAbsolute(resolvedImportUrl); + if (isProxyImport) { + resolvedImportUrl = resolvedImportUrl + '.proxy.js'; } - missingPackages.push(spec); - return spec; + // When dealing with an absolute import path, we need to honor the baseUrl + if (isAbsoluteUrlPath) { + resolvedImportUrl = relativeURL(path.posix.dirname(reqPath), resolvedImportUrl); + } + // Make sure that a relative URL always starts with "./" + if (!resolvedImportUrl.startsWith('.') && !resolvedImportUrl.startsWith('/')) { + resolvedImportUrl = './' + resolvedImportUrl; + } + return resolvedImportUrl; }, ); diff --git a/test-dev/__snapshots__/dev.test.ts.snap b/test-dev/__snapshots__/dev.test.ts.snap index 26374ce1da..f9cc0f1e75 100644 --- a/test-dev/__snapshots__/dev.test.ts.snap +++ b/test-dev/__snapshots__/dev.test.ts.snap @@ -37,7 +37,7 @@ exports[`snowpack dev smoke: js 1`] = ` * When you're ready to start on your site, clear the file. Happy hacking! **/ -import confetti from '/web_modules/canvas-confetti.js'; +import confetti from '../web_modules/canvas-confetti.js'; confetti.create(document.getElementById('canvas'), { resize: true, diff --git a/test/build/config-mount/__snapshots__ b/test/build/config-mount/__snapshots__ index ef03921b35..587d5a1a56 100644 --- a/test/build/config-mount/__snapshots__ +++ b/test/build/config-mount/__snapshots__ @@ -23,7 +23,7 @@ Array [ ] `; -exports[`snowpack build config-mount: build/__snowpack__/env.js 1`] = `"export default {\\"MODE\\":\\"production\\",\\"NODE_ENV\\":\\"production\\"};"`; +exports[`snowpack build config-mount: build/__snowpack__/env.js 1`] = `"export default {\\"MODE\\":\\"production\\",\\"NODE_ENV\\":\\"production\\",\\"SSR\\":false};"`; exports[`snowpack build config-mount: build/a/index.js 1`] = `"console.log('a');"`; diff --git a/test/build/resolve-imports/__snapshots__ b/test/build/resolve-imports/__snapshots__ index 6f3fe6e07a..050e4dc6b6 100644 --- a/test/build/resolve-imports/__snapshots__ +++ b/test/build/resolve-imports/__snapshots__ @@ -10,6 +10,8 @@ Array [ "_dist_/index.js", "_dist_/sort.js", "_dist_/test-mjs.js", + "robots.txt", + "robots.txt.proxy.js", "TEST_WMU/@css/package-b/style.css", "TEST_WMU/@css/package-b/style.css.proxy.js", "TEST_WMU/@fortawesome/fontawesome-free/svgs/solid/ad.svg", @@ -94,6 +96,9 @@ exports[`snowpack build resolve-imports: build/_dist_/index.html 1`] = ` import styles from './components/style.css.proxy.js'; // relative import import styles_ from './components/style.css.proxy.js'; // relative import console.log(styles, styles_); + // Importing across mounted directories + import robotsTxtRef from '../robots.txt.proxy.js'; + console.log(robotsTxtRef); @@ -148,7 +153,10 @@ import styles from './components/style.css.proxy.js'; // relative import import styles_ from './components/style.css.proxy.js'; // relative import console.log(styles, styles_); import adSvg from '../TEST_WMU/@fortawesome/fontawesome-free/svgs/solid/ad.svg.proxy.js'; -console.log(adSvg);" +console.log(adSvg); +// Importing across mounted directories +import robotsTxtRef from '../robots.txt.proxy.js'; +console.log(robotsTxtRef);" `; exports[`snowpack build resolve-imports: build/_dist_/sort.js 1`] = `"export default (arr) => arr.sort();"`; @@ -233,3 +241,5 @@ exports[`snowpack build resolve-imports: build/TEST_WMU/import-map.json 1`] = ` } }" `; + +exports[`snowpack build resolve-imports: build/robots.txt.proxy.js 1`] = `"export default \\"/robots.txt\\";"`; diff --git a/test/build/resolve-imports/public/robots.txt b/test/build/resolve-imports/public/robots.txt new file mode 100644 index 0000000000..761f2f1199 --- /dev/null +++ b/test/build/resolve-imports/public/robots.txt @@ -0,0 +1 @@ +THIS IS A ROBOTS.TXT TEST FILE \ No newline at end of file diff --git a/test/build/resolve-imports/snowpack.config.js b/test/build/resolve-imports/snowpack.config.js index 47342cc538..fea4bb12b8 100644 --- a/test/build/resolve-imports/snowpack.config.js +++ b/test/build/resolve-imports/snowpack.config.js @@ -7,6 +7,7 @@ module.exports = { }, mount: { './src': '/_dist_', + './public': '/', }, devOptions: { fallback: '_dist_/index.html', diff --git a/test/build/resolve-imports/src/index.html b/test/build/resolve-imports/src/index.html index 8ae375acf8..b4175d6f23 100644 --- a/test/build/resolve-imports/src/index.html +++ b/test/build/resolve-imports/src/index.html @@ -48,6 +48,10 @@ import styles from './components/style.css'; // relative import import styles_ from '@app/components/style.css'; // relative import console.log(styles, styles_); + + // Importing across mounted directories + import robotsTxtRef from '../public/robots.txt'; + console.log(robotsTxtRef); diff --git a/test/build/resolve-imports/src/index.js b/test/build/resolve-imports/src/index.js index 3b4d556afc..15377fe531 100644 --- a/test/build/resolve-imports/src/index.js +++ b/test/build/resolve-imports/src/index.js @@ -45,3 +45,7 @@ console.log(styles, styles_); import adSvg from '@fortawesome/fontawesome-free/svgs/solid/ad.svg'; console.log(adSvg); + +// Importing across mounted directories +import robotsTxtRef from '../public/robots.txt'; +console.log(robotsTxtRef);