diff --git a/index.ts b/index.ts index 043fc9e..efed9d6 100644 --- a/index.ts +++ b/index.ts @@ -135,10 +135,47 @@ export default function scss(options: CSSPluginOptions = {}): Plugin { * The paths property tells `require.resolve()` where to begin * resolution (i.e. who is requesting the file). */ try { - const resolved = require.resolve(cleanUrl, { + let resolved = require.resolve(cleanUrl, { paths: [prefix + scss] }) + const allowedExtensions: string[] = ['.css', '.scss', '.sass']; + + const resolvedHasAllowedExtension = allowedExtensions.some((allowedExtension: string) => { + return resolved.endsWith(allowedExtension) + }); + + /* It is possible that the `resolved` value is unintentionally + * a path to an unintended file which shares the same name + * but has a different file extension. */ + if (!resolvedHasAllowedExtension) { + for (const [index, allowedExtension] of allowedExtensions.entries()) { + try { + /* Make an additional attempt to resolve the path by + * specifying the file extension. */ + resolved = require.resolve(cleanUrl + allowedExtension, { + paths: [prefix + scss] + }) + + /* For the first file extension that allows the path to + * be reolved, break out of the loop. */ + break + /* If not the path could not be resolved with the + * file extension. */ + } catch (e) { + if (index < allowedExtensions.length - 1) { + /* If not the final iteration then proceed + * to the next. */ + continue + } else { + /* If the final iteration then re-throw the error + * onto the next catch. */ + throw e + } + } + } + } + /* Since `require.resolve()` will throw an error if a file * doesn't exist. It's safe to assume the file exists and * pass it off to the sass compiler. */ diff --git a/package.json b/package.json index 6ecb3a7..d649c5e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "test:postcss": "cd test/postcss && rm -f output.* && rollup -c && cmp output.js ../expected.js && cmp output.css expected.css && cd ../..", "test:processor": "cd test/processor && rm -f output.* && rollup -c && cmp output.js ../expected.js && cmp output.css expected.css && cd ../..", "test:sourcemap": "cd test/sourcemap && rm -f output.* && rollup -c && cmp output.js ../expected.js && cmp nested/output.css expected.css && cmp nested/output.css.map expected.css.map && cd ../..", - "test": "npm run test:node-sass && npm run test:sass && npm run test:processor && npm run test:postcss && npm run test:sourcemap && npm run test:insert", + "test:import-resolution": "cd test/import-resolution && node setup.js && rm -f output.* && rollup -c && node teardown.js && cmp output.js expected.js && cmp output.css expected.css && cd ../..", + "test": "npm run test:node-sass && npm run test:sass && npm run test:processor && npm run test:postcss && npm run test:sourcemap && npm run test:insert && npm run test:import-resolution", "testw": "cd test/node-sass && rm -f output.* && rollup -cw; cd ..", "prepare": "rollup -c" }, diff --git a/test/import-resolution/expected.css b/test/import-resolution/expected.css new file mode 100644 index 0000000..479394d --- /dev/null +++ b/test/import-resolution/expected.css @@ -0,0 +1,8 @@ +.importable { + color: red; +} + +.rollup .plugin .scss { + color: green; + user-select: none; +} \ No newline at end of file diff --git a/test/import-resolution/expected.js b/test/import-resolution/expected.js new file mode 100644 index 0000000..cc6784a --- /dev/null +++ b/test/import-resolution/expected.js @@ -0,0 +1 @@ +console.log('scss imported from native files and node_modules'); diff --git a/test/import-resolution/input.js b/test/import-resolution/input.js new file mode 100644 index 0000000..5b751fe --- /dev/null +++ b/test/import-resolution/input.js @@ -0,0 +1,3 @@ +import './input.scss' + +console.log('scss imported from native files and node_modules') diff --git a/test/import-resolution/input.scss b/test/import-resolution/input.scss new file mode 100644 index 0000000..dd27407 --- /dev/null +++ b/test/import-resolution/input.scss @@ -0,0 +1,10 @@ +@import 'import-resolution-test/main'; + +.rollup { + .plugin { + .scss { + color: green; + user-select: none; + } + } +} diff --git a/test/import-resolution/main.js b/test/import-resolution/main.js new file mode 100644 index 0000000..d630036 --- /dev/null +++ b/test/import-resolution/main.js @@ -0,0 +1 @@ +// A .js file with the same name as the intended .scss file to be imported. diff --git a/test/import-resolution/main.scss b/test/import-resolution/main.scss new file mode 100644 index 0000000..c4c8143 --- /dev/null +++ b/test/import-resolution/main.scss @@ -0,0 +1,3 @@ +.importable { + color: red; +} diff --git a/test/import-resolution/rollup.config.js b/test/import-resolution/rollup.config.js new file mode 100644 index 0000000..50cec43 --- /dev/null +++ b/test/import-resolution/rollup.config.js @@ -0,0 +1,10 @@ +import scss from '../../index.es.js' + +export default { + input: './input.js', + output: { + file: 'output.js', + format: 'esm' + }, + plugins: [scss({ fileName: 'output.css' })] +} diff --git a/test/import-resolution/setup.js b/test/import-resolution/setup.js new file mode 100644 index 0000000..c409561 --- /dev/null +++ b/test/import-resolution/setup.js @@ -0,0 +1,39 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const NODE_MODULES_MOCK_PACKAGE_PATH = path.join(__dirname, '../../node_modules/import-resolution-test'); +const CURRENT_DIRECTORY_PATH = path.join(__dirname, '.'); + +const IMPORTABLE_FILE_NAME = 'main'; +const IMPORTABLE_JS_FILE_NAME = `${IMPORTABLE_FILE_NAME}.js`; +const IMPORTABLE_SCSS_FILE_NAME = `${IMPORTABLE_FILE_NAME}.scss`; + +/* If one does not already exist, + * create a path to the node_modules mock package. */ +if (!fs.existsSync(NODE_MODULES_MOCK_PACKAGE_PATH)) { + fs.mkdirSync(NODE_MODULES_MOCK_PACKAGE_PATH); +} + +/* Copy the specified file with the .js extension + * to the node_modules mock package. */ +fs.copyFile( + `${CURRENT_DIRECTORY_PATH}/${IMPORTABLE_JS_FILE_NAME}`, + `${NODE_MODULES_MOCK_PACKAGE_PATH}/${IMPORTABLE_JS_FILE_NAME}`, + error => { + if (error) { + console.log(error); + } + } +) + +/* Copy the specified file with the .scss extension + * to the node_modules mock package. */ +fs.copyFile( + `${CURRENT_DIRECTORY_PATH}/${IMPORTABLE_SCSS_FILE_NAME}`, + `${NODE_MODULES_MOCK_PACKAGE_PATH}/${IMPORTABLE_SCSS_FILE_NAME}`, + error => { + if (error) { + console.log(error); + } + } +) \ No newline at end of file diff --git a/test/import-resolution/teardown.js b/test/import-resolution/teardown.js new file mode 100644 index 0000000..ecf542f --- /dev/null +++ b/test/import-resolution/teardown.js @@ -0,0 +1,16 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const NODE_MODULES_MOCK_PACKAGE_PATH = path.join(__dirname, '../../node_modules/import-resolution-test'); + +/* Now that it has served its purpose, + * remove the node_modules mock package. */ +fs.rm( + NODE_MODULES_MOCK_PACKAGE_PATH, + { recursive: true, force: true }, + error => { + if (error) { + throw error; + } + } +); \ No newline at end of file