deslop-js 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +199 -16
  2. package/dist/index.mjs +199 -16
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -167,6 +167,10 @@ const IMPLICIT_DEPENDENCIES = new Set([
167
167
  "lint-staged",
168
168
  "tslib",
169
169
  "@babel/core",
170
+ "@babel/runtime",
171
+ "babel-core",
172
+ "babel-jest",
173
+ "babel-loader",
170
174
  "postcss",
171
175
  "cross-env",
172
176
  "sass",
@@ -177,7 +181,22 @@ const IMPLICIT_DEPENDENCIES = new Set([
177
181
  "@biomejs/biome",
178
182
  "patch-package",
179
183
  "simple-git-hooks",
180
- "lefthook"
184
+ "lefthook",
185
+ "ts-node",
186
+ "ts-jest",
187
+ "tsx",
188
+ "jsdom",
189
+ "rimraf",
190
+ "concurrently",
191
+ "npm-run-all",
192
+ "npm-run-all2",
193
+ "dotenv-cli",
194
+ "webpack",
195
+ "rollup",
196
+ "terser",
197
+ "autoprefixer",
198
+ "tailwindcss",
199
+ "react-test-renderer"
181
200
  ]);
182
201
  const BUILTIN_MODULES = new Set([
183
202
  "assert",
@@ -4479,9 +4498,24 @@ const buildSourceTargetMap = (graph) => {
4479
4498
 
4480
4499
  //#endregion
4481
4500
  //#region src/report/files.ts
4482
- const isHtmlFile = (filePath) => {
4483
- return filePath.endsWith(".html");
4501
+ const EXCLUDED_EXTENSIONS = new Set([
4502
+ ".html",
4503
+ ".mdx",
4504
+ ".md",
4505
+ ".css",
4506
+ ".scss",
4507
+ ".less",
4508
+ ".sass",
4509
+ ".graphql",
4510
+ ".gql"
4511
+ ]);
4512
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story)\.|(?:^|\/)__tests__\/)/;
4513
+ const hasExcludedExtension = (filePath) => {
4514
+ const lastDot = filePath.lastIndexOf(".");
4515
+ if (lastDot === -1) return false;
4516
+ return EXCLUDED_EXTENSIONS.has(filePath.slice(lastDot));
4484
4517
  };
4518
+ const isTestFile = (filePath) => TEST_FILE_PATTERN.test(filePath);
4485
4519
  const detectOrphanFiles = (graph) => {
4486
4520
  const unusedFiles = [];
4487
4521
  for (const module of graph.modules) {
@@ -4489,7 +4523,8 @@ const detectOrphanFiles = (graph) => {
4489
4523
  if (module.isEntryPoint) continue;
4490
4524
  if (module.isDeclarationFile) continue;
4491
4525
  if (module.isConfigFile) continue;
4492
- if (isHtmlFile(module.fileId.path)) continue;
4526
+ if (hasExcludedExtension(module.fileId.path)) continue;
4527
+ if (isTestFile(module.fileId.path)) continue;
4493
4528
  if (isBarrelWithReachableSources(module, graph)) continue;
4494
4529
  if (hasReachableDirectImporter(module.fileId.index, graph)) continue;
4495
4530
  unusedFiles.push({ path: module.fileId.path });
@@ -4654,6 +4689,51 @@ const extractPackageName = (specifier) => {
4654
4689
 
4655
4690
  //#endregion
4656
4691
  //#region src/report/packages.ts
4692
+ const MONOREPO_ROOT_MARKERS = [
4693
+ "pnpm-workspace.yaml",
4694
+ "pnpm-workspace.yml",
4695
+ "lerna.json",
4696
+ "nx.json",
4697
+ "turbo.json",
4698
+ "rush.json"
4699
+ ];
4700
+ const LOCKFILE_MARKERS = [
4701
+ "pnpm-lock.yaml",
4702
+ "yarn.lock",
4703
+ "package-lock.json",
4704
+ "bun.lockb",
4705
+ "bun.lock"
4706
+ ];
4707
+ const MAX_MONOREPO_WALK_DEPTH = 5;
4708
+ const findMonorepoRoot = (rootDir) => {
4709
+ let currentDirectory = (0, node_path.resolve)(rootDir);
4710
+ let walkedDepth = 0;
4711
+ while (walkedDepth < MAX_MONOREPO_WALK_DEPTH) {
4712
+ const parentDirectory = (0, node_path.dirname)(currentDirectory);
4713
+ if (parentDirectory === currentDirectory) break;
4714
+ currentDirectory = parentDirectory;
4715
+ walkedDepth++;
4716
+ if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, ".git"))) {
4717
+ for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
4718
+ const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
4719
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
4720
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
4721
+ if (JSON.parse(content).workspaces) return currentDirectory;
4722
+ } catch {}
4723
+ for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
4724
+ return;
4725
+ }
4726
+ for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
4727
+ const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
4728
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
4729
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
4730
+ if (JSON.parse(content).workspaces) return currentDirectory;
4731
+ } catch {
4732
+ continue;
4733
+ }
4734
+ for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
4735
+ }
4736
+ };
4657
4737
  const discoverAllPackageJsonPaths = (rootDir) => {
4658
4738
  const paths = [(0, node_path.join)(rootDir, "package.json")];
4659
4739
  const workspacePackageJsons = fast_glob.default.sync("**/package.json", {
@@ -4687,30 +4767,52 @@ const detectStalePackages = (graph, config) => {
4687
4767
  for (const dependencyName of Object.keys(devDependencies)) declaredDependencies.set(dependencyName, true);
4688
4768
  const declaredNames = new Set(declaredDependencies.keys());
4689
4769
  const usedPackageNames = collectUsedPackages(graph);
4770
+ const monorepoRoot = findMonorepoRoot(config.rootDir);
4771
+ const nodeModulesRoot = monorepoRoot ?? config.rootDir;
4690
4772
  const allPackageJsonPaths = discoverAllPackageJsonPaths(config.rootDir);
4691
- const binToPackage = buildBinToPackageMap(config.rootDir, declaredNames);
4773
+ if (monorepoRoot) {
4774
+ const monorepoPackageJson = (0, node_path.join)(monorepoRoot, "package.json");
4775
+ if (!allPackageJsonPaths.includes(monorepoPackageJson) && (0, node_fs.existsSync)(monorepoPackageJson)) allPackageJsonPaths.push(monorepoPackageJson);
4776
+ }
4777
+ const binToPackage = buildBinToPackageMap(nodeModulesRoot, declaredNames);
4692
4778
  for (const workspacePackageJsonPath of allPackageJsonPaths) {
4693
4779
  const scriptReferenced = collectScriptReferencedPackages(workspacePackageJsonPath, declaredNames, binToPackage);
4694
4780
  for (const packageName of scriptReferenced) usedPackageNames.add(packageName);
4695
4781
  const packageJsonConfigReferenced = collectPackageJsonConfigReferences(workspacePackageJsonPath, declaredNames);
4696
4782
  for (const packageName of packageJsonConfigReferenced) usedPackageNames.add(packageName);
4697
4783
  }
4698
- const configReferenced = collectConfigReferencedPackages(config.rootDir, graph, declaredNames);
4699
- for (const packageName of configReferenced) usedPackageNames.add(packageName);
4700
- const tsconfigReferenced = collectTsconfigReferencedPackages(config.rootDir);
4701
- for (const packageName of tsconfigReferenced) usedPackageNames.add(packageName);
4784
+ const configSearchRoots = monorepoRoot && monorepoRoot !== config.rootDir ? [config.rootDir, monorepoRoot] : [config.rootDir];
4785
+ for (const configSearchRoot of configSearchRoots) {
4786
+ const configReferenced = collectConfigReferencedPackages(configSearchRoot, graph, declaredNames);
4787
+ for (const packageName of configReferenced) usedPackageNames.add(packageName);
4788
+ const tsconfigReferenced = collectTsconfigReferencedPackages(configSearchRoot);
4789
+ for (const packageName of tsconfigReferenced) usedPackageNames.add(packageName);
4790
+ }
4702
4791
  if (hasJsxFiles(graph)) {
4703
4792
  if (declaredNames.has("react")) usedPackageNames.add("react");
4704
4793
  if (declaredNames.has("react-dom")) usedPackageNames.add("react-dom");
4705
4794
  if (declaredNames.has("react-native")) usedPackageNames.add("react-native");
4706
4795
  if (declaredNames.has("react-native-web")) usedPackageNames.add("react-native-web");
4707
4796
  }
4708
- const peerSatisfied = collectPeerSatisfiedPackages(config.rootDir, declaredNames, usedPackageNames);
4797
+ const peerSatisfied = collectPeerSatisfiedPackages(nodeModulesRoot, declaredNames, usedPackageNames);
4709
4798
  for (const packageName of peerSatisfied) usedPackageNames.add(packageName);
4710
- const unusedDependencies = [];
4711
- for (const [dependencyName, isDevDependency] of declaredDependencies) {
4799
+ const candidateUnused = /* @__PURE__ */ new Set();
4800
+ for (const [dependencyName] of declaredDependencies) {
4712
4801
  if (isAlwaysConsideredUsed(dependencyName)) continue;
4713
- if (!usedPackageNames.has(dependencyName)) unusedDependencies.push({
4802
+ if (usedPackageNames.has(dependencyName)) continue;
4803
+ candidateUnused.add(dependencyName);
4804
+ }
4805
+ if (candidateUnused.size > 0) {
4806
+ const sourceFileRescued = scanSourceFilesForPackageImports(config.rootDir, candidateUnused);
4807
+ for (const packageName of sourceFileRescued) {
4808
+ usedPackageNames.add(packageName);
4809
+ candidateUnused.delete(packageName);
4810
+ }
4811
+ }
4812
+ const unusedDependencies = [];
4813
+ for (const dependencyName of candidateUnused) {
4814
+ const isDevDependency = declaredDependencies.get(dependencyName) ?? false;
4815
+ unusedDependencies.push({
4714
4816
  name: dependencyName,
4715
4817
  isDevDependency
4716
4818
  });
@@ -4840,9 +4942,12 @@ const CONFIG_FILE_GLOBS = [
4840
4942
  "eslint.config.{js,cjs,mjs,ts,mts,cts}",
4841
4943
  "webpack.config.{js,ts,mjs,cjs}",
4842
4944
  "**/webpack*.config.{js,ts,mjs,cjs}",
4945
+ "**/webpack*.config*.{js,ts,mjs,cjs}",
4946
+ "**/webpack*.babel.{js,ts}",
4843
4947
  "vite.config.{js,ts,mjs,mts}",
4844
4948
  "rollup.config.{js,ts,mjs,cjs}",
4845
4949
  ".storybook/main.{js,ts,mjs,cjs}",
4950
+ ".storybook/preview.{js,ts,mjs,cjs,tsx,jsx}",
4846
4951
  "docusaurus.config.{js,ts,mjs}",
4847
4952
  "next.config.{js,ts,mjs,mts}",
4848
4953
  "tailwind.config.{js,ts,cjs,mjs}",
@@ -4855,7 +4960,14 @@ const CONFIG_FILE_GLOBS = [
4855
4960
  "wrangler.jsonc",
4856
4961
  "metro.config.{js,ts}",
4857
4962
  "electron.vite.config.{js,ts,mjs}",
4858
- "api-extractor.json"
4963
+ "api-extractor.json",
4964
+ "codegen.{ts,js,yml,yaml}",
4965
+ ".graphqlrc.{ts,js,json,yml,yaml}",
4966
+ "graphql.config.{ts,js,json,yml,yaml}",
4967
+ ".lintstagedrc.{js,cjs,mjs,json}",
4968
+ "commitlint.config.{js,cjs,mjs,ts}",
4969
+ ".commitlintrc.{js,cjs,mjs,json,yaml,yml}",
4970
+ "tslint.json"
4859
4971
  ];
4860
4972
  const collectConfigReferencedPackages = (rootDir, graph, declaredNames) => {
4861
4973
  const referenced = /* @__PURE__ */ new Set();
@@ -4963,6 +5075,41 @@ const extractExtendsPackageName = (extendsValue) => {
4963
5075
  }
4964
5076
  return extendsValue.split("/")[0];
4965
5077
  };
5078
+ const SOURCE_FILE_GLOBS = ["**/*.{ts,tsx,js,jsx,mts,mjs,cts,cjs}"];
5079
+ const SOURCE_FILE_IGNORES = [
5080
+ "**/node_modules/**",
5081
+ "**/dist/**",
5082
+ "**/build/**",
5083
+ "**/out/**",
5084
+ "**/.git/**",
5085
+ "**/coverage/**",
5086
+ "**/*.min.js",
5087
+ "**/*.d.ts"
5088
+ ];
5089
+ const scanSourceFilesForPackageImports = (rootDir, candidatePackages) => {
5090
+ const found = /* @__PURE__ */ new Set();
5091
+ if (candidatePackages.size === 0) return found;
5092
+ const sourceFiles = fast_glob.default.sync(SOURCE_FILE_GLOBS, {
5093
+ cwd: rootDir,
5094
+ absolute: true,
5095
+ onlyFiles: true,
5096
+ ignore: SOURCE_FILE_IGNORES,
5097
+ deep: 15
5098
+ });
5099
+ for (const filePath of sourceFiles) {
5100
+ if (candidatePackages.size === 0) break;
5101
+ try {
5102
+ const content = (0, node_fs.readFileSync)(filePath, "utf-8");
5103
+ for (const packageName of candidatePackages) if (content.includes(`'${packageName}'`) || content.includes(`"${packageName}"`) || content.includes(`'${packageName}/`) || content.includes(`"${packageName}/`)) {
5104
+ found.add(packageName);
5105
+ candidatePackages.delete(packageName);
5106
+ }
5107
+ } catch {
5108
+ continue;
5109
+ }
5110
+ }
5111
+ return found;
5112
+ };
4966
5113
  const ALWAYS_USED_PREFIXES = [
4967
5114
  "@types/",
4968
5115
  "eslint-config-",
@@ -4979,11 +5126,47 @@ const ALWAYS_USED_PREFIXES = [
4979
5126
  "@svgr/",
4980
5127
  "@docusaurus/",
4981
5128
  "stylelint-config-",
4982
- "stylelint-plugin-"
5129
+ "stylelint-plugin-",
5130
+ "@testing-library/",
5131
+ "@vitest/",
5132
+ "@playwright/",
5133
+ "@storybook/",
5134
+ "jest-environment-",
5135
+ "@graphql-codegen/",
5136
+ "@size-limit/",
5137
+ "@nestjs/",
5138
+ "@swc/",
5139
+ "@electron-forge/",
5140
+ "@parcel/",
5141
+ "@wyw-in-js/",
5142
+ "@typescript-eslint/",
5143
+ "@react-native/",
5144
+ "@react-native-community/",
5145
+ "postcss-",
5146
+ "@tailwindcss/",
5147
+ "rollup-plugin-",
5148
+ "vite-plugin-",
5149
+ "@vitejs/",
5150
+ "webpack-",
5151
+ "esbuild-",
5152
+ "@esbuild-plugins/",
5153
+ "@lingui/",
5154
+ "@emotion/",
5155
+ "tslint-config-",
5156
+ "eslint-import-resolver-",
5157
+ "@changesets/",
5158
+ "@react-navigation/",
5159
+ "@vercel/",
5160
+ "@expo/",
5161
+ "expo-",
5162
+ "react-native-"
4983
5163
  ];
5164
+ const ALWAYS_USED_SUFFIXES = ["-loader"];
4984
5165
  const isAlwaysConsideredUsed = (dependencyName) => {
4985
5166
  if (IMPLICIT_DEPENDENCIES.has(dependencyName)) return true;
4986
- return ALWAYS_USED_PREFIXES.some((prefix) => dependencyName.startsWith(prefix));
5167
+ if (ALWAYS_USED_PREFIXES.some((prefix) => dependencyName.startsWith(prefix))) return true;
5168
+ if (ALWAYS_USED_SUFFIXES.some((suffix) => dependencyName.endsWith(suffix))) return true;
5169
+ return false;
4987
5170
  };
4988
5171
 
4989
5172
  //#endregion
package/dist/index.mjs CHANGED
@@ -137,6 +137,10 @@ const IMPLICIT_DEPENDENCIES = new Set([
137
137
  "lint-staged",
138
138
  "tslib",
139
139
  "@babel/core",
140
+ "@babel/runtime",
141
+ "babel-core",
142
+ "babel-jest",
143
+ "babel-loader",
140
144
  "postcss",
141
145
  "cross-env",
142
146
  "sass",
@@ -147,7 +151,22 @@ const IMPLICIT_DEPENDENCIES = new Set([
147
151
  "@biomejs/biome",
148
152
  "patch-package",
149
153
  "simple-git-hooks",
150
- "lefthook"
154
+ "lefthook",
155
+ "ts-node",
156
+ "ts-jest",
157
+ "tsx",
158
+ "jsdom",
159
+ "rimraf",
160
+ "concurrently",
161
+ "npm-run-all",
162
+ "npm-run-all2",
163
+ "dotenv-cli",
164
+ "webpack",
165
+ "rollup",
166
+ "terser",
167
+ "autoprefixer",
168
+ "tailwindcss",
169
+ "react-test-renderer"
151
170
  ]);
152
171
  const BUILTIN_MODULES = new Set([
153
172
  "assert",
@@ -4449,9 +4468,24 @@ const buildSourceTargetMap = (graph) => {
4449
4468
 
4450
4469
  //#endregion
4451
4470
  //#region src/report/files.ts
4452
- const isHtmlFile = (filePath) => {
4453
- return filePath.endsWith(".html");
4471
+ const EXCLUDED_EXTENSIONS = new Set([
4472
+ ".html",
4473
+ ".mdx",
4474
+ ".md",
4475
+ ".css",
4476
+ ".scss",
4477
+ ".less",
4478
+ ".sass",
4479
+ ".graphql",
4480
+ ".gql"
4481
+ ]);
4482
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story)\.|(?:^|\/)__tests__\/)/;
4483
+ const hasExcludedExtension = (filePath) => {
4484
+ const lastDot = filePath.lastIndexOf(".");
4485
+ if (lastDot === -1) return false;
4486
+ return EXCLUDED_EXTENSIONS.has(filePath.slice(lastDot));
4454
4487
  };
4488
+ const isTestFile = (filePath) => TEST_FILE_PATTERN.test(filePath);
4455
4489
  const detectOrphanFiles = (graph) => {
4456
4490
  const unusedFiles = [];
4457
4491
  for (const module of graph.modules) {
@@ -4459,7 +4493,8 @@ const detectOrphanFiles = (graph) => {
4459
4493
  if (module.isEntryPoint) continue;
4460
4494
  if (module.isDeclarationFile) continue;
4461
4495
  if (module.isConfigFile) continue;
4462
- if (isHtmlFile(module.fileId.path)) continue;
4496
+ if (hasExcludedExtension(module.fileId.path)) continue;
4497
+ if (isTestFile(module.fileId.path)) continue;
4463
4498
  if (isBarrelWithReachableSources(module, graph)) continue;
4464
4499
  if (hasReachableDirectImporter(module.fileId.index, graph)) continue;
4465
4500
  unusedFiles.push({ path: module.fileId.path });
@@ -4624,6 +4659,51 @@ const extractPackageName = (specifier) => {
4624
4659
 
4625
4660
  //#endregion
4626
4661
  //#region src/report/packages.ts
4662
+ const MONOREPO_ROOT_MARKERS = [
4663
+ "pnpm-workspace.yaml",
4664
+ "pnpm-workspace.yml",
4665
+ "lerna.json",
4666
+ "nx.json",
4667
+ "turbo.json",
4668
+ "rush.json"
4669
+ ];
4670
+ const LOCKFILE_MARKERS = [
4671
+ "pnpm-lock.yaml",
4672
+ "yarn.lock",
4673
+ "package-lock.json",
4674
+ "bun.lockb",
4675
+ "bun.lock"
4676
+ ];
4677
+ const MAX_MONOREPO_WALK_DEPTH = 5;
4678
+ const findMonorepoRoot = (rootDir) => {
4679
+ let currentDirectory = resolve(rootDir);
4680
+ let walkedDepth = 0;
4681
+ while (walkedDepth < MAX_MONOREPO_WALK_DEPTH) {
4682
+ const parentDirectory = dirname(currentDirectory);
4683
+ if (parentDirectory === currentDirectory) break;
4684
+ currentDirectory = parentDirectory;
4685
+ walkedDepth++;
4686
+ if (existsSync(join(currentDirectory, ".git"))) {
4687
+ for (const marker of MONOREPO_ROOT_MARKERS) if (existsSync(join(currentDirectory, marker))) return currentDirectory;
4688
+ const packageJsonPath = join(currentDirectory, "package.json");
4689
+ if (existsSync(packageJsonPath)) try {
4690
+ const content = readFileSync(packageJsonPath, "utf-8");
4691
+ if (JSON.parse(content).workspaces) return currentDirectory;
4692
+ } catch {}
4693
+ for (const lockfile of LOCKFILE_MARKERS) if (existsSync(join(currentDirectory, lockfile))) return currentDirectory;
4694
+ return;
4695
+ }
4696
+ for (const marker of MONOREPO_ROOT_MARKERS) if (existsSync(join(currentDirectory, marker))) return currentDirectory;
4697
+ const packageJsonPath = join(currentDirectory, "package.json");
4698
+ if (existsSync(packageJsonPath)) try {
4699
+ const content = readFileSync(packageJsonPath, "utf-8");
4700
+ if (JSON.parse(content).workspaces) return currentDirectory;
4701
+ } catch {
4702
+ continue;
4703
+ }
4704
+ for (const lockfile of LOCKFILE_MARKERS) if (existsSync(join(currentDirectory, lockfile))) return currentDirectory;
4705
+ }
4706
+ };
4627
4707
  const discoverAllPackageJsonPaths = (rootDir) => {
4628
4708
  const paths = [join(rootDir, "package.json")];
4629
4709
  const workspacePackageJsons = fg.sync("**/package.json", {
@@ -4657,30 +4737,52 @@ const detectStalePackages = (graph, config) => {
4657
4737
  for (const dependencyName of Object.keys(devDependencies)) declaredDependencies.set(dependencyName, true);
4658
4738
  const declaredNames = new Set(declaredDependencies.keys());
4659
4739
  const usedPackageNames = collectUsedPackages(graph);
4740
+ const monorepoRoot = findMonorepoRoot(config.rootDir);
4741
+ const nodeModulesRoot = monorepoRoot ?? config.rootDir;
4660
4742
  const allPackageJsonPaths = discoverAllPackageJsonPaths(config.rootDir);
4661
- const binToPackage = buildBinToPackageMap(config.rootDir, declaredNames);
4743
+ if (monorepoRoot) {
4744
+ const monorepoPackageJson = join(monorepoRoot, "package.json");
4745
+ if (!allPackageJsonPaths.includes(monorepoPackageJson) && existsSync(monorepoPackageJson)) allPackageJsonPaths.push(monorepoPackageJson);
4746
+ }
4747
+ const binToPackage = buildBinToPackageMap(nodeModulesRoot, declaredNames);
4662
4748
  for (const workspacePackageJsonPath of allPackageJsonPaths) {
4663
4749
  const scriptReferenced = collectScriptReferencedPackages(workspacePackageJsonPath, declaredNames, binToPackage);
4664
4750
  for (const packageName of scriptReferenced) usedPackageNames.add(packageName);
4665
4751
  const packageJsonConfigReferenced = collectPackageJsonConfigReferences(workspacePackageJsonPath, declaredNames);
4666
4752
  for (const packageName of packageJsonConfigReferenced) usedPackageNames.add(packageName);
4667
4753
  }
4668
- const configReferenced = collectConfigReferencedPackages(config.rootDir, graph, declaredNames);
4669
- for (const packageName of configReferenced) usedPackageNames.add(packageName);
4670
- const tsconfigReferenced = collectTsconfigReferencedPackages(config.rootDir);
4671
- for (const packageName of tsconfigReferenced) usedPackageNames.add(packageName);
4754
+ const configSearchRoots = monorepoRoot && monorepoRoot !== config.rootDir ? [config.rootDir, monorepoRoot] : [config.rootDir];
4755
+ for (const configSearchRoot of configSearchRoots) {
4756
+ const configReferenced = collectConfigReferencedPackages(configSearchRoot, graph, declaredNames);
4757
+ for (const packageName of configReferenced) usedPackageNames.add(packageName);
4758
+ const tsconfigReferenced = collectTsconfigReferencedPackages(configSearchRoot);
4759
+ for (const packageName of tsconfigReferenced) usedPackageNames.add(packageName);
4760
+ }
4672
4761
  if (hasJsxFiles(graph)) {
4673
4762
  if (declaredNames.has("react")) usedPackageNames.add("react");
4674
4763
  if (declaredNames.has("react-dom")) usedPackageNames.add("react-dom");
4675
4764
  if (declaredNames.has("react-native")) usedPackageNames.add("react-native");
4676
4765
  if (declaredNames.has("react-native-web")) usedPackageNames.add("react-native-web");
4677
4766
  }
4678
- const peerSatisfied = collectPeerSatisfiedPackages(config.rootDir, declaredNames, usedPackageNames);
4767
+ const peerSatisfied = collectPeerSatisfiedPackages(nodeModulesRoot, declaredNames, usedPackageNames);
4679
4768
  for (const packageName of peerSatisfied) usedPackageNames.add(packageName);
4680
- const unusedDependencies = [];
4681
- for (const [dependencyName, isDevDependency] of declaredDependencies) {
4769
+ const candidateUnused = /* @__PURE__ */ new Set();
4770
+ for (const [dependencyName] of declaredDependencies) {
4682
4771
  if (isAlwaysConsideredUsed(dependencyName)) continue;
4683
- if (!usedPackageNames.has(dependencyName)) unusedDependencies.push({
4772
+ if (usedPackageNames.has(dependencyName)) continue;
4773
+ candidateUnused.add(dependencyName);
4774
+ }
4775
+ if (candidateUnused.size > 0) {
4776
+ const sourceFileRescued = scanSourceFilesForPackageImports(config.rootDir, candidateUnused);
4777
+ for (const packageName of sourceFileRescued) {
4778
+ usedPackageNames.add(packageName);
4779
+ candidateUnused.delete(packageName);
4780
+ }
4781
+ }
4782
+ const unusedDependencies = [];
4783
+ for (const dependencyName of candidateUnused) {
4784
+ const isDevDependency = declaredDependencies.get(dependencyName) ?? false;
4785
+ unusedDependencies.push({
4684
4786
  name: dependencyName,
4685
4787
  isDevDependency
4686
4788
  });
@@ -4810,9 +4912,12 @@ const CONFIG_FILE_GLOBS = [
4810
4912
  "eslint.config.{js,cjs,mjs,ts,mts,cts}",
4811
4913
  "webpack.config.{js,ts,mjs,cjs}",
4812
4914
  "**/webpack*.config.{js,ts,mjs,cjs}",
4915
+ "**/webpack*.config*.{js,ts,mjs,cjs}",
4916
+ "**/webpack*.babel.{js,ts}",
4813
4917
  "vite.config.{js,ts,mjs,mts}",
4814
4918
  "rollup.config.{js,ts,mjs,cjs}",
4815
4919
  ".storybook/main.{js,ts,mjs,cjs}",
4920
+ ".storybook/preview.{js,ts,mjs,cjs,tsx,jsx}",
4816
4921
  "docusaurus.config.{js,ts,mjs}",
4817
4922
  "next.config.{js,ts,mjs,mts}",
4818
4923
  "tailwind.config.{js,ts,cjs,mjs}",
@@ -4825,7 +4930,14 @@ const CONFIG_FILE_GLOBS = [
4825
4930
  "wrangler.jsonc",
4826
4931
  "metro.config.{js,ts}",
4827
4932
  "electron.vite.config.{js,ts,mjs}",
4828
- "api-extractor.json"
4933
+ "api-extractor.json",
4934
+ "codegen.{ts,js,yml,yaml}",
4935
+ ".graphqlrc.{ts,js,json,yml,yaml}",
4936
+ "graphql.config.{ts,js,json,yml,yaml}",
4937
+ ".lintstagedrc.{js,cjs,mjs,json}",
4938
+ "commitlint.config.{js,cjs,mjs,ts}",
4939
+ ".commitlintrc.{js,cjs,mjs,json,yaml,yml}",
4940
+ "tslint.json"
4829
4941
  ];
4830
4942
  const collectConfigReferencedPackages = (rootDir, graph, declaredNames) => {
4831
4943
  const referenced = /* @__PURE__ */ new Set();
@@ -4933,6 +5045,41 @@ const extractExtendsPackageName = (extendsValue) => {
4933
5045
  }
4934
5046
  return extendsValue.split("/")[0];
4935
5047
  };
5048
+ const SOURCE_FILE_GLOBS = ["**/*.{ts,tsx,js,jsx,mts,mjs,cts,cjs}"];
5049
+ const SOURCE_FILE_IGNORES = [
5050
+ "**/node_modules/**",
5051
+ "**/dist/**",
5052
+ "**/build/**",
5053
+ "**/out/**",
5054
+ "**/.git/**",
5055
+ "**/coverage/**",
5056
+ "**/*.min.js",
5057
+ "**/*.d.ts"
5058
+ ];
5059
+ const scanSourceFilesForPackageImports = (rootDir, candidatePackages) => {
5060
+ const found = /* @__PURE__ */ new Set();
5061
+ if (candidatePackages.size === 0) return found;
5062
+ const sourceFiles = fg.sync(SOURCE_FILE_GLOBS, {
5063
+ cwd: rootDir,
5064
+ absolute: true,
5065
+ onlyFiles: true,
5066
+ ignore: SOURCE_FILE_IGNORES,
5067
+ deep: 15
5068
+ });
5069
+ for (const filePath of sourceFiles) {
5070
+ if (candidatePackages.size === 0) break;
5071
+ try {
5072
+ const content = readFileSync(filePath, "utf-8");
5073
+ for (const packageName of candidatePackages) if (content.includes(`'${packageName}'`) || content.includes(`"${packageName}"`) || content.includes(`'${packageName}/`) || content.includes(`"${packageName}/`)) {
5074
+ found.add(packageName);
5075
+ candidatePackages.delete(packageName);
5076
+ }
5077
+ } catch {
5078
+ continue;
5079
+ }
5080
+ }
5081
+ return found;
5082
+ };
4936
5083
  const ALWAYS_USED_PREFIXES = [
4937
5084
  "@types/",
4938
5085
  "eslint-config-",
@@ -4949,11 +5096,47 @@ const ALWAYS_USED_PREFIXES = [
4949
5096
  "@svgr/",
4950
5097
  "@docusaurus/",
4951
5098
  "stylelint-config-",
4952
- "stylelint-plugin-"
5099
+ "stylelint-plugin-",
5100
+ "@testing-library/",
5101
+ "@vitest/",
5102
+ "@playwright/",
5103
+ "@storybook/",
5104
+ "jest-environment-",
5105
+ "@graphql-codegen/",
5106
+ "@size-limit/",
5107
+ "@nestjs/",
5108
+ "@swc/",
5109
+ "@electron-forge/",
5110
+ "@parcel/",
5111
+ "@wyw-in-js/",
5112
+ "@typescript-eslint/",
5113
+ "@react-native/",
5114
+ "@react-native-community/",
5115
+ "postcss-",
5116
+ "@tailwindcss/",
5117
+ "rollup-plugin-",
5118
+ "vite-plugin-",
5119
+ "@vitejs/",
5120
+ "webpack-",
5121
+ "esbuild-",
5122
+ "@esbuild-plugins/",
5123
+ "@lingui/",
5124
+ "@emotion/",
5125
+ "tslint-config-",
5126
+ "eslint-import-resolver-",
5127
+ "@changesets/",
5128
+ "@react-navigation/",
5129
+ "@vercel/",
5130
+ "@expo/",
5131
+ "expo-",
5132
+ "react-native-"
4953
5133
  ];
5134
+ const ALWAYS_USED_SUFFIXES = ["-loader"];
4954
5135
  const isAlwaysConsideredUsed = (dependencyName) => {
4955
5136
  if (IMPLICIT_DEPENDENCIES.has(dependencyName)) return true;
4956
- return ALWAYS_USED_PREFIXES.some((prefix) => dependencyName.startsWith(prefix));
5137
+ if (ALWAYS_USED_PREFIXES.some((prefix) => dependencyName.startsWith(prefix))) return true;
5138
+ if (ALWAYS_USED_SUFFIXES.some((suffix) => dependencyName.endsWith(suffix))) return true;
5139
+ return false;
4957
5140
  };
4958
5141
 
4959
5142
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deslop-js",
3
- "version": "0.0.3",
3
+ "version": "0.0.6",
4
4
  "description": "Deslop JavaScript code",
5
5
  "keywords": [
6
6
  "dead-code",