knip 5.70.0 → 5.70.2

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.
@@ -89,15 +89,7 @@ export class WorkspaceWorker {
89
89
  if (project.length === 0)
90
90
  return [];
91
91
  const excludeProductionNegations = project.filter(pattern => !(pattern.startsWith('!') && pattern.endsWith('!')));
92
- const negatedPluginConfigPatterns = this.getPluginConfigPatterns().map(negate);
93
- const negatedPluginProjectFilePatterns = this.getPluginProjectFilePatterns().map(negate);
94
- return [
95
- excludeProductionNegations,
96
- negatedPluginConfigPatterns,
97
- negatedPluginProjectFilePatterns,
98
- projectFilePatterns,
99
- this.negatedWorkspacePatterns,
100
- ].flat();
92
+ return [excludeProductionNegations, projectFilePatterns, this.negatedWorkspacePatterns].flat();
101
93
  }
102
94
  getPluginProjectFilePatterns(patterns = []) {
103
95
  for (const [pluginName, plugin] of PluginEntries) {
@@ -175,18 +167,10 @@ export class WorkspaceWorker {
175
167
  const createGetInputsFromScripts = (containingFilePath) => (scripts, options) => _getInputsFromScripts(scripts, { ...baseOptions, ...options, containingFilePath });
176
168
  const inputs = [];
177
169
  const remainingPlugins = new Set(this.enabledPlugins);
178
- const addInput = (input, containingFilePath = input.containingFilePath) => {
179
- if (isConfig(input)) {
180
- handleConfigInput(input.pluginName, { ...input, containingFilePath });
181
- }
182
- else {
183
- inputs.push({ ...input, containingFilePath });
184
- }
185
- };
186
170
  const configFilesMap = this.configFilesMap;
187
171
  const configFiles = this.configFilesMap.get(wsName);
188
172
  const seen = new Map();
189
- const handleConfigInput = (pluginName, input) => {
173
+ const storeConfigFilePath = (pluginName, input) => {
190
174
  const configFilePath = this.getReferencedInternalFilePath(input);
191
175
  if (configFilePath) {
192
176
  const workspace = this.findWorkspaceByFilePath(configFilePath);
@@ -202,20 +186,26 @@ export class WorkspaceWorker {
202
186
  };
203
187
  for (const input of [...inputsFromManifest, ...productionInputsFromManifest]) {
204
188
  if (isConfig(input)) {
205
- handleConfigInput(input.pluginName, { ...input, containingFilePath });
189
+ storeConfigFilePath(input.pluginName, { ...input, containingFilePath });
206
190
  }
207
- else {
208
- if (!isProduction)
209
- addInput(input, containingFilePath);
210
- else if (isProduction && (input.production || hasProductionInput(input)))
211
- addInput(input, containingFilePath);
191
+ else if (!isProduction || (isProduction && (input.production || hasProductionInput(input)))) {
192
+ inputs.push({ ...input, containingFilePath });
212
193
  }
213
194
  }
214
195
  const runPlugin = async (pluginName, patterns) => {
215
196
  const plugin = Plugins[pluginName];
216
197
  const config = this.getConfigForPlugin(pluginName);
217
198
  if (!config)
218
- return;
199
+ return [];
200
+ const inputs = [];
201
+ const addInput = (input, containingFilePath = input.containingFilePath) => {
202
+ if (isConfig(input)) {
203
+ storeConfigFilePath(input.pluginName, { ...input, containingFilePath });
204
+ }
205
+ else {
206
+ inputs.push(Object.assign(input, { containingFilePath }));
207
+ }
208
+ };
219
209
  const label = 'config file';
220
210
  const configFilePaths = await _glob({ patterns, cwd: rootCwd, dir: cwd, gitignore: false, label });
221
211
  const options = {
@@ -229,7 +219,7 @@ export class WorkspaceWorker {
229
219
  if (config.entry) {
230
220
  const toInput = isProduction && plugin.production && plugin.production.length > 0 ? toProductionEntry : toEntry;
231
221
  for (const id of config.entry)
232
- addInput(toInput(id));
222
+ inputs.push(toInput(id));
233
223
  }
234
224
  else if ((!plugin.resolveConfig && !plugin.resolveFromAST) ||
235
225
  (configFilePaths.filter(path => basename(path) !== 'package.json').length === 0 &&
@@ -237,10 +227,10 @@ export class WorkspaceWorker {
237
227
  this.configFilesMap.get(wsName)?.get(pluginName)?.size === 0))) {
238
228
  if (plugin.entry)
239
229
  for (const id of plugin.entry)
240
- addInput(toEntry(id));
230
+ inputs.push(toEntry(id));
241
231
  if (plugin.production)
242
232
  for (const id of plugin.production)
243
- addInput(toProductionEntry(id));
233
+ inputs.push(toProductionEntry(id));
244
234
  }
245
235
  for (const configFilePath of configFilePaths) {
246
236
  const isManifest = basename(configFilePath) === 'package.json';
@@ -295,8 +285,8 @@ export class WorkspaceWorker {
295
285
  }
296
286
  }
297
287
  if (!isManifest) {
298
- addInput(toEntry(configFilePath));
299
- addInput(toConfig(pluginName, configFilePath));
288
+ inputs.push(toEntry(configFilePath));
289
+ storeConfigFilePath(pluginName, toConfig(pluginName, configFilePath));
300
290
  cache.configFile = toEntry(configFilePath);
301
291
  if (fd?.changed && fd.meta && !seen.get(key)?.has(configFilePath)) {
302
292
  fd.meta.data = cache;
@@ -311,13 +301,18 @@ export class WorkspaceWorker {
311
301
  for (const id of dependencies)
312
302
  addInput(id, containingFilePath);
313
303
  }
304
+ if (inputs.some(input => input.specifier.startsWith('!')))
305
+ for (const input of inputs)
306
+ input.group = pluginName;
307
+ return inputs;
314
308
  };
315
309
  const enabledPluginTitles = this.enabledPlugins.map(name => Plugins[name].title);
316
310
  debugLogObject(this.name, 'Enabled plugins', enabledPluginTitles);
317
311
  for (const pluginName of this.enabledPlugins) {
318
312
  const patterns = [...this.getConfigurationFilePatterns(pluginName), ...(configFiles?.get(pluginName) ?? [])];
319
313
  configFiles?.delete(pluginName);
320
- await runPlugin(pluginName, compact(patterns));
314
+ for (const input of await runPlugin(pluginName, compact(patterns)))
315
+ inputs.push(input);
321
316
  remainingPlugins.delete(pluginName);
322
317
  }
323
318
  {
@@ -326,11 +321,13 @@ export class WorkspaceWorker {
326
321
  do {
327
322
  for (const [pluginName, dependencies] of configFiles.entries()) {
328
323
  configFiles.delete(pluginName);
329
- if (this.enabledPlugins.includes(pluginName))
330
- await runPlugin(pluginName, Array.from(dependencies));
324
+ if (this.enabledPlugins.includes(pluginName)) {
325
+ for (const input of await runPlugin(pluginName, Array.from(dependencies)))
326
+ inputs.push(input);
327
+ }
331
328
  else
332
329
  for (const id of dependencies)
333
- addInput(toEntry(id));
330
+ inputs.push(toEntry(id));
334
331
  }
335
332
  } while (remainingPlugins.size > 0 && configFiles.size > 0);
336
333
  }
@@ -106,33 +106,34 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
106
106
  for (const id of inputsFromPlugins)
107
107
  inputs.add(Object.assign(id, { skipExportsAnalysis: !id.allowIncludeExports }));
108
108
  enabledPluginsStore.set(name, worker.enabledPlugins);
109
- const entryPatterns = new Set();
110
- const entryPatternsSkipExports = new Set();
111
- const productionPatterns = new Set();
112
- const productionPatternsSkipExports = new Set();
109
+ const DEFAULT_GROUP = 'default';
110
+ const createPatternMap = () => new Map([[DEFAULT_GROUP, new Set()]]);
111
+ const groups = new Set([DEFAULT_GROUP]);
112
+ const entryPatterns = createPatternMap();
113
+ const entryPatternsSkipExports = createPatternMap();
114
+ const productionPatterns = createPatternMap();
115
+ const productionPatternsSkipExports = createPatternMap();
113
116
  const projectFilePatterns = new Set();
117
+ const addPattern = (map, input, pattern) => {
118
+ if (input.group && !map.has(input.group))
119
+ map.set(input.group, new Set());
120
+ map.get(input.group ?? DEFAULT_GROUP).add(pattern);
121
+ };
122
+ const toWorkspaceRelative = (path) => (isAbsolute(path) ? relative(dir, path) : path);
114
123
  for (const input of inputs) {
124
+ if (input.group)
125
+ groups.add(input.group);
115
126
  const specifier = input.specifier;
116
127
  if (isEntry(input)) {
117
- const relativePath = isAbsolute(specifier) ? relative(dir, specifier) : specifier;
118
- if (!input.skipExportsAnalysis) {
119
- entryPatterns.add(relativePath);
120
- }
121
- else {
122
- entryPatternsSkipExports.add(relativePath);
123
- }
128
+ const targetMap = input.skipExportsAnalysis ? entryPatternsSkipExports : entryPatterns;
129
+ addPattern(targetMap, input, toWorkspaceRelative(specifier));
124
130
  }
125
131
  else if (isProductionEntry(input)) {
126
- const relativePath = isAbsolute(specifier) ? relative(dir, specifier) : specifier;
127
- if (!input.skipExportsAnalysis) {
128
- productionPatterns.add(relativePath);
129
- }
130
- else {
131
- productionPatternsSkipExports.add(relativePath);
132
- }
132
+ const targetMap = input.skipExportsAnalysis ? productionPatternsSkipExports : productionPatterns;
133
+ addPattern(targetMap, input, toWorkspaceRelative(specifier));
133
134
  }
134
135
  else if (isProject(input)) {
135
- projectFilePatterns.add(isAbsolute(specifier) ? relative(dir, specifier) : specifier);
136
+ projectFilePatterns.add(toWorkspaceRelative(specifier));
136
137
  }
137
138
  else if (isAlias(input)) {
138
139
  principal.addPaths({ [input.specifier]: input.prefixes }, input.dir ?? dir);
@@ -150,11 +151,11 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
150
151
  const resolvedFilePath = getReferencedInternalFilePath(input, ws);
151
152
  if (resolvedFilePath) {
152
153
  if (isDeferResolveProductionEntry(input)) {
153
- productionPatternsSkipExports.add(resolvedFilePath);
154
+ addPattern(productionPatternsSkipExports, input, resolvedFilePath);
154
155
  }
155
156
  else if (isDeferResolveEntry(input)) {
156
157
  if (!options.isProduction || !input.optional)
157
- entryPatternsSkipExports.add(resolvedFilePath);
158
+ addPattern(entryPatternsSkipExports, input, resolvedFilePath);
158
159
  }
159
160
  else {
160
161
  principal.addEntryPath(resolvedFilePath, { skipExportsAnalysis: true });
@@ -162,82 +163,63 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
162
163
  }
163
164
  }
164
165
  }
166
+ const negatedEntryPatterns = [];
165
167
  if (options.isProduction) {
166
- const negatedEntryPatterns = [...entryPatterns, ...entryPatternsSkipExports].map(negate);
167
- {
168
- const label = 'entry paths';
169
- const patterns = worker.getProductionEntryFilePatterns(negatedEntryPatterns);
170
- const workspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, gitignore: false, label });
171
- principal.addEntryPaths(workspaceEntryPaths);
168
+ for (const map of [entryPatterns, entryPatternsSkipExports]) {
169
+ for (const patterns of map.values())
170
+ for (const pattern of patterns)
171
+ negatedEntryPatterns.push(negate(pattern));
172
172
  }
173
- {
174
- const label = 'production entry paths from plugins (ignore exports)';
175
- const patterns = Array.from(productionPatternsSkipExports);
176
- const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
177
- principal.addEntryPaths(pluginWorkspaceEntryPaths, { skipExportsAnalysis: true });
173
+ }
174
+ {
175
+ const patterns = options.isProduction
176
+ ? worker.getProductionEntryFilePatterns(negatedEntryPatterns)
177
+ : worker.getEntryFilePatterns();
178
+ const entryPaths = await _glob({ ...sharedGlobOptions, patterns, gitignore: false, label: 'entry paths' });
179
+ if (!options.isProduction) {
180
+ const hints = worker.getConfigurationHints('entry', patterns, entryPaths, principal.entryPaths);
181
+ for (const hint of hints)
182
+ collector.addConfigurationHint(hint);
178
183
  }
184
+ principal.addEntryPaths(entryPaths);
185
+ }
186
+ for (const group of groups) {
179
187
  {
180
- const label = 'production entry paths from plugins';
181
- const patterns = Array.from(productionPatterns);
188
+ const patterns = worker.getPluginEntryFilePatterns([
189
+ ...((!options.isProduction && entryPatterns.get(group)) || []),
190
+ ...((!options.isProduction && group === DEFAULT_GROUP && worker.getPluginConfigPatterns()) || []),
191
+ ...(productionPatterns.get(group) ?? []),
192
+ ]);
193
+ const label = `entry paths from plugins${group !== DEFAULT_GROUP ? ` - ${group}` : ''}`;
182
194
  const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
183
195
  principal.addEntryPaths(pluginWorkspaceEntryPaths);
184
196
  }
185
197
  {
186
- const label = 'project paths';
187
- const patterns = worker.getProductionProjectFilePatterns(negatedEntryPatterns);
188
- const workspaceProjectPaths = await _glob({ ...sharedGlobOptions, patterns, label });
189
- for (const projectPath of workspaceProjectPaths)
190
- principal.addProjectPath(projectPath);
191
- }
192
- }
193
- else {
194
- {
195
- const label = 'entry paths from plugins (ignore exports)';
196
198
  const patterns = worker.getPluginEntryFilePatterns([
197
- ...entryPatternsSkipExports,
198
- ...productionPatternsSkipExports,
199
+ ...((!options.isProduction && entryPatternsSkipExports.get(group)) || []),
200
+ ...(productionPatternsSkipExports.get(group) ?? []),
199
201
  ]);
202
+ const label = `entry paths from plugins (ignore exports)${group !== DEFAULT_GROUP ? ` - ${group}` : ''}`;
200
203
  const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
201
204
  principal.addEntryPaths(pluginWorkspaceEntryPaths, { skipExportsAnalysis: true });
202
205
  }
203
- {
204
- const label = 'entry paths from plugins';
205
- const patterns = worker.getPluginEntryFilePatterns([...entryPatterns, ...productionPatterns]);
206
- const pluginWorkspaceEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
207
- principal.addEntryPaths(pluginWorkspaceEntryPaths);
208
- }
209
- {
210
- const label = 'entry paths';
211
- const patterns = worker.getEntryFilePatterns();
212
- const entryPaths = await _glob({ ...sharedGlobOptions, patterns, gitignore: false, label });
213
- const hints = worker.getConfigurationHints('entry', patterns, entryPaths, principal.entryPaths);
214
- for (const hint of hints)
215
- collector.addConfigurationHint(hint);
216
- principal.addEntryPaths(entryPaths);
217
- }
218
- {
219
- const label = 'project paths from plugins';
220
- const patterns = worker.getPluginProjectFilePatterns();
221
- const pluginWorkspaceProjectPaths = await _glob({ ...sharedGlobOptions, patterns, label });
222
- for (const projectPath of pluginWorkspaceProjectPaths)
223
- principal.addProjectPath(projectPath);
224
- }
225
- {
226
- const label = 'plugin configuration paths (ignore exports)';
227
- const patterns = worker.getPluginConfigPatterns();
228
- const configurationEntryPaths = await _glob({ ...sharedGlobOptions, patterns, label });
229
- principal.addEntryPaths(configurationEntryPaths, { skipExportsAnalysis: true });
230
- }
231
- {
232
- const label = 'project paths';
233
- const patterns = worker.getProjectFilePatterns([...productionPatternsSkipExports, ...projectFilePatterns]);
234
- const projectPaths = await _glob({ ...sharedGlobOptions, patterns, label });
206
+ }
207
+ {
208
+ const patterns = options.isProduction
209
+ ? worker.getProductionProjectFilePatterns(negatedEntryPatterns)
210
+ : worker.getProjectFilePatterns([
211
+ ...(productionPatternsSkipExports.get(DEFAULT_GROUP) ?? []),
212
+ ...projectFilePatterns,
213
+ ...worker.getPluginProjectFilePatterns(),
214
+ ]);
215
+ const projectPaths = await _glob({ ...sharedGlobOptions, patterns, label: 'project paths' });
216
+ if (!options.isProduction) {
235
217
  const hints = worker.getConfigurationHints('project', config.project, projectPaths, principal.projectPaths);
236
218
  for (const hint of hints)
237
219
  collector.addConfigurationHint(hint);
238
- for (const projectPath of projectPaths)
239
- principal.addProjectPath(projectPath);
240
220
  }
221
+ for (const projectPath of projectPaths)
222
+ principal.addProjectPath(projectPath);
241
223
  }
242
224
  if (options.configFilePath) {
243
225
  factory.getPrincipals().at(0)?.addEntryPath(options.configFilePath, { skipExportsAnalysis: true });
@@ -294,13 +276,15 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
294
276
  if (!isIgnored)
295
277
  principal.addEntryPath(filePath, { skipExportsAnalysis: true });
296
278
  }
297
- for (const [_import, specifierFilePath] of file.imports.imports) {
298
- const packageName = getPackageNameFromModuleSpecifier(_import.specifier);
299
- if (packageName && isInternalWorkspace(packageName)) {
300
- file.imports.external.add({ ..._import, specifier: packageName });
301
- const principal = getPrincipalByFilePath(specifierFilePath);
302
- if (principal && !isGitIgnored(specifierFilePath)) {
303
- principal.addNonEntryPath(specifierFilePath);
279
+ for (const _import of file.imports.imports) {
280
+ if (_import.filePath) {
281
+ const packageName = getPackageNameFromModuleSpecifier(_import.specifier);
282
+ if (packageName && isInternalWorkspace(packageName)) {
283
+ file.imports.external.add({ ..._import, specifier: packageName });
284
+ const principal = getPrincipalByFilePath(_import.filePath);
285
+ if (principal && !isGitIgnored(_import.filePath)) {
286
+ principal.addNonEntryPath(_import.filePath);
287
+ }
304
288
  }
305
289
  }
306
290
  }
@@ -6,7 +6,6 @@ const enablers = ['astro'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  export const config = ['astro.config.{js,cjs,mjs,ts,mts}'];
8
8
  const entry = ['src/content/config.ts', 'src/content.config.ts'];
9
- const project = ['src/**/*'];
10
9
  const production = [
11
10
  'src/pages/**/*.{astro,mdx,js,ts}',
12
11
  '!src/pages/**/_*',
@@ -42,6 +41,5 @@ const plugin = {
42
41
  production,
43
42
  resolveFromAST,
44
43
  resolve,
45
- project,
46
44
  };
47
45
  export default plugin;
@@ -21,10 +21,12 @@ export type ImportMaps = {
21
21
  export type ImportMap = Map<FilePath, ImportMaps>;
22
22
  export type Import = {
23
23
  specifier: string;
24
- identifier: string;
24
+ filePath: string | undefined;
25
+ identifier: string | undefined;
25
26
  pos?: number;
26
27
  line?: number;
27
28
  col?: number;
29
+ isTypeOnly: boolean;
28
30
  };
29
31
  export interface Export {
30
32
  identifier: Identifier;
@@ -37,6 +39,7 @@ export interface Export {
37
39
  refs: [number, boolean];
38
40
  fixes: Fixes;
39
41
  symbol?: ts.Symbol;
42
+ isReExport?: boolean;
40
43
  }
41
44
  export type ExportMember = {
42
45
  identifier: Identifier;
@@ -50,7 +53,7 @@ export type ExportMember = {
50
53
  jsDocTags: Tags;
51
54
  };
52
55
  export type ExportMap = Map<Identifier, Export>;
53
- export type Imports = Set<[Import, FilePath]>;
56
+ export type Imports = Set<Import>;
54
57
  export type FileNode = {
55
58
  imports: {
56
59
  internal: ImportMap;
@@ -79,7 +79,15 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
79
79
  const { symbol, filePath, namespace, specifier, modifiers } = options;
80
80
  const identifier = options.identifier ?? (modifiers & IMPORT_MODIFIERS.OPAQUE ? OPAQUE : SIDE_EFFECTS);
81
81
  const isStar = identifier === IMPORT_STAR;
82
- imports.add([{ specifier, identifier, pos: options.pos, line: options.line, col: options.col }, filePath]);
82
+ imports.add({
83
+ filePath,
84
+ specifier,
85
+ identifier: namespace ?? options.identifier,
86
+ pos: options.pos,
87
+ line: options.line,
88
+ col: options.col,
89
+ isTypeOnly: !!(modifiers & IMPORT_MODIFIERS.TYPE_ONLY),
90
+ });
83
91
  const file = internal.get(filePath);
84
92
  const importMaps = file ?? createImports();
85
93
  if (!file)
@@ -105,7 +113,7 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
105
113
  addNsValue(importMaps.importedAs, identifier, nsOrAlias, sourceFile.fileName);
106
114
  }
107
115
  }
108
- else if (identifier !== SIDE_EFFECTS && identifier !== IMPORT_STAR) {
116
+ else if (identifier !== IMPORT_STAR) {
109
117
  addValue(importMaps.imported, identifier, sourceFile.fileName);
110
118
  }
111
119
  if (symbol)
@@ -144,11 +152,13 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
144
152
  const pos = node.moduleSpecifier?.getStart() ?? opts.pos;
145
153
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
146
154
  external.add({
155
+ filePath,
147
156
  specifier: sanitizedSpecifier,
148
157
  identifier: opts.identifier ?? SIDE_EFFECTS,
149
158
  pos: opts.pos,
150
159
  line: line + 1,
151
160
  col: character + 2,
161
+ isTypeOnly: !!(opts.modifiers & IMPORT_MODIFIERS.TYPE_ONLY),
152
162
  });
153
163
  }
154
164
  }
@@ -167,11 +177,13 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
167
177
  const pos = 'moduleSpecifier' in node ? node.moduleSpecifier.pos : node.pos;
168
178
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
169
179
  unresolved.add({
180
+ filePath: undefined,
170
181
  specifier: opts.specifier,
171
182
  identifier: opts.identifier ?? SIDE_EFFECTS,
172
183
  pos,
173
184
  line: line + 1,
174
185
  col: character + 2,
186
+ isTypeOnly: !!(opts.modifiers & IMPORT_MODIFIERS.TYPE_ONLY),
175
187
  });
176
188
  }
177
189
  };
@@ -17,7 +17,7 @@ export const collectCustomImports = (sourceFile) => {
17
17
  const isLocal = isInternal(id) || isAbsolute(id);
18
18
  const modifiers = isLocal ? IMPORT_MODIFIERS.ENTRY : IMPORT_MODIFIERS.NONE;
19
19
  const offset = match[0].length - match[2].length;
20
- const specifier = isLocal ? id : getEnvSpecifier(id);
20
+ const specifier = isLocal || id === 'node' ? id : getEnvSpecifier(id);
21
21
  importNodes.push({ specifier, identifier: undefined, pos: comment.pos + match.index + offset, modifiers });
22
22
  }
23
23
  }
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { IMPORT_MODIFIERS } from '../../../constants.js';
2
+ import { IMPORT_MODIFIERS, IMPORT_STAR } from '../../../constants.js';
3
3
  import { importVisitor as visit } from '../index.js';
4
4
  const supportsJSDocImportTag = 'isJSDocImportTag' in ts;
5
5
  const getImportSpecifiers = (node) => {
@@ -12,19 +12,35 @@ const getImportSpecifiers = (node) => {
12
12
  imports.push({
13
13
  specifier: importClause.literal.text,
14
14
  identifier,
15
- pos: importClause.literal.pos,
16
- modifiers: IMPORT_MODIFIERS.NONE,
15
+ pos: node.qualifier?.getStart() ?? importClause.literal.pos,
16
+ modifiers: IMPORT_MODIFIERS.TYPE_ONLY,
17
17
  });
18
18
  }
19
19
  }
20
20
  if (supportsJSDocImportTag && ts.isJSDocImportTag(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
21
21
  const moduleSpecifier = node.moduleSpecifier;
22
- imports.push({
23
- specifier: moduleSpecifier.text,
24
- identifier: undefined,
25
- pos: moduleSpecifier.pos,
26
- modifiers: IMPORT_MODIFIERS.NONE,
27
- });
22
+ const importClause = node.importClause;
23
+ if (moduleSpecifier && importClause?.namedBindings && ts.isNamedImportBindings(importClause.namedBindings)) {
24
+ const bindings = importClause.namedBindings;
25
+ if (ts.isNamespaceImport(bindings)) {
26
+ imports.push({
27
+ specifier: moduleSpecifier.text,
28
+ identifier: IMPORT_STAR,
29
+ pos: bindings.name.getStart(),
30
+ modifiers: IMPORT_MODIFIERS.TYPE_ONLY,
31
+ });
32
+ }
33
+ else {
34
+ for (const element of bindings.elements) {
35
+ imports.push({
36
+ specifier: moduleSpecifier.text,
37
+ identifier: String((element.propertyName ?? element.name).escapedText),
38
+ pos: element.name.getStart(),
39
+ modifiers: IMPORT_MODIFIERS.TYPE_ONLY,
40
+ });
41
+ }
42
+ }
43
+ }
28
44
  }
29
45
  ts.forEachChild(node, visit);
30
46
  }
@@ -15,7 +15,7 @@ export default visit(() => true, node => {
15
15
  });
16
16
  if (propertyAccessExpression) {
17
17
  const identifier = String(propertyAccessExpression.name.escapedText);
18
- return { identifier, specifier, pos: propertyAccessExpression.name.pos, modifiers };
18
+ return { identifier, specifier, pos: propertyAccessExpression.name.getStart(), modifiers };
19
19
  }
20
20
  const variableDeclaration = node.parent;
21
21
  if (ts.isVariableDeclaration(variableDeclaration) &&
@@ -28,7 +28,7 @@ export default visit(() => true, node => {
28
28
  alias,
29
29
  symbol: isTLA ? variableDeclaration.symbol : undefined,
30
30
  specifier,
31
- pos: node.arguments[0].pos,
31
+ pos: variableDeclaration.name.getStart(),
32
32
  modifiers,
33
33
  };
34
34
  }
@@ -38,7 +38,7 @@ export default visit(() => true, node => {
38
38
  const identifier = (element.propertyName ?? element.name).getText();
39
39
  const alias = element.propertyName ? element.name.getText() : undefined;
40
40
  const symbol = isTLA ? element.symbol : undefined;
41
- return { identifier, specifier, alias, symbol, pos: element.pos, modifiers };
41
+ return { identifier, specifier, alias, symbol, pos: element.name.getStart(), modifiers };
42
42
  });
43
43
  }
44
44
  return { identifier: 'default', specifier, pos: node.arguments[0].pos, modifiers };
@@ -53,7 +53,10 @@ export default visit(() => true, node => {
53
53
  modifiers: IMPORT_MODIFIERS.RE_EXPORT,
54
54
  };
55
55
  }
56
- return { identifier: 'default', specifier, pos: node.arguments[0].pos, modifiers };
56
+ if (ts.isCallExpression(node.parent)) {
57
+ return { identifier: 'default', specifier, pos: node.getEnd(), modifiers };
58
+ }
59
+ return { identifier: 'default', specifier, pos: node.getStart(), modifiers };
57
60
  }
58
61
  }
59
62
  }
@@ -9,6 +9,7 @@ export default visit(() => true, node => {
9
9
  return { specifier, identifier: undefined, pos: node.pos, modifiers: IMPORT_MODIFIERS.SIDE_EFFECTS };
10
10
  }
11
11
  const imports = [];
12
+ const modifiers = node.importClause.isTypeOnly ? IMPORT_MODIFIERS.TYPE_ONLY : IMPORT_MODIFIERS.NONE;
12
13
  if (isDefaultImport(node)) {
13
14
  imports.push({
14
15
  identifier: 'default',
@@ -16,7 +17,7 @@ export default visit(() => true, node => {
16
17
  specifier,
17
18
  symbol: node.importClause.symbol,
18
19
  pos: node.importClause.name?.getStart() ?? node.getStart(),
19
- modifiers: IMPORT_MODIFIERS.NONE,
20
+ modifiers,
20
21
  });
21
22
  }
22
23
  if (node.importClause?.namedBindings) {
@@ -27,7 +28,7 @@ export default visit(() => true, node => {
27
28
  specifier,
28
29
  identifier: IMPORT_STAR,
29
30
  pos: node.importClause.namedBindings.name.getStart(),
30
- modifiers: node.importClause?.isTypeOnly ? IMPORT_MODIFIERS.TYPE_ONLY : IMPORT_MODIFIERS.NONE,
31
+ modifiers,
31
32
  });
32
33
  }
33
34
  if (ts.isNamedImports(node.importClause.namedBindings)) {
@@ -38,7 +39,7 @@ export default visit(() => true, node => {
38
39
  specifier,
39
40
  symbol: element.symbol,
40
41
  pos: element.name.getStart(),
41
- modifiers: node.importClause?.isTypeOnly ? IMPORT_MODIFIERS.TYPE_ONLY : IMPORT_MODIFIERS.NONE,
42
+ modifiers,
42
43
  });
43
44
  }
44
45
  }
@@ -47,7 +48,7 @@ export default visit(() => true, node => {
47
48
  specifier,
48
49
  identifier: undefined,
49
50
  pos: node.importClause.namedBindings.pos,
50
- modifiers: node.importClause?.isTypeOnly ? IMPORT_MODIFIERS.TYPE_ONLY : IMPORT_MODIFIERS.NONE,
51
+ modifiers,
51
52
  });
52
53
  }
53
54
  }
@@ -8,7 +8,7 @@ export default visit(() => true, node => {
8
8
  return {
9
9
  identifier: IMPORT_STAR,
10
10
  specifier: node.moduleSpecifier.text,
11
- pos: node.pos,
11
+ pos: node.moduleSpecifier.getStart() - 7,
12
12
  modifiers: IMPORT_MODIFIERS.RE_EXPORT,
13
13
  };
14
14
  }
@@ -17,7 +17,7 @@ export default visit(() => true, node => {
17
17
  identifier: IMPORT_STAR,
18
18
  namespace: String(node.exportClause.name.text),
19
19
  specifier: node.moduleSpecifier.text,
20
- pos: node.exportClause.name.pos,
20
+ pos: node.exportClause.name.getStart(),
21
21
  modifiers: IMPORT_MODIFIERS.RE_EXPORT,
22
22
  };
23
23
  }
@@ -28,14 +28,14 @@ export default visit(() => true, node => {
28
28
  identifier: String(element.propertyName.text),
29
29
  alias: String(element.name.text),
30
30
  specifier: specifier.text,
31
- pos: element.pos,
31
+ pos: element.propertyName.getStart(),
32
32
  modifiers: IMPORT_MODIFIERS.RE_EXPORT,
33
33
  };
34
34
  }
35
35
  return {
36
36
  identifier: (element.propertyName ?? element.name).getText(),
37
37
  specifier: specifier.text,
38
- pos: element.pos,
38
+ pos: element.name.getStart(),
39
39
  modifiers: IMPORT_MODIFIERS.RE_EXPORT,
40
40
  };
41
41
  });
@@ -10,6 +10,7 @@ export interface Input {
10
10
  containingFilePath?: string;
11
11
  allowIncludeExports?: boolean;
12
12
  skipExportsAnalysis?: boolean;
13
+ group?: string;
13
14
  }
14
15
  export interface ConfigInput extends Input {
15
16
  type: 'config';
@@ -4,6 +4,6 @@ export declare const hasDependency: (dependencies: Set<string>, values: (string
4
4
  export declare const normalizePluginConfig: (pluginConfig: RawPluginConfiguration) => boolean | {
5
5
  config: string[] | null;
6
6
  entry: string[] | null;
7
- project: string[] | null;
7
+ project: string[];
8
8
  };
9
9
  export declare const loadConfigForPlugin: (configFilePath: string, plugin: Plugin, options: PluginOptions, pluginName: string) => Promise<any>;
@@ -16,9 +16,8 @@ export const hasDependency = (dependencies, values) => values.some(value => {
16
16
  return false;
17
17
  });
18
18
  export const normalizePluginConfig = (pluginConfig) => {
19
- if (typeof pluginConfig === 'boolean') {
19
+ if (typeof pluginConfig === 'boolean')
20
20
  return pluginConfig;
21
- }
22
21
  const isObject = typeof pluginConfig !== 'string' && !Array.isArray(pluginConfig);
23
22
  const config = isObject
24
23
  ? 'config' in pluginConfig
@@ -28,7 +27,9 @@ export const normalizePluginConfig = (pluginConfig) => {
28
27
  ? arrayify(pluginConfig)
29
28
  : null;
30
29
  const entry = isObject && 'entry' in pluginConfig ? arrayify(pluginConfig.entry) : null;
31
- const project = isObject && 'project' in pluginConfig ? arrayify(pluginConfig.project) : entry;
30
+ const project = isObject && 'project' in pluginConfig
31
+ ? arrayify(pluginConfig.project)
32
+ : (entry ?? []).filter(pattern => !pattern.startsWith('!'));
32
33
  return { config, entry, project };
33
34
  };
34
35
  export const loadConfigForPlugin = async (configFilePath, plugin, options, pluginName) => {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.70.0";
1
+ export declare const version = "5.70.2";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.70.0';
1
+ export const version = '5.70.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.70.0",
3
+ "version": "5.70.2",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {