knip 6.29.0 → 6.31.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 (119) hide show
  1. package/dist/CatalogCounselor.d.ts +5 -2
  2. package/dist/CatalogCounselor.js +26 -8
  3. package/dist/ConfigurationChief.d.ts +7 -1
  4. package/dist/ConfigurationChief.js +29 -36
  5. package/dist/DependencyDeputy.d.ts +8 -1
  6. package/dist/DependencyDeputy.js +5 -5
  7. package/dist/PackagePeeker.d.ts +5 -0
  8. package/dist/PackagePeeker.js +31 -3
  9. package/dist/WorkspaceWorker.d.ts +1 -1
  10. package/dist/WorkspaceWorker.js +11 -7
  11. package/dist/binaries/bash-parser.js +3 -3
  12. package/dist/binaries/create-script-parser-context.d.ts +7 -0
  13. package/dist/binaries/create-script-parser-context.js +17 -0
  14. package/dist/binaries/resolvers/pnpx.js +10 -5
  15. package/dist/compilers/index.d.ts +15 -5
  16. package/dist/compilers/scss.js +32 -5
  17. package/dist/compilers/shared.d.ts +1 -0
  18. package/dist/compilers/shared.js +1 -1
  19. package/dist/compilers/svelte.d.ts +2 -0
  20. package/dist/compilers/svelte.js +22 -0
  21. package/dist/constants.d.ts +4 -1
  22. package/dist/constants.js +4 -0
  23. package/dist/graph/analyze.js +5 -2
  24. package/dist/graph/build.d.ts +3 -1
  25. package/dist/graph/build.js +38 -19
  26. package/dist/graph-explorer/operations/build-exports-tree.js +2 -1
  27. package/dist/graph-explorer/operations/is-referenced.js +7 -4
  28. package/dist/graph-explorer/utils.js +2 -2
  29. package/dist/plugins/_template/index.js +1 -1
  30. package/dist/plugins/astro-markdoc/index.js +3 -2
  31. package/dist/plugins/eleventy/index.js +1 -1
  32. package/dist/plugins/esbuild/index.js +1 -1
  33. package/dist/plugins/eslint/helpers.js +2 -1
  34. package/dist/plugins/gatsby/index.js +2 -2
  35. package/dist/plugins/index.d.ts +1 -0
  36. package/dist/plugins/index.js +2 -0
  37. package/dist/plugins/jest/helpers.js +1 -1
  38. package/dist/plugins/marko/compiler.d.ts +2 -0
  39. package/dist/plugins/marko/compiler.js +85 -0
  40. package/dist/plugins/marko/index.d.ts +3 -0
  41. package/dist/plugins/marko/index.js +63 -0
  42. package/dist/plugins/marko/taglibs.d.ts +4 -0
  43. package/dist/plugins/marko/taglibs.js +111 -0
  44. package/dist/plugins/marko/types.d.ts +9 -0
  45. package/dist/plugins/marko/types.js +1 -0
  46. package/dist/plugins/metro/index.js +1 -1
  47. package/dist/plugins/nest/index.js +1 -1
  48. package/dist/plugins/node/index.js +11 -2
  49. package/dist/plugins/nx/index.js +4 -3
  50. package/dist/plugins/openclaw/index.js +1 -1
  51. package/dist/plugins/oxlint/index.js +9 -2
  52. package/dist/plugins/remark/helpers.d.ts +3 -0
  53. package/dist/plugins/remark/helpers.js +42 -0
  54. package/dist/plugins/remark/index.js +4 -5
  55. package/dist/plugins/rolldown/index.js +1 -1
  56. package/dist/plugins/rollup/index.js +1 -1
  57. package/dist/plugins/rslib/index.js +1 -1
  58. package/dist/plugins/stryker/index.js +6 -1
  59. package/dist/plugins/svelte/compiler.js +7 -5
  60. package/dist/plugins/tsdown/index.js +2 -2
  61. package/dist/plugins/tsup/index.js +1 -1
  62. package/dist/plugins/vitest/index.js +1 -1
  63. package/dist/plugins/vitest/types.d.ts +1 -1
  64. package/dist/reporters/codeclimate.js +1 -5
  65. package/dist/reporters/github-actions.js +5 -3
  66. package/dist/reporters/index.d.ts +1 -0
  67. package/dist/reporters/index.js +2 -0
  68. package/dist/reporters/json.d.ts +1 -0
  69. package/dist/reporters/json.js +1 -0
  70. package/dist/reporters/sarif.d.ts +3 -0
  71. package/dist/reporters/sarif.js +79 -0
  72. package/dist/reporters/trace.js +3 -2
  73. package/dist/reporters/util/util.d.ts +2 -1
  74. package/dist/reporters/util/util.js +6 -1
  75. package/dist/run.d.ts +1 -1
  76. package/dist/run.js +30 -0
  77. package/dist/schema/configuration.d.ts +21 -6
  78. package/dist/schema/configuration.js +1 -0
  79. package/dist/schema/plugins.d.ts +5 -0
  80. package/dist/schema/plugins.js +1 -0
  81. package/dist/session/build-maps.js +2 -1
  82. package/dist/types/PluginNames.d.ts +2 -2
  83. package/dist/types/PluginNames.js +1 -0
  84. package/dist/types/issues.d.ts +1 -0
  85. package/dist/types/package-json.d.ts +10 -0
  86. package/dist/typescript/get-imports-and-exports.js +3 -2
  87. package/dist/typescript/get-published-type-dependencies.d.ts +13 -0
  88. package/dist/typescript/get-published-type-dependencies.js +155 -0
  89. package/dist/typescript/visitors/calls.js +8 -0
  90. package/dist/util/catalog.d.ts +6 -1
  91. package/dist/util/catalog.js +16 -2
  92. package/dist/util/cli-arguments.d.ts +1 -1
  93. package/dist/util/cli-arguments.js +3 -3
  94. package/dist/util/codeowners.js +1 -1
  95. package/dist/util/create-input-handler.js +8 -2
  96. package/dist/util/create-options.d.ts +16 -5
  97. package/dist/util/disk-cache.js +1 -1
  98. package/dist/util/errors.js +3 -2
  99. package/dist/util/file-entry-cache.js +5 -1
  100. package/dist/util/fs.js +2 -2
  101. package/dist/util/get-included-issue-types.js +2 -1
  102. package/dist/util/gitignore-cache.js +4 -1
  103. package/dist/util/glob-cache.js +2 -2
  104. package/dist/util/glob-core.js +3 -1
  105. package/dist/util/input.d.ts +7 -1
  106. package/dist/util/input.js +6 -0
  107. package/dist/util/package-json.d.ts +347 -0
  108. package/dist/util/package-json.js +123 -0
  109. package/dist/util/path.d.ts +8 -7
  110. package/dist/util/resolve.d.ts +6 -0
  111. package/dist/util/resolve.js +24 -0
  112. package/dist/util/string.d.ts +2 -0
  113. package/dist/util/string.js +6 -0
  114. package/dist/util/table.js +2 -2
  115. package/dist/util/to-source-path.js +1 -1
  116. package/dist/version.d.ts +1 -1
  117. package/dist/version.js +1 -1
  118. package/package.json +5 -5
  119. package/schema.json +8 -0
@@ -0,0 +1,111 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { isFile, loadJSON } from '../../util/fs.js';
3
+ import { basename, dirname, join } from '../../util/path.js';
4
+ const dependencyFields = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
5
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ const getDependencies = async (cwd) => {
7
+ const manifest = await loadJSON(join(cwd, 'package.json'));
8
+ const dependencies = new Set();
9
+ if (!isRecord(manifest))
10
+ return dependencies;
11
+ for (const field of dependencyFields) {
12
+ const values = manifest[field];
13
+ if (isRecord(values))
14
+ for (const packageName in values)
15
+ dependencies.add(packageName);
16
+ }
17
+ return dependencies;
18
+ };
19
+ const findConfig = (cwd, packageName) => {
20
+ let dir = cwd;
21
+ while (true) {
22
+ const filePath = join(dir, 'node_modules', packageName, 'marko.json');
23
+ if (isFile(filePath))
24
+ return filePath;
25
+ const parent = dirname(dir);
26
+ if (parent === dir)
27
+ return;
28
+ dir = parent;
29
+ }
30
+ };
31
+ const readDir = (dir) => {
32
+ try {
33
+ return readdirSync(dir, { withFileTypes: true });
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ };
39
+ const getTagName = (fileName) => (fileName.endsWith('.marko') ? basename(fileName, '.marko') : undefined);
40
+ const isTagDir = (dir, name) => isFile(dir, 'index.marko') || isFile(dir, `${name}.marko`);
41
+ const getTags = (dir) => {
42
+ const tags = new Set();
43
+ for (const entry of readDir(dir)) {
44
+ if (entry.name.startsWith('.'))
45
+ continue;
46
+ if (entry.isFile()) {
47
+ const name = getTagName(entry.name);
48
+ if (name)
49
+ tags.add(name);
50
+ continue;
51
+ }
52
+ if (!entry.isDirectory())
53
+ continue;
54
+ const tagDir = join(dir, entry.name);
55
+ if (isTagDir(tagDir, entry.name)) {
56
+ tags.add(entry.name);
57
+ continue;
58
+ }
59
+ if (entry.name === 'tags' || entry.name === 'components')
60
+ continue;
61
+ for (const child of readDir(tagDir)) {
62
+ if (child.isFile()) {
63
+ const name = getTagName(child.name);
64
+ if (name)
65
+ tags.add(name);
66
+ }
67
+ else if (child.isDirectory() && isTagDir(join(tagDir, child.name), child.name)) {
68
+ tags.add(child.name);
69
+ }
70
+ }
71
+ }
72
+ return tags;
73
+ };
74
+ export const getTaglibDependencies = async (cwd) => {
75
+ const tagDependencies = new Map();
76
+ const fallbackDependencies = [];
77
+ for (const packageName of await getDependencies(cwd)) {
78
+ const configPath = findConfig(cwd, packageName);
79
+ if (!configPath)
80
+ continue;
81
+ let config;
82
+ try {
83
+ config = await loadJSON(configPath);
84
+ }
85
+ catch {
86
+ fallbackDependencies.push(packageName);
87
+ continue;
88
+ }
89
+ if (!isRecord(config)) {
90
+ fallbackDependencies.push(packageName);
91
+ continue;
92
+ }
93
+ const tagsDir = typeof config.exports === 'string'
94
+ ? config.exports
95
+ : typeof config['tags-dir'] === 'string'
96
+ ? config['tags-dir']
97
+ : undefined;
98
+ if (!tagsDir) {
99
+ fallbackDependencies.push(packageName);
100
+ continue;
101
+ }
102
+ for (const tagName of getTags(join(dirname(configPath), tagsDir))) {
103
+ const dependencies = tagDependencies.get(tagName);
104
+ if (dependencies)
105
+ dependencies.push(packageName);
106
+ else
107
+ tagDependencies.set(tagName, [packageName]);
108
+ }
109
+ }
110
+ return { tagDependencies, fallbackDependencies };
111
+ };
@@ -0,0 +1,9 @@
1
+ export type MarkoTagDef = {
2
+ template?: string;
3
+ renderer?: string;
4
+ parse?: string;
5
+ migrate?: string;
6
+ transform?: string;
7
+ analyze?: string;
8
+ translate?: string;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -41,7 +41,7 @@ const resolveConfig = async (config) => {
41
41
  inputs.push(transformer.minifierPath);
42
42
  if (transformer?.babelTransformerPath)
43
43
  inputs.push(transformer.babelTransformerPath);
44
- return Array.from(i).concat([...inputs].map(id => toDeferResolve(id, {
44
+ return Array.from(i).concat(Array.from(inputs, id => toDeferResolve(id, {
45
45
  optional: id === DEFAULT_TRANSFORMER_PACKAGE || id === DEFAULT_MINIFIER_PACKAGE,
46
46
  })));
47
47
  };
@@ -6,7 +6,7 @@ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
6
  const config = ['nest-cli.json', '.nestcli.json', '.nest-cli.json', 'nest.json'];
7
7
  const resolveConfig = async (config) => {
8
8
  const inputs = config?.collection ? [config.collection] : [];
9
- return [...inputs].map(id => toDependency(id));
9
+ return Array.from(inputs, id => toDependency(id));
10
10
  };
11
11
  const plugin = {
12
12
  title,
@@ -8,9 +8,18 @@ const patterns = [
8
8
  '**/test.{cjs,mjs,js,cts,mts,ts}',
9
9
  '**/test/**/*.{cjs,mjs,js,cts,mts,ts}',
10
10
  ];
11
- const hasNodeTest = (scripts) => scripts &&
12
- Object.values(scripts).some(script => typeof script === 'string' &&
11
+ const hasNodeTestCache = new WeakMap();
12
+ const hasNodeTest = (scripts) => {
13
+ if (!scripts)
14
+ return false;
15
+ const cached = hasNodeTestCache.get(scripts);
16
+ if (cached !== undefined)
17
+ return cached;
18
+ const result = Object.values(scripts).some(script => typeof script === 'string' &&
13
19
  getScriptCommands(script).some(({ binary, args }) => (binary === 'node' || binary === 'nub') && args.includes('--test')));
20
+ hasNodeTestCache.set(scripts, result);
21
+ return result;
22
+ };
14
23
  const entry = ['server.js'];
15
24
  const resolve = options => {
16
25
  const entries = entry.map(id => toProductionEntry(id));
@@ -2,6 +2,7 @@ import { compact } from '../../util/array.js';
2
2
  import { toConfig, toDependency } from '../../util/input.js';
3
3
  import { join } from '../../util/path.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
5
+ import { substringBefore } from '../../util/string.js';
5
6
  const title = 'Nx';
6
7
  const enablers = ['nx', /^@nrwl\//, /^@nx\//];
7
8
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
@@ -10,7 +11,7 @@ const findNxDependenciesInNxJson = async (localConfig) => {
10
11
  const targetsDefault = localConfig.targetDefaults
11
12
  ? Object.keys(localConfig.targetDefaults)
12
13
  .filter(it => it.includes(':') && it.startsWith('@'))
13
- .map(it => it.split(':')[0])
14
+ .map(it => substringBefore(it, ':'))
14
15
  : [];
15
16
  const plugins = localConfig.plugins && Array.isArray(localConfig.plugins)
16
17
  ? localConfig.plugins
@@ -20,7 +21,7 @@ const findNxDependenciesInNxJson = async (localConfig) => {
20
21
  const generators = localConfig.generators
21
22
  ? Object.keys(localConfig.generators)
22
23
  .filter(value => value !== undefined)
23
- .map(value => value.split(':')[0])
24
+ .map(value => substringBefore(value, ':'))
24
25
  : [];
25
26
  return compact([...targetsDefault, ...plugins, ...generators]).map(id => toDependency(id));
26
27
  };
@@ -34,7 +35,7 @@ const resolveConfig = async (localConfig, options) => {
34
35
  const executors = targets
35
36
  .map(target => target?.executor)
36
37
  .filter(executor => executor && !executor.startsWith('.'))
37
- .map(executor => executor?.split(':')[0]);
38
+ .map(executor => executor && substringBefore(executor, ':'));
38
39
  const expand = (value) => value.replaceAll('{projectRoot}', options.configFileDir).replaceAll('{workspaceRoot}', options.rootCwd);
39
40
  const resolveTargetCwd = (targetCwd) => {
40
41
  if (!targetCwd)
@@ -38,7 +38,7 @@ const resolveFromAST = (_, options) => {
38
38
  return [];
39
39
  const sourceText = options.readFile(options.configFilePath).replace(/^\uFEFF/, '');
40
40
  const { program } = _parseFile(`${options.configFilePath}.ts`, `(${sourceText}\n)`);
41
- return [...collectPropertyValues(program, 'providerCatalogEntry')].map(entry => toProductionEntry(entry));
41
+ return Array.from(collectPropertyValues(program, 'providerCatalogEntry'), entry => toProductionEntry(entry));
42
42
  };
43
43
  const plugin = {
44
44
  title,
@@ -38,9 +38,16 @@ const resolveFromAST = (program, options) => {
38
38
  const visitor = new Visitor({
39
39
  ObjectExpression(node) {
40
40
  const lint = findProperty(node, 'lint');
41
- if (lint?.type === 'ObjectExpression')
42
- for (const specifier of getPropertyValues(lint, 'jsPlugins'))
41
+ if (lint?.type !== 'ObjectExpression')
42
+ return;
43
+ for (const specifier of getPropertyValues(lint, 'jsPlugins'))
44
+ jsPlugins.add(specifier);
45
+ for (const plugin of findProperty(lint, 'jsPlugins')?.elements ?? []) {
46
+ if (plugin?.type !== 'ObjectExpression')
47
+ continue;
48
+ for (const specifier of getPropertyValues(plugin, 'specifier'))
43
49
  jsPlugins.add(specifier);
50
+ }
44
51
  },
45
52
  });
46
53
  visitor.visit(program);
@@ -0,0 +1,3 @@
1
+ import { type Input } from '../../util/input.ts';
2
+ import type { Manifest } from '../../util/package-json.ts';
3
+ export declare const resolveLoadPluginStylePluginName: (prefix: string, identifier: string, manifest: Manifest) => Input;
@@ -0,0 +1,42 @@
1
+ import { toDeferResolve, toDependency } from '../../util/input.js';
2
+ import { isInternal } from '../../util/path.js';
3
+ const getCandidates = (prefix, identifier) => {
4
+ if (isInternal(identifier))
5
+ return [identifier];
6
+ if (identifier.startsWith('@')) {
7
+ const [scope, name, ...rest] = identifier.split('/');
8
+ if (rest.length > 0)
9
+ return [identifier];
10
+ if (scope) {
11
+ if (!name)
12
+ return [[scope, prefix].join('/')];
13
+ if (name.startsWith(prefix))
14
+ return [identifier];
15
+ return [[scope, prefix + name].join('/'), identifier];
16
+ }
17
+ }
18
+ const [name, ...rest] = identifier.split('/');
19
+ if (rest.length > 0)
20
+ return [identifier];
21
+ if (name.startsWith(prefix))
22
+ return [identifier];
23
+ return [prefix + name, name];
24
+ };
25
+ const getDeclaredDependencies = (manifest) => new Set([
26
+ ...Object.keys(manifest.dependencies ?? {}),
27
+ ...Object.keys(manifest.devDependencies ?? {}),
28
+ ...Object.keys(manifest.optionalDependencies ?? {}),
29
+ ...Object.keys(manifest.peerDependencies ?? {}),
30
+ ]);
31
+ const pickCandidate = (prefix, identifier, manifest) => {
32
+ const candidates = getCandidates(prefix, identifier);
33
+ if (candidates.length === 1 || isInternal(candidates[0]))
34
+ return candidates[0];
35
+ const dependencies = getDeclaredDependencies(manifest);
36
+ return candidates.find(candidate => dependencies.has(candidate)) ?? candidates[0];
37
+ };
38
+ export const resolveLoadPluginStylePluginName = (prefix, identifier, manifest) => {
39
+ prefix = prefix + (prefix.at(-1) === '-' ? '' : '-');
40
+ const candidate = pickCandidate(prefix, identifier, manifest);
41
+ return isInternal(candidate) ? toDeferResolve(candidate) : toDependency(candidate);
42
+ };
@@ -1,12 +1,11 @@
1
- import { toDeferResolve } from '../../util/input.js';
2
- import { isInternal } from '../../util/path.js';
3
1
  import { hasDependency } from '../../util/plugin.js';
2
+ import { resolveLoadPluginStylePluginName } from './helpers.js';
4
3
  const title = 'Remark';
5
4
  const enablers = ['remark-cli'];
6
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
6
  const packageJsonPath = 'remarkConfig';
8
7
  const config = ['package.json', '.remarkrc', '.remarkrc.json', '.remarkrc.{js,cjs,mjs}', '.remarkrc.{yml,yaml}'];
9
- const resolveConfig = config => {
8
+ const resolveConfig = (config, options) => {
10
9
  const plugins = config.plugins
11
10
  ?.flatMap(plugin => {
12
11
  if (typeof plugin === 'string')
@@ -15,8 +14,8 @@ const resolveConfig = config => {
15
14
  return plugin[0];
16
15
  return [];
17
16
  })
18
- .map(plugin => (isInternal(plugin) ? plugin : plugin.startsWith('remark-') ? plugin : `remark-${plugin}`)) ?? [];
19
- return plugins.map(id => toDeferResolve(id));
17
+ .map(plugin => resolveLoadPluginStylePluginName('remark-', plugin, options.manifest)) ?? [];
18
+ return plugins;
20
19
  };
21
20
  const plugin = {
22
21
  title,
@@ -5,7 +5,7 @@ const title = 'Rolldown';
5
5
  const enablers = ['rolldown'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const config = ['rolldown.config.{js,cjs,mjs,ts,cts,mts}'];
8
- const resolveFromAST = program => [...collectPropertyValues(program, 'input')].map(id => toProductionEntry(id));
8
+ const resolveFromAST = program => Array.from(collectPropertyValues(program, 'input'), id => toProductionEntry(id));
9
9
  const plugin = {
10
10
  title,
11
11
  enablers,
@@ -11,7 +11,7 @@ const args = {
11
11
  fromArgs: ['_exec'],
12
12
  resolve: ['plugin', 'configPlugin'],
13
13
  };
14
- const resolveFromAST = program => [...collectPropertyValues(program, 'input')].map(id => toProductionEntry(id));
14
+ const resolveFromAST = program => Array.from(collectPropertyValues(program, 'input'), id => toProductionEntry(id));
15
15
  const plugin = {
16
16
  title,
17
17
  enablers,
@@ -5,7 +5,7 @@ const title = 'Rslib';
5
5
  const enablers = ['@rslib/core'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const config = ['rslib*.config.{mjs,ts,js,cjs,mts,cts}'];
8
- const resolveFromAST = program => [...collectPropertyValues(program, 'entry')].map(id => toProductionEntry(id, { allowIncludeExports: true }));
8
+ const resolveFromAST = program => Array.from(collectPropertyValues(program, 'entry'), id => toProductionEntry(id, { allowIncludeExports: true }));
9
9
  const plugin = {
10
10
  title,
11
11
  enablers,
@@ -1,4 +1,4 @@
1
- import { toDeferResolve } from '../../util/input.js';
1
+ import { toConfig, toDeferResolve } from '../../util/input.js';
2
2
  import { hasDependency } from '../../util/plugin.js';
3
3
  const title = 'Stryker';
4
4
  const enablers = ['@stryker-mutator/core'];
@@ -12,11 +12,16 @@ const resolveConfig = localConfig => {
12
12
  const plugins = localConfig.plugins ?? [];
13
13
  return [...runners, ...checkers, ...plugins].map(id => toDeferResolve(id));
14
14
  };
15
+ const args = {
16
+ boolean: ['allowEmpty', 'disableBail', 'dryRunOnly', 'force', 'ignoreStatic', 'incremental', 'inPlace'],
17
+ resolveInputs: parsed => (parsed._[0] === 'run' && parsed._[1] ? [toConfig('stryker', parsed._[1])] : []),
18
+ };
15
19
  const plugin = {
16
20
  title,
17
21
  enablers,
18
22
  isEnabled,
19
23
  config,
20
24
  resolveConfig,
25
+ args,
21
26
  };
22
27
  export default plugin;
@@ -1,10 +1,12 @@
1
1
  import { importsWithinScripts } from '../../compilers/compilers.js';
2
2
  import { stylePreprocessorImports } from '../../compilers/style-preprocessors.js';
3
+ import { dynamicImportsWithinTemplate } from '../../compilers/svelte.js';
3
4
  const compiler = (text, path) => {
4
- const scripts = importsWithinScripts(text, path);
5
- const styles = stylePreprocessorImports(text, path);
6
- if (!scripts)
7
- return styles;
8
- return styles ? `${scripts};\n${styles}` : scripts;
5
+ const parts = [
6
+ importsWithinScripts(text, path),
7
+ dynamicImportsWithinTemplate(text, path),
8
+ stylePreprocessorImports(text, path),
9
+ ];
10
+ return parts.filter(Boolean).join(';\n');
9
11
  };
10
12
  export default compiler;
@@ -38,8 +38,8 @@ const resolveConfig = async (config) => {
38
38
  return [...entryPatterns, ...externalDependencies];
39
39
  };
40
40
  const resolveFromAST = program => [
41
- ...[...collectPropertyValues(program, 'entry')].map(id => toProductionEntry(id, { allowIncludeExports: true })),
42
- ...[...collectPropertyValues(program, 'neverBundle')].map(id => toDependency(id, { optional: true })),
41
+ ...Array.from(collectPropertyValues(program, 'entry'), id => toProductionEntry(id, { allowIncludeExports: true })),
42
+ ...Array.from(collectPropertyValues(program, 'neverBundle'), id => toDependency(id, { optional: true })),
43
43
  ];
44
44
  const args = {
45
45
  config: true,
@@ -21,7 +21,7 @@ const resolveConfig = async (config) => {
21
21
  .map(id => toProductionEntry(id, { allowIncludeExports: true }));
22
22
  return entryPatterns;
23
23
  };
24
- const resolveFromAST = program => [...collectPropertyValues(program, 'entry')].map(id => toProductionEntry(id, { allowIncludeExports: true }));
24
+ const resolveFromAST = program => Array.from(collectPropertyValues(program, 'entry'), id => toProductionEntry(id, { allowIncludeExports: true }));
25
25
  const args = {
26
26
  config: true,
27
27
  };
@@ -70,7 +70,7 @@ const getConfigs = async (localConfig) => {
70
70
  for (const config of [localConfig].flat()) {
71
71
  if (config && typeof config !== 'string') {
72
72
  if (typeof config === 'function') {
73
- for (const command of ['dev', 'serve', 'build']) {
73
+ for (const command of ['serve', 'build']) {
74
74
  for (const mode of ['development', 'production']) {
75
75
  const cfg = await config({ command, mode, ssrBuild: undefined });
76
76
  configs.push(cfg);
@@ -58,7 +58,7 @@ export interface ViteConfig extends VitestConfig {
58
58
  extensions?: string[];
59
59
  };
60
60
  }
61
- export type COMMAND = 'dev' | 'serve' | 'build';
61
+ export type COMMAND = 'serve' | 'build';
62
62
  export type MODE = 'development' | 'production';
63
63
  interface Options {
64
64
  command: COMMAND;
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { toRelative } from '../util/path.js';
3
- import { flattenIssues, getIssuePrefix, getIssueTypeTitle } from './util/util.js';
3
+ import { flattenIssues, getIssueDescription, getIssuePrefix, getIssueTypeTitle } from './util/util.js';
4
4
  export default async ({ report, issues, cwd }) => {
5
5
  const entries = [];
6
6
  for (const [type, isReportType] of Object.entries(report)) {
@@ -47,10 +47,6 @@ function convertSeverity(severity) {
47
47
  return 'info';
48
48
  }
49
49
  }
50
- function getIssueDescription({ type, symbol, symbols, parentSymbol }) {
51
- const symbolDescription = symbols ? `${symbols.map(s => s.symbol).join(', ')}` : symbol;
52
- return `${getIssuePrefix(type)}: ${symbolDescription}${parentSymbol ? ` (${parentSymbol})` : ''}`;
53
- }
54
50
  function getSymbolDescription({ type, symbol, parentSymbol, }) {
55
51
  return `${getIssuePrefix(type)}: ${symbol.symbol}${parentSymbol ? ` (${parentSymbol})` : ''}`;
56
52
  }
@@ -1,5 +1,6 @@
1
1
  import { ISSUE_TYPE_TITLE } from '../constants.js';
2
2
  import { relative } from '../util/path.js';
3
+ import { compareStrings } from '../util/string.js';
3
4
  import { hintPrinters } from './util/configuration-hints.js';
4
5
  import { flattenIssues, getIssueTypeTitle } from './util/util.js';
5
6
  const createGitHubActionsLogger = () => {
@@ -31,7 +32,7 @@ export default ({ report, issues, cwd, configurationHints, tagHints, isDisableCo
31
32
  if (isReportType) {
32
33
  const title = reportMultipleGroups && getIssueTypeTitle(reportType);
33
34
  const issuesForType = flattenIssues(issues[reportType]);
34
- issuesForType.sort((a, b) => a.filePath.localeCompare(b.filePath) || (a.line ?? 0) - (b.line ?? 0));
35
+ issuesForType.sort((a, b) => compareStrings(a.filePath, b.filePath) || (a.line ?? 0) - (b.line ?? 0));
35
36
  if (issuesForType.length > 0) {
36
37
  title && core.info(`${title} (${issuesForType.length})`);
37
38
  for (const issue of issuesForType) {
@@ -55,7 +56,7 @@ export default ({ report, issues, cwd, configurationHints, tagHints, isDisableCo
55
56
  if (!isDisableConfigHints && configurationHints.length > 0) {
56
57
  const CONFIG_HINTS_TITLE = 'Configuration hints';
57
58
  core.info(`${CONFIG_HINTS_TITLE} (${configurationHints.length})`);
58
- const sortedHints = [...configurationHints].sort((a, b) => (a.filePath ?? '').localeCompare(b.filePath ?? ''));
59
+ const sortedHints = configurationHints.toSorted((a, b) => compareStrings(a.filePath ?? '', b.filePath ?? ''));
59
60
  for (const hint of sortedHints) {
60
61
  const hintPrinter = hintPrinters.get(hint.type);
61
62
  const message = hintPrinter?.print({
@@ -94,7 +95,8 @@ export default ({ report, issues, cwd, configurationHints, tagHints, isDisableCo
94
95
  if (!isDisableTagHints && tagHints.size > 0) {
95
96
  const TAG_HINTS_TITLE = 'Tag hints';
96
97
  core.info(`${TAG_HINTS_TITLE} (${tagHints.size})`);
97
- const sortedHints = [...tagHints].sort((a, b) => a.filePath.localeCompare(b.filePath));
98
+ const sortedHints = Array.from(tagHints);
99
+ sortedHints.sort((a, b) => compareStrings(a.filePath, b.filePath));
98
100
  for (const hint of sortedHints) {
99
101
  const file = relative(cwd, hint.filePath);
100
102
  const hintMessage = `Unused tag in ${file}: ${hint.identifier} → ${hint.tagName}`;
@@ -7,6 +7,7 @@ declare const _default: {
7
7
  codeclimate: ({ report, issues, cwd }: import("../types.ts").ReporterOptions) => Promise<void>;
8
8
  json: ({ report, issues, options, cwd }: import("../types.ts").ReporterOptions) => Promise<void>;
9
9
  markdown: ({ report, issues, cwd }: import("../types.ts").ReporterOptions) => void;
10
+ sarif: ({ report, issues, cwd }: import("../types.ts").ReporterOptions) => void;
10
11
  'github-actions': ({ report, issues, cwd, configurationHints, tagHints, isDisableConfigHints, isDisableTagHints, isTreatConfigHintsAsErrors, isTreatTagHintsAsErrors, configFilePath, }: import("../types.ts").ReporterOptions) => void;
11
12
  };
12
13
  export default _default;
@@ -6,6 +6,7 @@ import disclosure from './disclosure.js';
6
6
  import githubActions from './github-actions.js';
7
7
  import json from './json.js';
8
8
  import markdown from './markdown.js';
9
+ import sarif from './sarif.js';
9
10
  import symbols from './symbols.js';
10
11
  export default {
11
12
  symbols,
@@ -16,5 +17,6 @@ export default {
16
17
  codeclimate,
17
18
  json,
18
19
  markdown,
20
+ sarif,
19
21
  'github-actions': githubActions,
20
22
  };
@@ -15,6 +15,7 @@ export type JSONReportEntry = {
15
15
  owners?: Array<JSONReportNamedItem>;
16
16
  binaries?: Array<JSONReportNamedItem>;
17
17
  catalog?: Array<JSONReportItem>;
18
+ catalogReferences?: Array<JSONReportItem>;
18
19
  cycles?: Array<Array<JSONReportItem>>;
19
20
  dependencies?: Array<JSONReportItem>;
20
21
  devDependencies?: Array<JSONReportItem>;
@@ -20,6 +20,7 @@ export default async ({ report, issues, options, cwd }) => {
20
20
  ...(findOwners && { owners: findOwners(file).map(name => ({ name })) }),
21
21
  ...(report.binaries && { binaries: [] }),
22
22
  ...(report.catalog && { catalog: [] }),
23
+ ...(report.catalogReferences && { catalogReferences: [] }),
23
24
  ...(report.cycles && { cycles: [] }),
24
25
  ...(report.dependencies && { dependencies: [] }),
25
26
  ...(report.devDependencies && { devDependencies: [] }),
@@ -0,0 +1,3 @@
1
+ import type { ReporterOptions } from '../types/issues.ts';
2
+ export default _default;
3
+ declare function _default({ report, issues, cwd }: ReporterOptions): void;
@@ -0,0 +1,79 @@
1
+ import { ISSUE_TYPE_TITLE } from '../constants.js';
2
+ import { toPosix, toRelative } from '../util/path.js';
3
+ import { version } from '../version.js';
4
+ import { flattenIssues, getIssueDescription } from './util/util.js';
5
+ const schema = 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json';
6
+ const getLevel = (severity) => severity === 'error' ? 'error' : severity === 'warn' ? 'warning' : 'note';
7
+ const getProblemSeverity = (severity) => severity === 'error' ? 'error' : severity === 'warn' ? 'warning' : 'recommendation';
8
+ const getRuleId = (type) => `knip/${type}`;
9
+ const getUri = (filePath, cwd) => toPosix(toRelative(filePath, cwd))
10
+ .split('/')
11
+ .map(segment => encodeURIComponent(segment))
12
+ .join('/');
13
+ const getLocation = ({ filePath, line, col, symbol }, cwd) => ({
14
+ physicalLocation: {
15
+ artifactLocation: { uri: getUri(filePath, cwd) },
16
+ ...(line !== undefined && {
17
+ region: {
18
+ startLine: Math.max(line, 1),
19
+ ...(col !== undefined && {
20
+ startColumn: Math.max(col, 1),
21
+ endColumn: Math.max(col, 1) + Math.max(symbol.length, 1),
22
+ }),
23
+ },
24
+ }),
25
+ },
26
+ });
27
+ const sortByLocation = (a, b) => a.filePath.localeCompare(b.filePath) ||
28
+ (a.line ?? 0) - (b.line ?? 0) ||
29
+ (a.col ?? 0) - (b.col ?? 0) ||
30
+ a.symbol.localeCompare(b.symbol);
31
+ export default ({ report, issues, cwd }) => {
32
+ const groups = [];
33
+ for (const [type, isReportType] of Object.entries(report)) {
34
+ if (!isReportType)
35
+ continue;
36
+ const issuesForType = flattenIssues(issues[type]).sort(sortByLocation);
37
+ if (issuesForType.length > 0)
38
+ groups.push({ type, issues: issuesForType });
39
+ }
40
+ const rules = groups.map(({ type, issues }) => {
41
+ const severity = issues[0].severity;
42
+ const title = ISSUE_TYPE_TITLE[type];
43
+ return {
44
+ id: getRuleId(type),
45
+ name: type,
46
+ shortDescription: { text: title },
47
+ helpUri: 'https://knip.dev/reference/issue-types',
48
+ defaultConfiguration: { level: getLevel(severity) },
49
+ properties: { 'problem.severity': getProblemSeverity(severity) },
50
+ };
51
+ });
52
+ const results = groups.flatMap(({ type, issues }, ruleIndex) => issues.map(issue => ({
53
+ ruleId: getRuleId(type),
54
+ ruleIndex,
55
+ level: getLevel(issue.severity),
56
+ message: { text: getIssueDescription(issue) },
57
+ locations: [getLocation(issue, cwd)],
58
+ })));
59
+ const output = JSON.stringify({
60
+ $schema: schema,
61
+ version: '2.1.0',
62
+ runs: [
63
+ {
64
+ tool: {
65
+ driver: {
66
+ name: 'knip',
67
+ version,
68
+ semanticVersion: version,
69
+ informationUri: 'https://knip.dev',
70
+ rules,
71
+ },
72
+ },
73
+ results,
74
+ },
75
+ ],
76
+ });
77
+ process.stdout._handle?.setBlocking?.(true);
78
+ process.stdout.write(`${output}\n`);
79
+ };
@@ -1,6 +1,7 @@
1
1
  import st from '../util/colors.js';
2
2
  import { toRelative } from '../util/path.js';
3
3
  import { toRegexOrString } from '../util/regex.js';
4
+ import { compareStrings } from '../util/string.js';
4
5
  import { Table } from '../util/table.js';
5
6
  import { formatTrace } from '../util/trace.js';
6
7
  export default ({ graph, explorer, options, workspaceFilePathFilter, issues }) => {
@@ -11,7 +12,7 @@ export default ({ graph, explorer, options, workspaceFilePathFilter, issues }) =
11
12
  const seen = new Set();
12
13
  for (const [packageName, { imports }] of explorer.getDependencyUsage(pattern)) {
13
14
  const filtered = imports.filter(i => workspaceFilePathFilter(i.filePath));
14
- filtered.sort((a, b) => a.filePath.localeCompare(b.filePath) || (a.line ?? 0) - (b.line ?? 0));
15
+ filtered.sort((a, b) => compareStrings(a.filePath, b.filePath) || (a.line ?? 0) - (b.line ?? 0));
15
16
  for (const _import of filtered) {
16
17
  const pos = _import.line ? `:${_import.line}:${_import.col}` : '';
17
18
  const key = `${_import.filePath}${pos}:${packageName}`;
@@ -54,7 +55,7 @@ export default ({ graph, explorer, options, workspaceFilePathFilter, issues }) =
54
55
  }
55
56
  }
56
57
  }
57
- nodes.sort((a, b) => a.filePath.localeCompare(b.filePath) || a.identifier.localeCompare(b.identifier));
58
+ nodes.sort((a, b) => compareStrings(a.filePath, b.filePath) || compareStrings(a.identifier, b.identifier));
58
59
  const toRel = (path) => toRelative(path, options.cwd);
59
60
  if (nodes.length === 0) {
60
61
  if (options.traceFile && !graph.has(options.traceFile)) {