diff --git a/src/webpackImporter.js b/src/webpackImporter.js index 725b4564..560da4dc 100644 --- a/src/webpackImporter.js +++ b/src/webpackImporter.js @@ -70,41 +70,59 @@ function webpackImporter(loaderContext, includePaths) { mainFiles: [], modules: [], }); + // TODO implement the `restrictions` option for `enhanced-resolve` and avoid resolution `js` files from the `mainFields` + // TODO avoid resolsing `_index`, `index` and files without extensions + // TODO avoid resolving with multiple extensions - `file.sass.sass`/`file.sass.scss`/`file.sass.css` const webpackResolve = loaderContext.getResolve({ mainFields: ['sass', 'style', 'main', '...'], mainFiles: ['_index', 'index', '...'], extensions: ['.sass', '.scss', '.css'], }); - return (url, prev, done) => { - // The order of import precedence is as follows: - // - // 1. Filesystem imports relative to the base file. - // 2. Custom importer imports. - // 3. Filesystem imports relative to the working directory. - // 4. Filesystem imports relative to an `includePaths` path. - // 5. Filesystem imports relative to a `SASS_PATH` path. - // - // Because `sass`/`node-sass` run custom importers after `3`, `4` and `5` points, we need to emulate this behavior to avoid wrong resolution. - const sassPossibleRequests = getPossibleRequests(url); - const webpackPossibleRequests = getPossibleRequests(url, true); - const resolutionMap = [] - .concat( + return (originalUrl, prev, done) => { + let url = originalUrl; + + const isFileScheme = originalUrl.startsWith('file:'); + + if (isFileScheme) { + // eslint-disable-next-line no-param-reassign + url = originalUrl.slice(5); + } + + let resolutionMap = []; + + if (includePaths.length > 0 && !isFileScheme) { + // The order of import precedence is as follows: + // + // 1. Filesystem imports relative to the base file. + // 2. Custom importer imports. + // 3. Filesystem imports relative to the working directory. + // 4. Filesystem imports relative to an `includePaths` path. + // 5. Filesystem imports relative to a `SASS_PATH` path. + // + // Because `sass`/`node-sass` run custom importers before `3`, `4` and `5` points, we need to emulate this behavior to avoid wrong resolution. + const sassPossibleRequests = getPossibleRequests(url); + + resolutionMap = resolutionMap.concat( includePaths.map((context) => ({ resolve: sassResolve, context, possibleRequests: sassPossibleRequests, })) - ) - .concat({ - resolve: webpackResolve, - context: dirContextFrom(prev), - possibleRequests: webpackPossibleRequests, - }); + ); + } + + const webpackPossibleRequests = getPossibleRequests(url, true); + + resolutionMap = resolutionMap.concat({ + resolve: webpackResolve, + context: isFileScheme ? '/' : dirContextFrom(prev), + possibleRequests: webpackPossibleRequests, + }); startResolving(resolutionMap) // Catch all resolving errors, return the original file and pass responsibility back to other custom importers - .catch(() => ({ file: url })) + .catch(() => ({ file: originalUrl })) .then(done); }; } diff --git a/test/helpers/getCodeFromSass.js b/test/helpers/getCodeFromSass.js index d8f0618c..d16e3a9e 100644 --- a/test/helpers/getCodeFromSass.js +++ b/test/helpers/getCodeFromSass.js @@ -252,6 +252,9 @@ function getCodeFromSass(testId, options) { testFolder, 'node_modules/package-with-js-and-css-main-files/index' ); + const pathToLanguage = isSass + ? path.resolve(testFolder, 'sass/language.sass') + : path.resolve(testFolder, 'scss/language.scss'); // Pseudo importer for tests function testImporter(url) { @@ -710,6 +713,7 @@ function getCodeFromSass(testId, options) { /^~package-with-js-and-css-main-files/, pathToPackageWithJsAndCssMainFiles ) + .replace(/^file:language/, pathToLanguage) .replace(/^~/, testNodeModules); } diff --git a/test/loader.test.js b/test/loader.test.js index d1ead149..9ab9ed62 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -721,6 +721,30 @@ describe('loader', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); + // TODO need fix on webpack@5 side + it.skip(`should support resolving using the "file" schema (${implementationName}) (${syntax})`, async () => { + const testId = getTestId('import-file-scheme', syntax); + const options = { + implementation: getImplementationByName(implementationName), + }; + const compiler = getCompiler(testId, { + loader: { options }, + resolve: { + alias: { + '/language': path.resolve('./test', syntax, `language.${syntax}`), + }, + }, + }); + const stats = await compile(compiler); + const codeFromBundle = getCodeFromBundle(stats, compiler); + const codeFromSass = getCodeFromSass(testId, options); + + expect(codeFromBundle.css).toBe(codeFromSass.css); + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + if (implementation === dartSass) { it(`should output an understandable error with a problem in "@use" (${implementationName}) (${syntax})`, async () => { const testId = getTestId('error-use', syntax); diff --git a/test/sass/import-file-scheme.sass b/test/sass/import-file-scheme.sass new file mode 100644 index 00000000..48956979 --- /dev/null +++ b/test/sass/import-file-scheme.sass @@ -0,0 +1 @@ +@import 'file:language' diff --git a/test/scss/import-file-scheme.scss b/test/scss/import-file-scheme.scss new file mode 100644 index 00000000..c0c9950c --- /dev/null +++ b/test/scss/import-file-scheme.scss @@ -0,0 +1 @@ +@import 'file:language';