knip 6.40.0 → 6.42.0

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 (91) hide show
  1. package/README.md +1 -1
  2. package/dist/ConfigurationChief.d.ts +30 -0
  3. package/dist/DependencyDeputy.d.ts +1 -0
  4. package/dist/DependencyDeputy.js +28 -2
  5. package/dist/IssueCollector.js +13 -15
  6. package/dist/IssueFixer.js +1 -1
  7. package/dist/WorkspaceWorker.js +15 -8
  8. package/dist/binaries/bash-parser.js +16 -33
  9. package/dist/binaries/command.d.ts +1 -1
  10. package/dist/binaries/command.js +14 -8
  11. package/dist/binaries/resolvers/bun.js +1 -0
  12. package/dist/graph/build.js +18 -9
  13. package/dist/graph-explorer/operations/is-referenced.js +8 -2
  14. package/dist/manifest/index.d.ts +1 -0
  15. package/dist/manifest/index.js +7 -3
  16. package/dist/plugins/_component-style-urls/component-style-urls-visitor.d.ts +2 -0
  17. package/dist/plugins/_component-style-urls/component-style-urls-visitor.js +40 -0
  18. package/dist/plugins/angular/index.js +7 -1
  19. package/dist/plugins/biome/index.js +2 -2
  20. package/dist/plugins/commitlint/index.js +2 -2
  21. package/dist/plugins/eslint/helpers.js +1 -1
  22. package/dist/plugins/expo/helpers.js +1 -1
  23. package/dist/plugins/expo/index.js +4 -24
  24. package/dist/plugins/expo-router/index.d.ts +3 -0
  25. package/dist/plugins/expo-router/index.js +32 -0
  26. package/dist/plugins/filesystem-routing/index.d.ts +3 -0
  27. package/dist/plugins/filesystem-routing/index.js +57 -0
  28. package/dist/plugins/flue/index.d.ts +3 -0
  29. package/dist/plugins/flue/index.js +42 -0
  30. package/dist/plugins/github-actions/index.js +1 -1
  31. package/dist/plugins/index.d.ts +5 -0
  32. package/dist/plugins/index.js +10 -0
  33. package/dist/plugins/jest/index.js +5 -0
  34. package/dist/plugins/jest/visitors/mock.d.ts +2 -0
  35. package/dist/plugins/jest/visitors/mock.js +91 -0
  36. package/dist/plugins/mise/scripts.js +56 -76
  37. package/dist/plugins/moonrepo/index.js +2 -1
  38. package/dist/plugins/next/index.js +33 -1
  39. package/dist/plugins/nx/index.js +50 -8
  40. package/dist/plugins/nx/types.d.ts +8 -0
  41. package/dist/plugins/oxlint/index.js +1 -1
  42. package/dist/plugins/react-native/index.js +1 -1
  43. package/dist/plugins/react-router/index.d.ts +3 -0
  44. package/dist/plugins/react-router/index.js +3 -0
  45. package/dist/plugins/rsbuild/index.js +20 -6
  46. package/dist/plugins/rsbuild/types.d.ts +6 -0
  47. package/dist/plugins/rstest/index.js +1 -1
  48. package/dist/plugins/rstest/types.d.ts +1 -5
  49. package/dist/plugins/solidjs-vite-plugin/index.d.ts +6 -0
  50. package/dist/plugins/solidjs-vite-plugin/index.js +17 -0
  51. package/dist/plugins/solidjs-vite-plugin/resolveFromAST.d.ts +3 -0
  52. package/dist/plugins/solidjs-vite-plugin/resolveFromAST.js +76 -0
  53. package/dist/plugins/sst/resolveFromAST.js +1 -1
  54. package/dist/plugins/stencil/index.js +2 -39
  55. package/dist/plugins/storybook/index.js +2 -4
  56. package/dist/plugins/sveltejs-package/helpers.js +7 -44
  57. package/dist/plugins/taskfile/index.js +3 -6
  58. package/dist/plugins/tsrs/index.d.ts +3 -0
  59. package/dist/plugins/tsrs/index.js +15 -0
  60. package/dist/plugins/typescript/index.d.ts +12 -1
  61. package/dist/plugins/typescript/index.js +5 -7
  62. package/dist/plugins/vite/helpers.d.ts +1 -0
  63. package/dist/plugins/vite/helpers.js +15 -1
  64. package/dist/plugins/vitest/index.js +2 -2
  65. package/dist/run.js +2 -0
  66. package/dist/schema/configuration.d.ts +75 -0
  67. package/dist/schema/plugins.d.ts +25 -0
  68. package/dist/schema/plugins.js +5 -0
  69. package/dist/types/PluginNames.d.ts +2 -2
  70. package/dist/types/PluginNames.js +5 -0
  71. package/dist/types/config.d.ts +1 -0
  72. package/dist/types/workspace.d.ts +1 -0
  73. package/dist/typescript/visitors/imports.js +11 -0
  74. package/dist/typescript/visitors/jsdoc.js +27 -22
  75. package/dist/util/create-options.d.ts +50 -0
  76. package/dist/util/create-options.js +2 -3
  77. package/dist/util/glob-cache.d.ts +2 -2
  78. package/dist/util/glob-core.js +70 -47
  79. package/dist/util/glob.js +15 -10
  80. package/dist/util/load-tsconfig.d.ts +1 -1
  81. package/dist/util/load-tsconfig.js +21 -11
  82. package/dist/util/module-graph.js +9 -4
  83. package/dist/util/path.d.ts +1 -0
  84. package/dist/util/path.js +1 -0
  85. package/dist/util/scripts.d.ts +4 -2
  86. package/dist/util/scripts.js +33 -67
  87. package/dist/util/to-source-path.js +1 -1
  88. package/dist/version.d.ts +1 -1
  89. package/dist/version.js +1 -1
  90. package/package.json +10 -11
  91. package/schema.json +20 -0
package/README.md CHANGED
@@ -48,7 +48,7 @@ following projects:
48
48
 
49
49
  [1]: https://www.npmx.dev/package/knip
50
50
  [2]: https://img.shields.io/npm/v/knip?color=f56e0f
51
- [3]: https://img.shields.io/npm/dm/knip?color=f56e0f
51
+ [3]: https://img.shields.io/badge/dynamic/json?url=https://npm-gapless.webpro.nl/downloads/range/last-month/knip&query=$.totalCompact&label=downloads&suffix=/month&color=f56e0f
52
52
  [4]: https://github.com/webpro-nl/knip
53
53
  [5]:
54
54
  https://img.shields.io/github/stars/webpro-nl/knip?style=flat&color=f56e0f
@@ -237,6 +237,11 @@ export declare class ConfigurationChief {
237
237
  entry?: string | string[] | undefined;
238
238
  project?: string | string[] | undefined;
239
239
  } | undefined;
240
+ 'expo-router'?: string | boolean | string[] | {
241
+ config?: string | string[] | undefined;
242
+ entry?: string | string[] | undefined;
243
+ project?: string | string[] | undefined;
244
+ } | undefined;
240
245
  'expressive-code'?: string | boolean | string[] | {
241
246
  config?: string | string[] | undefined;
242
247
  entry?: string | string[] | undefined;
@@ -247,6 +252,16 @@ export declare class ConfigurationChief {
247
252
  entry?: string | string[] | undefined;
248
253
  project?: string | string[] | undefined;
249
254
  } | undefined;
255
+ 'filesystem-routing'?: string | boolean | string[] | {
256
+ config?: string | string[] | undefined;
257
+ entry?: string | string[] | undefined;
258
+ project?: string | string[] | undefined;
259
+ } | undefined;
260
+ flue?: string | boolean | string[] | {
261
+ config?: string | string[] | undefined;
262
+ entry?: string | string[] | undefined;
263
+ project?: string | string[] | undefined;
264
+ } | undefined;
250
265
  fumadocs?: string | boolean | string[] | {
251
266
  config?: string | string[] | undefined;
252
267
  entry?: string | string[] | undefined;
@@ -712,6 +727,11 @@ export declare class ConfigurationChief {
712
727
  entry?: string | string[] | undefined;
713
728
  project?: string | string[] | undefined;
714
729
  } | undefined;
730
+ 'solidjs-vite-plugin'?: string | boolean | string[] | {
731
+ config?: string | string[] | undefined;
732
+ entry?: string | string[] | undefined;
733
+ project?: string | string[] | undefined;
734
+ } | undefined;
715
735
  sst?: string | boolean | string[] | {
716
736
  config?: string | string[] | undefined;
717
737
  entry?: string | string[] | undefined;
@@ -827,6 +847,11 @@ export declare class ConfigurationChief {
827
847
  entry?: string | string[] | undefined;
828
848
  project?: string | string[] | undefined;
829
849
  } | undefined;
850
+ tsrs?: string | boolean | string[] | {
851
+ config?: string | string[] | undefined;
852
+ entry?: string | string[] | undefined;
853
+ project?: string | string[] | undefined;
854
+ } | undefined;
830
855
  tsup?: string | boolean | string[] | {
831
856
  config?: string | string[] | undefined;
832
857
  entry?: string | string[] | undefined;
@@ -1076,8 +1101,11 @@ export declare class ConfigurationChief {
1076
1101
  eve?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1077
1102
  execa?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1078
1103
  expo?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1104
+ "expo-router"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1079
1105
  "expressive-code"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1080
1106
  fast?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1107
+ "filesystem-routing"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1108
+ flue?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1081
1109
  fumadocs?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1082
1110
  gatsby?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1083
1111
  "github-action"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
@@ -1171,6 +1199,7 @@ export declare class ConfigurationChief {
1171
1199
  "serverless-framework"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1172
1200
  "simple-git-hooks"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1173
1201
  "size-limit"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1202
+ "solidjs-vite-plugin"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1174
1203
  sst?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1175
1204
  starlight?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1176
1205
  stencil?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
@@ -1194,6 +1223,7 @@ export declare class ConfigurationChief {
1194
1223
  "ts-node"?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1195
1224
  tsd?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1196
1225
  tsdown?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1226
+ tsrs?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1197
1227
  tsup?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1198
1228
  tsx?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
1199
1229
  turbo?: (boolean | import("./types/config.ts").EnsuredPluginConfiguration) | undefined;
@@ -40,6 +40,7 @@ export declare class DependencyDeputy {
40
40
  peerDependencies: DependencySet;
41
41
  optionalPeerDependencies: DependencySet;
42
42
  requiredPeerDependencies: DependencyArray;
43
+ uninstalledOptionalDependencies: DependencySet;
43
44
  allDependencies: DependencySet;
44
45
  engines: Record<string, string>;
45
46
  isPrivate: boolean;
@@ -38,15 +38,25 @@ export class DependencyDeputy {
38
38
  const requiredPeerDependencies = peerDependencies.filter(dep => !optionalPeerDependencies.has(dep));
39
39
  const devDependencies = Object.keys(manifest.devDependencies ?? {});
40
40
  const allDependencies = [...dependencies, ...devDependencies, ...peerDependencies, ...optionalDependencies];
41
- const packageNames = [...dependencies, ...peerDependencies, ...(this.isProduction ? [] : devDependencies)];
41
+ const uninstalledOptionalDependencies = new Set();
42
+ const packageNames = [
43
+ ...dependencies,
44
+ ...peerDependencies,
45
+ ...optionalDependencies,
46
+ ...(this.isProduction ? [] : devDependencies),
47
+ ];
42
48
  if (this.isReportDependencies) {
43
- const { hostDependencies, installedBinaries, hasTypesIncluded } = getDependencyMetaData({
49
+ const { hostDependencies, installedBinaries, hasTypesIncluded, uninstalledDependencies } = getDependencyMetaData({
44
50
  packageNames,
45
51
  dir,
46
52
  });
47
53
  this.setHostDependencies(name, hostDependencies);
48
54
  this.installedBinaries.set(name, installedBinaries);
49
55
  this.hasTypesIncluded.set(name, hasTypesIncluded);
56
+ for (const dependency of optionalDependencies) {
57
+ if (uninstalledDependencies.has(dependency))
58
+ uninstalledOptionalDependencies.add(dependency);
59
+ }
50
60
  }
51
61
  const ignoreDependencies = id.flatMap(id => filterIsProduction(id, this.isProduction)).map(toRegexOrString);
52
62
  const ignoreBinaries = ib.flatMap(ib => filterIsProduction(ib, this.isProduction)).map(toRegexOrString);
@@ -66,6 +76,7 @@ export class DependencyDeputy {
66
76
  peerDependencies: new Set(peerDependencies),
67
77
  optionalPeerDependencies,
68
78
  requiredPeerDependencies,
79
+ uninstalledOptionalDependencies,
69
80
  allDependencies: new Set(allDependencies),
70
81
  engines: manifest.engines ?? {},
71
82
  isPrivate: Boolean(manifest.private),
@@ -171,6 +182,21 @@ export class DependencyDeputy {
171
182
  }
172
183
  }
173
184
  }
185
+ const optionalDependencies = new Set();
186
+ for (const name of workspaceNames) {
187
+ const manifest = this._manifests.get(name);
188
+ if (!manifest)
189
+ continue;
190
+ for (const dependency of manifest.uninstalledOptionalDependencies) {
191
+ const defaultBinaryName = dependency.replace(/^@[^/]+\//, '');
192
+ if (defaultBinaryName === binaryName) {
193
+ optionalDependencies.add(dependency);
194
+ this.addReferencedDependency(name, dependency);
195
+ }
196
+ }
197
+ }
198
+ if (optionalDependencies.size)
199
+ return optionalDependencies;
174
200
  return;
175
201
  }
176
202
  isInDependencies(workspaceName, packageName, isDevOnly) {
@@ -1,7 +1,6 @@
1
1
  import picomatch from 'picomatch';
2
2
  import { ROOT_WORKSPACE_NAME } from './constants.js';
3
3
  import { partition } from './util/array.js';
4
- import { prependDirToPattern } from './util/glob.js';
5
4
  import { initCounters, initIssues } from './util/issue-initializers.js';
6
5
  import { relative } from './util/path.js';
7
6
  const createMatcher = (patterns) => {
@@ -82,23 +81,22 @@ export class IssueCollector {
82
81
  return;
83
82
  const issueTypePatterns = new Map();
84
83
  for (const [pattern, issueTypes] of Object.entries(ignoreIssues)) {
85
- const id = prependDirToPattern(this.cwd, pattern);
86
84
  for (const issueType of issueTypes) {
87
85
  if (!issueTypePatterns.has(issueType)) {
88
86
  issueTypePatterns.set(issueType, []);
89
87
  }
90
- issueTypePatterns.get(issueType)?.push(id);
88
+ issueTypePatterns.get(issueType)?.push(pattern);
91
89
  }
92
90
  }
93
91
  for (const [issueType, patterns] of issueTypePatterns) {
94
92
  this.issueMatchers.set(issueType, picomatch(patterns, { dot: true }));
95
93
  }
96
94
  }
97
- shouldIgnoreIssue(filePath, issueType) {
95
+ shouldIgnoreIssue(relativePath, issueType) {
98
96
  const matcher = this.issueMatchers.get(issueType);
99
97
  if (!matcher)
100
98
  return false;
101
- return matcher(filePath);
99
+ return matcher(relativePath);
102
100
  }
103
101
  addFileCounts({ processed, unused }) {
104
102
  this.counters.processed += processed;
@@ -110,17 +108,17 @@ export class IssueCollector {
110
108
  continue;
111
109
  if (this.referencedFiles.has(filePath))
112
110
  continue;
113
- if (this.isMatch(filePath)) {
114
- this.markUsedPatterns(filePath, this.unusedIgnorePatterns);
111
+ const symbol = relative(this.cwd, filePath);
112
+ if (this.isMatch(symbol)) {
113
+ this.markUsedPatterns(symbol, this.unusedIgnorePatterns);
115
114
  continue;
116
115
  }
117
- if (this.isFileMatch(filePath)) {
118
- this.markUsedPatterns(filePath, this.unusedIgnoreFilesPatterns);
116
+ if (this.isFileMatch(symbol)) {
117
+ this.markUsedPatterns(symbol, this.unusedIgnoreFilesPatterns);
119
118
  continue;
120
119
  }
121
- if (this.shouldIgnoreIssue(filePath, 'files'))
120
+ if (this.shouldIgnoreIssue(symbol, 'files'))
122
121
  continue;
123
- const symbol = relative(this.cwd, filePath);
124
122
  this.issues.files[symbol] = {
125
123
  [symbol]: { type: 'files', filePath, symbol, workspace: '', severity: this.rules.files, fixes: [] },
126
124
  };
@@ -131,15 +129,15 @@ export class IssueCollector {
131
129
  addIssue(issue) {
132
130
  if (!this.workspaceFilter(issue.filePath))
133
131
  return;
134
- if (this.isMatch(issue.filePath)) {
135
- this.markUsedPatterns(issue.filePath, this.unusedIgnorePatterns);
132
+ const key = relative(this.cwd, issue.filePath);
133
+ if (this.isMatch(key)) {
134
+ this.markUsedPatterns(key, this.unusedIgnorePatterns);
136
135
  return;
137
136
  }
138
- if (this.shouldIgnoreIssue(issue.filePath, issue.type))
137
+ if (this.shouldIgnoreIssue(key, issue.type))
139
138
  return;
140
139
  if (this.rules[issue.type] === 'off')
141
140
  return;
142
- const key = relative(this.cwd, issue.filePath);
143
141
  issue.severity = this.rules[issue.type];
144
142
  const issues = this.issues[issue.type];
145
143
  issues[key] = issues[key] ?? {};
@@ -21,7 +21,7 @@ export const fix = async (issues, counters, options) => {
21
21
  }
22
22
  if (options.isFormat) {
23
23
  const report = await formatly(Array.from(touchedFiles));
24
- if (report.ran && report.result && (report.result.runner === 'virtual' || report.result.code === 0)) {
24
+ if (report.ran && report.result.runner !== 'dry-run' && report.result.code === 0) {
25
25
  debugLogArray('*', `Formatted files using ${report.formatter.name} (${report.formatter.runner})`, touchedFiles);
26
26
  }
27
27
  else {
@@ -14,7 +14,7 @@ import { _glob, hasNoProductionSuffix, hasProductionSuffix, negate } from './uti
14
14
  import { isCatalog, isConfig, toConfig, toDebugString, toEntry, toProductionEntry, } from './util/input.js';
15
15
  import { filterTransitiveDependencies } from './util/filter-transitive-dependencies.js';
16
16
  import { timerify } from './util/Performance.js';
17
- import { basename, dirname, join, toRelative } from './util/path.js';
17
+ import { basename, dirname, join, relative, toRelative } from './util/path.js';
18
18
  import { extractPatternExtensions } from './util/pattern-extensions.js';
19
19
  import { formatCauseMessage } from './util/errors.js';
20
20
  import { logError } from './util/log.js';
@@ -294,6 +294,7 @@ export class WorkspaceWorker {
294
294
  }
295
295
  const options = {
296
296
  ...baseScriptOptions,
297
+ pluginName,
297
298
  config,
298
299
  configFilePath: containingFilePath,
299
300
  configFileDir: cwd,
@@ -469,18 +470,24 @@ export class WorkspaceWorker {
469
470
  hints.push({ type: `${type}-empty`, identifier, workspaceName });
470
471
  return hints;
471
472
  }
473
+ const dir = this.dir;
474
+ const relativePaths = [];
475
+ const hasMatch = (isMatch) => {
476
+ for (let i = 0; i < filePaths.length; i++) {
477
+ if (isMatch((relativePaths[i] ??= relative(dir, filePaths[i]))))
478
+ return true;
479
+ }
480
+ return false;
481
+ };
472
482
  for (const pattern of patterns) {
473
483
  if (pattern.startsWith('!'))
474
484
  continue;
475
- const filePathOrPattern = join(this.dir, pattern.replace(/!$/, ''));
476
- if (includedPaths.has(filePathOrPattern)) {
485
+ const id = pattern.replace(/!$/, '');
486
+ if (includedPaths.has(join(dir, id))) {
477
487
  hints.push({ type: `${type}-redundant`, identifier: pattern, workspaceName });
478
488
  }
479
- else {
480
- const matcher = picomatch(filePathOrPattern);
481
- if (!filePaths.some(filePath => matcher(filePath))) {
482
- hints.push({ type: `${type}-empty`, identifier: pattern, workspaceName });
483
- }
489
+ else if (!hasMatch(picomatch(id))) {
490
+ hints.push({ type: `${type}-empty`, identifier: pattern, workspaceName });
484
491
  }
485
492
  }
486
493
  if (type === 'project' && compilerExtensions) {
@@ -1,26 +1,11 @@
1
- import { parse } from 'unbash';
1
+ import { forEachChild, parse } from 'unbash';
2
2
  import { debugLogObject } from '../util/debug.js';
3
3
  import { extractBinary, isValidBinary } from '../util/modules.js';
4
4
  import { relative } from '../util/path.js';
5
5
  import { substringBefore, truncate } from '../util/string.js';
6
- import { walkCommands } from '../util/scripts.js';
6
+ import { isScriptExpansion } from '../util/scripts.js';
7
7
  import { getDependenciesFromCommand } from './command.js';
8
8
  import { toScript } from './util.js';
9
- const collectExpansionScripts = (word, out) => {
10
- if (!word.parts)
11
- return;
12
- for (const part of word.parts) {
13
- if ((part.type === 'CommandExpansion' || part.type === 'ProcessSubstitution') && part.script) {
14
- out.push(part.script);
15
- }
16
- else if (part.type === 'DoubleQuoted' || part.type === 'LocaleString') {
17
- for (const child of part.parts) {
18
- if (child.type === 'CommandExpansion' && child.script)
19
- out.push(child.script);
20
- }
21
- }
22
- }
23
- };
24
9
  export const getDependenciesFromScript = (script, options) => {
25
10
  if (!script)
26
11
  return [];
@@ -51,23 +36,21 @@ export const getDependenciesFromScript = (script, options) => {
51
36
  };
52
37
  const processScript = (s) => {
53
38
  collectFunctionNames(s.commands);
54
- const pending = [];
55
- const mainDeps = [];
56
- for (const statement of s.commands) {
57
- for (const command of walkCommands(statement.command))
58
- mainDeps.push(...processCommand(command, pending));
59
- }
60
- const expansionDeps = pending.flatMap(inner => processScript(inner));
61
- return [...mainDeps, ...expansionDeps];
39
+ const inputs = [];
40
+ const visit = (node) => {
41
+ if (node.type === 'Command')
42
+ inputs.push(...processCommand(node));
43
+ if (isScriptExpansion(node)) {
44
+ if (node.script)
45
+ inputs.push(...processScript(node.script));
46
+ }
47
+ else
48
+ forEachChild(node, visit);
49
+ };
50
+ visit(s);
51
+ return inputs;
62
52
  };
63
- const processCommand = (node, pending) => {
64
- if (node.name)
65
- collectExpansionScripts(node.name, pending);
66
- for (const prefix of node.prefix)
67
- if (prefix.value)
68
- collectExpansionScripts(prefix.value, pending);
69
- for (const suffix of node.suffix)
70
- collectExpansionScripts(suffix, pending);
53
+ const processCommand = (node) => {
71
54
  if (definedFunctions.size && node.name && definedFunctions.has(extractBinary(node.name.value)))
72
55
  return [];
73
56
  return getDependenciesFromCommand(node, { ...options, fromArgs }, fromWords);
@@ -2,4 +2,4 @@ import type { Command, Word } from 'unbash';
2
2
  import type { BinaryResolverOptions, GetInputsFromScriptsOptions } from '../types/config.ts';
3
3
  import { type Input } from '../util/input.ts';
4
4
  export declare const getInputsFromNodeOptions: (prefix: Command['prefix']) => Input[];
5
- export declare const getDependenciesFromCommand: (node: Command, options: BinaryResolverOptions, fromWords?: (words: Word[], options: GetInputsFromScriptsOptions) => Input[]) => Input[];
5
+ export declare const getDependenciesFromCommand: (node: Pick<Command, 'name' | 'prefix' | 'args'>, options: BinaryResolverOptions, fromWords?: (words: Word[], options: GetInputsFromScriptsOptions) => Input[]) => Input[];
@@ -1,17 +1,23 @@
1
1
  import { Plugins, pluginArgsMap } from '../plugins.js';
2
2
  import { toDeferResolve } from '../util/input.js';
3
3
  import { extractBinary, isInNodeModulesBin, isRelativeNodeModulesBin } from '../util/modules.js';
4
+ import { getCommandWords } from '../util/scripts.js';
4
5
  import { resolve as fallbackResolve, spawningBinaries } from './fallback.js';
5
6
  import KnownResolvers, { isPackageManager } from './resolvers/index.js';
6
7
  import { resolve as resolverFromPlugins } from './plugins.js';
7
8
  import { parseNodeArgs, toCommandBinary } from './util.js';
8
- export const getInputsFromNodeOptions = (prefix) => prefix
9
- .filter(a => a.name === 'NODE_OPTIONS' && a.value)
10
- .map(a => a.value.value)
11
- .map(arg => parseNodeArgs(arg.split(' ')))
12
- .filter(args => args.require)
13
- .flatMap(arg => arg.require)
14
- .map(id => toDeferResolve(id));
9
+ export const getInputsFromNodeOptions = (prefix) => {
10
+ const inputs = [];
11
+ for (const item of prefix) {
12
+ if (item.type !== 'Assignment' || item.name !== 'NODE_OPTIONS' || item.value.type !== 'Word')
13
+ continue;
14
+ const args = parseNodeArgs(item.value.value.split(' '));
15
+ if (args.require)
16
+ for (const id of [args.require].flat())
17
+ inputs.push(toDeferResolve(id));
18
+ }
19
+ return inputs;
20
+ };
15
21
  export const getDependenciesFromCommand = (node, options, fromWords = options.fromArgs) => {
16
22
  const text = node.name?.value;
17
23
  if (!text)
@@ -31,7 +37,7 @@ export const getDependenciesFromCommand = (node, options, fromWords = options.fr
31
37
  };
32
38
  }
33
39
  const { fromArgs } = options;
34
- const words = node.suffix;
40
+ const words = getCommandWords(node);
35
41
  if (binary === '!' || binary === 'test')
36
42
  return fromArgs(words);
37
43
  const fromNodeOptions = getInputsFromNodeOptions(node.prefix);
@@ -10,6 +10,7 @@ const commands = new Set([
10
10
  'auth',
11
11
  'build',
12
12
  'c',
13
+ 'check',
13
14
  'ci',
14
15
  'cloud',
15
16
  'completions',
@@ -51,8 +51,8 @@ export async function build({ chief, collector, counselor, deputy, principal, is
51
51
  });
52
52
  }
53
53
  deputy.setWorkspacePkgNames(chief.availableWorkspacePkgNames);
54
- collector.addIgnorePatterns(chief.config.ignore.map(id => ({ pattern: prependDir(options.cwd, id), id })));
55
- collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(id => ({ pattern: prependDir(options.cwd, id), id })));
54
+ collector.addIgnorePatterns(chief.config.ignore.map(id => ({ pattern: id, id })));
55
+ collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(id => ({ pattern: id, id })));
56
56
  if (options.configFilePath) {
57
57
  principal.addEntryPath(options.configFilePath, { skipExportsAnalysis: true });
58
58
  }
@@ -79,7 +79,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
79
79
  continue;
80
80
  const dependencies = deputy.getDependencies(name);
81
81
  const tsConfigFilePath = join(dir, options.tsConfigFile ?? 'tsconfig.json');
82
- const { isFile, compilerOptions, fileNames, include, exclude, sourceMapPairs, paths: tsConfigPaths, } = await loadTSConfig(tsConfigFilePath);
82
+ const { isFile, compilerOptions, fileNames, include, exclude, sourceMapPairs, paths: tsConfigPaths, } = await loadTSConfig(tsConfigFilePath, options.isUseTscFiles);
83
83
  const [definitionPaths, tscSourcePaths] = partition(fileNames, filePath => IS_DTS.test(filePath));
84
84
  const worker = new WorkspaceWorker({
85
85
  name,
@@ -122,7 +122,7 @@ export async function build({ chief, collector, counselor, deputy, principal, is
122
122
  inputs.add(toProductionEntry(tryRealpath(id), { containingFilePath: tsConfigFilePath }));
123
123
  }
124
124
  const sharedGlobOptions = { cwd: options.cwd, dir, gitignore: options.gitignore };
125
- const fn = (id) => ({ pattern: prependDir(options.cwd, prependDir(name, id)), id, workspaceName: name });
125
+ const fn = (id) => ({ pattern: prependDir(name, id), id, workspaceName: name });
126
126
  collector.addIgnorePatterns(config.ignore.map(fn));
127
127
  collector.addIgnoreFilesPatterns(config.ignoreFiles.map(fn));
128
128
  const entrySpecifiersFromManifest = getEntrySpecifiersFromManifest(manifest);
@@ -249,7 +249,10 @@ export async function build({ chief, collector, counselor, deputy, principal, is
249
249
  }
250
250
  const developmentMatchers = [];
251
251
  if (options.isProduction) {
252
- const toPattern = (pattern) => join(isAbsolute(pattern) ? '' : dir, removeProductionSuffix(pattern));
252
+ const toPattern = (pattern) => {
253
+ const id = removeProductionSuffix(pattern);
254
+ return isAbsolute(id) ? relative(options.cwd, id) : join(name, id);
255
+ };
253
256
  for (const map of [entryPatterns, entryPatternsSkipExports]) {
254
257
  for (const patterns of map.values()) {
255
258
  const [excluded, included] = partition(patterns, pattern => pattern.startsWith('!'));
@@ -260,7 +263,12 @@ export async function build({ chief, collector, counselor, deputy, principal, is
260
263
  }
261
264
  }
262
265
  }
263
- const productionPaths = (paths) => developmentMatchers.length ? paths.filter(path => !developmentMatchers.some(match => match(path))) : paths;
266
+ const productionPaths = (paths) => developmentMatchers.length
267
+ ? paths.filter(path => {
268
+ const relativePath = relative(options.cwd, path);
269
+ return !developmentMatchers.some(match => match(relativePath));
270
+ })
271
+ : paths;
264
272
  const userEntryPatterns = options.isProduction
265
273
  ? worker.getProductionEntryFilePatterns()
266
274
  : worker.getEntryFilePatterns();
@@ -313,10 +321,11 @@ export async function build({ chief, collector, counselor, deputy, principal, is
313
321
  }
314
322
  if (extensions.length > 0) {
315
323
  const extPart = extensions.length === 1 ? extensions[0] : `.{${extensions.map(ext => ext.slice(1)).join(',')}}`;
316
- const bases = include ? new Set(include.map(p => picomatch.scan(p).base || dir)) : new Set([dir]);
324
+ const tsConfigDir = relative(dir, dirname(tsConfigFilePath));
325
+ const bases = include ? new Set(include.map(p => join(tsConfigDir, picomatch.scan(p).base))) : new Set(['']);
317
326
  const patterns = [
318
- ...Array.from(bases, base => `${base}/**/*${extPart}`),
319
- ...(exclude?.map(p => `!${p}`) ?? []),
327
+ ...Array.from(bases, base => join(base, `**/*${extPart}`)),
328
+ ...(exclude?.map(p => `!${join(tsConfigDir, p)}`) ?? []),
320
329
  ];
321
330
  const compilerPaths = await _glob({ ...sharedGlobOptions, patterns, label: 'compiler extension paths' });
322
331
  for (const compilerPath of compilerPaths) {
@@ -38,10 +38,16 @@ export const isReferenced = (graph, entryPaths, filePath, id, options) => {
38
38
  return hasCompleteResult();
39
39
  }
40
40
  const node = graph.get(path);
41
- const file = node?.importedBy;
42
- if (!identifier || !node || !file) {
41
+ if (!identifier || !node) {
43
42
  return false;
44
43
  }
44
+ if (isEntryFile && node.skipExports) {
45
+ isReferenced = true;
46
+ return hasCompleteResult();
47
+ }
48
+ const file = node.importedBy;
49
+ if (!file)
50
+ return false;
45
51
  const follow = (sources, nextId, nextViaStar = viaStar) => {
46
52
  for (const byFilePath of sources) {
47
53
  if (walkDown(byFilePath, nextId, nextViaStar))
@@ -7,6 +7,7 @@ declare const getMetaDataFromPackageJson: ({ dir, packageNames }: Options) => {
7
7
  hostDependencies: HostDependencies;
8
8
  installedBinaries: InstalledBinaries;
9
9
  hasTypesIncluded: Set<string>;
10
+ uninstalledDependencies: Set<string>;
10
11
  };
11
12
  export declare const getDependencyMetaData: typeof getMetaDataFromPackageJson;
12
13
  export {};
@@ -5,6 +5,7 @@ const getMetaDataFromPackageJson = ({ dir, packageNames }) => {
5
5
  const hostDependencies = new Map();
6
6
  const installedBinaries = new Map();
7
7
  const hasTypesIncluded = new Set();
8
+ const uninstalledDependencies = new Set();
8
9
  const addBinary = (key, value) => {
9
10
  const set = installedBinaries.get(key);
10
11
  if (set)
@@ -15,10 +16,9 @@ const getMetaDataFromPackageJson = ({ dir, packageNames }) => {
15
16
  for (const packageName of packageNames) {
16
17
  const manifest = loadPackageManifest({ dir, packageName });
17
18
  if (manifest) {
18
- const defaultBinaryName = packageName.replace(/^@[^/]+\//, '');
19
- const binaries = typeof manifest.bin === 'string' ? [defaultBinaryName] : Object.keys(manifest.bin ?? {});
19
+ const binaries = typeof manifest.bin === 'string' ? [packageName] : Object.keys(manifest.bin ?? {});
20
20
  for (const binaryName of binaries)
21
- addBinary(binaryName, packageName);
21
+ addBinary(binaryName.replace(/^@[^/]+\//, ''), packageName);
22
22
  const packagePeerDependencies = Object.keys(manifest.peerDependencies ?? {});
23
23
  for (const packagePeerDependency of packagePeerDependencies) {
24
24
  const hostDependency = {
@@ -35,11 +35,15 @@ const getMetaDataFromPackageJson = ({ dir, packageNames }) => {
35
35
  if (!isDefinitelyTyped(packageName) && (manifest.types || manifest.typings))
36
36
  hasTypesIncluded.add(packageName);
37
37
  }
38
+ else {
39
+ uninstalledDependencies.add(packageName);
40
+ }
38
41
  }
39
42
  return {
40
43
  hostDependencies,
41
44
  installedBinaries,
42
45
  hasTypesIncluded,
46
+ uninstalledDependencies,
43
47
  };
44
48
  };
45
49
  export const getDependencyMetaData = timerify(getMetaDataFromPackageJson);
@@ -0,0 +1,2 @@
1
+ import type { PluginVisitorContext, PluginVisitorObject } from '../../types/config.ts';
2
+ export declare function createComponentStyleUrlsVisitor(ctx: PluginVisitorContext, isSpecifier: (specifier: string) => boolean): PluginVisitorObject;
@@ -0,0 +1,40 @@
1
+ import { IMPORT_FLAGS } from '../../constants.js';
2
+ import { getPropertyValues } from '../../typescript/ast-helpers.js';
3
+ import { dirname, join } from '../../util/path.js';
4
+ export function createComponentStyleUrlsVisitor(ctx, isSpecifier) {
5
+ const componentNames = new Set();
6
+ return {
7
+ Program() {
8
+ componentNames.clear();
9
+ },
10
+ ImportDeclaration(node) {
11
+ if (!isSpecifier(node.source?.value))
12
+ return;
13
+ for (const spec of node.specifiers ?? []) {
14
+ if (spec.type === 'ImportSpecifier' &&
15
+ spec.imported.type === 'Identifier' &&
16
+ spec.imported.name === 'Component')
17
+ componentNames.add(spec.local.name);
18
+ }
19
+ },
20
+ ClassDeclaration(node) {
21
+ if (componentNames.size === 0)
22
+ return;
23
+ const dir = dirname(ctx.filePath);
24
+ for (const decorator of node.decorators ?? []) {
25
+ const expr = decorator.expression;
26
+ if (expr?.type !== 'CallExpression')
27
+ continue;
28
+ if (expr.callee?.type !== 'Identifier' || !componentNames.has(expr.callee.name))
29
+ continue;
30
+ const arg = expr.arguments?.[0];
31
+ if (arg?.type !== 'ObjectExpression')
32
+ continue;
33
+ for (const url of getPropertyValues(arg, 'styleUrl'))
34
+ ctx.addImport(join(dir, url), arg.start, IMPORT_FLAGS.NONE);
35
+ for (const url of getPropertyValues(arg, 'styleUrls'))
36
+ ctx.addImport(join(dir, url), arg.start, IMPORT_FLAGS.NONE);
37
+ }
38
+ },
39
+ };
40
+ }
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { toConfig, toDeferResolve, toDependency, toEntry, toProductionEntry } from '../../util/input.js';
3
3
  import { isInternal, join } from '../../util/path.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
5
+ import { createComponentStyleUrlsVisitor } from '../_component-style-urls/component-style-urls-visitor.js';
5
6
  import * as karma from '../karma/helpers.js';
6
7
  const title = 'Angular';
7
8
  const enablers = ['@angular/cli'];
@@ -72,7 +73,7 @@ const resolveConfig = async (config, options) => {
72
73
  karma.inputsFromFrameworks(['jasmine']).forEach(inputs.add, inputs);
73
74
  }
74
75
  if (karmaConfig && !karma.configFiles.includes(karmaConfig)) {
75
- inputs.add(toConfig('karma', karmaConfig, { containingFilePath: options.configFilePath }));
76
+ inputs.add(toConfig('karma', karmaConfig));
76
77
  }
77
78
  }
78
79
  }
@@ -111,11 +112,16 @@ const isAngularBuilderRefWithName = ({ builderRef, name }) => {
111
112
  };
112
113
  const PRODUCTION_CONFIG_NAME = 'production';
113
114
  const BUILD_TARGET_NAME = 'build';
115
+ const isAngularSpecifier = (specifier) => specifier === '@angular/core';
116
+ const registerVisitors = ({ ctx, registerVisitor }) => {
117
+ registerVisitor(createComponentStyleUrlsVisitor(ctx, isAngularSpecifier));
118
+ };
114
119
  const plugin = {
115
120
  title,
116
121
  enablers,
117
122
  isEnabled,
118
123
  config,
119
124
  resolveConfig,
125
+ registerVisitors,
120
126
  };
121
127
  export default plugin;