Repository: depcheck/depcheck
Pull request #658 (2021-07-31 22:55:21)

Fix scss parsing for non-import '@' keywords
It looked like the scss parser was treating everything with `@` as an import.

I'm not super familiar with scss, but I consulted this doc for what can be an import: https://sass-lang.com/documentation/at-rules

It looks to me that only `@use`, `@import`, and `@forward` should be treated as imports.

Relates to https://github.com/depcheck/depcheck/issues/653
And https://github.com/depcheck/depcheck/issues/627#issuecomment-808093756


Patch:
diff --git a/src/parser/sass.js b/src/parser/sass.js
index eb595cc..313789c 100644
--- a/src/parser/sass.js
+++ b/src/parser/sass.js
@@ -8,6 +8,7 @@ const createQueryWrapper = require('query-ast');
 const sass = require('sass');
 
 const IMPORT_RULE_TYPE = 'atrule';
+const IMPORT_KEYWORDS = ['import', 'use', 'forward'];
 
 function unixSlashes(packagePath) {
   return packagePath.replace(/\\/g, '/');
@@ -54,9 +55,11 @@ function parseSCSS(filename) {
   const fileContents = fs.readFileSync(filename).toString();
   const ast = parse(fileContents);
   const queryWrapper = createQueryWrapper(ast);
-  const imports = queryWrapper(IMPORT_RULE_TYPE).nodes.map(
-    (node) => node.children[2].node.value,
-  );
+  const imports = queryWrapper(IMPORT_RULE_TYPE)
+    .nodes.filter((node) =>
+      IMPORT_KEYWORDS.includes(node.children[0].node.value),
+    )
+    .map((node) => node.children[2].node.value);
 
   const result = lodash(imports)
     .filter((packagePath) => packagePath !== filename)
diff --git a/src/special/babel.js b/src/special/babel.js
index ef6165d..3ce7622 100755
--- a/src/special/babel.js
+++ b/src/special/babel.js
@@ -82,7 +82,7 @@ function checkOptions(deps, options = {}) {
   return optDeps.concat(envDeps);
 }
 
-const regex = /^(\.babelrc(\.(cjs|js(on)?))?|babel\.config\.(cjs|js(on)?))?$/;
+const regex = /^(\.babelrc|babelrc\.js|babel\.config\.js)?$/;
 
 export default async function parseBabel(filename, deps, rootDir) {
   const config = await loadConfig('babel', regex, filename, rootDir);
diff --git a/src/special/bin.js b/src/special/bin.js
index 423ef38..30ec637 100644
--- a/src/special/bin.js
+++ b/src/special/bin.js
@@ -30,8 +30,6 @@ function getBinaryFeatures(dep, [key, value]) {
     key,
     `--require ${key}`,
     `--require ${key}/register`,
-    `-r ${key}`,
-    `-r ${key}/register`,
     `$(npm bin)/${key}`,
     `node_modules/.bin/${key}`,
     `./node_modules/.bin/${key}`,


Test patch:
diff --git a/test/fake_modules/sass/scss2.scss b/test/fake_modules/sass/scss2.scss
index 76ba9fd..9637a73 100644
--- a/test/fake_modules/sass/scss2.scss
+++ b/test/fake_modules/sass/scss2.scss
@@ -1,7 +1,20 @@
 @use "@test-dep/aFile2";
 @use "mixin"; // should ignore...
 @use "sass:math"; // should show only sass...
+@import "@test-dep/aFile3";
+@forward "@test-dep/aFile4";
+
+%ignore-extend {
+  margin: 0;
+}
 
 div {
+  @extend %ignore-extend;
   font-size: $font-size;
 }
+
+@keyframes ignore-keyframes {
+  70% {
+    margin-left: 90%;
+  }
+}
diff --git a/test/spec.js b/test/spec.js
index e856579..32ef03b 100755
--- a/test/spec.js
+++ b/test/spec.js
@@ -232,6 +232,8 @@ export default [
       missing: {
         '@test-dep/aFile': ['sass2.sass'],
         '@test-dep/aFile2': ['scss2.scss'],
+        '@test-dep/aFile3': ['scss2.scss'],
+        '@test-dep/aFile4': ['scss2.scss'],
         sass: ['scss2.scss'],
       },
       using: {
@@ -243,6 +245,8 @@ export default [
         'scss-dep': ['scss.scss'],
         '@test-dep/aFile': ['sass2.sass'],
         '@test-dep/aFile2': ['scss2.scss'],
+        '@test-dep/aFile3': ['scss2.scss'],
+        '@test-dep/aFile4': ['scss2.scss'],
         sass: ['scss2.scss'],
       },
     },
diff --git a/test/special/babel.js b/test/special/babel.js
index f0af99d..222d8ee 100755
--- a/test/special/babel.js
+++ b/test/special/babel.js
@@ -155,7 +155,7 @@ describe('babel special parser', () => {
   });
 
   it('should detect babel.config.js', async () => {
-    const content = "module.exports = { presets: ['es2015'] }";
+    const content = "module.exports = { presets: ['es2015' ] }";
 
     const result = await testParser(content, '/path/to/babel.config.js', [
       'babel-preset-es2015',
@@ -164,26 +164,6 @@ describe('babel special parser', () => {
     result.should.deepEqual(['babel-preset-es2015']);
   });
 
-  it('should detect babel.config.cjs', async () => {
-    const content = "module.exports = { presets: ['es2015'] }";
-
-    const result = await testParser(content, '/path/to/babel.config.cjs', [
-      'babel-preset-es2015',
-      'dep',
-    ]);
-    result.should.deepEqual(['babel-preset-es2015']);
-  });
-
-  it('should detect babel.config.json', async () => {
-    const content = '{ "presets": ["es2015"] }';
-
-    const result = await testParser(content, '/path/to/babel.config.json', [
-      'babel-preset-es2015',
-      'dep',
-    ]);
-    result.should.deepEqual(['babel-preset-es2015']);
-  });
-
   testCases.forEach((testCase) =>
     it(`should ${testCase.name} in .babelrc file`, () =>
       testBabel('.babelrc', testCase.deps, testCase.options)),
diff --git a/test/special/bin.js b/test/special/bin.js
index fac1a8e..70d27ec 100644
--- a/test/special/bin.js
+++ b/test/special/bin.js
@@ -95,18 +95,6 @@ const testCases = [
     dependencies: ['binary-package'],
     expected: ['binary-package'],
   },
-  {
-    name: 'detect packages used with -r and /register',
-    script: 'module -r binary-entry/register',
-    dependencies: ['binary-package'],
-    expected: ['binary-package'],
-  },
-  {
-    name: 'detect packages used with -r',
-    script: 'module -r binary-entry',
-    dependencies: ['binary-package'],
-    expected: ['binary-package'],
-  },
   {
     name: 'detect packages with single binary',
     script: 'single-binary-package --argument',