knip 6.38.0 → 6.40.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 (77) hide show
  1. package/dist/ConfigurationChief.d.ts +18 -0
  2. package/dist/WorkspaceWorker.d.ts +3 -4
  3. package/dist/WorkspaceWorker.js +12 -38
  4. package/dist/compilers/compilers.js +1 -1
  5. package/dist/constants.js +1 -0
  6. package/dist/graph/analyze.js +14 -5
  7. package/dist/graph/build.js +17 -10
  8. package/dist/graph-explorer/explorer.d.ts +1 -1
  9. package/dist/graph-explorer/operations/is-referenced.d.ts +1 -1
  10. package/dist/graph-explorer/operations/is-referenced.js +8 -4
  11. package/dist/plugins/_vue/auto-import.js +17 -10
  12. package/dist/plugins/angular/index.js +1 -1
  13. package/dist/plugins/capacitor/index.js +1 -1
  14. package/dist/plugins/commitlint/index.js +34 -9
  15. package/dist/plugins/cucumber/index.js +36 -4
  16. package/dist/plugins/cucumber/types.d.ts +2 -1
  17. package/dist/plugins/eslint/resolveFromAST.d.ts +0 -1
  18. package/dist/plugins/eslint/resolveFromAST.js +1 -1
  19. package/dist/plugins/index.d.ts +3 -0
  20. package/dist/plugins/index.js +6 -0
  21. package/dist/plugins/jasmine/index.d.ts +3 -0
  22. package/dist/plugins/jasmine/index.js +55 -0
  23. package/dist/plugins/jasmine/types.d.ts +7 -0
  24. package/dist/plugins/jasmine/types.js +1 -0
  25. package/dist/plugins/jest/helpers.d.ts +1 -0
  26. package/dist/plugins/jest/helpers.js +2 -0
  27. package/dist/plugins/jest/index.js +20 -11
  28. package/dist/plugins/osls/index.d.ts +3 -0
  29. package/dist/plugins/osls/index.js +13 -0
  30. package/dist/plugins/oxlint/index.d.ts +3 -0
  31. package/dist/plugins/oxlint/index.js +4 -31
  32. package/dist/plugins/oxlint/types.d.ts +1 -0
  33. package/dist/plugins/playwright/index.js +20 -3
  34. package/dist/plugins/playwright/types.d.ts +5 -0
  35. package/dist/plugins/postcss/index.js +5 -1
  36. package/dist/plugins/railway/index.d.ts +3 -0
  37. package/dist/plugins/railway/index.js +129 -0
  38. package/dist/plugins/rsbuild/index.js +42 -4
  39. package/dist/plugins/rsbuild/types.d.ts +18 -1
  40. package/dist/plugins/rspack/index.d.ts +3 -1
  41. package/dist/plugins/rspack/index.js +1 -1
  42. package/dist/plugins/rstest/index.js +116 -17
  43. package/dist/plugins/rstest/types.d.ts +29 -5
  44. package/dist/plugins/rstest/visitors/mock.d.ts +2 -0
  45. package/dist/plugins/rstest/visitors/mock.js +52 -0
  46. package/dist/plugins/serverless-framework/index.d.ts +4 -1
  47. package/dist/plugins/serverless-framework/index.js +40 -7
  48. package/dist/plugins/serverless-framework/types.d.ts +4 -2
  49. package/dist/plugins/simple-git-hooks/index.js +1 -1
  50. package/dist/plugins/vite/helpers.d.ts +1 -1
  51. package/dist/plugins/vite/helpers.js +7 -8
  52. package/dist/plugins/vitest/index.js +31 -7
  53. package/dist/plugins/vitest/types.d.ts +12 -0
  54. package/dist/schema/configuration.d.ts +45 -0
  55. package/dist/schema/plugins.d.ts +15 -0
  56. package/dist/schema/plugins.js +3 -0
  57. package/dist/types/PluginNames.d.ts +2 -2
  58. package/dist/types/PluginNames.js +3 -0
  59. package/dist/types/package-json.d.ts +5 -0
  60. package/dist/typescript/ast-nodes.js +291 -0
  61. package/dist/typescript/resolve-module-names.js +15 -4
  62. package/dist/typescript/visitors/walk.js +12 -0
  63. package/dist/util/create-options.d.ts +30 -0
  64. package/dist/util/filter-transitive-dependencies.d.ts +2 -0
  65. package/dist/util/filter-transitive-dependencies.js +38 -0
  66. package/dist/util/git.d.ts +1 -0
  67. package/dist/util/git.js +23 -1
  68. package/dist/util/package-json.d.ts +1 -348
  69. package/dist/util/package-json.js +56 -10
  70. package/dist/util/tag.d.ts +1 -0
  71. package/dist/util/tag.js +1 -1
  72. package/dist/util/to-source-path.d.ts +1 -1
  73. package/dist/util/to-source-path.js +7 -5
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/package.json +1 -1
  77. package/schema.json +12 -0
@@ -51,6 +51,7 @@ export declare const Plugins: {
51
51
  hardhat: import("../types/config.ts").Plugin;
52
52
  husky: import("../types/config.ts").Plugin;
53
53
  'i18next-parser': import("../types/config.ts").Plugin;
54
+ jasmine: import("../types/config.ts").Plugin;
54
55
  jest: import("../types/config.ts").Plugin;
55
56
  karma: import("../types/config.ts").Plugin;
56
57
  knex: import("../types/config.ts").Plugin;
@@ -92,6 +93,7 @@ export declare const Plugins: {
92
93
  'openapi-ts': import("../types/config.ts").Plugin;
93
94
  openclaw: import("../types/config.ts").Plugin;
94
95
  orval: import("../types/config.ts").Plugin;
96
+ osls: import("../types/config.ts").Plugin;
95
97
  oxfmt: import("../types/config.ts").Plugin;
96
98
  oxlint: import("../types/config.ts").Plugin;
97
99
  'panda-css': import("../types/config.ts").Plugin;
@@ -111,6 +113,7 @@ export declare const Plugins: {
111
113
  prisma: import("../types/config.ts").Plugin;
112
114
  quasar: import("../types/config.ts").Plugin;
113
115
  qwik: import("../types/config.ts").Plugin;
116
+ railway: import("../types/config.ts").Plugin;
114
117
  raycast: import("../types/config.ts").Plugin;
115
118
  'react-cosmos': import("../types/config.ts").Plugin;
116
119
  'react-email': import("../types/config.ts").Plugin;
@@ -45,6 +45,7 @@ import { default as graphqlCodegen } from './graphql-codegen/index.js';
45
45
  import { default as hardhat } from './hardhat/index.js';
46
46
  import { default as husky } from './husky/index.js';
47
47
  import { default as i18nextParser } from './i18next-parser/index.js';
48
+ import { default as jasmine } from './jasmine/index.js';
48
49
  import { default as jest } from './jest/index.js';
49
50
  import { default as karma } from './karma/index.js';
50
51
  import { default as knex } from './knex/index.js';
@@ -86,6 +87,7 @@ import { default as oclif } from './oclif/index.js';
86
87
  import { default as openapiTs } from './openapi-ts/index.js';
87
88
  import { default as openclaw } from './openclaw/index.js';
88
89
  import { default as orval } from './orval/index.js';
90
+ import { default as osls } from './osls/index.js';
89
91
  import { default as oxfmt } from './oxfmt/index.js';
90
92
  import { default as oxlint } from './oxlint/index.js';
91
93
  import { default as pandaCss } from './panda-css/index.js';
@@ -105,6 +107,7 @@ import { default as prettier } from './prettier/index.js';
105
107
  import { default as prisma } from './prisma/index.js';
106
108
  import { default as quasar } from './quasar/index.js';
107
109
  import { default as qwik } from './qwik/index.js';
110
+ import { default as railway } from './railway/index.js';
108
111
  import { default as raycast } from './raycast/index.js';
109
112
  import { default as reactCosmos } from './react-cosmos/index.js';
110
113
  import { default as reactEmail } from './react-email/index.js';
@@ -233,6 +236,7 @@ export const Plugins = {
233
236
  hardhat,
234
237
  husky,
235
238
  'i18next-parser': i18nextParser,
239
+ jasmine,
236
240
  jest,
237
241
  karma,
238
242
  knex,
@@ -274,6 +278,7 @@ export const Plugins = {
274
278
  'openapi-ts': openapiTs,
275
279
  openclaw,
276
280
  orval,
281
+ osls,
277
282
  oxfmt,
278
283
  oxlint,
279
284
  'panda-css': pandaCss,
@@ -293,6 +298,7 @@ export const Plugins = {
293
298
  prisma,
294
299
  quasar,
295
300
  qwik,
301
+ railway,
296
302
  raycast,
297
303
  'react-cosmos': reactCosmos,
298
304
  'react-email': reactEmail,
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,55 @@
1
+ import { toDeferResolve, toEntry } from '../../util/input.js';
2
+ import { isAbsolute, join } from '../../util/path.js';
3
+ import { hasDependency } from '../../util/plugin.js';
4
+ const title = 'Jasmine';
5
+ const enablers = ['jasmine'];
6
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
+ const config = ['spec/support/jasmine.{json,jsonc,js,cjs,mjs}'];
8
+ const defaultSpecFiles = 'spec/**/*[sS]pec.{js,mjs}';
9
+ const entry = [defaultSpecFiles, 'spec/helpers/**/*.{js,mjs}'];
10
+ const toSpecDirPattern = (specDir, pattern) => {
11
+ const isNegated = pattern.startsWith('!');
12
+ const id = isNegated ? pattern.slice(1) : pattern;
13
+ const resolved = isAbsolute(id) ? id : join(specDir, id);
14
+ return isNegated ? `!${resolved}` : resolved;
15
+ };
16
+ const resolveConfig = (localConfig, options) => {
17
+ const specDir = localConfig.spec_dir ?? '';
18
+ const specFiles = localConfig.spec_files?.map(pattern => toSpecDirPattern(specDir, pattern)) ?? [defaultSpecFiles];
19
+ const helpers = localConfig.helpers?.map(pattern => toSpecDirPattern(specDir, pattern)) ?? [];
20
+ const requires = localConfig.requires ?? [];
21
+ const inputs = [];
22
+ for (const id of [...specFiles, ...helpers])
23
+ inputs.push(toEntry(id));
24
+ for (const id of requires)
25
+ inputs.push(toDeferResolve(id, { dir: options.cwd }));
26
+ if (localConfig.loader)
27
+ inputs.push(toDeferResolve(localConfig.loader, { dir: options.cwd }));
28
+ return inputs;
29
+ };
30
+ const commands = new Set(['init', 'examples', 'help', 'version']);
31
+ const args = {
32
+ boolean: ['color', 'fail-fast', 'list-not-applicable', 'no-color'],
33
+ string: ['config', 'reporter', 'require'],
34
+ config: ['config'],
35
+ resolve: ['require', 'reporter'],
36
+ resolveInputs: (parsed) => {
37
+ const inputs = [];
38
+ for (const value of parsed._) {
39
+ if (commands.has(value) || /^\w+=/.test(value))
40
+ continue;
41
+ inputs.push(toEntry(value));
42
+ }
43
+ return inputs;
44
+ },
45
+ };
46
+ const plugin = {
47
+ title,
48
+ enablers,
49
+ isEnabled,
50
+ config,
51
+ entry,
52
+ resolveConfig,
53
+ args,
54
+ };
55
+ export default plugin;
@@ -0,0 +1,7 @@
1
+ export type JasmineConfig = {
2
+ spec_dir?: string;
3
+ spec_files?: string[];
4
+ helpers?: string[];
5
+ requires?: string[];
6
+ loader?: string;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { PluginOptions } from '../../types/config.ts';
2
2
  import type { JestInitialOptions } from './types.ts';
3
3
  export declare const resolveExtensibleConfig: (configFilePath: string) => Promise<any>;
4
+ export declare const resolveWithPrefix: (prefix: string, id: string, rootDir: string) => string;
4
5
  export declare const getReportersDependencies: (config: JestInitialOptions, options: PluginOptions) => string[];
@@ -1,5 +1,6 @@
1
1
  import { dirname, isInternal, join, toAbsolute } from '../../util/path.js';
2
2
  import { load } from '../../util/plugin.js';
3
+ import { _resolveSync } from '../../util/resolve.js';
3
4
  export const resolveExtensibleConfig = async (configFilePath) => {
4
5
  let config = await load(configFilePath);
5
6
  if (config?.preset) {
@@ -12,6 +13,7 @@ export const resolveExtensibleConfig = async (configFilePath) => {
12
13
  }
13
14
  return config;
14
15
  };
16
+ export const resolveWithPrefix = (prefix, id, rootDir) => _resolveSync(prefix + id, rootDir) ? prefix + id : id;
15
17
  const getStringPropOrFallback = (prop, fallback) => {
16
18
  return typeof prop === 'string' ? prop : fallback;
17
19
  };
@@ -1,10 +1,10 @@
1
1
  import { arrayify } from '../../util/array.js';
2
2
  import { _glob, _dirGlob } from '../../util/glob.js';
3
- import { toConfig, toDeferResolve, toEntry } from '../../util/input.js';
3
+ import { toConfig, toDeferResolve, toDependency, toEntry } from '../../util/input.js';
4
4
  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
- import { getReportersDependencies, resolveExtensibleConfig } from './helpers.js';
7
+ import { getReportersDependencies, resolveExtensibleConfig, resolveWithPrefix } from './helpers.js';
8
8
  const title = 'Jest';
9
9
  const enablers = ['jest'];
10
10
  const isEnabled = ({ dependencies, manifest }) => hasDependency(dependencies, enablers) || Boolean(manifest.name?.startsWith('jest-presets'));
@@ -38,16 +38,23 @@ const resolveDependencies = async (config, rootDir, options) => {
38
38
  projects.push(dependency);
39
39
  }
40
40
  }
41
- const runner = config.runner ? [typeof config.runner === 'string' ? config.runner : config.runner[0]] : [];
41
+ const runner = config.runner
42
+ ? [resolveWithPrefix('jest-runner-', typeof config.runner === 'string' ? config.runner : config.runner[0], rootDir)]
43
+ : [];
42
44
  const runtime = config.runtime && config.runtime !== 'jest-circus' ? [config.runtime] : [];
43
- const environments = config.testEnvironment === 'jsdom'
44
- ? ['jest-environment-jsdom']
45
- : config.testEnvironment
46
- ? [config.testEnvironment]
47
- : [];
45
+ const environments = [];
46
+ if (config.testEnvironment === 'node') {
47
+ environments.push(toDependency('jest-environment-node', { optional: true }));
48
+ }
49
+ else if (config.testEnvironment) {
50
+ const environment = config.testEnvironment === 'jsdom'
51
+ ? 'jest-environment-jsdom'
52
+ : resolveWithPrefix('jest-environment-', config.testEnvironment, rootDir);
53
+ environments.push(toDeferResolve(environment));
54
+ }
48
55
  const resolvers = config.resolver ? [config.resolver] : [];
49
56
  const reporters = getReportersDependencies(config, options);
50
- const watchPlugins = config.watchPlugins?.map(watchPlugin => (typeof watchPlugin === 'string' ? watchPlugin : watchPlugin[0])) ?? [];
57
+ const watchPlugins = config.watchPlugins?.map(watchPlugin => resolveWithPrefix('jest-watch-', typeof watchPlugin === 'string' ? watchPlugin : watchPlugin[0], rootDir)) ?? [];
51
58
  const transform = [];
52
59
  for (const transformer of config.transform ? Object.values(config.transform) : []) {
53
60
  if (typeof transformer === 'string') {
@@ -69,7 +76,9 @@ const resolveDependencies = async (config, rootDir, options) => {
69
76
  const testResultsProcessor = config.testResultsProcessor ? [config.testResultsProcessor] : [];
70
77
  const snapshotResolver = config.snapshotResolver ? [config.snapshotResolver] : [];
71
78
  const snapshotSerializers = config.snapshotSerializers ?? [];
72
- const testSequencer = config.testSequencer ? [config.testSequencer] : [];
79
+ const testSequencer = config.testSequencer
80
+ ? [resolveWithPrefix('jest-sequencer-', config.testSequencer, rootDir)]
81
+ : [];
73
82
  const setupFiles = config.setupFiles ?? [];
74
83
  const setupFilesAfterEnv = config.setupFilesAfterEnv ?? [];
75
84
  const globalSetup = config.globalSetup ? [config.globalSetup] : [];
@@ -99,7 +108,7 @@ const resolveConfig = async (localConfig, options) => {
99
108
  const { configFileDir } = options;
100
109
  if (typeof localConfig === 'function')
101
110
  localConfig = await localConfig();
102
- const rootDir = localConfig.rootDir ?? configFileDir;
111
+ const rootDir = toAbsolute(localConfig.rootDir ?? configFileDir, configFileDir);
103
112
  const replaceRootDir = (name) => name.replace(rootDirRe, rootDir);
104
113
  const inputs = await resolveDependencies(localConfig, rootDir, options);
105
114
  const entries = localConfig.testMatch
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from '../../types/config.ts';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
@@ -0,0 +1,13 @@
1
+ import { hasDependency } from '../../util/plugin.js';
2
+ import { config, resolveConfig } from '../serverless-framework/index.js';
3
+ const title = 'osls';
4
+ const enablers = ['osls'];
5
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
+ const plugin = {
7
+ title,
8
+ enablers,
9
+ isEnabled,
10
+ config,
11
+ resolveConfig,
12
+ };
13
+ export default plugin;
@@ -1,3 +1,6 @@
1
1
  import type { Plugin } from '../../types/config.ts';
2
+ import { type Input } from '../../util/input.ts';
3
+ import type { OxlintConfig } from './types.ts';
4
+ export declare const resolveExtendedConfig: (config: OxlintConfig, configFilePath: string) => Input[];
2
5
  declare const plugin: Plugin;
3
6
  export default plugin;
@@ -1,15 +1,11 @@
1
- import { Visitor } from 'oxc-parser';
2
- import { findProperty, getPropertyValues } from '../../typescript/ast-helpers.js';
3
1
  import { toConfig, toDependency, toEntry } from '../../util/input.js';
4
2
  import { dirname, isInternal, toAbsolute } from '../../util/path.js';
5
3
  import { hasDependency } from '../../util/plugin.js';
6
4
  import { getInputsFromSettings } from '../eslint/helpers.js';
7
- import { getInputsFromSettingsAST } from '../eslint/resolveFromAST.js';
8
5
  const title = 'Oxlint';
9
6
  const enablers = ['oxlint', 'vite-plus'];
10
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
11
- const config = ['.oxlintrc.{json,jsonc}', 'oxlint.config.{ts,mts}', 'vite.config.{js,mjs,ts,cjs,mts,cts}'];
12
- const isViteConfig = (configFileName) => configFileName.startsWith('vite.config.');
8
+ const config = ['.oxlintrc.{json,jsonc}', 'oxlint.config.{ts,mts}'];
13
9
  const args = {
14
10
  config: true,
15
11
  };
@@ -25,8 +21,7 @@ const resolveJsPlugins = (jsPlugins, configFilePath) => {
25
21
  }
26
22
  return inputs;
27
23
  };
28
- const isLoadConfig = ({ configFileName }) => !isViteConfig(configFileName);
29
- const resolveExtendedConfig = (config, configFilePath) => {
24
+ export const resolveExtendedConfig = (config, configFilePath) => {
30
25
  const inputs = [];
31
26
  for (const entry of config.extends ?? []) {
32
27
  if (typeof entry === 'string') {
@@ -43,43 +38,21 @@ const resolveExtendedConfig = (config, configFilePath) => {
43
38
  for (const override of config.overrides ?? []) {
44
39
  for (const input of resolveJsPlugins(override.jsPlugins, configFilePath))
45
40
  inputs.push(input);
41
+ for (const input of getInputsFromSettings(override.settings))
42
+ inputs.push(input);
46
43
  }
47
44
  for (const input of getInputsFromSettings(config.settings))
48
45
  inputs.push(input);
49
46
  return inputs;
50
47
  };
51
48
  const resolveConfig = (config, options) => resolveExtendedConfig(config, options.configFilePath);
52
- const resolveFromAST = (program, options) => {
53
- if (!isViteConfig(options.configFileName))
54
- return [];
55
- const jsPlugins = new Set();
56
- const visitor = new Visitor({
57
- ObjectExpression(node) {
58
- const lint = findProperty(node, 'lint');
59
- if (lint?.type !== 'ObjectExpression')
60
- return;
61
- for (const specifier of getPropertyValues(lint, 'jsPlugins'))
62
- jsPlugins.add(specifier);
63
- for (const plugin of findProperty(lint, 'jsPlugins')?.elements ?? []) {
64
- if (plugin?.type !== 'ObjectExpression')
65
- continue;
66
- for (const specifier of getPropertyValues(plugin, 'specifier'))
67
- jsPlugins.add(specifier);
68
- }
69
- },
70
- });
71
- visitor.visit(program);
72
- return [...resolveJsPlugins([...jsPlugins], options.configFilePath), ...getInputsFromSettingsAST(program)];
73
- };
74
49
  const isFilterTransitiveDependencies = true;
75
50
  const plugin = {
76
51
  title,
77
52
  enablers,
78
53
  isEnabled,
79
54
  config,
80
- isLoadConfig,
81
55
  resolveConfig,
82
- resolveFromAST,
83
56
  isFilterTransitiveDependencies,
84
57
  args,
85
58
  };
@@ -4,6 +4,7 @@ type JsPlugin = string | {
4
4
  specifier: string;
5
5
  };
6
6
  type Override = {
7
+ settings?: Settings;
7
8
  jsPlugins?: JsPlugin[];
8
9
  };
9
10
  export type OxlintConfig = {
@@ -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,26 @@ 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
+ for (const server of [localConfig.webServer].flat()) {
25
+ if (!server?.command)
26
+ continue;
27
+ const dir = server.cwd ? toAbsolute(server.cwd, configFileDir) : configFileDir;
28
+ for (const input of options.getInputsFromScripts(server.command, {
29
+ optionalBinaries: true,
30
+ cwd: dir,
31
+ manifest: options.getManifest(dir) ?? options.manifest,
32
+ })) {
33
+ const inputDir = input.dir ?? dir;
34
+ inputs.push({
35
+ ...input,
36
+ ...(input.type === 'entry' ? { specifier: toAbsolute(input.specifier, inputDir) } : {}),
37
+ dir: inputDir,
38
+ });
39
+ }
40
+ }
24
41
  const projects = localConfig.projects ? [localConfig, ...localConfig.projects] : [localConfig];
25
42
  const reporters = [localConfig.reporter].flat().flatMap(reporter => {
26
43
  const name = typeof reporter === 'string' ? reporter : reporter?.[0];
@@ -31,6 +31,10 @@ type Project = {
31
31
  use: string;
32
32
  testMatch?: string | RegExp | (string | RegExp)[];
33
33
  };
34
+ type TestConfigWebServer = {
35
+ command: string;
36
+ cwd?: string;
37
+ };
34
38
  export type PlaywrightTestConfig = {
35
39
  projects?: Project[];
36
40
  testMatch?: string | RegExp | (string | RegExp)[];
@@ -38,5 +42,6 @@ export type PlaywrightTestConfig = {
38
42
  reporter?: LiteralUnion<'dot' | 'line' | 'list' | 'junit' | 'html' | 'json' | 'github' | 'null', string> | ReporterDescription[];
39
43
  globalSetup?: string | Array<string>;
40
44
  globalTeardown?: string | Array<string>;
45
+ webServer?: TestConfigWebServer | TestConfigWebServer[];
41
46
  };
42
47
  export {};
@@ -11,7 +11,11 @@ const config = [
11
11
  ];
12
12
  const resolveConfig = config => {
13
13
  const plugins = config.plugins
14
- ? (Array.isArray(config.plugins) ? config.plugins : Object.keys(config.plugins)).flatMap(plugin => {
14
+ ? (Array.isArray(config.plugins)
15
+ ? config.plugins
16
+ : Object.entries(config.plugins)
17
+ .filter(([, options]) => options !== false)
18
+ .map(([plugin]) => plugin)).flatMap(plugin => {
15
19
  if (typeof plugin === 'string')
16
20
  return plugin;
17
21
  if (Array.isArray(plugin) && typeof plugin[0] === 'string')
@@ -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,11 +1,13 @@
1
1
  import { toProductionEntry } from '../../util/input.js';
2
2
  import { hasDependency } from '../../util/plugin.js';
3
+ import { resolveConfig as resolveRspackConfig } from '../rspack/index.js';
3
4
  const title = 'Rsbuild';
4
5
  const enablers = ['@rsbuild/core'];
5
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
7
  const config = ['rsbuild*.config.{mjs,ts,js,cjs,mts,cts}'];
7
- const resolveConfig = async (config) => {
8
+ const resolveConfig = async (config, options) => {
8
9
  const entries = new Set();
10
+ const inputs = [];
9
11
  const checkSource = (source) => {
10
12
  if (source?.entry) {
11
13
  for (const entry of Object.values(source.entry)) {
@@ -32,13 +34,49 @@ const resolveConfig = async (config) => {
32
34
  entries.add(e);
33
35
  }
34
36
  };
35
- checkSource(config.source);
37
+ const checkConfig = async (localConfig) => {
38
+ checkSource(localConfig.source);
39
+ if (!localConfig.tools?.rspack)
40
+ return;
41
+ const target = localConfig.output?.target ?? config.output?.target ?? 'web';
42
+ const mode = localConfig.mode ?? config.mode;
43
+ const passes = mode ? [mode === 'production'] : options.isProduction ? [true] : [false, true];
44
+ for (const rspack of [localConfig.tools.rspack].flat()) {
45
+ for (const isProduction of typeof rspack === 'function' ? passes : [options.isProduction]) {
46
+ const baseConfig = {
47
+ mode: mode ?? (isProduction ? 'production' : 'development'),
48
+ entry: {},
49
+ resolve: { alias: {} },
50
+ module: { rules: [] },
51
+ plugins: [],
52
+ };
53
+ const utils = {
54
+ env: process.env.NODE_ENV ?? '',
55
+ isDev: baseConfig.mode === 'development',
56
+ isProd: baseConfig.mode === 'production',
57
+ target,
58
+ isServer: target === 'node',
59
+ isWebWorker: target === 'web-worker',
60
+ };
61
+ const resolvedConfig = typeof rspack === 'function' ? await rspack(baseConfig, utils) : rspack;
62
+ const resolvedInputs = await resolveRspackConfig(resolvedConfig ?? baseConfig, {
63
+ ...options,
64
+ isProduction: options.isProduction || isProduction,
65
+ });
66
+ for (const input of resolvedInputs)
67
+ inputs.push(input);
68
+ }
69
+ }
70
+ };
71
+ await checkConfig(config);
36
72
  if (config.environments) {
37
73
  for (const environment of Object.values(config.environments)) {
38
- checkSource(environment.source);
74
+ await checkConfig(environment);
39
75
  }
40
76
  }
41
- return Array.from(entries).map(input => toProductionEntry(input));
77
+ for (const entry of entries)
78
+ inputs.push(toProductionEntry(entry));
79
+ return inputs;
42
80
  };
43
81
  const plugin = {
44
82
  title,
@@ -1,15 +1,32 @@
1
+ import type { Configuration } from 'webpack';
1
2
  type EntryDescription = Record<string, unknown>;
2
3
  type Entry = Record<string, string | string[] | (EntryDescription & {
3
4
  html?: boolean;
4
5
  })>;
6
+ type RspackConfigUtils = {
7
+ env: string;
8
+ isDev: boolean;
9
+ isProd: boolean;
10
+ target: 'web' | 'node' | 'web-worker';
11
+ isServer: boolean;
12
+ isWebWorker: boolean;
13
+ };
14
+ type RspackConfig = Configuration | ((config: Configuration, utils: RspackConfigUtils) => Configuration | void | Promise<Configuration | void>);
5
15
  export type RsbuildConfig = {
6
16
  plugins?: unknown[];
17
+ mode?: 'development' | 'production' | 'none';
18
+ output?: {
19
+ target?: RspackConfigUtils['target'];
20
+ };
7
21
  source?: {
8
22
  entry?: Entry;
9
23
  preEntry?: string | string[];
10
24
  };
25
+ tools?: {
26
+ rspack?: RspackConfig | RspackConfig[];
27
+ };
11
28
  environments?: {
12
- [k: string]: Pick<RsbuildConfig, 'plugins' | 'source'>;
29
+ [k: string]: Omit<RsbuildConfig, 'environments'>;
13
30
  };
14
31
  };
15
32
  export {};
@@ -1,3 +1,5 @@
1
- import type { Plugin } from '../../types/config.ts';
1
+ import type { Plugin, ResolveConfig } from '../../types/config.ts';
2
+ import type { WebpackConfig } from '../webpack/types.ts';
3
+ export declare const resolveConfig: ResolveConfig<WebpackConfig>;
2
4
  declare const plugin: Plugin;
3
5
  export default plugin;