Skip to content

Commit bf6f203

Browse files
authored
Merge pull request #111 from andygout/handle-node-module-duplicate-filename-import-resolution
Handle node module duplicate filename import resolution
2 parents dd41d2d + 69df3ab commit bf6f203

File tree

11 files changed

+131
-2
lines changed

11 files changed

+131
-2
lines changed

index.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,47 @@ export default function scss(options: CSSPluginOptions = {}): Plugin {
135135
* The paths property tells `require.resolve()` where to begin
136136
* resolution (i.e. who is requesting the file). */
137137
try {
138-
const resolved = require.resolve(cleanUrl, {
138+
let resolved = require.resolve(cleanUrl, {
139139
paths: [prefix + scss]
140140
})
141141

142+
const allowedExtensions: string[] = ['.css', '.scss', '.sass'];
143+
144+
const resolvedHasAllowedExtension = allowedExtensions.some((allowedExtension: string) => {
145+
return resolved.endsWith(allowedExtension)
146+
});
147+
148+
/* It is possible that the `resolved` value is unintentionally
149+
* a path to an unintended file which shares the same name
150+
* but has a different file extension. */
151+
if (!resolvedHasAllowedExtension) {
152+
for (const [index, allowedExtension] of allowedExtensions.entries()) {
153+
try {
154+
/* Make an additional attempt to resolve the path by
155+
* specifying the file extension. */
156+
resolved = require.resolve(cleanUrl + allowedExtension, {
157+
paths: [prefix + scss]
158+
})
159+
160+
/* For the first file extension that allows the path to
161+
* be reolved, break out of the loop. */
162+
break
163+
/* If not the path could not be resolved with the
164+
* file extension. */
165+
} catch (e) {
166+
if (index < allowedExtensions.length - 1) {
167+
/* If not the final iteration then proceed
168+
* to the next. */
169+
continue
170+
} else {
171+
/* If the final iteration then re-throw the error
172+
* onto the next catch. */
173+
throw e
174+
}
175+
}
176+
}
177+
}
178+
142179
/* Since `require.resolve()` will throw an error if a file
143180
* doesn't exist. It's safe to assume the file exists and
144181
* pass it off to the sass compiler. */

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"test:postcss": "cd test/postcss && rm -f output.* && rollup -c && cmp output.js ../expected.js && cmp output.css expected.css && cd ../..",
1717
"test:processor": "cd test/processor && rm -f output.* && rollup -c && cmp output.js ../expected.js && cmp output.css expected.css && cd ../..",
1818
"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 ../..",
19-
"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",
19+
"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 ../..",
20+
"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",
2021
"testw": "cd test/node-sass && rm -f output.* && rollup -cw; cd ..",
2122
"prepare": "rollup -c"
2223
},
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.importable {
2+
color: red;
3+
}
4+
5+
.rollup .plugin .scss {
6+
color: green;
7+
user-select: none;
8+
}

test/import-resolution/expected.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('scss imported from native files and node_modules');

test/import-resolution/input.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import './input.scss'
2+
3+
console.log('scss imported from native files and node_modules')

test/import-resolution/input.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@import 'import-resolution-test/main';
2+
3+
.rollup {
4+
.plugin {
5+
.scss {
6+
color: green;
7+
user-select: none;
8+
}
9+
}
10+
}

test/import-resolution/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// A .js file with the same name as the intended .scss file to be imported.

test/import-resolution/main.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.importable {
2+
color: red;
3+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scss from '../../index.es.js'
2+
3+
export default {
4+
input: './input.js',
5+
output: {
6+
file: 'output.js',
7+
format: 'esm'
8+
},
9+
plugins: [scss({ fileName: 'output.css' })]
10+
}

test/import-resolution/setup.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const fs = require('node:fs');
2+
const path = require('node:path');
3+
4+
const NODE_MODULES_MOCK_PACKAGE_PATH = path.join(__dirname, '../../node_modules/import-resolution-test');
5+
const CURRENT_DIRECTORY_PATH = path.join(__dirname, '.');
6+
7+
const IMPORTABLE_FILE_NAME = 'main';
8+
const IMPORTABLE_JS_FILE_NAME = `${IMPORTABLE_FILE_NAME}.js`;
9+
const IMPORTABLE_SCSS_FILE_NAME = `${IMPORTABLE_FILE_NAME}.scss`;
10+
11+
/* If one does not already exist,
12+
* create a path to the node_modules mock package. */
13+
if (!fs.existsSync(NODE_MODULES_MOCK_PACKAGE_PATH)) {
14+
fs.mkdirSync(NODE_MODULES_MOCK_PACKAGE_PATH);
15+
}
16+
17+
/* Copy the specified file with the .js extension
18+
* to the node_modules mock package. */
19+
fs.copyFile(
20+
`${CURRENT_DIRECTORY_PATH}/${IMPORTABLE_JS_FILE_NAME}`,
21+
`${NODE_MODULES_MOCK_PACKAGE_PATH}/${IMPORTABLE_JS_FILE_NAME}`,
22+
error => {
23+
if (error) {
24+
console.log(error);
25+
}
26+
}
27+
)
28+
29+
/* Copy the specified file with the .scss extension
30+
* to the node_modules mock package. */
31+
fs.copyFile(
32+
`${CURRENT_DIRECTORY_PATH}/${IMPORTABLE_SCSS_FILE_NAME}`,
33+
`${NODE_MODULES_MOCK_PACKAGE_PATH}/${IMPORTABLE_SCSS_FILE_NAME}`,
34+
error => {
35+
if (error) {
36+
console.log(error);
37+
}
38+
}
39+
)

0 commit comments

Comments
 (0)