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.
- package/dist/index.cjs +199 -16
- package/dist/index.mjs +199 -16
- 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
|
|
4483
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
4699
|
-
for (const
|
|
4700
|
-
|
|
4701
|
-
|
|
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(
|
|
4797
|
+
const peerSatisfied = collectPeerSatisfiedPackages(nodeModulesRoot, declaredNames, usedPackageNames);
|
|
4709
4798
|
for (const packageName of peerSatisfied) usedPackageNames.add(packageName);
|
|
4710
|
-
const
|
|
4711
|
-
for (const [dependencyName
|
|
4799
|
+
const candidateUnused = /* @__PURE__ */ new Set();
|
|
4800
|
+
for (const [dependencyName] of declaredDependencies) {
|
|
4712
4801
|
if (isAlwaysConsideredUsed(dependencyName)) continue;
|
|
4713
|
-
if (
|
|
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
|
-
|
|
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
|
|
4453
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
4669
|
-
for (const
|
|
4670
|
-
|
|
4671
|
-
|
|
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(
|
|
4767
|
+
const peerSatisfied = collectPeerSatisfiedPackages(nodeModulesRoot, declaredNames, usedPackageNames);
|
|
4679
4768
|
for (const packageName of peerSatisfied) usedPackageNames.add(packageName);
|
|
4680
|
-
const
|
|
4681
|
-
for (const [dependencyName
|
|
4769
|
+
const candidateUnused = /* @__PURE__ */ new Set();
|
|
4770
|
+
for (const [dependencyName] of declaredDependencies) {
|
|
4682
4771
|
if (isAlwaysConsideredUsed(dependencyName)) continue;
|
|
4683
|
-
if (
|
|
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
|
-
|
|
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
|