Skip to content

Commit

Permalink
Handle node module duplicate filename import resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
andygout committed Jul 20, 2024
1 parent dd41d2d commit 69df3ab
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 2 deletions.
39 changes: 38 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
8 changes: 8 additions & 0 deletions test/import-resolution/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.importable {
color: red;
}

.rollup .plugin .scss {
color: green;
user-select: none;
}
1 change: 1 addition & 0 deletions test/import-resolution/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('scss imported from native files and node_modules');
3 changes: 3 additions & 0 deletions test/import-resolution/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './input.scss'

console.log('scss imported from native files and node_modules')
10 changes: 10 additions & 0 deletions test/import-resolution/input.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import 'import-resolution-test/main';

.rollup {
.plugin {
.scss {
color: green;
user-select: none;
}
}
}
1 change: 1 addition & 0 deletions test/import-resolution/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// A .js file with the same name as the intended .scss file to be imported.
3 changes: 3 additions & 0 deletions test/import-resolution/main.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.importable {
color: red;
}
10 changes: 10 additions & 0 deletions test/import-resolution/rollup.config.js
Original file line number Diff line number Diff line change
@@ -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' })]
}
39 changes: 39 additions & 0 deletions test/import-resolution/setup.js
Original file line number Diff line number Diff line change
@@ -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);
}
}
)
16 changes: 16 additions & 0 deletions test/import-resolution/teardown.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
);

0 comments on commit 69df3ab

Please sign in to comment.