@warlock.js/core 5.33.0 → 5.34.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 (103) hide show
  1. package/CHANGELOG.md +26 -46
  2. package/esm/cli/commands/check/check-command.action.mjs +2 -1
  3. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  4. package/esm/cli/commands/check.command.mjs +2 -2
  5. package/esm/cli/commands/check.command.mjs.map +1 -1
  6. package/esm/cli/commands/e2e.command.mjs +63 -0
  7. package/esm/cli/commands/e2e.command.mjs.map +1 -0
  8. package/esm/cli/commands/load-e2e-spec.mjs +47 -0
  9. package/esm/cli/commands/load-e2e-spec.mjs.map +1 -0
  10. package/esm/cli/framework-cli-commands.mjs +2 -0
  11. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  12. package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +1 -1
  13. package/esm/dev-server/health-checker/default-health-checkers.mjs +1 -6
  14. package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -1
  15. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +0 -2
  16. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
  17. package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
  18. package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
  19. package/esm/e2e/browser.d.mts +4 -0
  20. package/esm/e2e/browser.d.mts.map +1 -0
  21. package/esm/e2e/browser.mjs +88 -0
  22. package/esm/e2e/browser.mjs.map +1 -0
  23. package/esm/e2e/cdp.d.mts +152 -0
  24. package/esm/e2e/cdp.d.mts.map +1 -0
  25. package/esm/e2e/cdp.mjs +106 -0
  26. package/esm/e2e/cdp.mjs.map +1 -0
  27. package/esm/e2e/fixture.mjs +195 -0
  28. package/esm/e2e/fixture.mjs.map +1 -0
  29. package/esm/e2e/index.d.mts +5 -0
  30. package/esm/e2e/index.mjs +6 -0
  31. package/esm/e2e/owned-process.mjs +193 -0
  32. package/esm/e2e/owned-process.mjs.map +1 -0
  33. package/esm/e2e/page.d.mts +64 -0
  34. package/esm/e2e/page.d.mts.map +1 -0
  35. package/esm/e2e/page.mjs +376 -0
  36. package/esm/e2e/page.mjs.map +1 -0
  37. package/esm/e2e/run-e2e.d.mts +17 -0
  38. package/esm/e2e/run-e2e.d.mts.map +1 -0
  39. package/esm/e2e/run-e2e.mjs +143 -0
  40. package/esm/e2e/run-e2e.mjs.map +1 -0
  41. package/esm/e2e/types.d.mts +123 -0
  42. package/esm/e2e/types.d.mts.map +1 -0
  43. package/esm/e2e/types.mjs +24 -0
  44. package/esm/e2e/types.mjs.map +1 -0
  45. package/esm/secrets/errors.d.mts +1 -1
  46. package/esm/secrets/errors.d.mts.map +1 -1
  47. package/esm/secrets/errors.mjs +2 -0
  48. package/esm/secrets/errors.mjs.map +1 -1
  49. package/esm/secrets/resolve-driver.mjs +14 -0
  50. package/esm/secrets/resolve-driver.mjs.map +1 -0
  51. package/esm/secrets/secret-store.d.mts.map +1 -1
  52. package/esm/secrets/secret-store.mjs +4 -1
  53. package/esm/secrets/secret-store.mjs.map +1 -1
  54. package/esm/secrets/secrets.d.mts.map +1 -1
  55. package/esm/secrets/secrets.mjs +10 -1
  56. package/esm/secrets/secrets.mjs.map +1 -1
  57. package/esm/secrets/types.d.mts +4 -0
  58. package/esm/secrets/types.d.mts.map +1 -1
  59. package/esm/tooling/check/lint-step.d.mts +3 -3
  60. package/esm/tooling/check/lint-step.d.mts.map +1 -1
  61. package/esm/tooling/check/lint-step.mjs +3 -47
  62. package/esm/tooling/check/lint-step.mjs.map +1 -1
  63. package/esm/tooling/check/run-check.d.mts +2 -2
  64. package/esm/tooling/check/run-check.mjs +4 -2
  65. package/esm/tooling/check/run-check.mjs.map +1 -1
  66. package/esm/tooling/problem.d.mts +1 -1
  67. package/esm/tooling/problem.d.mts.map +1 -1
  68. package/esm/tooling/problem.mjs.map +1 -1
  69. package/esm/updater/package-manager.mjs +5 -1
  70. package/esm/updater/package-manager.mjs.map +1 -1
  71. package/esm/upgrade/codemods/index.mjs +2 -1
  72. package/esm/upgrade/codemods/index.mjs.map +1 -1
  73. package/esm/upgrade/codemods/oxc-toolchain.mjs +59 -10
  74. package/esm/upgrade/codemods/oxc-toolchain.mjs.map +1 -1
  75. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs +6 -0
  76. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs.map +1 -0
  77. package/esm/upgrade/codemods/port-eslint-rules.mjs +343 -0
  78. package/esm/upgrade/codemods/port-eslint-rules.mjs.map +1 -0
  79. package/esm/upgrade/codemods/toolchain-templates.mjs +1 -1
  80. package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -1
  81. package/esm/upgrade/codemods/validate-oxlint-rule.mjs +60 -0
  82. package/esm/upgrade/codemods/validate-oxlint-rule.mjs.map +1 -0
  83. package/esm/upgrade/notes.mjs +14 -7
  84. package/esm/upgrade/notes.mjs.map +1 -1
  85. package/esm/warlock-config/types.d.mts +0 -1
  86. package/esm/warlock-config/types.d.mts.map +1 -1
  87. package/llms-full.txt +243 -41
  88. package/llms.txt +2 -1
  89. package/package.json +19 -20
  90. package/skills/create-module/SKILL.md +2 -2
  91. package/skills/run-app/SKILL.md +3 -3
  92. package/skills/store-secrets/SKILL.md +40 -1
  93. package/skills/test-browser/SKILL.md +119 -0
  94. package/skills/upgrade-from-5-29/SKILL.md +70 -29
  95. package/skills/warlock-check/SKILL.md +3 -3
  96. package/skills/warlock-doctor/SKILL.md +2 -3
  97. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +0 -152
  98. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +0 -1
  99. package/esm/dev-server/health-checker/eslint-support.mjs +0 -27
  100. package/esm/dev-server/health-checker/eslint-support.mjs.map +0 -1
  101. package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +0 -1
  102. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +0 -183
  103. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +0 -1
@@ -6,6 +6,10 @@ export interface SecretBackend {
6
6
  remove(namespace: string, name: string): Promise<boolean>;
7
7
  }
8
8
  export type SecretsConfigurations = {
9
+ /** auto selects keychain/environment/files; memory is isolated to this process. */
10
+ driver?: "auto" | "memory";
11
+ /** Explicitly permit the volatile memory driver under NODE_ENV=production. */
12
+ allowMemoryInProduction?: boolean;
9
13
  /** Stable identity shared only by processes belonging to this app installation. */
10
14
  namespace?: string;
11
15
  /** Trusted parent location; defaults to storage/secrets. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/types.ts"],"mappings":";;iBACiB;EACf,IAAI,mBAAmB,eAAe;EACtC,IAAI,mBAAmB,cAAc,gBAAgB;EACrD,OAAO,mBAAmB,eAAe;;YAG/B;;EAEV;;EAEA;;YAGU,qBAAqB;EAC/B;;EAEA,UAAU;;EAEV"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/types.ts"],"mappings":";;iBACiB;EACf,IAAI,mBAAmB,eAAe;EACtC,IAAI,mBAAmB,cAAc,gBAAgB;EACrD,OAAO,mBAAmB,eAAe;;YAG/B;;EAEV;;EAEA;;EAEA;;EAEA;;YAGU,qBAAqB;EAC/B;;EAEA,UAAU;;EAEV"}
@@ -3,10 +3,10 @@ import { ToolingProblem } from "../problem.mjs";
3
3
  /** What the lint step found, and which linters ran. */
4
4
  export type LintOutcome = {
5
5
  problems: ToolingProblem[];
6
- /** The linters that ran (`eslint`, `oxlint`); empty when the app configures none. */
6
+ /** The linters that ran (`oxlint`); empty when the app configures none. */
7
7
  linters: string[];
8
8
  };
9
- /** Oxlint by default. ESLint requires an explicit selection and a flat config. */
10
- export declare function runLint(cwd: string, files: readonly string[], selection?: "oxlint" | "eslint"): Promise<LintOutcome>;
9
+ /** Run configured Oxlint over lintable source files. */
10
+ export declare function runLint(cwd: string, files: readonly string[]): Promise<LintOutcome>;
11
11
  //#endregion
12
12
  //# sourceMappingURL=lint-step.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lint-step.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/lint-step.ts"],"mappings":";;;YAWY;EACV,UAAU;;EAEV;;;wBAiFoB,QACpB,aACA,0BACA,kCACC,QAAQ"}
1
+ {"version":3,"file":"lint-step.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/lint-step.ts"],"mappings":";;;YASY;EACV,UAAU;;EAEV;;;wBAmDoB,QAAQ,aAAa,2BAA2B,QAAQ"}
@@ -1,8 +1,6 @@
1
- import { hasEslint, warnEslintDeprecation } from "../../dev-server/health-checker/eslint-support.mjs";
2
1
  import { OxlintRunner, findOxlint, hasOxlintConfig } from "../../dev-server/health-checker/oxlint-runner.mjs";
3
2
  import { createProblem } from "../problem.mjs";
4
3
  import path from "node:path";
5
- import { ESLint } from "eslint";
6
4
 
7
5
  //#region ../../packages/core/src/tooling/check/lint-step.ts
8
6
  const LINTABLE = /\.[cm]?[jt]sx?$/i;
@@ -19,31 +17,6 @@ function toolFailure(tool, error) {
19
17
  message: `${tool} could not run: ${message.split("\n")[0]}`
20
18
  });
21
19
  }
22
- async function runEslint(cwd, files) {
23
- const results = await new ESLint({
24
- cwd,
25
- errorOnUnmatchedPattern: false,
26
- warnIgnored: false
27
- }).lintFiles([...files]);
28
- const problems = [];
29
- for (const result of results) {
30
- const file = path.relative(cwd, result.filePath).split(path.sep).join("/");
31
- for (const message of result.messages) {
32
- const error = message.severity === 2 || message.fatal === true;
33
- problems.push(createProblem({
34
- code: error ? "WL7201" : "WL7202",
35
- source: "eslint",
36
- severity: error ? "error" : "warning",
37
- message: message.message,
38
- file,
39
- line: message.line || null,
40
- column: message.column || null,
41
- rule: message.ruleId ?? null
42
- }));
43
- }
44
- }
45
- return problems;
46
- }
47
20
  async function runOxlint(cwd, files) {
48
21
  const runner = new OxlintRunner();
49
22
  runner.initialize(cwd);
@@ -65,8 +38,8 @@ async function runOxlint(cwd, files) {
65
38
  await runner.shutdown();
66
39
  }
67
40
  }
68
- /** Oxlint by default. ESLint requires an explicit selection and a flat config. */
69
- async function runLint(cwd, files, selection = "oxlint") {
41
+ /** Run configured Oxlint over lintable source files. */
42
+ async function runLint(cwd, files) {
70
43
  const lintable = files.filter((file) => LINTABLE.test(file));
71
44
  const problems = [];
72
45
  const linters = [];
@@ -74,24 +47,7 @@ async function runLint(cwd, files, selection = "oxlint") {
74
47
  problems,
75
48
  linters
76
49
  };
77
- if (selection === "eslint") {
78
- warnEslintDeprecation();
79
- if (!hasEslint(cwd)) return {
80
- linters: ["eslint"],
81
- problems: [createProblem({
82
- code: "WL8002",
83
- source: "environment",
84
- message: "Cannot find ESLint flat config in the app."
85
- })]
86
- };
87
- linters.push("eslint");
88
- try {
89
- problems.push(...await runEslint(cwd, lintable));
90
- } catch (error) {
91
- problems.push(toolFailure("ESLint", error));
92
- }
93
- }
94
- if (selection === "oxlint" && hasOxlintConfig(cwd)) {
50
+ if (hasOxlintConfig(cwd)) {
95
51
  linters.push("oxlint");
96
52
  if (!findOxlint(cwd)) problems.push(createProblem({
97
53
  code: "WL8002",
@@ -1 +1 @@
1
- {"version":3,"file":"lint-step.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/lint-step.ts"],"sourcesContent":["import { ESLint } from \"eslint\";\nimport path from \"node:path\";\nimport { hasEslint, warnEslintDeprecation } from \"../../dev-server/health-checker/eslint-support\";\nimport {\n findOxlint,\n hasOxlintConfig,\n OxlintRunner,\n} from \"../../dev-server/health-checker/oxlint-runner\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\n\n/** What the lint step found, and which linters ran. */\nexport type LintOutcome = {\n problems: ToolingProblem[];\n /** The linters that ran (`eslint`, `oxlint`); empty when the app configures none. */\n linters: string[];\n};\n\nconst LINTABLE = /\\.[cm]?[jt]sx?$/i;\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A missing package/plugin is the machine's install; anything else is the tool crashing. */\nfunction toolFailure(tool: string, error: unknown): ToolingProblem {\n const message = errorMessage(error);\n const missing = /cannot find (?:module|package)|ERR_MODULE_NOT_FOUND/i.test(message);\n\n return createProblem({\n code: missing ? \"WL8002\" : \"WL8007\",\n source: \"environment\",\n message: `${tool} could not run: ${message.split(\"\\n\")[0]}`,\n });\n}\n\nasync function runEslint(cwd: string, files: readonly string[]): Promise<ToolingProblem[]> {\n // The same flat-config lookup the dev server's ESLint checker uses.\n const eslint = new ESLint({ cwd, errorOnUnmatchedPattern: false, warnIgnored: false });\n const results = await eslint.lintFiles([...files]);\n const problems: ToolingProblem[] = [];\n\n for (const result of results) {\n const file = path.relative(cwd, result.filePath).split(path.sep).join(\"/\");\n\n for (const message of result.messages) {\n const error = message.severity === 2 || message.fatal === true;\n\n problems.push(\n createProblem({\n code: error ? \"WL7201\" : \"WL7202\",\n source: \"eslint\",\n severity: error ? \"error\" : \"warning\",\n message: message.message,\n file,\n line: message.line || null,\n column: message.column || null,\n rule: message.ruleId ?? null,\n }),\n );\n }\n }\n\n return problems;\n}\n\nasync function runOxlint(cwd: string, files: readonly string[]): Promise<ToolingProblem[]> {\n const runner = new OxlintRunner();\n\n runner.initialize(cwd);\n\n try {\n const results = await runner.checkFiles(\n files.map((file) => ({ path: path.join(cwd, file), relativePath: file })),\n );\n\n return results.flatMap((result) =>\n [...result.errors, ...result.warnings].map((message) =>\n createProblem({\n code: message.type === \"error\" ? \"WL7201\" : \"WL7202\",\n source: \"oxlint\",\n severity: message.type,\n message: message.message,\n file: message.relativePath,\n line: message.lineNumber,\n column: message.columnNumber,\n rule: message.ruleId ?? null,\n }),\n ),\n );\n } finally {\n await runner.shutdown();\n }\n}\n\n/** Oxlint by default. ESLint requires an explicit selection and a flat config. */\nexport async function runLint(\n cwd: string,\n files: readonly string[],\n selection: \"oxlint\" | \"eslint\" = \"oxlint\",\n): Promise<LintOutcome> {\n const lintable = files.filter((file) => LINTABLE.test(file));\n const problems: ToolingProblem[] = [];\n const linters: string[] = [];\n\n // ESLint lints the whole directory when handed no pattern; nothing to lint\n // means nothing to run.\n if (lintable.length === 0) return { problems, linters };\n\n if (selection === \"eslint\") {\n warnEslintDeprecation();\n if (!hasEslint(cwd)) {\n return {\n linters: [\"eslint\"],\n problems: [\n createProblem({\n code: \"WL8002\",\n source: \"environment\",\n message: \"Cannot find ESLint flat config in the app.\",\n }),\n ],\n };\n }\n linters.push(\"eslint\");\n\n try {\n problems.push(...(await runEslint(cwd, lintable)));\n } catch (error) {\n problems.push(toolFailure(\"ESLint\", error));\n }\n }\n\n if (selection === \"oxlint\" && hasOxlintConfig(cwd)) {\n linters.push(\"oxlint\");\n\n if (!findOxlint(cwd)) {\n problems.push(\n createProblem({\n code: \"WL8002\",\n source: \"environment\",\n message: \"An oxlint config exists but the oxlint package is not installed in the app.\",\n }),\n );\n } else {\n try {\n problems.push(...(await runOxlint(cwd, lintable)));\n } catch (error) {\n problems.push(toolFailure(\"oxlint\", error));\n }\n }\n }\n\n return { problems, linters };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lint-step.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/lint-step.ts"],"sourcesContent":["import path from \"node:path\";\nimport {\n findOxlint,\n hasOxlintConfig,\n OxlintRunner,\n} from \"../../dev-server/health-checker/oxlint-runner\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\n\n/** What the lint step found, and which linters ran. */\nexport type LintOutcome = {\n problems: ToolingProblem[];\n /** The linters that ran (`oxlint`); empty when the app configures none. */\n linters: string[];\n};\n\nconst LINTABLE = /\\.[cm]?[jt]sx?$/i;\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A missing package/plugin is the machine's install; anything else is the tool crashing. */\nfunction toolFailure(tool: string, error: unknown): ToolingProblem {\n const message = errorMessage(error);\n const missing = /cannot find (?:module|package)|ERR_MODULE_NOT_FOUND/i.test(message);\n\n return createProblem({\n code: missing ? \"WL8002\" : \"WL8007\",\n source: \"environment\",\n message: `${tool} could not run: ${message.split(\"\\n\")[0]}`,\n });\n}\n\nasync function runOxlint(cwd: string, files: readonly string[]): Promise<ToolingProblem[]> {\n const runner = new OxlintRunner();\n\n runner.initialize(cwd);\n\n try {\n const results = await runner.checkFiles(\n files.map((file) => ({ path: path.join(cwd, file), relativePath: file })),\n );\n\n return results.flatMap((result) =>\n [...result.errors, ...result.warnings].map((message) =>\n createProblem({\n code: message.type === \"error\" ? \"WL7201\" : \"WL7202\",\n source: \"oxlint\",\n severity: message.type,\n message: message.message,\n file: message.relativePath,\n line: message.lineNumber,\n column: message.columnNumber,\n rule: message.ruleId ?? null,\n }),\n ),\n );\n } finally {\n await runner.shutdown();\n }\n}\n\n/** Run configured Oxlint over lintable source files. */\nexport async function runLint(cwd: string, files: readonly string[]): Promise<LintOutcome> {\n const lintable = files.filter((file) => LINTABLE.test(file));\n const problems: ToolingProblem[] = [];\n const linters: string[] = [];\n\n if (lintable.length === 0) return { problems, linters };\n\n if (hasOxlintConfig(cwd)) {\n linters.push(\"oxlint\");\n\n if (!findOxlint(cwd)) {\n problems.push(\n createProblem({\n code: \"WL8002\",\n source: \"environment\",\n message: \"An oxlint config exists but the oxlint package is not installed in the app.\",\n }),\n );\n } else {\n try {\n problems.push(...(await runOxlint(cwd, lintable)));\n } catch (error) {\n problems.push(toolFailure(\"oxlint\", error));\n }\n }\n }\n\n return { problems, linters };\n}\n"],"mappings":";;;;;AAeA,MAAM,WAAW;AAEjB,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;;AAGA,SAAS,YAAY,MAAc,OAAgC;CACjE,MAAM,UAAU,aAAa,KAAK;CAClC,MAAM,UAAU,uDAAuD,KAAK,OAAO;CAEnF,OAAO,cAAc;EACnB,MAAM,UAAU,WAAW;EAC3B,QAAQ;EACR,SAAS,GAAG,KAAK,kBAAkB,QAAQ,MAAM,IAAI,CAAC,CAAC;CACzD,CAAC;AACH;AAEA,eAAe,UAAU,KAAa,OAAqD;CACzF,MAAM,SAAS,IAAI,aAAa;CAEhC,OAAO,WAAW,GAAG;CAErB,IAAI;EAKF,QAAO,MAJe,OAAO,WAC3B,MAAM,KAAK,UAAU;GAAE,MAAM,KAAK,KAAK,KAAK,IAAI;GAAG,cAAc;EAAK,EAAE,CAC1E,EAEc,CAAC,SAAS,WACtB,CAAC,GAAG,OAAO,QAAQ,GAAG,OAAO,QAAQ,CAAC,CAAC,KAAK,YAC1C,cAAc;GACZ,MAAM,QAAQ,SAAS,UAAU,WAAW;GAC5C,QAAQ;GACR,UAAU,QAAQ;GAClB,SAAS,QAAQ;GACjB,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,QAAQ,QAAQ;GAChB,MAAM,QAAQ,UAAU;EAC1B,CAAC,CACH,CACF;CACF,UAAU;EACR,MAAM,OAAO,SAAS;CACxB;AACF;;AAGA,eAAsB,QAAQ,KAAa,OAAgD;CACzF,MAAM,WAAW,MAAM,QAAQ,SAAS,SAAS,KAAK,IAAI,CAAC;CAC3D,MAAM,WAA6B,CAAC;CACpC,MAAM,UAAoB,CAAC;CAE3B,IAAI,SAAS,WAAW,GAAG,OAAO;EAAE;EAAU;CAAQ;CAEtD,IAAI,gBAAgB,GAAG,GAAG;EACxB,QAAQ,KAAK,QAAQ;EAErB,IAAI,CAAC,WAAW,GAAG,GACjB,SAAS,KACP,cAAc;GACZ,MAAM;GACN,QAAQ;GACR,SAAS;EACX,CAAC,CACH;OAEA,IAAI;GACF,SAAS,KAAK,GAAI,MAAM,UAAU,KAAK,QAAQ,CAAE;EACnD,SAAS,OAAO;GACd,SAAS,KAAK,YAAY,UAAU,KAAK,CAAC;EAC5C;CAEJ;CAEA,OAAO;EAAE;EAAU;CAAQ;AAC7B"}
@@ -9,8 +9,8 @@ export type TypecheckRunner = (options: TypecheckOptions) => Promise<TypecheckRe
9
9
  export type CheckOptions = {
10
10
  /** Project root. Default: `process.cwd()`. */
11
11
  cwd?: string;
12
- /** Oxlint by default; ESLint is deprecated and requires explicit selection. */
13
- lint?: "oxlint" | "eslint";
12
+ /** Oxlint is the only supported linter. */
13
+ lint?: "oxlint";
14
14
  /** Limit every step to the files changed against `base` (default HEAD) plus untracked files. */
15
15
  changed?: boolean;
16
16
  /** Compare with `git merge-base <base> HEAD` instead of HEAD. Implies `changed`. */
@@ -29,6 +29,8 @@ function statusOf(problems) {
29
29
  * list, and every step after `environment` is `blocked`.
30
30
  */
31
31
  async function runCheck(options = {}) {
32
+ const selection = options.lint;
33
+ if (selection !== void 0 && selection !== "oxlint") throw new Error("ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default; run npx warlock upgrade --from 5.33 --to 5.34.0.");
32
34
  const cwd = path.resolve(options.cwd ?? process.cwd());
33
35
  const changed = options.changed === true || options.base !== void 0;
34
36
  const skip = new Set(options.skip ?? []);
@@ -119,10 +121,10 @@ async function runCheck(options = {}) {
119
121
  detail: "no files to lint",
120
122
  problems: []
121
123
  };
122
- const { linters, problems: found } = await runLint(cwd, lintFiles, options.lint);
124
+ const { linters, problems: found } = await runLint(cwd, lintFiles);
123
125
  if (linters.length === 0) return {
124
126
  status: "skipped",
125
- detail: "no Oxlint config (ESLint requires --lint eslint)",
127
+ detail: "no Oxlint config",
126
128
  problems: []
127
129
  };
128
130
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"run-check.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/run-check.ts"],"sourcesContent":["import path from \"node:path\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\nimport {\n runTypecheck,\n type TypecheckOptions,\n type TypecheckResult,\n} from \"../typecheck/run-typecheck\";\nimport { type GitRunner, listChangedFiles } from \"./changed-files\";\nimport {\n CHECK_STEPS,\n type CheckReport,\n type CheckStep,\n type CheckStepName,\n type CheckStepStatus,\n checkExitCode,\n} from \"./check-report.types\";\nimport { runConventions } from \"./conventions-step\";\nimport { checkNodeVersion } from \"./environment-step\";\nimport { runLint } from \"./lint-step\";\nimport { listSourceFiles, listTestFiles } from \"./source-files\";\nimport {\n findVitest,\n parseVitestReport,\n runVitest,\n type VitestRunner,\n vitestArguments,\n vitestRunFailure,\n} from \"./tests-step\";\n\n/** Runs the typecheck step. The CLI swaps in a child process so a heap overflow cannot end `check`. */\nexport type TypecheckRunner = (\n options: TypecheckOptions,\n) => Promise<TypecheckResult> | TypecheckResult;\n\n/** Inputs of {@link runCheck}. */\nexport type CheckOptions = {\n /** Project root. Default: `process.cwd()`. */\n cwd?: string;\n /** Oxlint by default; ESLint is deprecated and requires explicit selection. */\n lint?: \"oxlint\" | \"eslint\";\n /** Limit every step to the files changed against `base` (default HEAD) plus untracked files. */\n changed?: boolean;\n /** Compare with `git merge-base <base> HEAD` instead of HEAD. Implies `changed`. */\n base?: string;\n /** Steps not to run. */\n skip?: readonly CheckStepName[];\n /** tsconfig path, relative to `cwd`. Default: `tsconfig.json`. */\n project?: string;\n /** How git is run. */\n git?: GitRunner;\n /** How the typecheck runs. Default: in this process. */\n typecheck?: TypecheckRunner;\n /** How vitest runs. */\n vitest?: VitestRunner;\n /** Where the app's vitest CLI is. Default: resolved from the app root. */\n vitestBinary?: string | null;\n /** The Node.js version to check. Default: the running one. */\n nodeVersion?: string;\n};\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction statusOf(problems: readonly ToolingProblem[]): CheckStepStatus {\n const errors = problems.filter((problem) => problem.severity === \"error\");\n\n if (errors.some((problem) => problem.category === \"environment\")) return \"blocked\";\n\n return errors.length > 0 ? \"failed\" : \"passed\";\n}\n\n/** Bundles one step's timing, status and problems. */\ntype StepOutcome = { status?: CheckStepStatus; detail: string | null; problems: ToolingProblem[] };\n\n/**\n * The single verify command: typecheck, lint, the tests affected by the\n * change, and the framework's convention checks, as one report with one entry\n * per problem and an exit code that tells \"your code\" (1) from \"this machine\"\n * (2).\n *\n * Every step runs even when an earlier one failed, so one run lists every\n * problem. The only exception: with `changed`, a git failure leaves no file\n * list, and every step after `environment` is `blocked`.\n */\nexport async function runCheck(options: CheckOptions = {}): Promise<CheckReport> {\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const changed = options.changed === true || options.base !== undefined;\n const skip = new Set(options.skip ?? []);\n const steps: CheckStep[] = [];\n const problems: ToolingProblem[] = [];\n let files: string[] | null = null;\n let filesBlocked = false;\n\n const run = async (name: CheckStepName, body: () => Promise<StepOutcome> | StepOutcome) => {\n const startedAt = Date.now();\n\n if (skip.has(name)) {\n steps.push({ name, status: \"skipped\", durationMs: 0, detail: \"skipped with --skip\" });\n return;\n }\n\n if (filesBlocked && name !== \"environment\") {\n steps.push({\n name,\n status: \"blocked\",\n durationMs: 0,\n detail: \"the changed files could not be listed\",\n });\n return;\n }\n\n let outcome: StepOutcome;\n\n try {\n outcome = await body();\n } catch (error) {\n outcome = {\n detail: null,\n problems: [\n createProblem({\n code: \"WL8007\",\n source: \"environment\",\n message: `The ${name} step crashed: ${errorMessage(error)}`,\n }),\n ],\n };\n }\n\n problems.push(...outcome.problems);\n steps.push({\n name,\n status: outcome.status ?? statusOf(outcome.problems),\n durationMs: Date.now() - startedAt,\n detail: outcome.detail,\n });\n };\n\n await run(\"environment\", async () => {\n const found = checkNodeVersion(cwd, options.nodeVersion);\n\n if (changed) {\n const listed = await listChangedFiles({ cwd, base: options.base, git: options.git });\n\n if (\"problem\" in listed) {\n filesBlocked = true;\n found.push(listed.problem);\n } else {\n files = listed.files;\n }\n }\n\n return {\n detail: `Node ${options.nodeVersion ?? process.versions.node}${files ? `, ${files.length} changed file(s)` : \"\"}`,\n problems: found,\n };\n });\n\n const targets = async (): Promise<string[]> => files ?? (await listSourceFiles(cwd));\n\n await run(\"typecheck\", async () => {\n if (files !== null && files.length === 0) {\n return { status: \"skipped\", detail: \"no changed files\", problems: [] };\n }\n\n const typecheck = options.typecheck ?? runTypecheck;\n const result = await typecheck({\n cwd,\n project: options.project,\n ...(files !== null ? { files } : {}),\n });\n\n return {\n detail: `${result.checkedFiles} file(s) checked in ${result.durationMs}ms`,\n problems: result.problems,\n };\n });\n\n await run(\"lint\", async () => {\n const lintFiles = await targets();\n\n if (lintFiles.length === 0) {\n return { status: \"skipped\", detail: \"no files to lint\", problems: [] };\n }\n\n const { linters, problems: found } = await runLint(cwd, lintFiles, options.lint);\n\n if (linters.length === 0) {\n return {\n status: \"skipped\",\n detail: \"no Oxlint config (ESLint requires --lint eslint)\",\n problems: [],\n };\n }\n\n return { detail: `${linters.join(\" + \")} over ${lintFiles.length} file(s)`, problems: found };\n });\n\n await run(\"tests\", async () => {\n if (files !== null && files.length === 0) {\n return { status: \"skipped\", detail: \"no changed files\", problems: [] };\n }\n\n const binary = options.vitestBinary === undefined ? findVitest(cwd) : options.vitestBinary;\n\n if (!binary) {\n if ((await listTestFiles(cwd)).length === 0) {\n return { status: \"skipped\", detail: \"no test files\", problems: [] };\n }\n\n return {\n detail: null,\n problems: [\n createProblem({\n code: \"WL8002\",\n source: \"environment\",\n message: \"The project has test files but vitest is not installed in the app.\",\n }),\n ],\n };\n }\n\n const vitest = options.vitest ?? runVitest;\n const outcome = await vitest(binary, vitestArguments(files), cwd);\n const found =\n outcome.report === undefined\n ? vitestRunFailure(outcome)\n : parseVitestReport(outcome.report, cwd);\n const total =\n typeof outcome.report === \"object\" && outcome.report !== null\n ? Reflect.get(outcome.report, \"numTotalTests\")\n : undefined;\n\n return {\n detail: typeof total === \"number\" ? `${total} test(s) ran` : null,\n problems: found,\n };\n });\n\n await run(\"conventions\", async () => {\n const conventionFiles = await targets();\n\n return {\n detail: `${conventionFiles.length} file(s) checked`,\n problems: runConventions(cwd, conventionFiles, options.project),\n };\n });\n\n // Keep the declared order even if a future step is added out of sequence.\n steps.sort((first, second) => CHECK_STEPS.indexOf(first.name) - CHECK_STEPS.indexOf(second.name));\n\n const exitCode = checkExitCode(problems);\n const errors = problems.filter((problem) => problem.severity === \"error\");\n\n return {\n schemaVersion: 1,\n ok: exitCode === 0,\n exitCode,\n changed,\n files,\n steps,\n problems,\n summary: {\n errors: errors.length,\n warnings: problems.length - errors.length,\n code: errors.filter((problem) => problem.category === \"code\").length,\n environment: errors.filter((problem) => problem.category === \"environment\").length,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AA4DA,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,SAAS,UAAsD;CACtE,MAAM,SAAS,SAAS,QAAQ,YAAY,QAAQ,aAAa,OAAO;CAExE,IAAI,OAAO,MAAM,YAAY,QAAQ,aAAa,aAAa,GAAG,OAAO;CAEzE,OAAO,OAAO,SAAS,IAAI,WAAW;AACxC;;;;;;;;;;;AAeA,eAAsB,SAAS,UAAwB,CAAC,GAAyB;CAC/E,MAAM,MAAM,KAAK,QAAQ,QAAQ,OAAO,QAAQ,IAAI,CAAC;CACrD,MAAM,UAAU,QAAQ,YAAY,QAAQ,QAAQ,SAAS;CAC7D,MAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ,CAAC,CAAC;CACvC,MAAM,QAAqB,CAAC;CAC5B,MAAM,WAA6B,CAAC;CACpC,IAAI,QAAyB;CAC7B,IAAI,eAAe;CAEnB,MAAM,MAAM,OAAO,MAAqB,SAAmD;EACzF,MAAM,YAAY,KAAK,IAAI;EAE3B,IAAI,KAAK,IAAI,IAAI,GAAG;GAClB,MAAM,KAAK;IAAE;IAAM,QAAQ;IAAW,YAAY;IAAG,QAAQ;GAAsB,CAAC;GACpF;EACF;EAEA,IAAI,gBAAgB,SAAS,eAAe;GAC1C,MAAM,KAAK;IACT;IACA,QAAQ;IACR,YAAY;IACZ,QAAQ;GACV,CAAC;GACD;EACF;EAEA,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,KAAK;EACvB,SAAS,OAAO;GACd,UAAU;IACR,QAAQ;IACR,UAAU,CACR,cAAc;KACZ,MAAM;KACN,QAAQ;KACR,SAAS,OAAO,KAAK,iBAAiB,aAAa,KAAK;IAC1D,CAAC,CACH;GACF;EACF;EAEA,SAAS,KAAK,GAAG,QAAQ,QAAQ;EACjC,MAAM,KAAK;GACT;GACA,QAAQ,QAAQ,UAAU,SAAS,QAAQ,QAAQ;GACnD,YAAY,KAAK,IAAI,IAAI;GACzB,QAAQ,QAAQ;EAClB,CAAC;CACH;CAEA,MAAM,IAAI,eAAe,YAAY;EACnC,MAAM,QAAQ,iBAAiB,KAAK,QAAQ,WAAW;EAEvD,IAAI,SAAS;GACX,MAAM,SAAS,MAAM,iBAAiB;IAAE;IAAK,MAAM,QAAQ;IAAM,KAAK,QAAQ;GAAI,CAAC;GAEnF,IAAI,aAAa,QAAQ;IACvB,eAAe;IACf,MAAM,KAAK,OAAO,OAAO;GAC3B,OACE,QAAQ,OAAO;EAEnB;EAEA,OAAO;GACL,QAAQ,QAAQ,QAAQ,eAAe,QAAQ,SAAS,OAAO,QAAQ,KAAK,MAAM,OAAO,oBAAoB;GAC7G,UAAU;EACZ;CACF,CAAC;CAED,MAAM,UAAU,YAA+B,SAAU,MAAM,gBAAgB,GAAG;CAElF,MAAM,IAAI,aAAa,YAAY;EACjC,IAAI,UAAU,QAAQ,MAAM,WAAW,GACrC,OAAO;GAAE,QAAQ;GAAW,QAAQ;GAAoB,UAAU,CAAC;EAAE;EAIvE,MAAM,SAAS,OADG,QAAQ,aAAa,aACT,CAAC;GAC7B;GACA,SAAS,QAAQ;GACjB,GAAI,UAAU,OAAO,EAAE,MAAM,IAAI,CAAC;EACpC,CAAC;EAED,OAAO;GACL,QAAQ,GAAG,OAAO,aAAa,sBAAsB,OAAO,WAAW;GACvE,UAAU,OAAO;EACnB;CACF,CAAC;CAED,MAAM,IAAI,QAAQ,YAAY;EAC5B,MAAM,YAAY,MAAM,QAAQ;EAEhC,IAAI,UAAU,WAAW,GACvB,OAAO;GAAE,QAAQ;GAAW,QAAQ;GAAoB,UAAU,CAAC;EAAE;EAGvE,MAAM,EAAE,SAAS,UAAU,UAAU,MAAM,QAAQ,KAAK,WAAW,QAAQ,IAAI;EAE/E,IAAI,QAAQ,WAAW,GACrB,OAAO;GACL,QAAQ;GACR,QAAQ;GACR,UAAU,CAAC;EACb;EAGF,OAAO;GAAE,QAAQ,GAAG,QAAQ,KAAK,KAAK,EAAE,QAAQ,UAAU,OAAO;GAAW,UAAU;EAAM;CAC9F,CAAC;CAED,MAAM,IAAI,SAAS,YAAY;EAC7B,IAAI,UAAU,QAAQ,MAAM,WAAW,GACrC,OAAO;GAAE,QAAQ;GAAW,QAAQ;GAAoB,UAAU,CAAC;EAAE;EAGvE,MAAM,SAAS,QAAQ,iBAAiB,SAAY,WAAW,GAAG,IAAI,QAAQ;EAE9E,IAAI,CAAC,QAAQ;GACX,KAAK,MAAM,cAAc,GAAG,EAAC,CAAE,WAAW,GACxC,OAAO;IAAE,QAAQ;IAAW,QAAQ;IAAiB,UAAU,CAAC;GAAE;GAGpE,OAAO;IACL,QAAQ;IACR,UAAU,CACR,cAAc;KACZ,MAAM;KACN,QAAQ;KACR,SAAS;IACX,CAAC,CACH;GACF;EACF;EAGA,MAAM,UAAU,OADD,QAAQ,UAAU,UACL,CAAC,QAAQ,gBAAgB,KAAK,GAAG,GAAG;EAChE,MAAM,QACJ,QAAQ,WAAW,SACf,iBAAiB,OAAO,IACxB,kBAAkB,QAAQ,QAAQ,GAAG;EAC3C,MAAM,QACJ,OAAO,QAAQ,WAAW,YAAY,QAAQ,WAAW,OACrD,QAAQ,IAAI,QAAQ,QAAQ,eAAe,IAC3C;EAEN,OAAO;GACL,QAAQ,OAAO,UAAU,WAAW,GAAG,MAAM,gBAAgB;GAC7D,UAAU;EACZ;CACF,CAAC;CAED,MAAM,IAAI,eAAe,YAAY;EACnC,MAAM,kBAAkB,MAAM,QAAQ;EAEtC,OAAO;GACL,QAAQ,GAAG,gBAAgB,OAAO;GAClC,UAAU,eAAe,KAAK,iBAAiB,QAAQ,OAAO;EAChE;CACF,CAAC;CAGD,MAAM,MAAM,OAAO,WAAW,YAAY,QAAQ,MAAM,IAAI,IAAI,YAAY,QAAQ,OAAO,IAAI,CAAC;CAEhG,MAAM,WAAW,cAAc,QAAQ;CACvC,MAAM,SAAS,SAAS,QAAQ,YAAY,QAAQ,aAAa,OAAO;CAExE,OAAO;EACL,eAAe;EACf,IAAI,aAAa;EACjB;EACA;EACA;EACA;EACA;EACA,SAAS;GACP,QAAQ,OAAO;GACf,UAAU,SAAS,SAAS,OAAO;GACnC,MAAM,OAAO,QAAQ,YAAY,QAAQ,aAAa,MAAM,CAAC,CAAC;GAC9D,aAAa,OAAO,QAAQ,YAAY,QAAQ,aAAa,aAAa,CAAC,CAAC;EAC9E;CACF;AACF"}
1
+ {"version":3,"file":"run-check.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/tooling/check/run-check.ts"],"sourcesContent":["import path from \"node:path\";\nimport { createProblem, type ToolingProblem } from \"../problem\";\nimport {\n runTypecheck,\n type TypecheckOptions,\n type TypecheckResult,\n} from \"../typecheck/run-typecheck\";\nimport { type GitRunner, listChangedFiles } from \"./changed-files\";\nimport {\n CHECK_STEPS,\n type CheckReport,\n type CheckStep,\n type CheckStepName,\n type CheckStepStatus,\n checkExitCode,\n} from \"./check-report.types\";\nimport { runConventions } from \"./conventions-step\";\nimport { checkNodeVersion } from \"./environment-step\";\nimport { runLint } from \"./lint-step\";\nimport { listSourceFiles, listTestFiles } from \"./source-files\";\nimport {\n findVitest,\n parseVitestReport,\n runVitest,\n type VitestRunner,\n vitestArguments,\n vitestRunFailure,\n} from \"./tests-step\";\n\n/** Runs the typecheck step. The CLI swaps in a child process so a heap overflow cannot end `check`. */\nexport type TypecheckRunner = (\n options: TypecheckOptions,\n) => Promise<TypecheckResult> | TypecheckResult;\n\n/** Inputs of {@link runCheck}. */\nexport type CheckOptions = {\n /** Project root. Default: `process.cwd()`. */\n cwd?: string;\n /** Oxlint is the only supported linter. */\n lint?: \"oxlint\";\n /** Limit every step to the files changed against `base` (default HEAD) plus untracked files. */\n changed?: boolean;\n /** Compare with `git merge-base <base> HEAD` instead of HEAD. Implies `changed`. */\n base?: string;\n /** Steps not to run. */\n skip?: readonly CheckStepName[];\n /** tsconfig path, relative to `cwd`. Default: `tsconfig.json`. */\n project?: string;\n /** How git is run. */\n git?: GitRunner;\n /** How the typecheck runs. Default: in this process. */\n typecheck?: TypecheckRunner;\n /** How vitest runs. */\n vitest?: VitestRunner;\n /** Where the app's vitest CLI is. Default: resolved from the app root. */\n vitestBinary?: string | null;\n /** The Node.js version to check. Default: the running one. */\n nodeVersion?: string;\n};\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction statusOf(problems: readonly ToolingProblem[]): CheckStepStatus {\n const errors = problems.filter((problem) => problem.severity === \"error\");\n\n if (errors.some((problem) => problem.category === \"environment\")) return \"blocked\";\n\n return errors.length > 0 ? \"failed\" : \"passed\";\n}\n\n/** Bundles one step's timing, status and problems. */\ntype StepOutcome = { status?: CheckStepStatus; detail: string | null; problems: ToolingProblem[] };\n\n/**\n * The single verify command: typecheck, lint, the tests affected by the\n * change, and the framework's convention checks, as one report with one entry\n * per problem and an exit code that tells \"your code\" (1) from \"this machine\"\n * (2).\n *\n * Every step runs even when an earlier one failed, so one run lists every\n * problem. The only exception: with `changed`, a git failure leaves no file\n * list, and every step after `environment` is `blocked`.\n */\nexport async function runCheck(options: CheckOptions = {}): Promise<CheckReport> {\n const selection: unknown = options.lint;\n if (selection !== undefined && selection !== \"oxlint\")\n throw new Error(\n \"ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default; run npx warlock upgrade --from 5.33 --to 5.34.0.\",\n );\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const changed = options.changed === true || options.base !== undefined;\n const skip = new Set(options.skip ?? []);\n const steps: CheckStep[] = [];\n const problems: ToolingProblem[] = [];\n let files: string[] | null = null;\n let filesBlocked = false;\n\n const run = async (name: CheckStepName, body: () => Promise<StepOutcome> | StepOutcome) => {\n const startedAt = Date.now();\n\n if (skip.has(name)) {\n steps.push({ name, status: \"skipped\", durationMs: 0, detail: \"skipped with --skip\" });\n return;\n }\n\n if (filesBlocked && name !== \"environment\") {\n steps.push({\n name,\n status: \"blocked\",\n durationMs: 0,\n detail: \"the changed files could not be listed\",\n });\n return;\n }\n\n let outcome: StepOutcome;\n\n try {\n outcome = await body();\n } catch (error) {\n outcome = {\n detail: null,\n problems: [\n createProblem({\n code: \"WL8007\",\n source: \"environment\",\n message: `The ${name} step crashed: ${errorMessage(error)}`,\n }),\n ],\n };\n }\n\n problems.push(...outcome.problems);\n steps.push({\n name,\n status: outcome.status ?? statusOf(outcome.problems),\n durationMs: Date.now() - startedAt,\n detail: outcome.detail,\n });\n };\n\n await run(\"environment\", async () => {\n const found = checkNodeVersion(cwd, options.nodeVersion);\n\n if (changed) {\n const listed = await listChangedFiles({ cwd, base: options.base, git: options.git });\n\n if (\"problem\" in listed) {\n filesBlocked = true;\n found.push(listed.problem);\n } else {\n files = listed.files;\n }\n }\n\n return {\n detail: `Node ${options.nodeVersion ?? process.versions.node}${files ? `, ${files.length} changed file(s)` : \"\"}`,\n problems: found,\n };\n });\n\n const targets = async (): Promise<string[]> => files ?? (await listSourceFiles(cwd));\n\n await run(\"typecheck\", async () => {\n if (files !== null && files.length === 0) {\n return { status: \"skipped\", detail: \"no changed files\", problems: [] };\n }\n\n const typecheck = options.typecheck ?? runTypecheck;\n const result = await typecheck({\n cwd,\n project: options.project,\n ...(files !== null ? { files } : {}),\n });\n\n return {\n detail: `${result.checkedFiles} file(s) checked in ${result.durationMs}ms`,\n problems: result.problems,\n };\n });\n\n await run(\"lint\", async () => {\n const lintFiles = await targets();\n\n if (lintFiles.length === 0) {\n return { status: \"skipped\", detail: \"no files to lint\", problems: [] };\n }\n\n const { linters, problems: found } = await runLint(cwd, lintFiles);\n\n if (linters.length === 0) {\n return {\n status: \"skipped\",\n detail: \"no Oxlint config\",\n problems: [],\n };\n }\n\n return { detail: `${linters.join(\" + \")} over ${lintFiles.length} file(s)`, problems: found };\n });\n\n await run(\"tests\", async () => {\n if (files !== null && files.length === 0) {\n return { status: \"skipped\", detail: \"no changed files\", problems: [] };\n }\n\n const binary = options.vitestBinary === undefined ? findVitest(cwd) : options.vitestBinary;\n\n if (!binary) {\n if ((await listTestFiles(cwd)).length === 0) {\n return { status: \"skipped\", detail: \"no test files\", problems: [] };\n }\n\n return {\n detail: null,\n problems: [\n createProblem({\n code: \"WL8002\",\n source: \"environment\",\n message: \"The project has test files but vitest is not installed in the app.\",\n }),\n ],\n };\n }\n\n const vitest = options.vitest ?? runVitest;\n const outcome = await vitest(binary, vitestArguments(files), cwd);\n const found =\n outcome.report === undefined\n ? vitestRunFailure(outcome)\n : parseVitestReport(outcome.report, cwd);\n const total =\n typeof outcome.report === \"object\" && outcome.report !== null\n ? Reflect.get(outcome.report, \"numTotalTests\")\n : undefined;\n\n return {\n detail: typeof total === \"number\" ? `${total} test(s) ran` : null,\n problems: found,\n };\n });\n\n await run(\"conventions\", async () => {\n const conventionFiles = await targets();\n\n return {\n detail: `${conventionFiles.length} file(s) checked`,\n problems: runConventions(cwd, conventionFiles, options.project),\n };\n });\n\n // Keep the declared order even if a future step is added out of sequence.\n steps.sort((first, second) => CHECK_STEPS.indexOf(first.name) - CHECK_STEPS.indexOf(second.name));\n\n const exitCode = checkExitCode(problems);\n const errors = problems.filter((problem) => problem.severity === \"error\");\n\n return {\n schemaVersion: 1,\n ok: exitCode === 0,\n exitCode,\n changed,\n files,\n steps,\n problems,\n summary: {\n errors: errors.length,\n warnings: problems.length - errors.length,\n code: errors.filter((problem) => problem.category === \"code\").length,\n environment: errors.filter((problem) => problem.category === \"environment\").length,\n },\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ErrorCodeCategory, WarlockErrorCode } from "../errors/error-codes.mjs";
2
2
  //#region ../../packages/core/src/tooling/problem.d.ts
3
3
  /** The tool that found a problem. */
4
- export type ProblemSource = "typescript" | "eslint" | "oxlint" | "vitest" | "convention" | "environment" | "doctor";
4
+ export type ProblemSource = "typescript" | "oxlint" | "vitest" | "convention" | "environment" | "doctor";
5
5
  /**
6
6
  * One problem, in the shape every machine-readable command prints
7
7
  * (`warlock check --json`, `warlock typecheck --json`, `warlock doctor --json`).
@@ -1 +1 @@
1
- {"version":3,"file":"problem.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"mappings":";;;YAQY;;;;;;;;YAgBA;;EAEV,MAAM;;EAEN,UAAU;;EAEV;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;YAIU;EACV,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;;;;;;wBAOc,cAAc,OAAO,eAAe"}
1
+ {"version":3,"file":"problem.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"mappings":";;;YAQY;;;;;;;;YAeA;;EAEV,MAAM;;EAEN,UAAU;;EAEV;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;YAIU;EACV,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;;;;;;wBAOc,cAAc,OAAO,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"problem.mjs","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n type ErrorCodeCategory,\n type WarlockErrorCode,\n} from \"../errors/error-codes\";\n\n/** The tool that found a problem. */\nexport type ProblemSource =\n | \"typescript\"\n | \"eslint\"\n | \"oxlint\"\n | \"vitest\"\n | \"convention\"\n | \"environment\"\n | \"doctor\";\n\n/**\n * One problem, in the shape every machine-readable command prints\n * (`warlock check --json`, `warlock typecheck --json`, `warlock doctor --json`).\n *\n * Every key is always present, so a consumer never has to test for one:\n * `file`, `line` and `column` are `null` when the problem has no position.\n */\nexport type ToolingProblem = {\n /** Stable Warlock error code; `warlock explain <code>` prints the fix. */\n code: WarlockErrorCode;\n /** `code` = fix the project; `environment` = fix this machine. */\n category: ErrorCodeCategory;\n /** `error` fails the run; `warning` is reported and does not. */\n severity: \"error\" | \"warning\";\n /** The tool that reported it. */\n source: ProblemSource;\n /** Project-relative POSIX path, or `null`. */\n file: string | null;\n /** 1-based line, or `null`. */\n line: number | null;\n /** 1-based column, or `null`. */\n column: number | null;\n /** What is wrong, in the reporting tool's words. */\n message: string;\n /** One line on what to do, from the code registry. */\n hint: string;\n /** Slug of the skill that explains the fix. */\n skill: string;\n /** Docs URL of the code. */\n docs: string;\n /** The tool's own rule or code (`TS2345`, `no-unused-vars`, a test name), or `null`. */\n rule: string | null;\n};\n\n/** The fields a reporter supplies; the rest come from the registry. */\nexport type ProblemInput = {\n code: WarlockErrorCode;\n source: ProblemSource;\n message: string;\n severity?: \"error\" | \"warning\";\n file?: string | null;\n line?: number | null;\n column?: number | null;\n rule?: string | null;\n};\n\n/**\n * Build a {@link ToolingProblem}, filling `category`, `hint`, `skill` and `docs`\n * from the code registry so every reporter describes a code the same way.\n */\nexport function createProblem(input: ProblemInput): ToolingProblem {\n const definition = ERROR_CODES[input.code];\n\n return {\n code: input.code,\n category: definition.category,\n severity: input.severity ?? \"error\",\n source: input.source,\n file: input.file ?? null,\n line: input.line ?? null,\n column: input.column ?? null,\n message: input.message,\n hint: definition.fix,\n skill: definition.skill,\n docs: errorCodeDocsUrl(input.code),\n rule: input.rule ?? null,\n };\n}\n"],"mappings":";;;;;;;AAmEA,SAAgB,cAAc,OAAqC;CACjE,MAAM,aAAa,YAAY,MAAM;CAErC,OAAO;EACL,MAAM,MAAM;EACZ,UAAU,WAAW;EACrB,UAAU,MAAM,YAAY;EAC5B,QAAQ,MAAM;EACd,MAAM,MAAM,QAAQ;EACpB,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM,UAAU;EACxB,SAAS,MAAM;EACf,MAAM,WAAW;EACjB,OAAO,WAAW;EAClB,MAAM,iBAAiB,MAAM,IAAI;EACjC,MAAM,MAAM,QAAQ;CACtB;AACF"}
1
+ {"version":3,"file":"problem.mjs","names":[],"sources":["../../../../../../../../packages/core/src/tooling/problem.ts"],"sourcesContent":["import {\n ERROR_CODES,\n errorCodeDocsUrl,\n type ErrorCodeCategory,\n type WarlockErrorCode,\n} from \"../errors/error-codes\";\n\n/** The tool that found a problem. */\nexport type ProblemSource =\n | \"typescript\"\n | \"oxlint\"\n | \"vitest\"\n | \"convention\"\n | \"environment\"\n | \"doctor\";\n\n/**\n * One problem, in the shape every machine-readable command prints\n * (`warlock check --json`, `warlock typecheck --json`, `warlock doctor --json`).\n *\n * Every key is always present, so a consumer never has to test for one:\n * `file`, `line` and `column` are `null` when the problem has no position.\n */\nexport type ToolingProblem = {\n /** Stable Warlock error code; `warlock explain <code>` prints the fix. */\n code: WarlockErrorCode;\n /** `code` = fix the project; `environment` = fix this machine. */\n category: ErrorCodeCategory;\n /** `error` fails the run; `warning` is reported and does not. */\n severity: \"error\" | \"warning\";\n /** The tool that reported it. */\n source: ProblemSource;\n /** Project-relative POSIX path, or `null`. */\n file: string | null;\n /** 1-based line, or `null`. */\n line: number | null;\n /** 1-based column, or `null`. */\n column: number | null;\n /** What is wrong, in the reporting tool's words. */\n message: string;\n /** One line on what to do, from the code registry. */\n hint: string;\n /** Slug of the skill that explains the fix. */\n skill: string;\n /** Docs URL of the code. */\n docs: string;\n /** The tool's own rule or code (`TS2345`, `no-unused-vars`, a test name), or `null`. */\n rule: string | null;\n};\n\n/** The fields a reporter supplies; the rest come from the registry. */\nexport type ProblemInput = {\n code: WarlockErrorCode;\n source: ProblemSource;\n message: string;\n severity?: \"error\" | \"warning\";\n file?: string | null;\n line?: number | null;\n column?: number | null;\n rule?: string | null;\n};\n\n/**\n * Build a {@link ToolingProblem}, filling `category`, `hint`, `skill` and `docs`\n * from the code registry so every reporter describes a code the same way.\n */\nexport function createProblem(input: ProblemInput): ToolingProblem {\n const definition = ERROR_CODES[input.code];\n\n return {\n code: input.code,\n category: definition.category,\n severity: input.severity ?? \"error\",\n source: input.source,\n file: input.file ?? null,\n line: input.line ?? null,\n column: input.column ?? null,\n message: input.message,\n hint: definition.fix,\n skill: definition.skill,\n docs: errorCodeDocsUrl(input.code),\n rule: input.rule ?? null,\n };\n}\n"],"mappings":";;;;;;;AAkEA,SAAgB,cAAc,OAAqC;CACjE,MAAM,aAAa,YAAY,MAAM;CAErC,OAAO;EACL,MAAM,MAAM;EACZ,UAAU,WAAW;EACrB,UAAU,MAAM,YAAY;EAC5B,QAAQ,MAAM;EACd,MAAM,MAAM,QAAQ;EACpB,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM,UAAU;EACxB,SAAS,MAAM;EACf,MAAM,WAAW;EACjB,OAAO,WAAW;EAClB,MAAM,iBAAiB,MAAM,IAAI;EACjC,MAAM,MAAM,QAAQ;CACtB;AACF"}
@@ -48,7 +48,11 @@ async function detectPackageManager(startDir = rootPath()) {
48
48
  const declared = await readDeclaredPackageManager(dir);
49
49
  if (declared) return declared;
50
50
  }
51
- for (const dir of dirs) if (await fileExistsAsync(path.join(dir, "pnpm-workspace.yaml"))) return "pnpm";
51
+ for (const dir of dirs) if (await fileExistsAsync(path.join(dir, "pnpm-workspace.yaml"))) {
52
+ if (await fileExistsAsync(path.join(dir, "pnpm-lock.yaml"))) return "pnpm";
53
+ for (const { file, packageManager } of LOCKFILES) if (await fileExistsAsync(path.join(dir, file))) return packageManager;
54
+ return "pnpm";
55
+ }
52
56
  for (const dir of dirs) for (const { file, packageManager } of LOCKFILES) if (await fileExistsAsync(path.join(dir, file))) return packageManager;
53
57
  const agent = process.env.npm_config_user_agent?.split("/")[0];
54
58
  if (agent === "pnpm" || agent === "yarn" || agent === "bun") return agent;
@@ -1 +1 @@
1
- {"version":3,"file":"package-manager.mjs","names":[],"sources":["../../../../../../../../packages/core/src/updater/package-manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { rootPath } from \"../utils\";\n\n/** Package managers the framework knows how to drive. */\nexport type PackageManager = \"npm\" | \"yarn\" | \"pnpm\" | \"bun\";\n\n/**\n * The lockfile that identifies each package manager, in detection order.\n * Bun is checked first because a Bun project may also carry a `yarn.lock`\n * (Bun writes one for tooling compatibility) — matching yarn there would run\n * the wrong installer against the wrong lockfile.\n */\nconst LOCKFILES: ReadonlyArray<{ file: string; packageManager: PackageManager }> = [\n { file: \"bun.lock\", packageManager: \"bun\" },\n { file: \"bun.lockb\", packageManager: \"bun\" },\n { file: \"pnpm-lock.yaml\", packageManager: \"pnpm\" },\n { file: \"yarn.lock\", packageManager: \"yarn\" },\n { file: \"package-lock.json\", packageManager: \"npm\" },\n];\n\n/**\n * Detect the project's package manager from its lockfile, falling back to\n * npm when none is present. Shared by `warlock update` and `warlock add` so\n * both agree on a project that happens to carry more than one lockfile.\n */\nexport async function detectPackageManager(startDir: string = rootPath()): Promise<PackageManager> {\n const dirs: string[] = [];\n\n for (let dir = path.resolve(startDir); ; dir = path.dirname(dir)) {\n dirs.push(dir);\n\n if (path.dirname(dir) === dir) break;\n }\n\n // 1. nearest declared `packageManager` field\n for (const dir of dirs) {\n const declared = await readDeclaredPackageManager(dir);\n\n if (declared) return declared;\n }\n\n // 2. a pnpm workspace marker beats any (possibly stray) lockfile\n for (const dir of dirs) {\n if (await fileExistsAsync(path.join(dir, \"pnpm-workspace.yaml\"))) return \"pnpm\";\n }\n\n // 3. lockfiles, nearest level first\n for (const dir of dirs) {\n for (const { file, packageManager } of LOCKFILES) {\n if (await fileExistsAsync(path.join(dir, file))) return packageManager;\n }\n }\n\n const agent = process.env.npm_config_user_agent?.split(\"/\")[0];\n\n if (agent === \"pnpm\" || agent === \"yarn\" || agent === \"bun\") return agent;\n\n return \"npm\";\n}\n\n/** The manager named by the app's `packageManager` field, if any. */\nasync function readDeclaredPackageManager(dir: string): Promise<PackageManager | undefined> {\n try {\n const json = JSON.parse(await readFile(path.join(dir, \"package.json\"), \"utf8\"));\n const name = String(json.packageManager ?? \"\").split(\"@\")[0];\n\n if (name === \"pnpm\" || name === \"yarn\" || name === \"bun\" || name === \"npm\") return name;\n } catch {\n // no readable package.json — fall through to lockfiles\n }\n\n return undefined;\n}\n\n/**\n * The lockfile-syncing install command for the given manager. No package\n * arguments — `warlock update` rewrites the versions in package.json first,\n * then a plain install reconciles `node_modules` to match.\n */\nexport function getInstallCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn install\";\n\n case \"pnpm\":\n return \"pnpm install\";\n\n case \"bun\":\n return \"bun install\";\n\n default:\n return \"npm install\";\n }\n}\n\n/**\n * The command that installs *specific* packages — what `warlock add` needs.\n * Distinct from {@link getInstallCommand}, which only reconciles what is\n * already written into package.json.\n */\nexport function getAddCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn add\";\n\n case \"pnpm\":\n return \"pnpm add\";\n\n case \"bun\":\n return \"bun add\";\n\n default:\n return \"npm install\";\n }\n}\n\n/**\n * The package-manager command that saves an exact version. Warlock family\n * dependencies use this path so the version selected by the executing Core is\n * preserved verbatim in package.json instead of being widened by a manager's\n * default save prefix.\n */\nexport function getExactAddCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn add --exact\";\n\n case \"pnpm\":\n return \"pnpm add --save-exact\";\n\n case \"bun\":\n return \"bun add --exact\";\n\n default:\n return \"npm install --save-exact\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAM,YAA6E;CACjF;EAAE,MAAM;EAAY,gBAAgB;CAAM;CAC1C;EAAE,MAAM;EAAa,gBAAgB;CAAM;CAC3C;EAAE,MAAM;EAAkB,gBAAgB;CAAO;CACjD;EAAE,MAAM;EAAa,gBAAgB;CAAO;CAC5C;EAAE,MAAM;EAAqB,gBAAgB;CAAM;AACrD;;;;;;AAOA,eAAsB,qBAAqB,WAAmB,SAAS,GAA4B;CACjG,MAAM,OAAiB,CAAC;CAExB,KAAK,IAAI,MAAM,KAAK,QAAQ,QAAQ,IAAK,MAAM,KAAK,QAAQ,GAAG,GAAG;EAChE,KAAK,KAAK,GAAG;EAEb,IAAI,KAAK,QAAQ,GAAG,MAAM,KAAK;CACjC;CAGA,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,MAAM,2BAA2B,GAAG;EAErD,IAAI,UAAU,OAAO;CACvB;CAGA,KAAK,MAAM,OAAO,MAChB,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,qBAAqB,CAAC,GAAG,OAAO;CAI3E,KAAK,MAAM,OAAO,MAChB,KAAK,MAAM,EAAE,MAAM,oBAAoB,WACrC,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,OAAO;CAI5D,MAAM,QAAQ,QAAQ,IAAI,uBAAuB,MAAM,GAAG,CAAC,CAAC;CAE5D,IAAI,UAAU,UAAU,UAAU,UAAU,UAAU,OAAO,OAAO;CAEpE,OAAO;AACT;;AAGA,eAAe,2BAA2B,KAAkD;CAC1F,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS,KAAK,KAAK,KAAK,cAAc,GAAG,MAAM,CAAC;EAC9E,MAAM,OAAO,OAAO,KAAK,kBAAkB,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;EAE1D,IAAI,SAAS,UAAU,SAAS,UAAU,SAAS,SAAS,SAAS,OAAO,OAAO;CACrF,QAAQ,CAER;AAGF;;;;;;AAOA,SAAgB,kBAAkB,gBAAwC;CACxE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF;;;;;;AAOA,SAAgB,cAAc,gBAAwC;CACpE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF;;;;;;;AAQA,SAAgB,mBAAmB,gBAAwC;CACzE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF"}
1
+ {"version":3,"file":"package-manager.mjs","names":[],"sources":["../../../../../../../../packages/core/src/updater/package-manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { rootPath } from \"../utils\";\n\n/** Package managers the framework knows how to drive. */\nexport type PackageManager = \"npm\" | \"yarn\" | \"pnpm\" | \"bun\";\n\n/**\n * The lockfile that identifies each package manager, in detection order.\n * Bun is checked first because a Bun project may also carry a `yarn.lock`\n * (Bun writes one for tooling compatibility) — matching yarn there would run\n * the wrong installer against the wrong lockfile.\n */\nconst LOCKFILES: ReadonlyArray<{ file: string; packageManager: PackageManager }> = [\n { file: \"bun.lock\", packageManager: \"bun\" },\n { file: \"bun.lockb\", packageManager: \"bun\" },\n { file: \"pnpm-lock.yaml\", packageManager: \"pnpm\" },\n { file: \"yarn.lock\", packageManager: \"yarn\" },\n { file: \"package-lock.json\", packageManager: \"npm\" },\n];\n\n/**\n * Detect the project's package manager from its lockfile, falling back to\n * npm when none is present. Shared by `warlock update` and `warlock add` so\n * both agree on a project that happens to carry more than one lockfile.\n */\nexport async function detectPackageManager(startDir: string = rootPath()): Promise<PackageManager> {\n const dirs: string[] = [];\n\n for (let dir = path.resolve(startDir); ; dir = path.dirname(dir)) {\n dirs.push(dir);\n\n if (path.dirname(dir) === dir) break;\n }\n\n // 1. nearest declared `packageManager` field\n for (const dir of dirs) {\n const declared = await readDeclaredPackageManager(dir);\n\n if (declared) return declared;\n }\n\n // 2. a workspace governs descendants, but its own real lockfile wins over a marker\n for (const dir of dirs) {\n if (await fileExistsAsync(path.join(dir, \"pnpm-workspace.yaml\"))) {\n if (await fileExistsAsync(path.join(dir, \"pnpm-lock.yaml\"))) return \"pnpm\";\n for (const { file, packageManager } of LOCKFILES) {\n if (await fileExistsAsync(path.join(dir, file))) return packageManager;\n }\n return \"pnpm\";\n }\n }\n\n // 3. lockfiles, nearest level first\n for (const dir of dirs) {\n for (const { file, packageManager } of LOCKFILES) {\n if (await fileExistsAsync(path.join(dir, file))) return packageManager;\n }\n }\n\n const agent = process.env.npm_config_user_agent?.split(\"/\")[0];\n\n if (agent === \"pnpm\" || agent === \"yarn\" || agent === \"bun\") return agent;\n\n return \"npm\";\n}\n\n/** The manager named by the app's `packageManager` field, if any. */\nasync function readDeclaredPackageManager(dir: string): Promise<PackageManager | undefined> {\n try {\n const json = JSON.parse(await readFile(path.join(dir, \"package.json\"), \"utf8\"));\n const name = String(json.packageManager ?? \"\").split(\"@\")[0];\n\n if (name === \"pnpm\" || name === \"yarn\" || name === \"bun\" || name === \"npm\") return name;\n } catch {\n // no readable package.json — fall through to lockfiles\n }\n\n return undefined;\n}\n\n/**\n * The lockfile-syncing install command for the given manager. No package\n * arguments — `warlock update` rewrites the versions in package.json first,\n * then a plain install reconciles `node_modules` to match.\n */\nexport function getInstallCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn install\";\n\n case \"pnpm\":\n return \"pnpm install\";\n\n case \"bun\":\n return \"bun install\";\n\n default:\n return \"npm install\";\n }\n}\n\n/**\n * The command that installs *specific* packages — what `warlock add` needs.\n * Distinct from {@link getInstallCommand}, which only reconciles what is\n * already written into package.json.\n */\nexport function getAddCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn add\";\n\n case \"pnpm\":\n return \"pnpm add\";\n\n case \"bun\":\n return \"bun add\";\n\n default:\n return \"npm install\";\n }\n}\n\n/**\n * The package-manager command that saves an exact version. Warlock family\n * dependencies use this path so the version selected by the executing Core is\n * preserved verbatim in package.json instead of being widened by a manager's\n * default save prefix.\n */\nexport function getExactAddCommand(packageManager: PackageManager): string {\n switch (packageManager) {\n case \"yarn\":\n return \"yarn add --exact\";\n\n case \"pnpm\":\n return \"pnpm add --save-exact\";\n\n case \"bun\":\n return \"bun add --exact\";\n\n default:\n return \"npm install --save-exact\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAM,YAA6E;CACjF;EAAE,MAAM;EAAY,gBAAgB;CAAM;CAC1C;EAAE,MAAM;EAAa,gBAAgB;CAAM;CAC3C;EAAE,MAAM;EAAkB,gBAAgB;CAAO;CACjD;EAAE,MAAM;EAAa,gBAAgB;CAAO;CAC5C;EAAE,MAAM;EAAqB,gBAAgB;CAAM;AACrD;;;;;;AAOA,eAAsB,qBAAqB,WAAmB,SAAS,GAA4B;CACjG,MAAM,OAAiB,CAAC;CAExB,KAAK,IAAI,MAAM,KAAK,QAAQ,QAAQ,IAAK,MAAM,KAAK,QAAQ,GAAG,GAAG;EAChE,KAAK,KAAK,GAAG;EAEb,IAAI,KAAK,QAAQ,GAAG,MAAM,KAAK;CACjC;CAGA,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,MAAM,2BAA2B,GAAG;EAErD,IAAI,UAAU,OAAO;CACvB;CAGA,KAAK,MAAM,OAAO,MAChB,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,qBAAqB,CAAC,GAAG;EAChE,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,gBAAgB,CAAC,GAAG,OAAO;EACpE,KAAK,MAAM,EAAE,MAAM,oBAAoB,WACrC,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,OAAO;EAE1D,OAAO;CACT;CAIF,KAAK,MAAM,OAAO,MAChB,KAAK,MAAM,EAAE,MAAM,oBAAoB,WACrC,IAAI,MAAM,gBAAgB,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,OAAO;CAI5D,MAAM,QAAQ,QAAQ,IAAI,uBAAuB,MAAM,GAAG,CAAC,CAAC;CAE5D,IAAI,UAAU,UAAU,UAAU,UAAU,UAAU,OAAO,OAAO;CAEpE,OAAO;AACT;;AAGA,eAAe,2BAA2B,KAAkD;CAC1F,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS,KAAK,KAAK,KAAK,cAAc,GAAG,MAAM,CAAC;EAC9E,MAAM,OAAO,OAAO,KAAK,kBAAkB,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;EAE1D,IAAI,SAAS,UAAU,SAAS,UAAU,SAAS,SAAS,SAAS,OAAO,OAAO;CACrF,QAAQ,CAER;AAGF;;;;;;AAOA,SAAgB,kBAAkB,gBAAwC;CACxE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF;;;;;;AAOA,SAAgB,cAAc,gBAAwC;CACpE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF;;;;;;;AAQA,SAAgB,mBAAmB,gBAAwC;CACzE,QAAQ,gBAAR;EACE,KAAK,QACH,OAAO;EAET,KAAK,QACH,OAAO;EAET,KAAK,OACH,OAAO;EAET,SACE,OAAO;CACX;AACF"}
@@ -2,7 +2,7 @@ import { importedLocalNames, importsFrom, manualAt, objectProperty, objectProper
2
2
  import { headViewport } from "./head-viewport.mjs";
3
3
  import { loggerSyncCalls } from "./logger-sync-calls.mjs";
4
4
  import { node2215, warlockVersions } from "./package-json.mjs";
5
- import { oxcToolchain } from "./oxc-toolchain.mjs";
5
+ import { oxcToolchain, oxcToolchainRemoval } from "./oxc-toolchain.mjs";
6
6
  import { requestLogSnapshot } from "./request-log-snapshot.mjs";
7
7
  import { aiAnthropicBody, aiCompactionOptIn, aiFinishReason, aiToolDispatch, rateLimitDefaults, sealContainerCatch, sealRefineOutput, sealUnionOutput, sealValidateResult } from "./review-sites.mjs";
8
8
 
@@ -28,6 +28,7 @@ const CODEMODS = [
28
28
  sealUnionOutput,
29
29
  aiCompactionOptIn,
30
30
  oxcToolchain,
31
+ oxcToolchainRemoval,
31
32
  warlockVersions
32
33
  ];
33
34
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/index.ts"],"sourcesContent":["import type { Codemod } from \"../types\";\nimport { headViewport } from \"./head-viewport\";\nimport { loggerSyncCalls } from \"./logger-sync-calls\";\nimport { node2215, warlockVersions } from \"./package-json\";\nimport { oxcToolchain } from \"./oxc-toolchain\";\nimport { requestLogSnapshot } from \"./request-log-snapshot\";\nimport {\n aiAnthropicBody,\n aiCompactionOptIn,\n aiFinishReason,\n aiToolDispatch,\n rateLimitDefaults,\n sealContainerCatch,\n sealRefineOutput,\n sealUnionOutput,\n sealValidateResult,\n} from \"./review-sites\";\n\n/**\n * Every codemod, oldest release first. A codemod runs when the app upgrades\n * across its `version` (`from < version <= to`); `\"target\"` ones run on every\n * upgrade. Add one here for each breaking change, and name its id next to the\n * `**BREAKING**` entry in the package changelog.\n */\nexport const CODEMODS: readonly Codemod[] = [\n loggerSyncCalls,\n requestLogSnapshot,\n headViewport,\n sealValidateResult,\n aiToolDispatch,\n aiAnthropicBody,\n aiFinishReason,\n node2215,\n rateLimitDefaults,\n sealRefineOutput,\n sealContainerCatch,\n sealUnionOutput,\n aiCompactionOptIn,\n oxcToolchain,\n warlockVersions,\n];\n\nexport * from \"./helpers\";\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAa,WAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/index.ts"],"sourcesContent":["import type { Codemod } from \"../types\";\nimport { headViewport } from \"./head-viewport\";\nimport { loggerSyncCalls } from \"./logger-sync-calls\";\nimport { node2215, warlockVersions } from \"./package-json\";\nimport { oxcToolchain, oxcToolchainRemoval } from \"./oxc-toolchain\";\nimport { requestLogSnapshot } from \"./request-log-snapshot\";\nimport {\n aiAnthropicBody,\n aiCompactionOptIn,\n aiFinishReason,\n aiToolDispatch,\n rateLimitDefaults,\n sealContainerCatch,\n sealRefineOutput,\n sealUnionOutput,\n sealValidateResult,\n} from \"./review-sites\";\n\n/**\n * Every codemod, oldest release first. A codemod runs when the app upgrades\n * across its `version` (`from < version <= to`); `\"target\"` ones run on every\n * upgrade. Add one here for each breaking change, and name its id next to the\n * `**BREAKING**` entry in the package changelog.\n */\nexport const CODEMODS: readonly Codemod[] = [\n loggerSyncCalls,\n requestLogSnapshot,\n headViewport,\n sealValidateResult,\n aiToolDispatch,\n aiAnthropicBody,\n aiFinishReason,\n node2215,\n rateLimitDefaults,\n sealRefineOutput,\n sealContainerCatch,\n sealUnionOutput,\n aiCompactionOptIn,\n oxcToolchain,\n oxcToolchainRemoval,\n warlockVersions,\n];\n\nexport * from \"./helpers\";\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAa,WAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
@@ -1,3 +1,5 @@
1
+ import { isRecord } from "./validate-oxlint-rule.mjs";
2
+ import { portEslintRules } from "./port-eslint-rules.mjs";
1
3
  import { TOOLCHAIN_TEMPLATES } from "./toolchain-templates.mjs";
2
4
 
3
5
  //#region ../../packages/core/src/upgrade/codemods/oxc-toolchain.ts
@@ -19,7 +21,7 @@ const OXC_SCRIPTS = {
19
21
  "format:check": "oxfmt --ignore-path .gitignore --ignore-path .oxfmtignore --check ./src"
20
22
  };
21
23
  function legacyDependency(name) {
22
- return /^(eslint|prettier|@types\/prettier|eslint-config-prettier)$/.test(name) || name.startsWith("@typescript-eslint/") || /(?:^|\/)eslint-plugin-/.test(name) || /(?:^|\/)prettier-plugin-/.test(name) || name.startsWith("@prettier/plugin-");
24
+ return /^(eslint|prettier|typescript-eslint|@types\/prettier)$/.test(name) || name.startsWith("@typescript-eslint/") || /(?:^|\/)eslint-config-/.test(name) || /(?:^|\/)eslint-plugin-/.test(name) || /(?:^|\/)prettier-plugin-/.test(name) || name.startsWith("@prettier/plugin-");
23
25
  }
24
26
  function sameGenerated(source, expected) {
25
27
  try {
@@ -48,8 +50,14 @@ function migrateHealthSwitches(context, output) {
48
50
  if (value?.getText() !== "false") review(output, file, "Review custom healthCheckers; remove ESLint checkers and enable Oxlint. ESLint support is removed in 5.34.0.", property.getStartLineNumber());
49
51
  continue;
50
52
  }
51
- for (const spread of value.getProperties().filter(context.tsMorph.Node.isSpreadAssignment)) review(output, file, "Review spread healthCheckers options; set eslint: false and oxlint: true explicitly after the spread.", spread.getStartLineNumber());
52
- for (const [name, next] of [["eslint", "false"], ["oxlint", "true"]]) {
53
+ for (const spread of value.getProperties().filter(context.tsMorph.Node.isSpreadAssignment)) review(output, file, "Review spread healthCheckers options; remove eslint and enable oxlint after the spread.", spread.getStartLineNumber());
54
+ const eslint = value.getProperty("eslint");
55
+ if (!eslint) continue;
56
+ if (eslint) {
57
+ eslint.remove();
58
+ changed = true;
59
+ }
60
+ for (const [name, next] of [["oxlint", "true"]]) {
53
61
  const option = value.getProperty(name);
54
62
  if (!option) continue;
55
63
  if (!context.tsMorph.Node.isPropertyAssignment(option) || !["true", "false"].includes(option.getInitializer()?.getText() ?? "")) {
@@ -81,10 +89,13 @@ const oxcToolchain = {
81
89
  const source = context.text.read("package.json");
82
90
  if (source === void 0) return output;
83
91
  const pkg = JSON.parse(source);
84
- const legacyFiles = context.text.list().filter((file) => /^(eslint\.config\..+|\.eslintrc(?:\..+)?|\.prettierrc(?:\..+)?|prettier\.config\..+|\.prettierignore)$/.test(file));
92
+ const legacyFiles = context.text.list().filter((file) => /^(eslint\.config\..+|\.eslintrc(?:\..+)?|\.prettierrc(?:\..+)?|prettier\.config\..+|\.prettierignore|\.eslintignore)$/.test(file));
85
93
  const dependencies = [pkg.dependencies, pkg.devDependencies];
86
94
  const legacyScripts = Object.values(pkg.scripts ?? {}).some((script) => /\b(?:eslint|prettier)\b/.test(script));
87
- if (!legacyFiles.length && !legacyScripts && !dependencies.some((deps) => Object.keys(deps ?? {}).some(legacyDependency))) return output;
95
+ if (!legacyFiles.length && !legacyScripts && pkg.eslintConfig === void 0 && pkg.prettier === void 0 && !dependencies.some((deps) => Object.keys(deps ?? {}).some(legacyDependency))) {
96
+ if (/\beslint\b|EslintHealthChecker|ESLint/.test(context.text.read("warlock.config.ts") ?? "")) migrateHealthSwitches(context, output);
97
+ return output;
98
+ }
88
99
  const change = {
89
100
  line: 1,
90
101
  description: "Replace ESLint/Prettier with the default Oxlint/Oxfmt toolchain"
@@ -100,23 +111,52 @@ const oxcToolchain = {
100
111
  };
101
112
  if (/\beslint\b/.test(pkg.scripts.fix ?? "")) pkg.scripts.fix = OXC_SCRIPTS.lint;
102
113
  for (const [name, command] of Object.entries(pkg.scripts)) if (/\b(?:eslint|prettier)\b/.test(command)) review(output, "package.json", `Replace the custom ${name} script (${command}) with oxlint or oxfmt; the legacy tools have been removed from dependencies.`);
103
- context.text.write("package.json", `${JSON.stringify(pkg, null, 2)}\n`, change);
114
+ const hadOxlint = context.text.read(".oxlintrc.json") !== void 0;
104
115
  for (const file of [".oxlintrc.json", ".oxfmtrc.json"]) if (context.text.read(file) === void 0) context.text.write(file, asset(file.slice(1)), change);
116
+ if (pkg.eslintConfig !== void 0) {
117
+ const findings = output.manual.length;
118
+ if (portEslintRules(context, output, "package.json", source, hadOxlint, "eslintConfig")) pkg.devDependencies["oxlint-tsgolint"] = "7.0.2004";
119
+ if (output.manual.length === findings) delete pkg.eslintConfig;
120
+ }
121
+ if (pkg.prettier !== void 0) {
122
+ if (sameGenerated(JSON.stringify(pkg.prettier), asset("legacy-prettierrc.json"))) delete pkg.prettier;
123
+ else review(output, "package.json", "Port the custom prettier field to .oxfmtrc.json, then remove it. Prettier support is gone in 5.34.0.");
124
+ }
105
125
  for (const file of legacyFiles) {
106
126
  const content = context.text.read(file);
127
+ if (file === ".eslintignore" && content !== void 0) {
128
+ try {
129
+ const config = JSON.parse(context.text.read(".oxlintrc.json") ?? "{}");
130
+ if (!isRecord(config) || config.ignorePatterns !== void 0 && (!Array.isArray(config.ignorePatterns) || !config.ignorePatterns.every((pattern) => typeof pattern === "string"))) throw new Error("Invalid ignore patterns");
131
+ config.ignorePatterns = [.../* @__PURE__ */ new Set([...Array.isArray(config.ignorePatterns) ? config.ignorePatterns : [], ...content.split(/\r?\n/).filter((line) => line.trim() && !line.trimStart().startsWith("#"))])];
132
+ context.text.write(".oxlintrc.json", `${JSON.stringify(config, null, 2)}\n`, change);
133
+ } catch {
134
+ review(output, file, "Port these ignore patterns after repairing .oxlintrc.json; ESLint support is gone in 5.34.0.");
135
+ continue;
136
+ }
137
+ context.text.remove(file, change);
138
+ continue;
139
+ }
107
140
  const known = [
108
141
  "eslint.config.js",
109
142
  ".prettierrc.json",
110
143
  ".prettierignore"
111
144
  ].includes(file);
112
145
  if (content === void 0) continue;
113
- if (!known || !sameGenerated(content, asset(`legacy-${file.replace(/^\./, "")}`))) {
114
- review(output, file, "Customized legacy config retained. Port its rules, plugins, formatting or ignore patterns to .oxlintrc.json / .oxfmtrc.json / .oxfmtignore, then remove this file. Legacy support is removed in 5.34.0.");
146
+ const generated = known && sameGenerated(content, asset(`legacy-${file.replace(/^\./, "")}`));
147
+ if (!generated && /^(eslint\.config\..+|\.eslintrc(?:\..+)?)$/.test(file)) {
148
+ const findings = output.manual.length;
149
+ if (portEslintRules(context, output, file, content, hadOxlint)) pkg.devDependencies["oxlint-tsgolint"] = "7.0.2004";
150
+ if (output.manual.length === findings) context.text.remove(file, change);
151
+ continue;
152
+ }
153
+ if (!generated) {
154
+ review(output, file, "Port customized formatting or ignore options to .oxfmtrc.json / .oxfmtignore, then remove this file. Legacy support is gone in 5.34.0.");
115
155
  continue;
116
156
  }
117
157
  if (file === ".prettierignore") {
118
158
  const existing = context.text.read(".oxfmtignore");
119
- if (existing === void 0) context.text.write(".oxfmtignore", content, change);
159
+ if (existing === void 0) context.text.write(".oxfmtignore", `${content.trimEnd()}\n`, change);
120
160
  else if (existing.trim() !== content.trim()) {
121
161
  review(output, file, "Merge these ignore patterns into the existing .oxfmtignore, then remove .prettierignore.");
122
162
  continue;
@@ -129,14 +169,23 @@ const oxcToolchain = {
129
169
  const file = `.vscode/${name}`;
130
170
  const content = context.text.read(file);
131
171
  if (content === void 0) continue;
172
+ if (name === "settings.json" && sameGenerated(content, asset(`oxc-vscode-${name}`))) continue;
132
173
  if (name === "settings.json" && sameGenerated(content, asset(`legacy-vscode-${name}`))) context.text.write(file, asset(`oxc-vscode-${name}`), change);
133
174
  else review(output, file, "Review editor settings: select oxc.oxc-vscode as the formatter, enable source.fixAll.oxc and remove ESLint/Prettier extension recommendations.");
134
175
  }
135
176
  migrateHealthSwitches(context, output);
177
+ context.text.write("package.json", `${JSON.stringify(pkg, null, 2)}\n`, change);
136
178
  return output;
137
179
  }
138
180
  };
181
+ /** Run again for apps that skipped or partially completed the 5.33 migration. */
182
+ const oxcToolchainRemoval = {
183
+ ...oxcToolchain,
184
+ id: "oxc-toolchain-removal",
185
+ version: "5.34.0",
186
+ title: "ESLint/Prettier support is gone in 5.34.0; migrate to the Oxlint/Oxfmt default"
187
+ };
139
188
 
140
189
  //#endregion
141
- export { OXC_SCRIPTS, OXC_VERSIONS, oxcToolchain };
190
+ export { OXC_SCRIPTS, OXC_VERSIONS, oxcToolchain, oxcToolchainRemoval };
142
191
  //# sourceMappingURL=oxc-toolchain.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"oxc-toolchain.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/oxc-toolchain.ts"],"sourcesContent":["import type { Codemod, CodemodContext, CodemodOutput } from \"../types\";\nimport { TOOLCHAIN_TEMPLATES } from \"./toolchain-templates\";\n\n/** Snapshots ship with core; parity specs keep them aligned with create-warlock. */\nfunction asset(name: string): string {\n const content = TOOLCHAIN_TEMPLATES[name];\n if (content === undefined) throw new Error(`Unknown toolchain template: ${name}`);\n return content;\n}\n\nexport const OXC_VERSIONS = { oxlint: \"1.88.0\", oxfmt: \"0.73.0\", typescript: \"7.0.2\" };\nexport const OXC_SCRIPTS = {\n lint: \"oxlint --fix ./src --max-warnings=0\",\n \"lint:check\": \"oxlint ./src --max-warnings=0\",\n format: \"oxfmt --ignore-path .gitignore --ignore-path .oxfmtignore --write ./src\",\n \"format:check\": \"oxfmt --ignore-path .gitignore --ignore-path .oxfmtignore --check ./src\",\n};\n\nfunction legacyDependency(name: string): boolean {\n return (\n /^(eslint|prettier|@types\\/prettier|eslint-config-prettier)$/.test(name) ||\n name.startsWith(\"@typescript-eslint/\") ||\n /(?:^|\\/)eslint-plugin-/.test(name) ||\n /(?:^|\\/)prettier-plugin-/.test(name) ||\n name.startsWith(\"@prettier/plugin-\")\n );\n}\n\nfunction sameGenerated(source: string, expected: string): boolean {\n // JSON may have been reformatted by the app; compare its data. Executable\n // configs are deliberately exact (apart from line endings/trailing space).\n try {\n return JSON.stringify(JSON.parse(source)) === JSON.stringify(JSON.parse(expected));\n } catch {\n return source.replace(/\\r\\n/g, \"\\n\").trim() === expected.replace(/\\r\\n/g, \"\\n\").trim();\n }\n}\n\nfunction review(output: CodemodOutput, file: string, hint: string, line = 1): void {\n output.manual.push({ file, line, hint });\n}\n\nfunction migrateHealthSwitches(context: CodemodContext, output: CodemodOutput): void {\n const file = \"warlock.config.ts\";\n const source = context.text.read(file);\n if (source === undefined) return;\n const project = new context.tsMorph.Project({ useInMemoryFileSystem: true });\n const config = project.createSourceFile(file, source);\n const switches = config\n .getDescendantsOfKind(context.tsMorph.SyntaxKind.PropertyAssignment)\n .filter((property) => property.getName() === \"healthCheckers\");\n let changed = false;\n for (const property of switches) {\n const value = property.getInitializer();\n if (!value || !context.tsMorph.Node.isObjectLiteralExpression(value)) {\n if (value?.getText() !== \"false\")\n review(\n output,\n file,\n \"Review custom healthCheckers; remove ESLint checkers and enable Oxlint. ESLint support is removed in 5.34.0.\",\n property.getStartLineNumber(),\n );\n continue;\n }\n for (const spread of value.getProperties().filter(context.tsMorph.Node.isSpreadAssignment)) {\n review(\n output,\n file,\n \"Review spread healthCheckers options; set eslint: false and oxlint: true explicitly after the spread.\",\n spread.getStartLineNumber(),\n );\n }\n for (const [name, next] of [\n [\"eslint\", \"false\"],\n [\"oxlint\", \"true\"],\n ] as const) {\n const option = value.getProperty(name);\n if (!option) continue;\n if (\n !context.tsMorph.Node.isPropertyAssignment(option) ||\n ![\"true\", \"false\"].includes(option.getInitializer()?.getText() ?? \"\")\n ) {\n review(\n output,\n file,\n `Set healthCheckers.${name} to ${next} after reviewing this computed option.`,\n option.getStartLineNumber(),\n );\n continue;\n }\n if (option.getInitializer()?.getText() !== next) {\n option.setInitializer(next);\n changed = true;\n }\n }\n }\n if (changed)\n context.text.write(file, config.getFullText(), {\n line: 1,\n description: \"Select Oxlint instead of ESLint for background health checks\",\n });\n}\n\nexport const oxcToolchain: Codemod = {\n id: \"oxc-toolchain\",\n version: \"5.33.0\",\n package: \"@warlock.js/core\",\n title: \"Migrate ESLint and Prettier to Oxlint and Oxfmt\",\n skill: \"upgrade-from-5-29\",\n run(context) {\n const output: CodemodOutput = { edits: [], manual: [] };\n const source = context.text.read(\"package.json\");\n if (source === undefined) return output;\n const pkg = JSON.parse(source) as {\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const legacyFiles = context.text\n .list()\n .filter((file) =>\n /^(eslint\\.config\\..+|\\.eslintrc(?:\\..+)?|\\.prettierrc(?:\\..+)?|prettier\\.config\\..+|\\.prettierignore)$/.test(\n file,\n ),\n );\n const dependencies = [pkg.dependencies, pkg.devDependencies];\n const legacyScripts = Object.values(pkg.scripts ?? {}).some((script) =>\n /\\b(?:eslint|prettier)\\b/.test(script),\n );\n if (\n !legacyFiles.length &&\n !legacyScripts &&\n !dependencies.some((deps) => Object.keys(deps ?? {}).some(legacyDependency))\n )\n return output;\n\n const change = {\n line: 1,\n description: \"Replace ESLint/Prettier with the default Oxlint/Oxfmt toolchain\",\n };\n for (const deps of dependencies) {\n for (const name of Object.keys(deps ?? {})) {\n if (deps && (legacyDependency(name) || name in OXC_VERSIONS)) delete deps[name];\n }\n }\n pkg.devDependencies = { ...pkg.devDependencies, ...OXC_VERSIONS };\n pkg.scripts = { ...pkg.scripts, ...OXC_SCRIPTS };\n if (/\\beslint\\b/.test(pkg.scripts.fix ?? \"\")) pkg.scripts.fix = OXC_SCRIPTS.lint;\n for (const [name, command] of Object.entries(pkg.scripts)) {\n if (/\\b(?:eslint|prettier)\\b/.test(command))\n review(\n output,\n \"package.json\",\n `Replace the custom ${name} script (${command}) with oxlint or oxfmt; the legacy tools have been removed from dependencies.`,\n );\n }\n context.text.write(\"package.json\", `${JSON.stringify(pkg, null, 2)}\\n`, change);\n\n for (const file of [\".oxlintrc.json\", \".oxfmtrc.json\"]) {\n if (context.text.read(file) === undefined)\n context.text.write(file, asset(file.slice(1)), change);\n }\n for (const file of legacyFiles) {\n const content = context.text.read(file);\n const known = [\"eslint.config.js\", \".prettierrc.json\", \".prettierignore\"].includes(file);\n if (content === undefined) continue;\n if (!known || !sameGenerated(content, asset(`legacy-${file.replace(/^\\./, \"\")}`))) {\n review(\n output,\n file,\n \"Customized legacy config retained. Port its rules, plugins, formatting or ignore patterns to .oxlintrc.json / .oxfmtrc.json / .oxfmtignore, then remove this file. Legacy support is removed in 5.34.0.\",\n );\n continue;\n }\n if (file === \".prettierignore\") {\n const existing = context.text.read(\".oxfmtignore\");\n if (existing === undefined) context.text.write(\".oxfmtignore\", content, change);\n else if (existing.trim() !== content.trim()) {\n review(\n output,\n file,\n \"Merge these ignore patterns into the existing .oxfmtignore, then remove .prettierignore.\",\n );\n continue;\n }\n }\n context.text.remove(file, change);\n }\n // Oxfmt 0.73 uses .prettierignore by default (verified with --help).\n // Explicit script ignore paths make the migrated .oxfmtignore effective.\n if (context.text.read(\".oxfmtignore\") === undefined) {\n context.text.write(\".oxfmtignore\", \"node_modules\\ndist\\n\", change);\n }\n for (const name of [\"settings.json\", \"extensions.json\"]) {\n const file = `.vscode/${name}`;\n const content = context.text.read(file);\n if (content === undefined) continue;\n if (name === \"settings.json\" && sameGenerated(content, asset(`legacy-vscode-${name}`)))\n context.text.write(file, asset(`oxc-vscode-${name}`), change);\n else\n review(\n output,\n file,\n \"Review editor settings: select oxc.oxc-vscode as the formatter, enable source.fixAll.oxc and remove ESLint/Prettier extension recommendations.\",\n );\n }\n migrateHealthSwitches(context, output);\n return output;\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"oxc-toolchain.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/oxc-toolchain.ts"],"sourcesContent":["import type { Codemod, CodemodContext, CodemodOutput } from \"../types\";\nimport { isRecord } from \"./validate-oxlint-rule\";\nimport { portEslintRules } from \"./port-eslint-rules\";\nimport { TOOLCHAIN_TEMPLATES } from \"./toolchain-templates\";\n\n/** Snapshots ship with core; parity specs keep them aligned with create-warlock. */\nfunction asset(name: string): string {\n const content = TOOLCHAIN_TEMPLATES[name];\n if (content === undefined) throw new Error(`Unknown toolchain template: ${name}`);\n return content;\n}\n\nexport const OXC_VERSIONS = { oxlint: \"1.88.0\", oxfmt: \"0.73.0\", typescript: \"7.0.2\" };\nexport const OXC_SCRIPTS = {\n lint: \"oxlint --fix ./src --max-warnings=0\",\n \"lint:check\": \"oxlint ./src --max-warnings=0\",\n format: \"oxfmt --ignore-path .gitignore --ignore-path .oxfmtignore --write ./src\",\n \"format:check\": \"oxfmt --ignore-path .gitignore --ignore-path .oxfmtignore --check ./src\",\n};\n\nfunction legacyDependency(name: string): boolean {\n return (\n /^(eslint|prettier|typescript-eslint|@types\\/prettier)$/.test(name) ||\n name.startsWith(\"@typescript-eslint/\") ||\n /(?:^|\\/)eslint-config-/.test(name) ||\n /(?:^|\\/)eslint-plugin-/.test(name) ||\n /(?:^|\\/)prettier-plugin-/.test(name) ||\n name.startsWith(\"@prettier/plugin-\")\n );\n}\n\nfunction sameGenerated(source: string, expected: string): boolean {\n // JSON may have been reformatted by the app; compare its data. Executable\n // configs are deliberately exact (apart from line endings/trailing space).\n try {\n return JSON.stringify(JSON.parse(source)) === JSON.stringify(JSON.parse(expected));\n } catch {\n return source.replace(/\\r\\n/g, \"\\n\").trim() === expected.replace(/\\r\\n/g, \"\\n\").trim();\n }\n}\n\nfunction review(output: CodemodOutput, file: string, hint: string, line = 1): void {\n output.manual.push({ file, line, hint });\n}\n\nfunction migrateHealthSwitches(context: CodemodContext, output: CodemodOutput): void {\n const file = \"warlock.config.ts\";\n const source = context.text.read(file);\n if (source === undefined) return;\n const project = new context.tsMorph.Project({ useInMemoryFileSystem: true });\n const config = project.createSourceFile(file, source);\n const switches = config\n .getDescendantsOfKind(context.tsMorph.SyntaxKind.PropertyAssignment)\n .filter((property) => property.getName() === \"healthCheckers\");\n let changed = false;\n for (const property of switches) {\n const value = property.getInitializer();\n if (!value || !context.tsMorph.Node.isObjectLiteralExpression(value)) {\n if (value?.getText() !== \"false\")\n review(\n output,\n file,\n \"Review custom healthCheckers; remove ESLint checkers and enable Oxlint. ESLint support is removed in 5.34.0.\",\n property.getStartLineNumber(),\n );\n continue;\n }\n for (const spread of value.getProperties().filter(context.tsMorph.Node.isSpreadAssignment)) {\n review(\n output,\n file,\n \"Review spread healthCheckers options; remove eslint and enable oxlint after the spread.\",\n spread.getStartLineNumber(),\n );\n }\n const eslint = value.getProperty(\"eslint\");\n if (!eslint) continue;\n if (eslint) {\n eslint.remove();\n changed = true;\n }\n for (const [name, next] of [[\"oxlint\", \"true\"]] as const) {\n const option = value.getProperty(name);\n if (!option) continue;\n if (\n !context.tsMorph.Node.isPropertyAssignment(option) ||\n ![\"true\", \"false\"].includes(option.getInitializer()?.getText() ?? \"\")\n ) {\n review(\n output,\n file,\n `Set healthCheckers.${name} to ${next} after reviewing this computed option.`,\n option.getStartLineNumber(),\n );\n continue;\n }\n if (option.getInitializer()?.getText() !== next) {\n option.setInitializer(next);\n changed = true;\n }\n }\n }\n if (changed)\n context.text.write(file, config.getFullText(), {\n line: 1,\n description: \"Select Oxlint instead of ESLint for background health checks\",\n });\n}\n\nexport const oxcToolchain: Codemod = {\n id: \"oxc-toolchain\",\n version: \"5.33.0\",\n package: \"@warlock.js/core\",\n title: \"Migrate ESLint and Prettier to Oxlint and Oxfmt\",\n skill: \"upgrade-from-5-29\",\n run(context) {\n const output: CodemodOutput = { edits: [], manual: [] };\n const source = context.text.read(\"package.json\");\n if (source === undefined) return output;\n const pkg = JSON.parse(source) as {\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n eslintConfig?: unknown;\n prettier?: unknown;\n };\n const legacyFiles = context.text\n .list()\n .filter((file) =>\n /^(eslint\\.config\\..+|\\.eslintrc(?:\\..+)?|\\.prettierrc(?:\\..+)?|prettier\\.config\\..+|\\.prettierignore|\\.eslintignore)$/.test(\n file,\n ),\n );\n const dependencies = [pkg.dependencies, pkg.devDependencies];\n const legacyScripts = Object.values(pkg.scripts ?? {}).some((script) =>\n /\\b(?:eslint|prettier)\\b/.test(script),\n );\n if (\n !legacyFiles.length &&\n !legacyScripts &&\n pkg.eslintConfig === undefined &&\n pkg.prettier === undefined &&\n !dependencies.some((deps) => Object.keys(deps ?? {}).some(legacyDependency))\n ) {\n if (\n /\\beslint\\b|EslintHealthChecker|ESLint/.test(context.text.read(\"warlock.config.ts\") ?? \"\")\n )\n migrateHealthSwitches(context, output);\n return output;\n }\n\n const change = {\n line: 1,\n description: \"Replace ESLint/Prettier with the default Oxlint/Oxfmt toolchain\",\n };\n for (const deps of dependencies) {\n for (const name of Object.keys(deps ?? {})) {\n if (deps && (legacyDependency(name) || name in OXC_VERSIONS)) delete deps[name];\n }\n }\n pkg.devDependencies = { ...pkg.devDependencies, ...OXC_VERSIONS };\n pkg.scripts = { ...pkg.scripts, ...OXC_SCRIPTS };\n if (/\\beslint\\b/.test(pkg.scripts.fix ?? \"\")) pkg.scripts.fix = OXC_SCRIPTS.lint;\n for (const [name, command] of Object.entries(pkg.scripts)) {\n if (/\\b(?:eslint|prettier)\\b/.test(command))\n review(\n output,\n \"package.json\",\n `Replace the custom ${name} script (${command}) with oxlint or oxfmt; the legacy tools have been removed from dependencies.`,\n );\n }\n\n const hadOxlint = context.text.read(\".oxlintrc.json\") !== undefined;\n for (const file of [\".oxlintrc.json\", \".oxfmtrc.json\"]) {\n if (context.text.read(file) === undefined)\n context.text.write(file, asset(file.slice(1)), change);\n }\n if (pkg.eslintConfig !== undefined) {\n const findings = output.manual.length;\n if (portEslintRules(context, output, \"package.json\", source, hadOxlint, \"eslintConfig\"))\n pkg.devDependencies[\"oxlint-tsgolint\"] = \"7.0.2004\";\n if (output.manual.length === findings) delete pkg.eslintConfig;\n }\n if (pkg.prettier !== undefined) {\n if (sameGenerated(JSON.stringify(pkg.prettier), asset(\"legacy-prettierrc.json\")))\n delete pkg.prettier;\n else\n review(\n output,\n \"package.json\",\n \"Port the custom prettier field to .oxfmtrc.json, then remove it. Prettier support is gone in 5.34.0.\",\n );\n }\n for (const file of legacyFiles) {\n const content = context.text.read(file);\n if (file === \".eslintignore\" && content !== undefined) {\n try {\n const config: unknown = JSON.parse(context.text.read(\".oxlintrc.json\") ?? \"{}\");\n if (\n !isRecord(config) ||\n (config.ignorePatterns !== undefined &&\n (!Array.isArray(config.ignorePatterns) ||\n !config.ignorePatterns.every((pattern) => typeof pattern === \"string\")))\n )\n throw new Error(\"Invalid ignore patterns\");\n config.ignorePatterns = [\n ...new Set([\n ...(Array.isArray(config.ignorePatterns) ? config.ignorePatterns : []),\n ...content\n .split(/\\r?\\n/)\n .filter((line) => line.trim() && !line.trimStart().startsWith(\"#\")),\n ]),\n ];\n context.text.write(\".oxlintrc.json\", `${JSON.stringify(config, null, 2)}\\n`, change);\n } catch {\n review(\n output,\n file,\n \"Port these ignore patterns after repairing .oxlintrc.json; ESLint support is gone in 5.34.0.\",\n );\n continue;\n }\n context.text.remove(file, change);\n continue;\n }\n const known = [\"eslint.config.js\", \".prettierrc.json\", \".prettierignore\"].includes(file);\n if (content === undefined) continue;\n const generated = known && sameGenerated(content, asset(`legacy-${file.replace(/^\\./, \"\")}`));\n if (!generated && /^(eslint\\.config\\..+|\\.eslintrc(?:\\..+)?)$/.test(file)) {\n const findings = output.manual.length;\n if (portEslintRules(context, output, file, content, hadOxlint))\n pkg.devDependencies[\"oxlint-tsgolint\"] = \"7.0.2004\";\n // Keep the source only when there is still something a person must translate.\n if (output.manual.length === findings) context.text.remove(file, change);\n continue;\n }\n if (!generated) {\n review(\n output,\n file,\n \"Port customized formatting or ignore options to .oxfmtrc.json / .oxfmtignore, then remove this file. Legacy support is gone in 5.34.0.\",\n );\n continue;\n }\n if (file === \".prettierignore\") {\n const existing = context.text.read(\".oxfmtignore\");\n if (existing === undefined)\n context.text.write(\".oxfmtignore\", `${content.trimEnd()}\\n`, change);\n else if (existing.trim() !== content.trim()) {\n review(\n output,\n file,\n \"Merge these ignore patterns into the existing .oxfmtignore, then remove .prettierignore.\",\n );\n continue;\n }\n }\n context.text.remove(file, change);\n }\n // Oxfmt 0.73 uses .prettierignore by default (verified with --help).\n // Explicit script ignore paths make the migrated .oxfmtignore effective.\n if (context.text.read(\".oxfmtignore\") === undefined) {\n context.text.write(\".oxfmtignore\", \"node_modules\\ndist\\n\", change);\n }\n for (const name of [\"settings.json\", \"extensions.json\"]) {\n const file = `.vscode/${name}`;\n const content = context.text.read(file);\n if (content === undefined) continue;\n if (name === \"settings.json\" && sameGenerated(content, asset(`oxc-vscode-${name}`))) continue;\n if (name === \"settings.json\" && sameGenerated(content, asset(`legacy-vscode-${name}`)))\n context.text.write(file, asset(`oxc-vscode-${name}`), change);\n else\n review(\n output,\n file,\n \"Review editor settings: select oxc.oxc-vscode as the formatter, enable source.fixAll.oxc and remove ESLint/Prettier extension recommendations.\",\n );\n }\n migrateHealthSwitches(context, output);\n context.text.write(\"package.json\", `${JSON.stringify(pkg, null, 2)}\\n`, change);\n return output;\n },\n};\n\n/** Run again for apps that skipped or partially completed the 5.33 migration. */\nexport const oxcToolchainRemoval: Codemod = {\n ...oxcToolchain,\n id: \"oxc-toolchain-removal\",\n version: \"5.34.0\",\n title: \"ESLint/Prettier support is gone in 5.34.0; migrate to the Oxlint/Oxfmt default\",\n};\n"],"mappings":";;;;;;AAMA,SAAS,MAAM,MAAsB;CACnC,MAAM,UAAU,oBAAoB;CACpC,IAAI,YAAY,QAAW,MAAM,IAAI,MAAM,+BAA+B,MAAM;CAChF,OAAO;AACT;AAEA,MAAa,eAAe;CAAE,QAAQ;CAAU,OAAO;CAAU,YAAY;AAAQ;AACrF,MAAa,cAAc;CACzB,MAAM;CACN,cAAc;CACd,QAAQ;CACR,gBAAgB;AAClB;AAEA,SAAS,iBAAiB,MAAuB;CAC/C,OACE,yDAAyD,KAAK,IAAI,KAClE,KAAK,WAAW,qBAAqB,KACrC,yBAAyB,KAAK,IAAI,KAClC,yBAAyB,KAAK,IAAI,KAClC,2BAA2B,KAAK,IAAI,KACpC,KAAK,WAAW,mBAAmB;AAEvC;AAEA,SAAS,cAAc,QAAgB,UAA2B;CAGhE,IAAI;EACF,OAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC,MAAM,KAAK,UAAU,KAAK,MAAM,QAAQ,CAAC;CACnF,QAAQ;EACN,OAAO,OAAO,QAAQ,SAAS,IAAI,CAAC,CAAC,KAAK,MAAM,SAAS,QAAQ,SAAS,IAAI,CAAC,CAAC,KAAK;CACvF;AACF;AAEA,SAAS,OAAO,QAAuB,MAAc,MAAc,OAAO,GAAS;CACjF,OAAO,OAAO,KAAK;EAAE;EAAM;EAAM;CAAK,CAAC;AACzC;AAEA,SAAS,sBAAsB,SAAyB,QAA6B;CACnF,MAAM,OAAO;CACb,MAAM,SAAS,QAAQ,KAAK,KAAK,IAAI;CACrC,IAAI,WAAW,QAAW;CAE1B,MAAM,SAAS,IADK,QAAQ,QAAQ,QAAQ,EAAE,uBAAuB,KAAK,CACrD,CAAC,CAAC,iBAAiB,MAAM,MAAM;CACpD,MAAM,WAAW,OACd,qBAAqB,QAAQ,QAAQ,WAAW,kBAAkB,CAAC,CACnE,QAAQ,aAAa,SAAS,QAAQ,MAAM,gBAAgB;CAC/D,IAAI,UAAU;CACd,KAAK,MAAM,YAAY,UAAU;EAC/B,MAAM,QAAQ,SAAS,eAAe;EACtC,IAAI,CAAC,SAAS,CAAC,QAAQ,QAAQ,KAAK,0BAA0B,KAAK,GAAG;GACpE,IAAI,OAAO,QAAQ,MAAM,SACvB,OACE,QACA,MACA,gHACA,SAAS,mBAAmB,CAC9B;GACF;EACF;EACA,KAAK,MAAM,UAAU,MAAM,cAAc,CAAC,CAAC,OAAO,QAAQ,QAAQ,KAAK,kBAAkB,GACvF,OACE,QACA,MACA,2FACA,OAAO,mBAAmB,CAC5B;EAEF,MAAM,SAAS,MAAM,YAAY,QAAQ;EACzC,IAAI,CAAC,QAAQ;EACb,IAAI,QAAQ;GACV,OAAO,OAAO;GACd,UAAU;EACZ;EACA,KAAK,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,UAAU,MAAM,CAAC,GAAY;GACxD,MAAM,SAAS,MAAM,YAAY,IAAI;GACrC,IAAI,CAAC,QAAQ;GACb,IACE,CAAC,QAAQ,QAAQ,KAAK,qBAAqB,MAAM,KACjD,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,SAAS,OAAO,eAAe,CAAC,EAAE,QAAQ,KAAK,EAAE,GACpE;IACA,OACE,QACA,MACA,sBAAsB,KAAK,MAAM,KAAK,yCACtC,OAAO,mBAAmB,CAC5B;IACA;GACF;GACA,IAAI,OAAO,eAAe,CAAC,EAAE,QAAQ,MAAM,MAAM;IAC/C,OAAO,eAAe,IAAI;IAC1B,UAAU;GACZ;EACF;CACF;CACA,IAAI,SACF,QAAQ,KAAK,MAAM,MAAM,OAAO,YAAY,GAAG;EAC7C,MAAM;EACN,aAAa;CACf,CAAC;AACL;AAEA,MAAa,eAAwB;CACnC,IAAI;CACJ,SAAS;CACT,SAAS;CACT,OAAO;CACP,OAAO;CACP,IAAI,SAAS;EACX,MAAM,SAAwB;GAAE,OAAO,CAAC;GAAG,QAAQ,CAAC;EAAE;EACtD,MAAM,SAAS,QAAQ,KAAK,KAAK,cAAc;EAC/C,IAAI,WAAW,QAAW,OAAO;EACjC,MAAM,MAAM,KAAK,MAAM,MAAM;EAO7B,MAAM,cAAc,QAAQ,KACzB,KAAK,CAAC,CACN,QAAQ,SACP,wHAAwH,KACtH,IACF,CACF;EACF,MAAM,eAAe,CAAC,IAAI,cAAc,IAAI,eAAe;EAC3D,MAAM,gBAAgB,OAAO,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,WAC3D,0BAA0B,KAAK,MAAM,CACvC;EACA,IACE,CAAC,YAAY,UACb,CAAC,iBACD,IAAI,iBAAiB,UACrB,IAAI,aAAa,UACjB,CAAC,aAAa,MAAM,SAAS,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC,GAC3E;GACA,IACE,wCAAwC,KAAK,QAAQ,KAAK,KAAK,mBAAmB,KAAK,EAAE,GAEzF,sBAAsB,SAAS,MAAM;GACvC,OAAO;EACT;EAEA,MAAM,SAAS;GACb,MAAM;GACN,aAAa;EACf;EACA,KAAK,MAAM,QAAQ,cACjB,KAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,CAAC,GACvC,IAAI,SAAS,iBAAiB,IAAI,KAAK,QAAQ,eAAe,OAAO,KAAK;EAG9E,IAAI,kBAAkB;GAAE,GAAG,IAAI;GAAiB,GAAG;EAAa;EAChE,IAAI,UAAU;GAAE,GAAG,IAAI;GAAS,GAAG;EAAY;EAC/C,IAAI,aAAa,KAAK,IAAI,QAAQ,OAAO,EAAE,GAAG,IAAI,QAAQ,MAAM,YAAY;EAC5E,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,IAAI,OAAO,GACtD,IAAI,0BAA0B,KAAK,OAAO,GACxC,OACE,QACA,gBACA,sBAAsB,KAAK,WAAW,QAAQ,8EAChD;EAGJ,MAAM,YAAY,QAAQ,KAAK,KAAK,gBAAgB,MAAM;EAC1D,KAAK,MAAM,QAAQ,CAAC,kBAAkB,eAAe,GACnD,IAAI,QAAQ,KAAK,KAAK,IAAI,MAAM,QAC9B,QAAQ,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,CAAC,CAAC,GAAG,MAAM;EAEzD,IAAI,IAAI,iBAAiB,QAAW;GAClC,MAAM,WAAW,OAAO,OAAO;GAC/B,IAAI,gBAAgB,SAAS,QAAQ,gBAAgB,QAAQ,WAAW,cAAc,GACpF,IAAI,gBAAgB,qBAAqB;GAC3C,IAAI,OAAO,OAAO,WAAW,UAAU,OAAO,IAAI;EACpD;EACA,IAAI,IAAI,aAAa,QAAW;GAC9B,IAAI,cAAc,KAAK,UAAU,IAAI,QAAQ,GAAG,MAAM,wBAAwB,CAAC,GAC7E,OAAO,IAAI;QAEX,OACE,QACA,gBACA,sGACF;EACJ;EACA,KAAK,MAAM,QAAQ,aAAa;GAC9B,MAAM,UAAU,QAAQ,KAAK,KAAK,IAAI;GACtC,IAAI,SAAS,mBAAmB,YAAY,QAAW;IACrD,IAAI;KACF,MAAM,SAAkB,KAAK,MAAM,QAAQ,KAAK,KAAK,gBAAgB,KAAK,IAAI;KAC9E,IACE,CAAC,SAAS,MAAM,KACf,OAAO,mBAAmB,WACxB,CAAC,MAAM,QAAQ,OAAO,cAAc,KACnC,CAAC,OAAO,eAAe,OAAO,YAAY,OAAO,YAAY,QAAQ,IAEzE,MAAM,IAAI,MAAM,yBAAyB;KAC3C,OAAO,iBAAiB,CACtB,mBAAG,IAAI,IAAI,CACT,GAAI,MAAM,QAAQ,OAAO,cAAc,IAAI,OAAO,iBAAiB,CAAC,GACpE,GAAG,QACA,MAAM,OAAO,CAAC,CACd,QAAQ,SAAS,KAAK,KAAK,KAAK,CAAC,KAAK,UAAU,CAAC,CAAC,WAAW,GAAG,CAAC,CACtE,CAAC,CACH;KACA,QAAQ,KAAK,MAAM,kBAAkB,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,KAAK,MAAM;IACrF,QAAQ;KACN,OACE,QACA,MACA,8FACF;KACA;IACF;IACA,QAAQ,KAAK,OAAO,MAAM,MAAM;IAChC;GACF;GACA,MAAM,QAAQ;IAAC;IAAoB;IAAoB;GAAiB,CAAC,CAAC,SAAS,IAAI;GACvF,IAAI,YAAY,QAAW;GAC3B,MAAM,YAAY,SAAS,cAAc,SAAS,MAAM,UAAU,KAAK,QAAQ,OAAO,EAAE,GAAG,CAAC;GAC5F,IAAI,CAAC,aAAa,6CAA6C,KAAK,IAAI,GAAG;IACzE,MAAM,WAAW,OAAO,OAAO;IAC/B,IAAI,gBAAgB,SAAS,QAAQ,MAAM,SAAS,SAAS,GAC3D,IAAI,gBAAgB,qBAAqB;IAE3C,IAAI,OAAO,OAAO,WAAW,UAAU,QAAQ,KAAK,OAAO,MAAM,MAAM;IACvE;GACF;GACA,IAAI,CAAC,WAAW;IACd,OACE,QACA,MACA,wIACF;IACA;GACF;GACA,IAAI,SAAS,mBAAmB;IAC9B,MAAM,WAAW,QAAQ,KAAK,KAAK,cAAc;IACjD,IAAI,aAAa,QACf,QAAQ,KAAK,MAAM,gBAAgB,GAAG,QAAQ,QAAQ,EAAE,KAAK,MAAM;SAChE,IAAI,SAAS,KAAK,MAAM,QAAQ,KAAK,GAAG;KAC3C,OACE,QACA,MACA,0FACF;KACA;IACF;GACF;GACA,QAAQ,KAAK,OAAO,MAAM,MAAM;EAClC;EAGA,IAAI,QAAQ,KAAK,KAAK,cAAc,MAAM,QACxC,QAAQ,KAAK,MAAM,gBAAgB,wBAAwB,MAAM;EAEnE,KAAK,MAAM,QAAQ,CAAC,iBAAiB,iBAAiB,GAAG;GACvD,MAAM,OAAO,WAAW;GACxB,MAAM,UAAU,QAAQ,KAAK,KAAK,IAAI;GACtC,IAAI,YAAY,QAAW;GAC3B,IAAI,SAAS,mBAAmB,cAAc,SAAS,MAAM,cAAc,MAAM,CAAC,GAAG;GACrF,IAAI,SAAS,mBAAmB,cAAc,SAAS,MAAM,iBAAiB,MAAM,CAAC,GACnF,QAAQ,KAAK,MAAM,MAAM,MAAM,cAAc,MAAM,GAAG,MAAM;QAE5D,OACE,QACA,MACA,gJACF;EACJ;EACA,sBAAsB,SAAS,MAAM;EACrC,QAAQ,KAAK,MAAM,gBAAgB,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE,KAAK,MAAM;EAC9E,OAAO;CACT;AACF;;AAGA,MAAa,sBAA+B;CAC1C,GAAG;CACH,IAAI;CACJ,SAAS;CACT,OAAO;AACT"}