knip 6.37.0 → 6.39.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 (61) hide show
  1. package/dist/ConfigurationChief.d.ts +18 -0
  2. package/dist/compilers/compilers.js +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/graph/analyze.js +30 -17
  5. package/dist/graph-explorer/explorer.d.ts +1 -1
  6. package/dist/graph-explorer/operations/is-referenced.d.ts +1 -1
  7. package/dist/graph-explorer/operations/is-referenced.js +8 -4
  8. package/dist/plugins/_vue/auto-import.js +17 -10
  9. package/dist/plugins/angular/index.js +1 -1
  10. package/dist/plugins/cucumber/index.js +36 -4
  11. package/dist/plugins/cucumber/types.d.ts +2 -1
  12. package/dist/plugins/eslint/helpers.d.ts +2 -0
  13. package/dist/plugins/eslint/helpers.js +14 -3
  14. package/dist/plugins/eslint/resolveFromAST.js +12 -8
  15. package/dist/plugins/eve/index.js +26 -8
  16. package/dist/plugins/index.d.ts +3 -0
  17. package/dist/plugins/index.js +6 -0
  18. package/dist/plugins/mdx/index.d.ts +3 -0
  19. package/dist/plugins/mdx/index.js +16 -6
  20. package/dist/plugins/mocha/index.js +3 -1
  21. package/dist/plugins/mocha/types.d.ts +1 -0
  22. package/dist/plugins/oxlint/index.js +24 -8
  23. package/dist/plugins/oxlint/types.d.ts +1 -0
  24. package/dist/plugins/playwright/index.js +3 -3
  25. package/dist/plugins/plop/index.js +2 -2
  26. package/dist/plugins/railway/index.d.ts +3 -0
  27. package/dist/plugins/railway/index.js +129 -0
  28. package/dist/plugins/rstest/index.js +116 -17
  29. package/dist/plugins/rstest/types.d.ts +29 -5
  30. package/dist/plugins/rstest/visitors/mock.d.ts +2 -0
  31. package/dist/plugins/rstest/visitors/mock.js +52 -0
  32. package/dist/plugins/turbo/index.d.ts +3 -0
  33. package/dist/plugins/turbo/index.js +23 -0
  34. package/dist/plugins/typescript/content-mappers.d.ts +4 -0
  35. package/dist/plugins/typescript/content-mappers.js +4 -0
  36. package/dist/plugins/typescript/index.js +14 -18
  37. package/dist/plugins/typescript-content-mapper/index.d.ts +3 -0
  38. package/dist/plugins/typescript-content-mapper/index.js +23 -0
  39. package/dist/plugins/typescript-content-mapper/types.d.ts +3 -0
  40. package/dist/plugins/typescript-content-mapper/types.js +1 -0
  41. package/dist/plugins/vite/helpers.d.ts +1 -1
  42. package/dist/plugins/vite/helpers.js +7 -8
  43. package/dist/plugins/vitest/index.js +15 -5
  44. package/dist/plugins/vitest/types.d.ts +10 -0
  45. package/dist/schema/configuration.d.ts +45 -0
  46. package/dist/schema/plugins.d.ts +15 -0
  47. package/dist/schema/plugins.js +3 -0
  48. package/dist/types/PluginNames.d.ts +2 -2
  49. package/dist/types/PluginNames.js +3 -0
  50. package/dist/types/package-json.d.ts +5 -0
  51. package/dist/types/tsconfig-json.d.ts +1 -3
  52. package/dist/util/create-options.d.ts +30 -0
  53. package/dist/util/git.d.ts +1 -0
  54. package/dist/util/git.js +23 -1
  55. package/dist/util/package-json.d.ts +8 -0
  56. package/dist/util/tag.d.ts +1 -0
  57. package/dist/util/tag.js +1 -1
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/package.json +1 -1
  61. package/schema.json +12 -0
@@ -1,25 +1,35 @@
1
- import { toDependency } from '../../util/input.js';
1
+ import { toDeferResolveProductionEntry, toDependency } from '../../util/input.js';
2
+ import { isInternal } from '../../util/path.js';
2
3
  import { hasDependency } from '../../util/plugin.js';
3
4
  const title = 'MDX';
4
5
  const enablers = ['astro', 'mdxlint'];
5
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
7
  const config = ['tsconfig.json'];
7
- const takeDependencies = (config) => {
8
+ const takeDependencies = (plugins) => {
8
9
  const inputs = [];
9
- if (Array.isArray(config.plugins)) {
10
- for (const plugin of config.plugins) {
10
+ if (Array.isArray(plugins)) {
11
+ for (const plugin of plugins) {
11
12
  if (typeof plugin === 'string')
12
13
  inputs.push(toDependency(plugin));
13
- else if (typeof plugin[0] === 'string')
14
+ else if (Array.isArray(plugin) && typeof plugin[0] === 'string')
14
15
  inputs.push(toDependency(plugin[0]));
15
16
  }
16
17
  }
17
18
  return inputs;
18
19
  };
20
+ export const contentMappers = ['mdx-content-mapper', '@mdx-js/content-mapper'];
21
+ const bundledPlugins = ['remark-mdx-frontmatter'];
22
+ export const resolveContentMapperOptions = (options) => {
23
+ const inputs = takeDependencies(options.remarkPlugins).filter(input => !bundledPlugins.includes(input.specifier));
24
+ const provider = options.providerImportSource;
25
+ if (typeof provider === 'string' && isInternal(provider))
26
+ inputs.push(toDeferResolveProductionEntry(provider));
27
+ return inputs;
28
+ };
19
29
  const resolveConfig = async (config, options) => {
20
30
  const { configFileName } = options;
21
31
  if (configFileName === 'tsconfig.json' && 'mdx' in config)
22
- return takeDependencies(config.mdx);
32
+ return takeDependencies(config.mdx.plugins);
23
33
  return [];
24
34
  };
25
35
  const plugin = {
@@ -1,4 +1,4 @@
1
- import { toDeferResolve, toEntry } from '../../util/input.js';
1
+ import { toDeferResolve, toDeferResolveEntry, toEntry } from '../../util/input.js';
2
2
  import { isDirectory } from '../../util/fs.js';
3
3
  import { hasDependency } from '../../util/plugin.js';
4
4
  const title = 'Mocha';
@@ -9,9 +9,11 @@ const entry = ['**/test/*.{js,cjs,mjs}'];
9
9
  const defaultExtensions = ['js', 'cjs', 'mjs'];
10
10
  const resolveConfig = localConfig => {
11
11
  const entryPatterns = localConfig.spec ? [localConfig.spec].flat() : entry;
12
+ const files = localConfig.file ? [localConfig.file].flat() : [];
12
13
  const require = localConfig.require ? [localConfig.require].flat() : [];
13
14
  const inputs = [];
14
15
  inputs.push(...entryPatterns.map(id => toEntry(id)));
16
+ inputs.push(...files.map(id => toDeferResolveEntry(id)));
15
17
  inputs.push(...require.map(id => toDeferResolve(id)));
16
18
  return inputs;
17
19
  };
@@ -1,4 +1,5 @@
1
1
  export interface MochaConfig {
2
+ file?: string | string[];
2
3
  require?: string | string[];
3
4
  spec?: string | string[];
4
5
  }
@@ -1,7 +1,7 @@
1
1
  import { Visitor } from 'oxc-parser';
2
2
  import { findProperty, getPropertyValues } from '../../typescript/ast-helpers.js';
3
- import { toDependency, toEntry } from '../../util/input.js';
4
- import { isInternal } from '../../util/path.js';
3
+ import { toConfig, toDependency, toEntry } from '../../util/input.js';
4
+ import { dirname, isInternal, toAbsolute } from '../../util/path.js';
5
5
  import { hasDependency } from '../../util/plugin.js';
6
6
  import { getInputsFromSettings } from '../eslint/helpers.js';
7
7
  import { getInputsFromSettingsAST } from '../eslint/resolveFromAST.js';
@@ -13,28 +13,42 @@ const isViteConfig = (configFileName) => configFileName.startsWith('vite.config.
13
13
  const args = {
14
14
  config: true,
15
15
  };
16
- const resolveJsPlugins = (jsPlugins) => {
16
+ const resolveJsPlugins = (jsPlugins, configFilePath) => {
17
17
  const inputs = [];
18
+ const dir = dirname(configFilePath);
18
19
  for (const plugin of jsPlugins ?? []) {
19
20
  const specifier = typeof plugin === 'string' ? plugin : plugin.specifier;
20
21
  if (!isInternal(specifier))
21
22
  inputs.push(toDependency(specifier));
22
23
  else
23
- inputs.push(toEntry(specifier));
24
+ inputs.push(toEntry(toAbsolute(specifier, dir)));
24
25
  }
25
26
  return inputs;
26
27
  };
27
28
  const isLoadConfig = ({ configFileName }) => !isViteConfig(configFileName);
28
- const resolveConfig = config => {
29
- const inputs = resolveJsPlugins(config.jsPlugins);
29
+ const resolveExtendedConfig = (config, configFilePath) => {
30
+ const inputs = [];
31
+ for (const entry of config.extends ?? []) {
32
+ if (typeof entry === 'string') {
33
+ const filePath = toAbsolute(entry, dirname(configFilePath));
34
+ inputs.push(toConfig('oxlint', filePath, { containingFilePath: configFilePath }));
35
+ }
36
+ else {
37
+ for (const input of resolveExtendedConfig(entry, configFilePath))
38
+ inputs.push(input);
39
+ }
40
+ }
41
+ for (const input of resolveJsPlugins(config.jsPlugins, configFilePath))
42
+ inputs.push(input);
30
43
  for (const override of config.overrides ?? []) {
31
- for (const input of resolveJsPlugins(override.jsPlugins))
44
+ for (const input of resolveJsPlugins(override.jsPlugins, configFilePath))
32
45
  inputs.push(input);
33
46
  }
34
47
  for (const input of getInputsFromSettings(config.settings))
35
48
  inputs.push(input);
36
49
  return inputs;
37
50
  };
51
+ const resolveConfig = (config, options) => resolveExtendedConfig(config, options.configFilePath);
38
52
  const resolveFromAST = (program, options) => {
39
53
  if (!isViteConfig(options.configFileName))
40
54
  return [];
@@ -55,8 +69,9 @@ const resolveFromAST = (program, options) => {
55
69
  },
56
70
  });
57
71
  visitor.visit(program);
58
- return [...resolveJsPlugins([...jsPlugins]), ...getInputsFromSettingsAST(program)];
72
+ return [...resolveJsPlugins([...jsPlugins], options.configFilePath), ...getInputsFromSettingsAST(program)];
59
73
  };
74
+ const isFilterTransitiveDependencies = true;
60
75
  const plugin = {
61
76
  title,
62
77
  enablers,
@@ -65,6 +80,7 @@ const plugin = {
65
80
  isLoadConfig,
66
81
  resolveConfig,
67
82
  resolveFromAST,
83
+ isFilterTransitiveDependencies,
68
84
  args,
69
85
  };
70
86
  export default plugin;
@@ -7,6 +7,7 @@ type Override = {
7
7
  jsPlugins?: JsPlugin[];
8
8
  };
9
9
  export type OxlintConfig = {
10
+ extends?: (string | OxlintConfig)[];
10
11
  jsPlugins?: JsPlugin[];
11
12
  overrides?: Override[];
12
13
  settings?: Settings;
@@ -1,6 +1,6 @@
1
1
  import { arrayify } from '../../util/array.js';
2
2
  import { toDeferResolve, toEntry } from '../../util/input.js';
3
- import { join, relative } from '../../util/path.js';
3
+ import { join, relative, toAbsolute } from '../../util/path.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
5
5
  const title = 'Playwright';
6
6
  const enablers = ['@playwright/test'];
@@ -18,9 +18,9 @@ export const resolveConfig = async (localConfig, options) => {
18
18
  const { cwd, configFileDir } = options;
19
19
  const inputs = [];
20
20
  for (const id of arrayify(localConfig.globalSetup))
21
- inputs.push(toEntry(id));
21
+ inputs.push(toEntry(toAbsolute(id, configFileDir)));
22
22
  for (const id of arrayify(localConfig.globalTeardown))
23
- inputs.push(toEntry(id));
23
+ inputs.push(toEntry(toAbsolute(id, configFileDir)));
24
24
  const projects = localConfig.projects ? [localConfig, ...localConfig.projects] : [localConfig];
25
25
  const reporters = [localConfig.reporter].flat().flatMap(reporter => {
26
26
  const name = typeof reporter === 'string' ? reporter : reporter?.[0];
@@ -2,11 +2,11 @@ import { hasDependency } from '../../util/plugin.js';
2
2
  const title = 'Plop';
3
3
  const enablers = ['plop'];
4
4
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
5
- const config = ['plopfile.{cjs,mjs,js,ts}'];
5
+ const entry = ['plopfile.{cjs,mjs,js,ts}'];
6
6
  const plugin = {
7
7
  title,
8
8
  enablers,
9
9
  isEnabled,
10
- config,
10
+ entry,
11
11
  };
12
12
  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,129 @@
1
+ import { Visitor } from 'oxc-parser';
2
+ import { findProperty, getFirstPropertyValue, getStringValues } from '../../typescript/ast-helpers.js';
3
+ import { getStringValue } from '../../typescript/ast-nodes.js';
4
+ import { getGitRemoteUrls } from '../../util/git.js';
5
+ import { isEntry } from '../../util/input.js';
6
+ import { join, toAbsolute } from '../../util/path.js';
7
+ import { hasDependency } from '../../util/plugin.js';
8
+ const title = 'Railway';
9
+ const enablers = ['railway'];
10
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
11
+ const config = ['.railway/railway.ts'];
12
+ const args = {
13
+ config: ['file'],
14
+ resolve: ['runner'],
15
+ };
16
+ const getCommands = (node) => {
17
+ const command = getStringValue(node);
18
+ return command ? [command] : [...getStringValues(node)];
19
+ };
20
+ const isExternalSourceType = (type) => type === 'github' || type === 'image' || type === 'template';
21
+ const getGitHubRepository = (value) => {
22
+ const normalized = value
23
+ ?.trim()
24
+ .replace(/^git\+/, '')
25
+ .replace(/\.git\/?$/, '')
26
+ .replace(/\/$/, '');
27
+ return normalized?.match(/(?:^|github(?:\.com)?[/:])([^/:]+\/[^/]+)$/i)?.[1].toLowerCase();
28
+ };
29
+ const resolveFromAST = (program, options) => {
30
+ const serviceNames = new Set();
31
+ const sourceTypes = new Map();
32
+ const bindings = new Map();
33
+ const localRepositories = new Set();
34
+ for (const repository of [options.manifest.repository, options.rootManifest?.repository]) {
35
+ const name = getGitHubRepository(typeof repository === 'string' ? repository : repository?.url);
36
+ if (name)
37
+ localRepositories.add(name);
38
+ }
39
+ for (const url of getGitRemoteUrls(options.rootCwd)) {
40
+ const name = getGitHubRepository(url);
41
+ if (name)
42
+ localRepositories.add(name);
43
+ }
44
+ for (const node of program.body) {
45
+ if (node.type === 'ImportDeclaration' && getStringValue(node.source) === 'railway/iac') {
46
+ for (const specifier of node.specifiers) {
47
+ if (specifier.type !== 'ImportSpecifier' || specifier.imported.type !== 'Identifier')
48
+ continue;
49
+ if (specifier.imported.name === 'service')
50
+ serviceNames.add(specifier.local.name);
51
+ if (isExternalSourceType(specifier.imported.name)) {
52
+ sourceTypes.set(specifier.local.name, specifier.imported.name);
53
+ }
54
+ }
55
+ }
56
+ else if (node.type === 'VariableDeclaration') {
57
+ for (const declaration of node.declarations) {
58
+ if (declaration.id.type === 'Identifier' && declaration.init)
59
+ bindings.set(declaration.id.name, declaration.init);
60
+ }
61
+ }
62
+ }
63
+ const resolveBinding = (node) => {
64
+ const seen = new Set();
65
+ while (node?.type === 'Identifier' && bindings.has(node.name) && !seen.has(node.name)) {
66
+ seen.add(node.name);
67
+ node = bindings.get(node.name);
68
+ }
69
+ return node;
70
+ };
71
+ const inputs = [];
72
+ const visitor = new Visitor({
73
+ CallExpression(node) {
74
+ if (node.callee.type !== 'Identifier' || !serviceNames.has(node.callee.name))
75
+ return;
76
+ const serviceConfig = node.arguments[1];
77
+ if (serviceConfig?.type !== 'ObjectExpression')
78
+ return;
79
+ const source = resolveBinding(findProperty(serviceConfig, 'source'));
80
+ const repository = getFirstPropertyValue(source, 'repo');
81
+ const image = getFirstPropertyValue(source, 'image');
82
+ const template = getFirstPropertyValue(source, 'template');
83
+ const sourceType = (source?.type === 'CallExpression' && source.callee.type === 'Identifier'
84
+ ? sourceTypes.get(source.callee.name)
85
+ : undefined) ??
86
+ getFirstPropertyValue(source, 'type') ??
87
+ (repository ? 'github' : image ? 'image' : template ? 'template' : undefined);
88
+ const sourceRepository = getGitHubRepository(source?.type === 'CallExpression' ? getStringValue(source.arguments[0]) : repository);
89
+ if (sourceType === 'image' ||
90
+ sourceType === 'template' ||
91
+ (sourceType === 'github' &&
92
+ sourceRepository !== undefined &&
93
+ localRepositories.size > 0 &&
94
+ !localRepositories.has(sourceRepository))) {
95
+ return;
96
+ }
97
+ const sourceConfig = source?.type === 'CallExpression' ? source.arguments[1] : source;
98
+ const rootDirectory = getFirstPropertyValue(sourceConfig, 'rootDirectory') ??
99
+ getFirstPropertyValue(serviceConfig, 'root') ??
100
+ getFirstPropertyValue(serviceConfig, 'rootDirectory');
101
+ const cwd = rootDirectory ? join(options.cwd, rootDirectory) : options.cwd;
102
+ const manifest = options.getManifest(cwd) ?? options.manifest;
103
+ const build = findProperty(serviceConfig, 'build');
104
+ const commands = [
105
+ ...getCommands(build),
106
+ ...getCommands(findProperty(build, 'buildCommand')),
107
+ ...getCommands(findProperty(serviceConfig, 'start')),
108
+ ...getCommands(findProperty(serviceConfig, 'preDeploy')),
109
+ ...getCommands(findProperty(findProperty(serviceConfig, 'deploy'), 'preDeployCommand')),
110
+ ];
111
+ for (const input of options.getInputsFromScripts(commands, { optionalBinaries: true, cwd, manifest })) {
112
+ if (isEntry(input))
113
+ input.specifier = toAbsolute(input.specifier, cwd);
114
+ inputs.push({ ...input, dir: cwd });
115
+ }
116
+ },
117
+ });
118
+ visitor.visit(program);
119
+ return inputs;
120
+ };
121
+ const plugin = {
122
+ title,
123
+ enablers,
124
+ isEnabled,
125
+ config,
126
+ args,
127
+ resolveFromAST,
128
+ };
129
+ export default plugin;
@@ -1,31 +1,130 @@
1
- import { toDeferResolve, toEntry } from '../../util/input.js';
2
- import { hasDependency } from '../../util/plugin.js';
1
+ import { arrayify } from '../../util/array.js';
2
+ import { findFileWithExtensions } from '../../util/fs.js';
3
+ import { _dirGlob, _glob, negate } from '../../util/glob.js';
4
+ import { toAlias, toConfig, toDeferResolve, toDependency, toEntry } from '../../util/input.js';
5
+ import { dirname, join, relative, toAbsolute } from '../../util/path.js';
6
+ import { hasDependency, load } from '../../util/plugin.js';
7
+ import { createRstestMockVisitor } from './visitors/mock.js';
3
8
  const title = 'Rstest';
4
9
  const enablers = ['@rstest/core'];
5
10
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
11
+ const configExtensions = ['.mts', '.mjs', '.ts', '.js', '.cjs', '.cts'];
6
12
  const config = ['rstest.config.{js,cjs,mjs,ts,cts,mts}'];
7
13
  const mocks = ['**/__mocks__/**/*.?(c|m)[jt]s?(x)'];
8
- const entry = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];
9
- function testEnvironment(config) {
10
- const environment = typeof config.testEnvironment === 'string' ? config.testEnvironment : config.testEnvironment?.name;
11
- if (!environment || environment === 'node')
12
- return [];
13
- return [environment];
14
- }
15
- const resolveConfig = async (config) => {
16
- const entries = (config.include ?? entry)
17
- .concat(...mocks)
18
- .map(toEntry)
19
- .concat(...(config.exclude ?? []).map(id => toEntry(`!${id}`)));
20
- const environments = testEnvironment(config);
21
- const setupFiles = config.setupFiles ?? [];
22
- return [...environments, ...setupFiles, ...entries].map(id => (typeof id === 'string' ? toDeferResolve(id) : id));
14
+ const testEntry = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];
15
+ const entry = [...testEntry, ...mocks];
16
+ const getEnvironmentDependency = (name) => (name && name !== 'node' ? [toDependency(name)] : []);
17
+ const replaceRootDir = (path, root) => path.replace('<rootDir>', root);
18
+ const configParams = { env: 'test', command: 'run', envMode: 'test' };
19
+ const getConfig = async (config) => typeof config === 'function' ? await config(configParams) : config;
20
+ const resolveConfig = async (localConfig, options) => {
21
+ const { cwd, configFileDir, configFilePath, getManifest, isResolvedConfigFile } = options;
22
+ const cfg = await getConfig(localConfig);
23
+ const runnerRoot = toAbsolute(cfg.root ?? '.', isResolvedConfigFile ? configFileDir : cwd);
24
+ const manifest = getManifest(cwd);
25
+ const seen = new Set([configFilePath]);
26
+ const inputs = [];
27
+ const addProject = async (project, projectRoot) => {
28
+ const toPattern = (pattern) => relative(cwd, toAbsolute(replaceRootDir(pattern, projectRoot), projectRoot));
29
+ if (!options.config.entry) {
30
+ const include = [];
31
+ const exclude = [...(Array.isArray(project.exclude) ? project.exclude : (project.exclude?.patterns ?? []))];
32
+ for (const pattern of [...(project.include ?? testEntry), ...mocks]) {
33
+ if (pattern.startsWith('!'))
34
+ exclude.push(pattern.slice(1));
35
+ else
36
+ include.push(toPattern(pattern));
37
+ }
38
+ const entries = exclude.length === 0
39
+ ? include
40
+ : await _glob({ cwd, patterns: [...include, ...exclude.map(pattern => negate(toPattern(pattern)))] });
41
+ for (const entry of entries)
42
+ inputs.push(toEntry(entry));
43
+ }
44
+ const env = project.testEnvironment;
45
+ inputs.push(...getEnvironmentDependency(typeof env === 'string' ? env : env?.name));
46
+ for (const specifier of [project.setupFiles ?? [], project.globalSetup ?? []].flat()) {
47
+ inputs.push(toDeferResolve(replaceRootDir(specifier, projectRoot), { dir: projectRoot }));
48
+ }
49
+ if (project.browser?.enabled) {
50
+ inputs.push(toDependency('@rstest/browser'), toDependency('playwright'));
51
+ }
52
+ for (const [name, value] of Object.entries(project.resolve?.alias ?? {})) {
53
+ const prefixes = [];
54
+ for (const target of arrayify(value))
55
+ prefixes.push(target.startsWith('.') ? join(runnerRoot, target) : target);
56
+ if (name.endsWith('$')) {
57
+ inputs.push(toAlias(name.slice(0, -1), prefixes));
58
+ }
59
+ else {
60
+ inputs.push(toAlias(name, prefixes), toAlias(`${name}/*`, prefixes.map(prefix => `${prefix}/*`)));
61
+ }
62
+ }
63
+ };
64
+ const addProjects = async (projects, projectsRoot) => {
65
+ const patterns = [];
66
+ for (const project of projects) {
67
+ if (typeof project === 'string')
68
+ patterns.push(replaceRootDir(project, projectsRoot));
69
+ else
70
+ await addProject(project, toAbsolute(replaceRootDir(project.root ?? projectsRoot, projectsRoot), runnerRoot));
71
+ }
72
+ const [files, dirs] = await Promise.all([
73
+ _glob({ cwd: projectsRoot, patterns, gitignore: false }),
74
+ _dirGlob({ cwd: projectsRoot, patterns, gitignore: false }),
75
+ ]);
76
+ for (const dir of dirs) {
77
+ const projectRoot = toAbsolute(dir, projectsRoot);
78
+ const filePath = findFileWithExtensions(join(projectRoot, 'rstest.config'), configExtensions);
79
+ if (filePath)
80
+ files.push(filePath);
81
+ else
82
+ await addProject({}, projectRoot);
83
+ }
84
+ for (const filePath of files) {
85
+ if (seen.has(filePath))
86
+ continue;
87
+ seen.add(filePath);
88
+ const project = getManifest(dirname(filePath)) === manifest
89
+ ? await load(filePath)
90
+ .then(getConfig)
91
+ .catch(() => undefined)
92
+ : undefined;
93
+ if (!project) {
94
+ inputs.push(toConfig('rstest', filePath, { containingFilePath: configFilePath }));
95
+ continue;
96
+ }
97
+ inputs.push(toEntry(filePath));
98
+ if (project.projects)
99
+ await addProjects(project.projects, dirname(filePath));
100
+ else
101
+ await addProject(project, toAbsolute(project.root ?? dirname(filePath), runnerRoot));
102
+ }
103
+ };
104
+ if (cfg.projects)
105
+ await addProjects(cfg.projects, runnerRoot);
106
+ else
107
+ await addProject(cfg, runnerRoot);
108
+ if (!isResolvedConfigFile) {
109
+ inputs.push(toDependency(`@rstest/coverage-${cfg.coverage?.provider ?? 'istanbul'}`, { optional: true }));
110
+ }
111
+ return inputs;
112
+ };
113
+ const args = {
114
+ config: true,
115
+ resolveInputs: parsed => getEnvironmentDependency(parsed.testEnvironment),
116
+ };
117
+ const registerVisitors = ({ ctx, registerVisitor }) => {
118
+ registerVisitor(createRstestMockVisitor(ctx));
23
119
  };
24
120
  const plugin = {
25
121
  title,
26
122
  enablers,
27
123
  isEnabled,
28
124
  config,
125
+ entry,
29
126
  resolveConfig,
127
+ args,
128
+ registerVisitors,
30
129
  };
31
130
  export default plugin;
@@ -1,8 +1,32 @@
1
- export type RstestConfig = {
1
+ type TestEnvironment = 'node' | 'jsdom' | 'happy-dom';
2
+ export type RstestProjectConfig = {
3
+ root?: string;
2
4
  include?: string[];
3
- exclude?: string[];
4
- testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | {
5
- name: 'node' | 'jsdom' | 'happy-dom';
5
+ exclude?: string[] | {
6
+ patterns: string[];
6
7
  };
7
- setupFiles?: string[];
8
+ testEnvironment?: TestEnvironment | {
9
+ name: TestEnvironment;
10
+ };
11
+ setupFiles?: string | string[];
12
+ globalSetup?: string | string[];
13
+ browser?: {
14
+ enabled?: boolean;
15
+ };
16
+ resolve?: {
17
+ alias?: Record<string, string | false | (string | false)[]>;
18
+ };
19
+ };
20
+ type RstestConfig = RstestProjectConfig & {
21
+ projects?: (string | RstestProjectConfig)[];
22
+ coverage?: {
23
+ provider?: 'istanbul' | 'v8';
24
+ };
25
+ };
26
+ type ConfigParams = {
27
+ env: string;
28
+ command: string;
29
+ envMode: string;
8
30
  };
31
+ export type RstestConfigOrFn = RstestConfig | ((params: ConfigParams) => RstestConfig | Promise<RstestConfig>);
32
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';
2
+ export declare function createRstestMockVisitor(ctx: PluginVisitorContext): PluginVisitorObject;
@@ -0,0 +1,52 @@
1
+ import { IMPORT_FLAGS } from '../../../constants.js';
2
+ import { getStringValue, isStringLiteral } from '../../../typescript/ast-nodes.js';
3
+ import { isShadowed } from '../../../typescript/visitors/walk.js';
4
+ const rstestApiNames = new Set(['rs', 'rstest']);
5
+ const mockMethods = new Set(['mock', 'doMock']);
6
+ export function createRstestMockVisitor(ctx) {
7
+ const rsNames = new Set();
8
+ return {
9
+ Program(node) {
10
+ rsNames.clear();
11
+ for (const statement of node.body) {
12
+ if (statement.type !== 'ImportDeclaration' ||
13
+ !isStringLiteral(statement.source) ||
14
+ getStringValue(statement.source) !== '@rstest/core')
15
+ continue;
16
+ for (const specifier of statement.specifiers ?? []) {
17
+ if (specifier.type === 'ImportSpecifier' &&
18
+ specifier.imported.type === 'Identifier' &&
19
+ rstestApiNames.has(specifier.imported.name)) {
20
+ rsNames.add(specifier.local.name);
21
+ }
22
+ }
23
+ }
24
+ },
25
+ CallExpression(node) {
26
+ const argument = node.arguments[0];
27
+ const factory = node.arguments[1];
28
+ const factoryBody = factory?.type === 'ArrowFunctionExpression' &&
29
+ factory.body.type === 'ParenthesizedExpression' &&
30
+ factory.body.expression.type === 'ObjectExpression'
31
+ ? factory.body.expression
32
+ : undefined;
33
+ if (argument?.type !== 'ImportExpression' ||
34
+ !isStringLiteral(argument.source) ||
35
+ factory?.type !== 'ArrowFunctionExpression' ||
36
+ !factoryBody ||
37
+ factory.async ||
38
+ factory.params.length > 0 ||
39
+ factoryBody.properties.some(property => property.type === 'SpreadElement') ||
40
+ node.callee.type !== 'MemberExpression' ||
41
+ node.callee.computed ||
42
+ node.callee.object.type !== 'Identifier' ||
43
+ !rsNames.has(node.callee.object.name) ||
44
+ isShadowed(node.callee.object.name, node.callee.object.start) ||
45
+ node.callee.property.type !== 'Identifier' ||
46
+ !mockMethods.has(node.callee.property.name))
47
+ return;
48
+ ctx.markImportExpressionHandled(argument.start);
49
+ ctx.addImport(getStringValue(argument.source), argument.source.start, IMPORT_FLAGS.SIDE_EFFECTS);
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,23 @@
1
+ import { toEntry } from '../../util/input.js';
2
+ import { hasDependency } from '../../util/plugin.js';
3
+ const title = 'Turborepo';
4
+ const enablers = ['turbo', '@turbo/gen'];
5
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
+ const extensions = '{ts,js,cjs,mts,mjs}';
7
+ const entry = [`turbo/generators/config.${extensions}`];
8
+ const resolve = ({ cwd, rootCwd }) => (cwd === rootCwd ? [toEntry(`plopfile.${extensions}`)] : []);
9
+ const args = {
10
+ string: ['config'],
11
+ resolveInputs: parsed => (parsed._[0] === 'gen' || parsed._[0] === 'generate') && typeof parsed.config === 'string'
12
+ ? [toEntry(parsed.config)]
13
+ : [],
14
+ };
15
+ const plugin = {
16
+ title,
17
+ enablers,
18
+ isEnabled,
19
+ entry,
20
+ resolve,
21
+ args,
22
+ };
23
+ export default plugin;
@@ -0,0 +1,4 @@
1
+ import type { Input } from '../../util/input.ts';
2
+ type ResolveContentMapperOptions = (options: Record<string, unknown>) => Input[];
3
+ export declare const contentMapperResolvers: Map<string, ResolveContentMapperOptions>;
4
+ export {};
@@ -0,0 +1,4 @@
1
+ import { contentMappers as mdxContentMappers, resolveContentMapperOptions as resolveMdxOptions } from '../mdx/index.js';
2
+ export const contentMapperResolvers = new Map();
3
+ for (const name of mdxContentMappers)
4
+ contentMapperResolvers.set(name, resolveMdxOptions);