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
@@ -10,9 +10,9 @@ const isRootConfigReference = (specifier) => specifier === '//';
10
10
  const resolveExtends = (extendsArray, options) => {
11
11
  return extendsArray.map(specifier => {
12
12
  if (isRootConfigReference(specifier)) {
13
- return toConfig('biome', join(options.rootCwd, 'biome'), { containingFilePath: options.configFilePath });
13
+ return toConfig('biome', join(options.rootCwd, 'biome'));
14
14
  }
15
- return toConfig('biome', specifier, { containingFilePath: options.configFilePath });
15
+ return toConfig('biome', specifier);
16
16
  });
17
17
  };
18
18
  const resolveConfig = (config, options) => {
@@ -25,12 +25,12 @@ const toPluginSpecifier = (id) => {
25
25
  return id;
26
26
  return id.replace('/', '/commitlint-plugin-');
27
27
  };
28
- const resolveConfig = async (config, options) => {
28
+ const resolveConfig = async (config) => {
29
29
  const inputs = [];
30
30
  const extendsConfigs = [];
31
31
  for (const id of config.extends ? [config.extends].flat() : []) {
32
32
  if (isInternal(id))
33
- inputs.push(toConfig('commitlint', id, { containingFilePath: options.configFilePath }));
33
+ inputs.push(toConfig('commitlint', id));
34
34
  else
35
35
  extendsConfigs.push(toExtendsSpecifier(id));
36
36
  }
@@ -20,7 +20,7 @@ const getInputsDeprecated = (config, options) => {
20
20
  const extendsSpecifiers = compact(extendsList.map(resolveExtendSpecifier));
21
21
  if (extendsSpecifiers.some(specifier => specifier?.startsWith('eslint-plugin-prettier')))
22
22
  extendsSpecifiers.push('eslint-config-prettier');
23
- const extendConfigs = [...extendsList.filter(isInternal), ...extendsSpecifiers].map(specifier => toConfig('eslint', specifier, { containingFilePath: options.configFilePath }));
23
+ const extendConfigs = [...extendsList.filter(isInternal), ...extendsSpecifiers].map(specifier => toConfig('eslint', specifier));
24
24
  const plugins = config.plugins ? config.plugins.map(resolvePluginSpecifier) : [];
25
25
  const parsers = getParsers(config);
26
26
  const babelDependencies = config.parserOptions?.babelOptions
@@ -50,7 +50,7 @@ export const getDependencies = async (localConfig, options) => {
50
50
  if (platforms.includes('web')) {
51
51
  inputs.add(toProductionDependency('react-native-web'));
52
52
  inputs.add(toProductionDependency('react-dom'));
53
- if (!isExpoRouter) {
53
+ if (!isExpoRouter && !options.enabledPlugins.includes('expo-router')) {
54
54
  inputs.add(toDependency('@expo/metro-runtime'));
55
55
  }
56
56
  }
@@ -1,37 +1,17 @@
1
- import { toConfig, toIgnore, toProductionEntry } from '../../util/input.js';
2
- import { join } from '../../util/path.js';
1
+ import { toConfig, toIgnore } from '../../util/input.js';
3
2
  import { hasDependency } from '../../util/plugin.js';
4
- import { getConfig, getDependencies } from './helpers.js';
3
+ import { getDependencies } from './helpers.js';
5
4
  const title = 'Expo';
6
5
  const enablers = ['expo'];
7
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
- const config = ['app.json', 'app.config.{ts,js}'];
9
- const production = ['app/**/*.{js,jsx,ts,tsx}', 'src/app/**/*.{js,jsx,ts,tsx}'];
7
+ const config = ['app.json', 'app.config.{json,ts,js,mts,cts,mjs,cjs}'];
10
8
  const resolve = () => [toConfig('babel', 'babel.config'), toIgnore('babel-preset-expo', 'unresolved')];
11
- const resolveConfig = async (localConfig, options) => {
12
- const { manifest } = options;
13
- const config = getConfig(localConfig, options);
14
- if (manifest.main === 'expo-router/entry') {
15
- let patterns = [...production];
16
- const normalizedPlugins = config.plugins?.map(plugin => (Array.isArray(plugin) ? plugin : [plugin])) ?? [];
17
- const expoRouterPlugin = normalizedPlugins.find(([plugin]) => plugin === 'expo-router');
18
- if (expoRouterPlugin) {
19
- const [, options] = expoRouterPlugin;
20
- if (typeof options?.root === 'string') {
21
- patterns = [join(options.root, '**/*.{js,jsx,ts,tsx}')];
22
- }
23
- }
24
- return patterns.map(entry => toProductionEntry(entry)).concat(await getDependencies(localConfig, options));
25
- }
26
- return production.map(entry => toProductionEntry(entry)).concat(await getDependencies(localConfig, options));
27
- };
28
9
  const plugin = {
29
10
  title,
30
11
  enablers,
31
12
  isEnabled,
32
13
  config,
33
- production,
34
14
  resolve,
35
- resolveConfig,
15
+ resolveConfig: getDependencies,
36
16
  };
37
17
  export default plugin;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,32 @@
1
+ import { toProductionEntry } from '../../util/input.js';
2
+ import { join } from '../../util/path.js';
3
+ import { hasDependency } from '../../util/plugin.js';
4
+ import expo from '../expo/index.js';
5
+ import { getConfig } from '../expo/helpers.js';
6
+ const title = 'Expo Router';
7
+ const enablers = ['expo-router'];
8
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
9
+ const config = expo.config;
10
+ const production = ['app/**/*.{js,jsx,ts,tsx}', 'src/app/**/*.{js,jsx,ts,tsx}'];
11
+ const resolveConfig = async (localConfig, options) => {
12
+ const config = getConfig(localConfig, options);
13
+ for (const plugin of config.plugins ?? []) {
14
+ if (!Array.isArray(plugin))
15
+ continue;
16
+ const [name, pluginOptions] = plugin;
17
+ const root = pluginOptions?.root;
18
+ if (name === 'expo-router' && typeof root === 'string') {
19
+ return [toProductionEntry(join(root, '**/*.{js,jsx,ts,tsx}'))];
20
+ }
21
+ }
22
+ return production.map(id => toProductionEntry(id));
23
+ };
24
+ const plugin = {
25
+ title,
26
+ enablers,
27
+ isEnabled,
28
+ config,
29
+ production,
30
+ resolveConfig,
31
+ };
32
+ export default plugin;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,57 @@
1
+ import { escapePath } from 'tinyglobby';
2
+ import { findImportedCalls, findProperty } from '../../typescript/ast-helpers.js';
3
+ import { getStringValue } from '../../typescript/ast-nodes.js';
4
+ import { toProductionEntry } from '../../util/input.js';
5
+ import { join, relative, toAbsolute } from '../../util/path.js';
6
+ import { hasDependency } from '../../util/plugin.js';
7
+ import { getViteRoot } from '../vite/helpers.js';
8
+ const title = 'filesystem-routing';
9
+ const enablers = ['filesystem-routing'];
10
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
11
+ const config = ['vite.config.{js,mjs,ts,cjs,mts,cts}'];
12
+ const defaultDir = 'src/routes';
13
+ const defaultExtensions = ['js', 'jsx', 'ts', 'tsx', 'tsrx'];
14
+ const production = [`${defaultDir}/**/*.{${defaultExtensions.join(',')}}`];
15
+ const resolveFromAST = (program, { config, cwd }) => {
16
+ if (config.entry)
17
+ return [];
18
+ const root = getViteRoot(program, cwd);
19
+ const toEntry = (dir, pattern) => toProductionEntry(join(cwd, escapePath(relative(cwd, dir)), pattern));
20
+ const calls = findImportedCalls(program, 'filesystem-routing/vite');
21
+ if (calls.length === 0)
22
+ return production.map(pattern => toEntry(root, pattern));
23
+ const inputs = [];
24
+ for (const call of calls) {
25
+ const options = call.arguments[0];
26
+ const dir = getStringValue(findProperty(options, 'dir')) ?? defaultDir;
27
+ const value = findProperty(options, 'extensions');
28
+ const extensions = new Set();
29
+ let useDefaults = value?.type !== 'ArrayExpression';
30
+ if (value?.type === 'ArrayExpression') {
31
+ for (const element of value.elements) {
32
+ const extension = getStringValue(element);
33
+ if (extension === undefined)
34
+ useDefaults = true;
35
+ else
36
+ extensions.add(extension);
37
+ }
38
+ }
39
+ if (useDefaults)
40
+ for (const extension of defaultExtensions)
41
+ extensions.add(extension);
42
+ if (extensions.size === 0)
43
+ continue;
44
+ const suffix = extensions.size === 1 ? [...extensions][0] : `{${[...extensions].join(',')}}`;
45
+ inputs.push(toEntry(toAbsolute(dir, root), `**/*.${suffix}`));
46
+ }
47
+ return inputs;
48
+ };
49
+ const plugin = {
50
+ title,
51
+ enablers,
52
+ isEnabled,
53
+ config,
54
+ production,
55
+ resolveFromAST,
56
+ };
57
+ export default plugin;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,42 @@
1
+ import { collectFirstPropertyValue } from '../../typescript/ast-helpers.js';
2
+ import { isDirectory } from '../../util/fs.js';
3
+ import { toProductionEntry } from '../../util/input.js';
4
+ import { join } from '../../util/path.js';
5
+ import { hasDependency } from '../../util/plugin.js';
6
+ const title = 'Flue';
7
+ const enablers = ['@flue/runtime'];
8
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
9
+ const config = ['flue.config.{ts,mts,mjs,js,cjs,cts}'];
10
+ const extensions = '{ts,mts,js,mjs}';
11
+ const entryNames = ['app', 'db', 'cloudflare'];
12
+ const agentsPattern = `{agent,agents/*,agents/*/agent}.${extensions}`;
13
+ const production = [`{.flue/,src/,}{app,db,cloudflare}.${extensions}`, `{.flue/,src/,}${agentsPattern}`];
14
+ const getSourceRoot = (dir) => {
15
+ for (const name of ['.flue', 'src'])
16
+ if (isDirectory(dir, name))
17
+ return join(dir, name);
18
+ return dir;
19
+ };
20
+ const resolveFromAST = (program, { configFileDir }) => {
21
+ const sourceRoot = getSourceRoot(configFileDir);
22
+ const inputs = [];
23
+ for (const name of entryNames) {
24
+ const filePath = collectFirstPropertyValue(program, name);
25
+ if (filePath)
26
+ inputs.push(toProductionEntry(join(configFileDir, filePath)));
27
+ else
28
+ inputs.push(toProductionEntry(join(sourceRoot, `${name}.${extensions}`)));
29
+ }
30
+ const agents = collectFirstPropertyValue(program, 'agents') ?? agentsPattern;
31
+ inputs.push(toProductionEntry(join(sourceRoot, agents)));
32
+ return inputs;
33
+ };
34
+ const plugin = {
35
+ title,
36
+ enablers,
37
+ isEnabled,
38
+ config,
39
+ production,
40
+ resolveFromAST,
41
+ };
42
+ export default plugin;
@@ -29,7 +29,7 @@ const resolveConfig = async (config, options) => {
29
29
  const path = action?.with?.path;
30
30
  for (const step of steps.steps) {
31
31
  const workingDir = step['working-directory'];
32
- const dir = join(rootCwd, path && workingDir ? relative(workingDir, path) : workingDir ? workingDir : '.');
32
+ const dir = join(rootCwd, path && workingDir ? relative(path, workingDir) : workingDir ? workingDir : '.');
33
33
  if (step.run) {
34
34
  const manifest = getManifest(dir) ?? options.manifest;
35
35
  const actionPath = relative(dir, configFileDir);
@@ -35,6 +35,7 @@ export declare const Plugins: {
35
35
  eve: import("../types/config.ts").Plugin;
36
36
  execa: import("../types/config.ts").Plugin;
37
37
  expo: import("../types/config.ts").Plugin;
38
+ 'expo-router': import("../types/config.ts").Plugin;
38
39
  'expressive-code': {
39
40
  title: string;
40
41
  enablers: string[];
@@ -42,6 +43,8 @@ export declare const Plugins: {
42
43
  config: string[];
43
44
  };
44
45
  fast: import("../types/config.ts").Plugin;
46
+ 'filesystem-routing': import("../types/config.ts").Plugin;
47
+ flue: import("../types/config.ts").Plugin;
45
48
  fumadocs: import("../types/config.ts").Plugin;
46
49
  gatsby: import("../types/config.ts").Plugin;
47
50
  'github-action': import("../types/config.ts").Plugin;
@@ -135,6 +138,7 @@ export declare const Plugins: {
135
138
  'serverless-framework': import("../types/config.ts").Plugin;
136
139
  'simple-git-hooks': import("../types/config.ts").Plugin;
137
140
  'size-limit': import("../types/config.ts").Plugin;
141
+ 'solidjs-vite-plugin': import("../types/config.ts").Plugin;
138
142
  sst: import("../types/config.ts").Plugin;
139
143
  starlight: import("../types/config.ts").Plugin;
140
144
  stencil: import("../types/config.ts").Plugin;
@@ -158,6 +162,7 @@ export declare const Plugins: {
158
162
  'ts-node': import("../types/config.ts").Plugin;
159
163
  tsd: import("../types/config.ts").Plugin;
160
164
  tsdown: import("../types/config.ts").Plugin;
165
+ tsrs: import("../types/config.ts").Plugin;
161
166
  tsup: import("../types/config.ts").Plugin;
162
167
  tsx: import("../types/config.ts").Plugin;
163
168
  turbo: import("../types/config.ts").Plugin;
@@ -34,8 +34,11 @@ import { default as eslint } from './eslint/index.js';
34
34
  import { default as eve } from './eve/index.js';
35
35
  import { default as execa } from './execa/index.js';
36
36
  import { default as expo } from './expo/index.js';
37
+ import { default as expoRouter } from './expo-router/index.js';
37
38
  import { default as expressiveCode } from './expressive-code/index.js';
38
39
  import { default as fast } from './fast/index.js';
40
+ import { default as filesystemRouting } from './filesystem-routing/index.js';
41
+ import { default as flue } from './flue/index.js';
39
42
  import { default as fumadocs } from './fumadocs/index.js';
40
43
  import { default as gatsby } from './gatsby/index.js';
41
44
  import { default as githubAction } from './github-action/index.js';
@@ -129,6 +132,7 @@ import { default as sentry } from './sentry/index.js';
129
132
  import { default as serverlessFramework } from './serverless-framework/index.js';
130
133
  import { default as simpleGitHooks } from './simple-git-hooks/index.js';
131
134
  import { default as sizeLimit } from './size-limit/index.js';
135
+ import { default as solidjsVitePlugin } from './solidjs-vite-plugin/index.js';
132
136
  import { default as sst } from './sst/index.js';
133
137
  import { default as starlight } from './starlight/index.js';
134
138
  import { default as stencil } from './stencil/index.js';
@@ -152,6 +156,7 @@ import { default as travis } from './travis/index.js';
152
156
  import { default as tsNode } from './ts-node/index.js';
153
157
  import { default as tsd } from './tsd/index.js';
154
158
  import { default as tsdown } from './tsdown/index.js';
159
+ import { default as tsrs } from './tsrs/index.js';
155
160
  import { default as tsup } from './tsup/index.js';
156
161
  import { default as tsx } from './tsx/index.js';
157
162
  import { default as turbo } from './turbo/index.js';
@@ -225,8 +230,11 @@ export const Plugins = {
225
230
  eve,
226
231
  execa,
227
232
  expo,
233
+ 'expo-router': expoRouter,
228
234
  'expressive-code': expressiveCode,
229
235
  fast,
236
+ 'filesystem-routing': filesystemRouting,
237
+ flue,
230
238
  fumadocs,
231
239
  gatsby,
232
240
  'github-action': githubAction,
@@ -320,6 +328,7 @@ export const Plugins = {
320
328
  'serverless-framework': serverlessFramework,
321
329
  'simple-git-hooks': simpleGitHooks,
322
330
  'size-limit': sizeLimit,
331
+ 'solidjs-vite-plugin': solidjsVitePlugin,
323
332
  sst,
324
333
  starlight,
325
334
  stencil,
@@ -343,6 +352,7 @@ export const Plugins = {
343
352
  'ts-node': tsNode,
344
353
  tsd,
345
354
  tsdown,
355
+ tsrs,
346
356
  tsup,
347
357
  tsx,
348
358
  turbo,
@@ -5,6 +5,7 @@ import { isInternal, join, normalize, toAbsolute } from '../../util/path.js';
5
5
  import { hasDependency } from '../../util/plugin.js';
6
6
  import { getDependenciesFromConfig } from '../babel/index.js';
7
7
  import { getReportersDependencies, resolveExtensibleConfig, resolveWithPrefix } from './helpers.js';
8
+ import { createJestMockVisitor } from './visitors/mock.js';
8
9
  const title = 'Jest';
9
10
  const enablers = ['jest'];
10
11
  const isEnabled = ({ dependencies, manifest }) => hasDependency(dependencies, enablers) || Boolean(manifest.name?.startsWith('jest-presets'));
@@ -127,6 +128,9 @@ const resolveConfig = async (localConfig, options) => {
127
128
  const args = {
128
129
  config: true,
129
130
  };
131
+ const registerVisitors = ({ ctx, registerVisitor }) => {
132
+ registerVisitor(createJestMockVisitor(ctx));
133
+ };
130
134
  const plugin = {
131
135
  title,
132
136
  enablers,
@@ -135,5 +139,6 @@ const plugin = {
135
139
  entry,
136
140
  resolveConfig,
137
141
  args,
142
+ registerVisitors,
138
143
  };
139
144
  export default plugin;
@@ -0,0 +1,2 @@
1
+ import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';
2
+ export declare function createJestMockVisitor(ctx: PluginVisitorContext): PluginVisitorObject;
@@ -0,0 +1,91 @@
1
+ import { IMPORT_FLAGS } from '../../../constants.js';
2
+ import { getPropertyKey } from '../../../typescript/ast-helpers.js';
3
+ import { getStringValue, isStringLiteral } from '../../../typescript/ast-nodes.js';
4
+ import { isShadowed } from '../../../typescript/visitors/walk.js';
5
+ const mockMethods = new Set([
6
+ 'mock',
7
+ 'doMock',
8
+ 'requireActual',
9
+ 'requireMock',
10
+ 'createMockFromModule',
11
+ 'unmock',
12
+ 'dontMock',
13
+ 'deepUnmock',
14
+ 'setMock',
15
+ 'unstable_mockModule',
16
+ 'unstable_unmockModule',
17
+ ]);
18
+ export function createJestMockVisitor(ctx) {
19
+ const jestNames = new Set();
20
+ const virtualMocks = new Set();
21
+ const virtualEsmMocks = new Set();
22
+ const mockLookups = [];
23
+ return {
24
+ Program(node) {
25
+ jestNames.clear();
26
+ virtualMocks.clear();
27
+ virtualEsmMocks.clear();
28
+ mockLookups.length = 0;
29
+ jestNames.add('jest');
30
+ for (const statement of node.body) {
31
+ if (statement.type !== 'ImportDeclaration' ||
32
+ !isStringLiteral(statement.source) ||
33
+ getStringValue(statement.source) !== '@jest/globals')
34
+ continue;
35
+ for (const specifier of statement.specifiers ?? []) {
36
+ if (specifier.type === 'ImportSpecifier' &&
37
+ specifier.imported.type === 'Identifier' &&
38
+ specifier.imported.name === 'jest') {
39
+ jestNames.add(specifier.local.name);
40
+ }
41
+ }
42
+ }
43
+ },
44
+ CallExpression(node) {
45
+ const argument = node.arguments[0];
46
+ const callee = node.callee;
47
+ if (!isStringLiteral(argument) ||
48
+ callee.type !== 'MemberExpression' ||
49
+ callee.computed ||
50
+ callee.object.type !== 'Identifier' ||
51
+ !jestNames.has(callee.object.name) ||
52
+ isShadowed(callee.object.name, callee.object.start) ||
53
+ callee.property.type !== 'Identifier' ||
54
+ !mockMethods.has(callee.property.name))
55
+ return;
56
+ const specifier = getStringValue(argument);
57
+ const method = callee.property.name;
58
+ const options = node.arguments[2];
59
+ if ((method === 'mock' || method === 'doMock' || method === 'unstable_mockModule') &&
60
+ options?.type === 'ObjectExpression') {
61
+ for (const property of options.properties) {
62
+ if (property.type === 'Property' &&
63
+ !property.computed &&
64
+ getPropertyKey(property) === 'virtual' &&
65
+ property.value.type === 'Literal' &&
66
+ property.value.value === true) {
67
+ (method === 'unstable_mockModule' ? virtualEsmMocks : virtualMocks).add(specifier);
68
+ return;
69
+ }
70
+ }
71
+ }
72
+ if (method === 'requireActual' || method === 'createMockFromModule') {
73
+ const flags = method === 'requireActual' ? IMPORT_FLAGS.OPAQUE : IMPORT_FLAGS.SIDE_EFFECTS;
74
+ ctx.addImport(specifier, argument.start, flags);
75
+ return;
76
+ }
77
+ mockLookups.push({
78
+ specifier,
79
+ pos: argument.start,
80
+ isEsm: method === 'unstable_mockModule' || method === 'unstable_unmockModule',
81
+ });
82
+ },
83
+ 'Program:exit'() {
84
+ for (const { specifier, pos, isEsm } of mockLookups) {
85
+ if ((isEsm ? virtualEsmMocks : virtualMocks).has(specifier))
86
+ continue;
87
+ ctx.addImport(specifier, pos, IMPORT_FLAGS.SIDE_EFFECTS);
88
+ }
89
+ },
90
+ };
91
+ }
@@ -1,4 +1,4 @@
1
- import { parse } from 'unbash';
1
+ import { forEachChild, parse } from 'unbash';
2
2
  import { getDependenciesFromCommand, getInputsFromNodeOptions } from '../../binaries/command.js';
3
3
  import { spawningBinaries } from '../../binaries/fallback.js';
4
4
  import { isWrapper } from '../../binaries/plugins.js';
@@ -7,99 +7,79 @@ import { toScript } from '../../binaries/util.js';
7
7
  import { SCRIPT_INTERPOLATION } from '../../constants.js';
8
8
  import { isBinary } from '../../util/input.js';
9
9
  import { extractBinary, isRelativeNodeModulesBin } from '../../util/modules.js';
10
- import { walkCommands } from '../../util/scripts.js';
10
+ import { isScriptExpansion, collectCommands } from '../../util/scripts.js';
11
11
  export const getInputsFromMiseScript = (script, options, hasLocalBinPath) => {
12
12
  const inputs = [];
13
13
  const definedFunctions = new Set();
14
- const readParts = (parts) => {
14
+ const readCommand = (command, source) => {
15
15
  let text = '';
16
- let value = '';
17
- let hasExpansion = false;
18
- for (const part of parts) {
19
- if (part.type === 'CommandExpansion' || part.type === 'ProcessSubstitution') {
20
- if (part.script)
21
- readScript(part.script);
22
- text += SCRIPT_INTERPOLATION;
23
- value += SCRIPT_INTERPOLATION;
24
- hasExpansion = true;
16
+ let cursor = command.pos;
17
+ const visit = (node) => {
18
+ if (!isScriptExpansion(node)) {
19
+ forEachChild(node, visit);
20
+ return;
25
21
  }
26
- else if (part.type === 'DoubleQuoted' || part.type === 'LocaleString') {
27
- const inner = readParts(part.parts);
28
- text += `${part.type === 'LocaleString' ? '$"' : '"'}${inner.text}"`;
29
- value += inner.value;
30
- if (inner.hasExpansion)
31
- hasExpansion = true;
22
+ if (node.script)
23
+ readScript(node.script, node.script.source ?? source);
24
+ if (node.pos >= cursor && node.end <= command.end) {
25
+ text += source.slice(cursor, node.pos) + SCRIPT_INTERPOLATION;
26
+ cursor = node.end;
32
27
  }
33
- else {
34
- text += part.text;
35
- value += 'value' in part ? part.value : part.text;
36
- }
37
- }
38
- return { text, value, hasExpansion };
39
- };
40
- const readWord = (word) => {
41
- if (!word.parts)
42
- return word;
43
- const { text, value, hasExpansion } = readParts(word.parts);
44
- return hasExpansion ? { ...word, text, value } : word;
28
+ };
29
+ forEachChild(command, visit);
30
+ return text + source.slice(cursor, command.end);
45
31
  };
46
- const readScript = (parsed) => {
32
+ const readScript = (parsed, source) => {
47
33
  for (const statement of parsed.commands) {
48
34
  if (statement.command.type === 'Function')
49
35
  definedFunctions.add(statement.command.name.value);
50
36
  }
51
- for (const statement of parsed.commands) {
52
- for (const command of walkCommands(statement.command)) {
53
- const name = command.name?.value;
54
- const word = command.name && readWord(command.name);
55
- const prefix = command.prefix.map(assignment => {
56
- const value = assignment.value && readWord(assignment.value);
57
- return value && value !== assignment.value
58
- ? { ...assignment, value, text: `${assignment.name}=${value.text}` }
59
- : assignment;
60
- });
61
- const words = command.suffix.map(readWord);
62
- if (!name || !word || definedFunctions.has(name))
37
+ for (const command of collectCommands(parsed)) {
38
+ const text = readCommand(command, source);
39
+ const name = command.name?.value;
40
+ if (!name || definedFunctions.has(name))
41
+ continue;
42
+ const binary = extractBinary(name);
43
+ const isPackageCommand = isPackageManager(binary);
44
+ const isExplicit = isPackageCommand || name.startsWith('./') || isRelativeNodeModulesBin(name);
45
+ const hasPath = command.prefix.some(item => item.type === 'Assignment' && item.name === 'PATH');
46
+ const isLocal = hasLocalBinPath && !hasPath;
47
+ const isWrapperCommand = !isPackageCommand && isWrapper(binary);
48
+ if (!isExplicit && !isLocal && !isWrapperCommand && binary !== 'node' && binary !== 'find')
49
+ continue;
50
+ let commandInputs;
51
+ if (isWrapperCommand || binary === 'find') {
52
+ const [marked] = collectCommands(parse(text));
53
+ if (!marked)
63
54
  continue;
64
- const binary = extractBinary(name);
65
- const isPackageCommand = isPackageManager(binary);
66
- const isExplicit = isPackageCommand || name.startsWith('./') || isRelativeNodeModulesBin(name);
67
- const isLocal = hasLocalBinPath && !command.prefix.some(assignment => assignment.name === 'PATH');
68
- const isWrapperCommand = !isPackageCommand && isWrapper(binary);
69
- if (!isExplicit && !isLocal && !isWrapperCommand && binary !== 'node' && binary !== 'find')
55
+ const fromScript = (script) => {
56
+ inputs.push(...getInputsFromMiseScript(script, options, isLocal));
57
+ return [];
58
+ };
59
+ const fromArgs = args => fromScript(toScript(args));
60
+ commandInputs = getDependenciesFromCommand(marked, { ...options, fromArgs });
61
+ if (spawningBinaries.includes(binary))
62
+ commandInputs.push(...getInputsFromNodeOptions(marked.prefix));
63
+ }
64
+ else {
65
+ commandInputs = options.getInputsFromScripts(text, { cwd: options.cwd, manifest: options.manifest });
66
+ }
67
+ if (!isExplicit && !isLocal && binary !== 'node' && binary !== 'find')
68
+ continue;
69
+ const binaryInput = commandInputs.find(input => isBinary(input) && input.specifier === binary);
70
+ if (!isExplicit && isLocal && binaryInput)
71
+ binaryInput.optional = true;
72
+ for (const input of commandInputs) {
73
+ if (!input.specifier || input.specifier.includes(SCRIPT_INTERPOLATION))
70
74
  continue;
71
- let commandInputs;
72
- if (isWrapperCommand || binary === 'find') {
73
- const fromScript = (script) => {
74
- inputs.push(...getInputsFromMiseScript(script, options, isLocal));
75
- return [];
76
- };
77
- const fromArgs = args => fromScript(toScript(args));
78
- commandInputs = getDependenciesFromCommand({ ...command, name: word, prefix, suffix: words }, { ...options, fromArgs });
79
- }
80
- else {
81
- commandInputs = options.getInputsFromScripts([...prefix.map(assignment => assignment.text), word.text, ...words.map(word => word.text)].join(' '), { cwd: options.cwd, manifest: options.manifest });
82
- }
83
- if (!isExplicit && !isLocal && binary !== 'node' && binary !== 'find')
75
+ if (!isExplicit && !isLocal && input === binaryInput)
84
76
  continue;
85
- if (isWrapperCommand && spawningBinaries.includes(binary)) {
86
- commandInputs.push(...getInputsFromNodeOptions(prefix));
87
- }
88
- const binaryInput = commandInputs.find(input => isBinary(input) && input.specifier === binary);
89
- if (!isExplicit && isLocal && binaryInput)
90
- binaryInput.optional = true;
91
- for (const input of commandInputs) {
92
- if (!input.specifier || input.specifier.includes(SCRIPT_INTERPOLATION))
93
- continue;
94
- if (!isExplicit && !isLocal && input === binaryInput)
95
- continue;
96
- inputs.push(input);
97
- }
77
+ inputs.push(input);
98
78
  }
99
79
  }
100
80
  };
101
81
  try {
102
- readScript(parse(script));
82
+ readScript(parse(script), script);
103
83
  }
104
84
  catch {
105
85
  return [];
@@ -1,3 +1,4 @@
1
+ import { relative } from '../../util/path.js';
1
2
  import { hasDependency } from '../../util/plugin.js';
2
3
  import { toShellCommand } from '../../util/scripts.js';
3
4
  const title = 'moonrepo';
@@ -7,7 +8,7 @@ const isRootOnly = true;
7
8
  const config = ['moon.yml', '.moon/tasks.yml', '.moon/tasks/*.yml'];
8
9
  const resolveConfig = async (config, options) => {
9
10
  const tasks = config.tasks ? Object.values(config.tasks) : [];
10
- const expand = (value) => value.replace('$workspaceRoot', options.rootCwd).replace('$projectRoot', options.cwd);
11
+ const expand = (value) => value.replace('$workspaceRoot', relative(options.cwd, options.rootCwd)).replace('$projectRoot', '.');
11
12
  const inputs = tasks
12
13
  .map(task => task.command)
13
14
  .filter(command => command)