@warlock.js/core 5.35.0 → 5.36.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.
- package/CHANGELOG.md +25 -0
- package/esm/dev-server/module-loader.mjs +1 -1
- package/esm/dev-server/parse-imports.mjs +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/e2e/cdp.d.mts.map +1 -1
- package/esm/e2e/cdp.mjs +9 -2
- package/esm/e2e/cdp.mjs.map +1 -1
- package/esm/e2e/page.d.mts +9 -1
- package/esm/e2e/page.d.mts.map +1 -1
- package/esm/e2e/page.mjs +45 -7
- package/esm/e2e/page.mjs.map +1 -1
- package/esm/e2e/types.d.mts +18 -0
- package/esm/e2e/types.d.mts.map +1 -1
- package/esm/e2e/types.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +24 -0
- package/esm/errors/error-codes.d.mts.map +1 -1
- package/esm/errors/error-codes.mjs +24 -0
- package/esm/errors/error-codes.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +7 -7
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +3 -3
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/index.d.mts +4 -2
- package/esm/index.mjs +3 -1
- package/esm/localization/index.mjs +3 -1
- package/esm/localization/translation-usage-graph.d.mts +49 -0
- package/esm/localization/translation-usage-graph.d.mts.map +1 -0
- package/esm/localization/translation-usage-graph.mjs +128 -0
- package/esm/localization/translation-usage-graph.mjs.map +1 -0
- package/esm/localization/translation-usage.d.mts +85 -0
- package/esm/localization/translation-usage.d.mts.map +1 -0
- package/esm/localization/translation-usage.mjs +307 -0
- package/esm/localization/translation-usage.mjs.map +1 -0
- package/esm/production/build-budgets.d.mts +18 -0
- package/esm/production/build-budgets.d.mts.map +1 -1
- package/esm/production/build-budgets.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +5 -3
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/router/router.d.mts +2 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +4 -1
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +19 -0
- package/esm/router/types.d.mts.map +1 -1
- package/esm/tooling/check/api-route-references.d.mts +16 -0
- package/esm/tooling/check/api-route-references.d.mts.map +1 -0
- package/esm/tooling/check/api-route-references.mjs +146 -0
- package/esm/tooling/check/api-route-references.mjs.map +1 -0
- package/esm/tooling/check/conventions-step.d.mts +6 -3
- package/esm/tooling/check/conventions-step.d.mts.map +1 -1
- package/esm/tooling/check/conventions-step.mjs +19 -10
- package/esm/tooling/check/conventions-step.mjs.map +1 -1
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +4 -2
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/check/translation-conventions.mjs +32 -0
- package/esm/tooling/check/translation-conventions.mjs.map +1 -0
- package/esm/tooling/inspect/collect-files.mjs +1 -1
- package/esm/upgrade/codemods/index.mjs +2 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/vite-vitest.mjs +170 -0
- package/esm/upgrade/codemods/vite-vitest.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +4 -0
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/warlock-config/index.d.mts +2 -2
- package/llms-full.txt +127 -16
- package/package.json +13 -13
- package/skills/register-route/SKILL.md +13 -0
- package/skills/test-service/SKILL.md +8 -8
- package/skills/upgrade-from-5-29/SKILL.md +91 -0
- package/skills/use-localization/SKILL.md +4 -0
- package/skills/warlock-check/SKILL.md +11 -8
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { manualAt, replaceNode } from "./helpers.mjs";
|
|
2
|
+
import { parseVersion } from "../version.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../packages/core/src/upgrade/codemods/vite-vitest.ts
|
|
5
|
+
/**
|
|
6
|
+
* The newest releases that are at least two weeks old on the day 5.36 shipped;
|
|
7
|
+
* `warlock add test` and `warlock add web` install the same ones.
|
|
8
|
+
*/
|
|
9
|
+
const TARGETS = {
|
|
10
|
+
vite: {
|
|
11
|
+
major: 8,
|
|
12
|
+
version: "8.3.3"
|
|
13
|
+
},
|
|
14
|
+
vitest: {
|
|
15
|
+
major: 5,
|
|
16
|
+
version: "5.0.3"
|
|
17
|
+
},
|
|
18
|
+
"@vitest/coverage-v8": {
|
|
19
|
+
major: 5,
|
|
20
|
+
version: "5.0.3"
|
|
21
|
+
},
|
|
22
|
+
"@vitest/coverage-istanbul": {
|
|
23
|
+
major: 5,
|
|
24
|
+
version: "5.0.3"
|
|
25
|
+
},
|
|
26
|
+
"@vitest/ui": {
|
|
27
|
+
major: 5,
|
|
28
|
+
version: "5.0.3"
|
|
29
|
+
},
|
|
30
|
+
"@vitejs/plugin-react": {
|
|
31
|
+
major: 6,
|
|
32
|
+
version: "6.1.2"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const MIGRATION_GUIDES = "https://vite.dev/guide/migration and https://vitest.dev/guide/migration";
|
|
36
|
+
/** Version specs that are not a plain registry range and must not be rewritten. */
|
|
37
|
+
const NON_REGISTRY_SPEC = /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\*$|^latest$/;
|
|
38
|
+
/** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */
|
|
39
|
+
const REMOVED_ENTRY_POINTS = /* @__PURE__ */ new Set([
|
|
40
|
+
"vitest/coverage",
|
|
41
|
+
"vitest/reporters",
|
|
42
|
+
"vitest/environments",
|
|
43
|
+
"vitest/snapshot",
|
|
44
|
+
"vitest/suite",
|
|
45
|
+
"vitest/mocker",
|
|
46
|
+
"vitest/internal/module-runner"
|
|
47
|
+
]);
|
|
48
|
+
const VITE_CONFIG_FILE = /^vite(st)?\.config\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
49
|
+
function serialize(original, value) {
|
|
50
|
+
const indent = /^[ \t]+(?=")/m.exec(original)?.[0];
|
|
51
|
+
return (indent ? JSON.stringify(value, null, indent) : JSON.stringify(value)) + (original.endsWith("\n") ? "\n" : "");
|
|
52
|
+
}
|
|
53
|
+
function lineOf(source, needle) {
|
|
54
|
+
const index = source.indexOf(needle);
|
|
55
|
+
return index < 0 ? 1 : source.slice(0, index).split("\n").length;
|
|
56
|
+
}
|
|
57
|
+
/** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */
|
|
58
|
+
function upgradedSpec(spec, target) {
|
|
59
|
+
if (NON_REGISTRY_SPEC.test(spec)) return void 0;
|
|
60
|
+
const parsed = parseVersion(spec);
|
|
61
|
+
if (!parsed || parsed[0] >= target.major) return void 0;
|
|
62
|
+
return `${/^[\^~]/.exec(spec)?.[0] ?? (/^\d/.test(spec) ? "" : "^")}${target.version}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one
|
|
66
|
+
* family and must move together.
|
|
67
|
+
*
|
|
68
|
+
* - package.json: raises `vite`, `vitest`, `@vitest/*` and
|
|
69
|
+
* `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and
|
|
70
|
+
* `resolutions`) to the supported majors.
|
|
71
|
+
* - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the
|
|
72
|
+
* runner class is `TestRunner`, exported from `vitest`, and
|
|
73
|
+
* `onCleanupWorkerContext` is an optional property.
|
|
74
|
+
* - Lists removed Vitest entry points, a top-level `esbuild` option, and object
|
|
75
|
+
* `manualChunks` in Vite configs for review.
|
|
76
|
+
*/
|
|
77
|
+
const viteVitest = {
|
|
78
|
+
id: "vite-8-vitest-5",
|
|
79
|
+
version: "5.36.0",
|
|
80
|
+
package: "@warlock.js/core",
|
|
81
|
+
title: "Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6",
|
|
82
|
+
skill: "upgrade-from-5-29",
|
|
83
|
+
run(context) {
|
|
84
|
+
const { Node, SyntaxKind } = context.tsMorph;
|
|
85
|
+
const output = {
|
|
86
|
+
edits: [],
|
|
87
|
+
manual: []
|
|
88
|
+
};
|
|
89
|
+
const source = context.text.read("package.json");
|
|
90
|
+
if (source !== void 0) {
|
|
91
|
+
const pkg = JSON.parse(source);
|
|
92
|
+
const changes = [];
|
|
93
|
+
for (const field of [
|
|
94
|
+
"dependencies",
|
|
95
|
+
"devDependencies",
|
|
96
|
+
"overrides",
|
|
97
|
+
"resolutions"
|
|
98
|
+
]) {
|
|
99
|
+
const entries = pkg[field];
|
|
100
|
+
if (!entries) continue;
|
|
101
|
+
for (const [name, target] of Object.entries(TARGETS)) {
|
|
102
|
+
const spec = entries[name];
|
|
103
|
+
if (typeof spec !== "string") continue;
|
|
104
|
+
const next = upgradedSpec(spec, target);
|
|
105
|
+
if (!next) continue;
|
|
106
|
+
entries[name] = next;
|
|
107
|
+
changes.push({
|
|
108
|
+
name: field === "dependencies" || field === "devDependencies" ? name : `${field}.${name}`,
|
|
109
|
+
from: spec,
|
|
110
|
+
to: next
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const first = changes[0];
|
|
115
|
+
if (first) context.text.write("package.json", serialize(source, pkg), {
|
|
116
|
+
line: lineOf(source, `"${first.name.replace(/^[a-z]+\./, "")}"`),
|
|
117
|
+
description: changes.map((change) => `${change.name} ${change.from} → ${change.to}`).join(", ")
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
for (const sourceFile of context.sourceFiles) for (const declaration of sourceFile.getImportDeclarations()) {
|
|
121
|
+
const moduleName = declaration.getModuleSpecifierValue();
|
|
122
|
+
if (REMOVED_ENTRY_POINTS.has(moduleName)) {
|
|
123
|
+
output.manual.push(manualAt(context, declaration, `Vitest 5 removed the "${moduleName}" entry point. Import from "vitest" or "vitest/node" instead; see ${MIGRATION_GUIDES}.`));
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (moduleName !== "vitest/runners") continue;
|
|
127
|
+
const specifiers = declaration.getNamedImports();
|
|
128
|
+
const runner = specifiers.find((specifier) => specifier.getName() === "VitestTestRunner");
|
|
129
|
+
if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {
|
|
130
|
+
output.manual.push(manualAt(context, declaration, "Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist."));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const alias = runner.getAliasNode()?.getText();
|
|
134
|
+
output.edits.push(replaceNode(context, declaration, alias ? `import { TestRunner as ${alias} } from "vitest";` : "import { TestRunner } from \"vitest\";", "imported the runner as TestRunner from \"vitest\""));
|
|
135
|
+
if (!alias) for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
136
|
+
if (identifier.getText() !== "VitestTestRunner") continue;
|
|
137
|
+
if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;
|
|
138
|
+
output.edits.push(replaceNode(context, identifier, "TestRunner", "renamed VitestTestRunner to TestRunner"));
|
|
139
|
+
}
|
|
140
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
141
|
+
const callee = call.getExpression();
|
|
142
|
+
if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "onCleanupWorkerContext" || call.getQuestionDotTokenNode()) continue;
|
|
143
|
+
output.edits.push(replaceNode(context, callee, `${callee.getText()}?.`, "onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)"));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const file of context.text.list()) {
|
|
147
|
+
if (!VITE_CONFIG_FILE.test(file)) continue;
|
|
148
|
+
const config = context.text.read(file);
|
|
149
|
+
if (config === void 0) continue;
|
|
150
|
+
config.split("\n").forEach((text, index) => {
|
|
151
|
+
const line = index + 1;
|
|
152
|
+
if (/^\s*esbuild\s*:/.test(text)) output.manual.push({
|
|
153
|
+
file,
|
|
154
|
+
line,
|
|
155
|
+
hint: "Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules."
|
|
156
|
+
});
|
|
157
|
+
if (/\bmanualChunks\s*:\s*\{/.test(text)) output.manual.push({
|
|
158
|
+
file,
|
|
159
|
+
line,
|
|
160
|
+
hint: "Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`."
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return output;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
export { viteVitest };
|
|
170
|
+
//# sourceMappingURL=vite-vitest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-vitest.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/vite-vitest.ts"],"sourcesContent":["import type { Codemod, CodemodOutput } from \"../types\";\nimport { parseVersion } from \"../version\";\nimport { manualAt, replaceNode } from \"./helpers\";\n\n/**\n * The newest releases that are at least two weeks old on the day 5.36 shipped;\n * `warlock add test` and `warlock add web` install the same ones.\n */\nconst TARGETS: Record<string, { major: number; version: string }> = {\n vite: { major: 8, version: \"8.3.3\" },\n vitest: { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-v8\": { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-istanbul\": { major: 5, version: \"5.0.3\" },\n \"@vitest/ui\": { major: 5, version: \"5.0.3\" },\n \"@vitejs/plugin-react\": { major: 6, version: \"6.1.2\" },\n};\n\nconst MIGRATION_GUIDES = \"https://vite.dev/guide/migration and https://vitest.dev/guide/migration\";\n\n/** Version specs that are not a plain registry range and must not be rewritten. */\nconst NON_REGISTRY_SPEC =\n /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\\*$|^latest$/;\n\n/** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */\nconst REMOVED_ENTRY_POINTS = new Set([\n \"vitest/coverage\",\n \"vitest/reporters\",\n \"vitest/environments\",\n \"vitest/snapshot\",\n \"vitest/suite\",\n \"vitest/mocker\",\n \"vitest/internal/module-runner\",\n]);\n\nconst VITE_CONFIG_FILE = /^vite(st)?\\.config\\.(ts|mts|cts|js|mjs|cjs)$/;\n\ntype PackageJson = Record<string, unknown> & {\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n overrides?: Record<string, unknown>;\n resolutions?: Record<string, unknown>;\n};\n\nfunction serialize(original: string, value: unknown): string {\n const indent = /^[ \\t]+(?=\")/m.exec(original)?.[0];\n const json = indent ? JSON.stringify(value, null, indent) : JSON.stringify(value);\n return json + (original.endsWith(\"\\n\") ? \"\\n\" : \"\");\n}\n\nfunction lineOf(source: string, needle: string): number {\n const index = source.indexOf(needle);\n return index < 0 ? 1 : source.slice(0, index).split(\"\\n\").length;\n}\n\n/** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */\nfunction upgradedSpec(spec: string, target: { major: number; version: string }) {\n if (NON_REGISTRY_SPEC.test(spec)) return undefined;\n const parsed = parseVersion(spec);\n // `>=7.3.5 <8` and ranges like it are rewritten too: the lower bound is what is read.\n if (!parsed || parsed[0] >= target.major) return undefined;\n const prefix = /^[\\^~]/.exec(spec)?.[0] ?? (/^\\d/.test(spec) ? \"\" : \"^\");\n return `${prefix}${target.version}`;\n}\n\n/**\n * 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one\n * family and must move together.\n *\n * - package.json: raises `vite`, `vitest`, `@vitest/*` and\n * `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and\n * `resolutions`) to the supported majors.\n * - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the\n * runner class is `TestRunner`, exported from `vitest`, and\n * `onCleanupWorkerContext` is an optional property.\n * - Lists removed Vitest entry points, a top-level `esbuild` option, and object\n * `manualChunks` in Vite configs for review.\n */\nexport const viteVitest: Codemod = {\n id: \"vite-8-vitest-5\",\n version: \"5.36.0\",\n package: \"@warlock.js/core\",\n title: \"Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6\",\n skill: \"upgrade-from-5-29\",\n run(context) {\n const { Node, SyntaxKind } = context.tsMorph;\n const output: CodemodOutput = { edits: [], manual: [] };\n\n const source = context.text.read(\"package.json\");\n if (source !== undefined) {\n const pkg = JSON.parse(source) as PackageJson;\n const changes: { name: string; from: string; to: string }[] = [];\n for (const field of [\n \"dependencies\",\n \"devDependencies\",\n \"overrides\",\n \"resolutions\",\n ] as const) {\n const entries = pkg[field];\n if (!entries) continue;\n for (const [name, target] of Object.entries(TARGETS)) {\n const spec = entries[name];\n if (typeof spec !== \"string\") continue;\n const next = upgradedSpec(spec, target);\n if (!next) continue;\n entries[name] = next;\n changes.push({\n name:\n field === \"dependencies\" || field === \"devDependencies\" ? name : `${field}.${name}`,\n from: spec,\n to: next,\n });\n }\n }\n const first = changes[0];\n if (first) {\n context.text.write(\"package.json\", serialize(source, pkg), {\n line: lineOf(source, `\"${first.name.replace(/^[a-z]+\\./, \"\")}\"`),\n description: changes\n .map((change) => `${change.name} ${change.from} → ${change.to}`)\n .join(\", \"),\n });\n }\n }\n\n for (const sourceFile of context.sourceFiles) {\n for (const declaration of sourceFile.getImportDeclarations()) {\n const moduleName = declaration.getModuleSpecifierValue();\n\n if (REMOVED_ENTRY_POINTS.has(moduleName)) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n `Vitest 5 removed the \"${moduleName}\" entry point. Import from \"vitest\" or \"vitest/node\" instead; see ${MIGRATION_GUIDES}.`,\n ),\n );\n continue;\n }\n if (moduleName !== \"vitest/runners\") continue;\n\n const specifiers = declaration.getNamedImports();\n const runner = specifiers.find((specifier) => specifier.getName() === \"VitestTestRunner\");\n if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n 'Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist.',\n ),\n );\n continue;\n }\n\n const alias = runner.getAliasNode()?.getText();\n output.edits.push(\n replaceNode(\n context,\n declaration,\n alias\n ? `import { TestRunner as ${alias} } from \"vitest\";`\n : 'import { TestRunner } from \"vitest\";',\n 'imported the runner as TestRunner from \"vitest\"',\n ),\n );\n\n if (!alias) {\n for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (identifier.getText() !== \"VitestTestRunner\") continue;\n if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;\n output.edits.push(\n replaceNode(\n context,\n identifier,\n \"TestRunner\",\n \"renamed VitestTestRunner to TestRunner\",\n ),\n );\n }\n }\n\n for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {\n const callee = call.getExpression();\n if (\n !Node.isPropertyAccessExpression(callee) ||\n callee.getName() !== \"onCleanupWorkerContext\" ||\n call.getQuestionDotTokenNode()\n ) {\n continue;\n }\n output.edits.push(\n replaceNode(\n context,\n callee,\n `${callee.getText()}?.`,\n \"onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)\",\n ),\n );\n }\n }\n }\n\n for (const file of context.text.list()) {\n if (!VITE_CONFIG_FILE.test(file)) continue;\n const config = context.text.read(file);\n if (config === undefined) continue;\n const lines = config.split(\"\\n\");\n lines.forEach((text, index) => {\n const line = index + 1;\n if (/^\\s*esbuild\\s*:/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules.\",\n });\n }\n if (/\\bmanualChunks\\s*:\\s*\\{/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`.\",\n });\n }\n });\n }\n\n return output;\n },\n};\n"],"mappings":"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"}
|
package/esm/upgrade/notes.mjs
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* operations. Printed so the rollout plans for them.
|
|
5
5
|
*/
|
|
6
6
|
const UPGRADE_NOTES = [
|
|
7
|
+
{
|
|
8
|
+
version: "5.36.0",
|
|
9
|
+
text: "Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown."
|
|
10
|
+
},
|
|
7
11
|
{
|
|
8
12
|
version: "5.34.0",
|
|
9
13
|
text: "ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.36.0\",\n text: \"Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown.\",\n },\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BudgetPage, BudgetViolation, BuildBudgetReport, BuildBudgets, PageBudget } from "../production/build-budgets.mjs";
|
|
1
|
+
import { BudgetPage, BudgetViolation, BuildBudgetReport, BuildBudgets, PageBudget, PayloadBudget } from "../production/build-budgets.mjs";
|
|
2
2
|
import { WarlockConfig } from "./types.mjs";
|
|
3
3
|
import { defineConfig } from "./define-config.mjs";
|
|
4
4
|
import { WarlockConfigManager, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager } from "./warlock-config.manager.mjs";
|
|
5
|
-
export { type BudgetPage, type BudgetViolation, type BuildBudgetReport, type BuildBudgets, type PageBudget, WarlockConfig, WarlockConfigManager, defineConfig, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager };
|
|
5
|
+
export { type BudgetPage, type BudgetViolation, type BuildBudgetReport, type BuildBudgets, type PageBudget, type PayloadBudget, WarlockConfig, WarlockConfigManager, defineConfig, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager };
|
package/llms-full.txt
CHANGED
|
@@ -4014,6 +4014,19 @@ router.get("/webhooks/status", webhookStatusController, { rateLimit: false });
|
|
|
4014
4014
|
- The `send-response` topic — picking the right `response.<helper>()`.
|
|
4015
4015
|
- The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
|
|
4016
4016
|
|
|
4017
|
+
## A named route the browser must never call — `clientVisible: false`
|
|
4018
|
+
|
|
4019
|
+
A named API route is embedded in a page's browser route table only when that page's client code reaches its name. Mark an endpoint that browser code must never reach with `clientVisible: false`:
|
|
4020
|
+
|
|
4021
|
+
```ts
|
|
4022
|
+
router.post("/internal/reindex", reindexController, {
|
|
4023
|
+
name: "internal.reindex",
|
|
4024
|
+
clientVisible: false,
|
|
4025
|
+
});
|
|
4026
|
+
```
|
|
4027
|
+
|
|
4028
|
+
It is then never embedded in a page, and `warlock check` reports a browser reference to its name as `WL9020` (an error). The server still authorizes the call as for any route.
|
|
4029
|
+
|
|
4017
4030
|
|
|
4018
4031
|
## report-framework-issue `@warlock.js/core/skills/report-framework-issue/SKILL.md`
|
|
4019
4032
|
|
|
@@ -8505,7 +8518,7 @@ and has no Warlock setup or HTTP global setup. `integration` discovers
|
|
|
8505
8518
|
It uses the framework setup, HTTP global setup and a generated `src/test-runner.ts`.
|
|
8506
8519
|
Keep pure service specs beside their source and mock repositories at the boundary.
|
|
8507
8520
|
Existing config files are preserved; copy the projects layout into them manually.
|
|
8508
|
-
Vitest
|
|
8521
|
+
Vitest 5 or newer is required for the generated runner (Vitest 5 removed `vitest/runners`; the runner extends `TestRunner` from `vitest`).
|
|
8509
8522
|
|
|
8510
8523
|
For a unit spec that needs configuration values, use:
|
|
8511
8524
|
|
|
@@ -8553,20 +8566,20 @@ Run with `npx vitest run --project integration`. Worker lifetime refuses watch,
|
|
|
8553
8566
|
threads, isolated module graphs, or a missing runner with a message explaining
|
|
8554
8567
|
the required settings. Keep `afterAll(teardownTest)` in the setup file: in worker
|
|
8555
8568
|
mode it marks the end of the file; Vitest's awaited worker cleanup closes the
|
|
8556
|
-
runtime once. The runner uses `
|
|
8557
|
-
in Vitest
|
|
8569
|
+
runtime once. The runner uses `TestRunner.onCleanupWorkerContext`, verified
|
|
8570
|
+
in Vitest 5.0.3's `TestRunner` declaration, runner implementation and worker cleanup source.
|
|
8558
8571
|
For an existing project, generate this runner without overwriting your config:
|
|
8559
8572
|
|
|
8560
8573
|
```ts title="src/test-runner.ts"
|
|
8561
8574
|
import { configureTestWorker } from "@warlock.js/core/tests";
|
|
8562
|
-
import {
|
|
8575
|
+
import { TestRunner } from "vitest";
|
|
8563
8576
|
|
|
8564
|
-
export default class WarlockTestRunner extends
|
|
8565
|
-
public constructor(config: ConstructorParameters<typeof
|
|
8577
|
+
export default class WarlockTestRunner extends TestRunner {
|
|
8578
|
+
public constructor(config: ConstructorParameters<typeof TestRunner>[0]) {
|
|
8566
8579
|
super(config);
|
|
8567
8580
|
configureTestWorker(
|
|
8568
8581
|
{ pool: this.pool, watch: config.watch, isolate: config.isolate },
|
|
8569
|
-
(cleanup) => this.onCleanupWorkerContext(cleanup),
|
|
8582
|
+
(cleanup) => this.onCleanupWorkerContext?.(cleanup),
|
|
8570
8583
|
);
|
|
8571
8584
|
}
|
|
8572
8585
|
}
|
|
@@ -8585,7 +8598,7 @@ This trades module isolation for startup cost: application singletons, config
|
|
|
8585
8598
|
mutations, mocks and module side effects can survive between files. Reset your
|
|
8586
8599
|
own state in `resetBetweenFiles`, restore mocks, and keep connector selection
|
|
8587
8600
|
consistent. Give each worker a distinct database/schema using
|
|
8588
|
-
`process.env.VITEST_POOL_ID` when preparing test connections. Never drop a shared
|
|
8601
|
+
`process.env.VITEST_POOL_ID` (1-based since Vitest 5) when preparing test connections. Never drop a shared
|
|
8589
8602
|
database from this hook while other workers are using it. The framework does not
|
|
8590
8603
|
create a memory replica set or a cross-worker lock; those belong to the application's
|
|
8591
8604
|
global setup. A shared HTTP server and a single replica set can contend on I/O,
|
|
@@ -9360,6 +9373,97 @@ one `#vessel` around `{children}`. Client navigation still swaps layout/page
|
|
|
9360
9373
|
children while preserving the document root. Remove temporary explicit form IDs
|
|
9361
9374
|
only when the application does not otherwise depend on those names.
|
|
9362
9375
|
|
|
9376
|
+
## 5.35 to 5.36 per-page API routes
|
|
9377
|
+
|
|
9378
|
+
A page document now carries only the named API routes its client code can reach. Before 5.36 every page carried every named route, so a browser call to a name the build cannot see worked on every page by accident. Check each `apiHref()` and `useSubmitForm({ route })` call:
|
|
9379
|
+
|
|
9380
|
+
- A name written as a string literal, a constant or a template with a static prefix is found automatically.
|
|
9381
|
+
- A name computed at run time, or taken from a module the build cannot follow, now throws on the page that calls it. Declare the names in that page's setup file, for example `export const config = { apiRoutes: ["orders.*"] };`, a layout or root `config` covers every page that renders it. Or call the endpoint with an explicit `path` and `method`.
|
|
9382
|
+
- `npx warlock check` reports the call sites that need a declaration as `WL9019`.
|
|
9383
|
+
|
|
9384
|
+
Routes registered with `clientVisible: false` are never embedded, and a browser call to one is an error (`WL9020`). Keep endpoints the browser must not reach on that option. Authorization is unchanged: the server still decides each request.
|
|
9385
|
+
|
|
9386
|
+
To measure a page's inline payload, set `build.budgets.payload` (default `64kb`). An over-budget route logs one line per process and never fails a request.
|
|
9387
|
+
|
|
9388
|
+
## 5.35 to 5.36 translation payloads
|
|
9389
|
+
|
|
9390
|
+
A page's hydration payload now carries only the translations its client code
|
|
9391
|
+
can reach, in route-dictionary (`locales.json`) and global-registry (`extend()`)
|
|
9392
|
+
apps alike. The groups every page uses (default `app` and `common`) arrive once
|
|
9393
|
+
per locale in a hashed, cached file that the page preloads. Nothing in a page's
|
|
9394
|
+
render changes: server output is the full dictionary, as before.
|
|
9395
|
+
|
|
9396
|
+
Run `warlock build`, then `warlock check`. A `WL9104` warning marks a page that
|
|
9397
|
+
renders a translation key computed at runtime. Declare the keys it renders in
|
|
9398
|
+
the page's setup file, for example
|
|
9399
|
+
`export const config = { translations: ["statuses.*", "members.title"] }`, so the
|
|
9400
|
+
page ships only what it uses.
|
|
9401
|
+
|
|
9402
|
+
In development, a warning names a key the page reads but its payload lacks, and
|
|
9403
|
+
resolves it from the full table so the page keeps working. In production the
|
|
9404
|
+
key renders as its own text and the miss is reported once through
|
|
9405
|
+
`onClientError` with `kind: "translations"`. Declare the key, or set
|
|
9406
|
+
`build: { translations: { prune: false } }` in the web connector options while migrating to
|
|
9407
|
+
restore the previous per-page payload.
|
|
9408
|
+
|
|
9409
|
+
Global-registry apps are covered too. Their shared table is served at
|
|
9410
|
+
`/__warlock/translations/<locale>.<hash>.json`, which is cached immutable in
|
|
9411
|
+
production. A direct `trans()` call from `@mongez/localization` is not checked
|
|
9412
|
+
for a pruning miss; the calls through `useTrans()` are.
|
|
9413
|
+
|
|
9414
|
+
## 5.35 to 5.36 Vite 8 and Vitest 5
|
|
9415
|
+
|
|
9416
|
+
`@warlock.js/web` requires Vite 8 (`^8.0.0`) and `@vitejs/plugin-react` 6, and the
|
|
9417
|
+
test kit requires Vitest 5. Vite 8 bundles with Rolldown and transforms
|
|
9418
|
+
TypeScript with Oxc. Vite 7 is not supported; plugin-react 6 needs Vite 8, so the
|
|
9419
|
+
family moves together. Vite 8 needs Node.js 20.19 or 22.12 and newer, Vitest 5
|
|
9420
|
+
needs 22.12 or newer; Warlock already requires 22.15.
|
|
9421
|
+
|
|
9422
|
+
```bash
|
|
9423
|
+
npx warlock upgrade --from 5.35 --to 5.36.0 --dry-run
|
|
9424
|
+
npx warlock upgrade --from 5.35 --to 5.36.0
|
|
9425
|
+
npm install
|
|
9426
|
+
npx warlock check
|
|
9427
|
+
```
|
|
9428
|
+
|
|
9429
|
+
The `vite-8-vitest-5` codemod:
|
|
9430
|
+
|
|
9431
|
+
- raises `vite` (8.3.3), `vitest` and `@vitest/*` (5.0.3) and `@vitejs/plugin-react`
|
|
9432
|
+
(6.1.2) in `dependencies`, `devDependencies`, `overrides` and `resolutions`,
|
|
9433
|
+
keeping `^`, `~` or exact style;
|
|
9434
|
+
- rewrites a generated `src/test-runner.ts` from `VitestTestRunner` in
|
|
9435
|
+
`vitest/runners` to `TestRunner` from `vitest`, and calls
|
|
9436
|
+
`this.onCleanupWorkerContext?.(cleanup)` (the hook is absent in vm pools);
|
|
9437
|
+
- lists, without editing, imports of the removed `vitest/coverage`,
|
|
9438
|
+
`vitest/reporters`, `vitest/environments`, `vitest/snapshot`, `vitest/suite`,
|
|
9439
|
+
`vitest/mocker` and `vitest/internal/module-runner` entry points, other names
|
|
9440
|
+
imported from `vitest/runners`, a top-level `esbuild` option in a Vite or
|
|
9441
|
+
Vitest config, and object-form `output.manualChunks`.
|
|
9442
|
+
|
|
9443
|
+
Do these by hand when the codemod lists them:
|
|
9444
|
+
|
|
9445
|
+
- Move a config's `esbuild: { jsx: "automatic" }` to
|
|
9446
|
+
`oxc: { jsx: { runtime: "automatic" } }`. `esbuild.target` is not converted, and
|
|
9447
|
+
Oxc does not lower native decorators: keep `lowerStage3Decorators()` from
|
|
9448
|
+
`@warlock.js/core/vite` first in `plugins` for decorated modules, as in the
|
|
9449
|
+
generated test config.
|
|
9450
|
+
- Replace object `manualChunks` with
|
|
9451
|
+
`build.rolldownOptions.output.codeSplitting.groups`; `build.rollupOptions` still
|
|
9452
|
+
works but is deprecated in favour of `build.rolldownOptions`.
|
|
9453
|
+
- A custom bundler `external` list that names Node builtins is still refused by the
|
|
9454
|
+
client build.
|
|
9455
|
+
|
|
9456
|
+
Vitest 5 changes defaults that can alter a green suite: `clearMocks` is true,
|
|
9457
|
+
`json` and `junit` reporters write to `.vitest/` instead of stdout, `-t` joins a
|
|
9458
|
+
test's names with `>`, `VITEST_POOL_ID` and `VITEST_WORKER_ID` start at 1, nested
|
|
9459
|
+
`vi.mock`/`vi.hoisted` calls throw, and an unawaited `resolves` or `rejects`
|
|
9460
|
+
assertion fails the test. Vitest also no longer searches parent directories for a
|
|
9461
|
+
config file; run it from the package that owns the config. Official guides:
|
|
9462
|
+
https://vite.dev/guide/migration and https://vitest.dev/guide/migration.
|
|
9463
|
+
|
|
9464
|
+
A project that uses `@warlock.js/ai`'s custom matchers needs Vitest 5 for their
|
|
9465
|
+
TypeScript types.
|
|
9466
|
+
|
|
9363
9467
|
|
|
9364
9468
|
## upload-file `@warlock.js/core/skills/upload-file/SKILL.md`
|
|
9365
9469
|
|
|
@@ -10347,6 +10451,10 @@ workers, and registered module cleanup hooks, including after partial startup.
|
|
|
10347
10451
|
A Node loader registration is process-scoped; its message channel is closed at
|
|
10348
10452
|
teardown. Module-owned timers/listeners must register their cleanup hooks.
|
|
10349
10453
|
|
|
10454
|
+
## Translation keys in pages (5.36)
|
|
10455
|
+
|
|
10456
|
+
`warlock check` reports `WL9104` when a page renders a translation key computed at runtime (`trans(key)` with no static prefix, or a translator passed on as a value). Such a page ships every translation group. Declare its keys in the page's setup file, `export const config = { translations: ["statuses.*", "members.title"] }`, to ship only what it uses. The `web` skill `localize-pages` covers the browser side.
|
|
10457
|
+
|
|
10350
10458
|
|
|
10351
10459
|
## use-middleware `@warlock.js/core/skills/use-middleware/SKILL.md`
|
|
10352
10460
|
|
|
@@ -12214,14 +12322,17 @@ and work through the manual-review list. Use `--lint=oxc` for new apps.
|
|
|
12214
12322
|
The following codes default to **warnings**, so upgrading does not fail existing projects.
|
|
12215
12323
|
They parse real TypeScript/JSX syntax with the same oxc-parser adapter as the existing conventions.
|
|
12216
12324
|
|
|
12217
|
-
| Code | Convention
|
|
12218
|
-
| -------- |
|
|
12219
|
-
| `WL9013` | A component file exceeds 150 lines
|
|
12220
|
-
| `WL9014` | More than one component in a file
|
|
12221
|
-
| `WL9015` | State/effects or event-handler functions inside a component
|
|
12222
|
-
| `WL9016` | Native select, checkbox, radio, date, time or datetime-local input
|
|
12223
|
-
| `WL9017` | DOM input/textarea/select native validation
|
|
12224
|
-
| `WL9018` | Exported `RequestHandler` in `src/app/**/controllers` lacks `responseSchema`
|
|
12325
|
+
| Code | Convention | Remedy |
|
|
12326
|
+
| -------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
12327
|
+
| `WL9013` | A component file exceeds 150 lines | Split components and extract hooks; configure `componentLineLimit`. |
|
|
12328
|
+
| `WL9014` | More than one component in a file | Move each component into its own TSX file. |
|
|
12329
|
+
| `WL9015` | State/effects or event-handler functions inside a component | Move `useState`, `useReducer`, `useEffect`, `useLayoutEffect` and handlers into `use-*.ts` hooks. |
|
|
12330
|
+
| `WL9016` | Native select, checkbox, radio, date, time or datetime-local input | Use the design-system control. Native controls are allowed in `src/web/design-system/**`. |
|
|
12331
|
+
| `WL9017` | DOM input/textarea/select native validation | Use shared schema validation instead of `required`, `pattern`, `min`, `max`, `minLength`, `maxLength` or input `type="email"`. Custom component props are allowed. |
|
|
12332
|
+
| `WL9018` | Exported `RequestHandler` in `src/app/**/controllers` lacks `responseSchema` | Assign `handler.responseSchema` to document each response status. |
|
|
12333
|
+
| `WL9019` | Browser route call (`apiHref`, `useSubmitForm({ route })`) takes a run-time name | Pass a string literal or a template with a static prefix, or declare the names with `config.apiRoutes` in a page setup file. |
|
|
12334
|
+
| `WL9020` | Browser code names a route registered with `clientVisible: false` (error) | Call the endpoint from the server, or remove `clientVisible: false` from the route. |
|
|
12335
|
+
| `WL9104` | Translation key computed at runtime in `src/web` (the page ships every group) | Declare the keys with `config.translations` in the page's setup file, for example `["statuses.*"]`. |
|
|
12225
12336
|
|
|
12226
12337
|
Web rules apply to `src/web/**/*.tsx`. Components are top-level JSX-returning functions
|
|
12227
12338
|
with a capitalized name (or an anonymous default export). Hook files and ordinary helpers
|
package/package.json
CHANGED
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"@mongez/slug": "^1.0.7",
|
|
26
26
|
"@mongez/supportive-is": "^3.0.0",
|
|
27
27
|
"@mongez/time-wizard": "^1.0.6",
|
|
28
|
-
"@warlock.js/cache": "5.
|
|
29
|
-
"@warlock.js/cascade": "5.
|
|
30
|
-
"@warlock.js/context": "5.
|
|
31
|
-
"@warlock.js/fs": "5.
|
|
32
|
-
"@warlock.js/logger": "5.
|
|
33
|
-
"@warlock.js/seal": "5.
|
|
28
|
+
"@warlock.js/cache": "5.36.0",
|
|
29
|
+
"@warlock.js/cascade": "5.36.0",
|
|
30
|
+
"@warlock.js/context": "5.36.0",
|
|
31
|
+
"@warlock.js/fs": "5.36.0",
|
|
32
|
+
"@warlock.js/logger": "5.36.0",
|
|
33
|
+
"@warlock.js/seal": "5.36.0",
|
|
34
34
|
"bcryptjs": "^3.0.3",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"dayjs": "^1.11.19",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"@aws-sdk/lib-storage": "^3.955.0",
|
|
57
57
|
"@aws-sdk/s3-request-presigner": "^3.955.0",
|
|
58
58
|
"@react-email/render": "^2.0.5",
|
|
59
|
-
"@warlock.js/access": "5.
|
|
60
|
-
"@warlock.js/ai": "5.
|
|
61
|
-
"@warlock.js/ai-tools": "5.
|
|
62
|
-
"@warlock.js/devtools": "5.
|
|
63
|
-
"@warlock.js/herald": "5.
|
|
64
|
-
"@warlock.js/notifications": "5.
|
|
59
|
+
"@warlock.js/access": "5.36.0",
|
|
60
|
+
"@warlock.js/ai": "5.36.0",
|
|
61
|
+
"@warlock.js/ai-tools": "5.36.0",
|
|
62
|
+
"@warlock.js/devtools": "5.36.0",
|
|
63
|
+
"@warlock.js/herald": "5.36.0",
|
|
64
|
+
"@warlock.js/notifications": "5.36.0",
|
|
65
65
|
"nodemailer": "^8.0.5",
|
|
66
66
|
"react": "^19.2.3",
|
|
67
67
|
"react-dom": "^19.2.3",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"engines": {
|
|
147
147
|
"node": ">=22.15"
|
|
148
148
|
},
|
|
149
|
-
"version": "5.
|
|
149
|
+
"version": "5.36.0",
|
|
150
150
|
"type": "module",
|
|
151
151
|
"main": "./esm/index.mjs",
|
|
152
152
|
"module": "./esm/index.mjs",
|
|
@@ -279,3 +279,16 @@ router.get("/webhooks/status", webhookStatusController, { rateLimit: false });
|
|
|
279
279
|
- The `create-controller` topic — the handler shape, validation, request/response surface.
|
|
280
280
|
- The `send-response` topic — picking the right `response.<helper>()`.
|
|
281
281
|
- The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
|
|
282
|
+
|
|
283
|
+
## A named route the browser must never call — `clientVisible: false`
|
|
284
|
+
|
|
285
|
+
A named API route is embedded in a page's browser route table only when that page's client code reaches its name. Mark an endpoint that browser code must never reach with `clientVisible: false`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
router.post("/internal/reindex", reindexController, {
|
|
289
|
+
name: "internal.reindex",
|
|
290
|
+
clientVisible: false,
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
It is then never embedded in a page, and `warlock check` reports a browser reference to its name as `WL9020` (an error). The server still authorizes the call as for any route.
|
|
@@ -454,7 +454,7 @@ and has no Warlock setup or HTTP global setup. `integration` discovers
|
|
|
454
454
|
It uses the framework setup, HTTP global setup and a generated `src/test-runner.ts`.
|
|
455
455
|
Keep pure service specs beside their source and mock repositories at the boundary.
|
|
456
456
|
Existing config files are preserved; copy the projects layout into them manually.
|
|
457
|
-
Vitest
|
|
457
|
+
Vitest 5 or newer is required for the generated runner (Vitest 5 removed `vitest/runners`; the runner extends `TestRunner` from `vitest`).
|
|
458
458
|
|
|
459
459
|
For a unit spec that needs configuration values, use:
|
|
460
460
|
|
|
@@ -502,20 +502,20 @@ Run with `npx vitest run --project integration`. Worker lifetime refuses watch,
|
|
|
502
502
|
threads, isolated module graphs, or a missing runner with a message explaining
|
|
503
503
|
the required settings. Keep `afterAll(teardownTest)` in the setup file: in worker
|
|
504
504
|
mode it marks the end of the file; Vitest's awaited worker cleanup closes the
|
|
505
|
-
runtime once. The runner uses `
|
|
506
|
-
in Vitest
|
|
505
|
+
runtime once. The runner uses `TestRunner.onCleanupWorkerContext`, verified
|
|
506
|
+
in Vitest 5.0.3's `TestRunner` declaration, runner implementation and worker cleanup source.
|
|
507
507
|
For an existing project, generate this runner without overwriting your config:
|
|
508
508
|
|
|
509
509
|
```ts title="src/test-runner.ts"
|
|
510
510
|
import { configureTestWorker } from "@warlock.js/core/tests";
|
|
511
|
-
import {
|
|
511
|
+
import { TestRunner } from "vitest";
|
|
512
512
|
|
|
513
|
-
export default class WarlockTestRunner extends
|
|
514
|
-
public constructor(config: ConstructorParameters<typeof
|
|
513
|
+
export default class WarlockTestRunner extends TestRunner {
|
|
514
|
+
public constructor(config: ConstructorParameters<typeof TestRunner>[0]) {
|
|
515
515
|
super(config);
|
|
516
516
|
configureTestWorker(
|
|
517
517
|
{ pool: this.pool, watch: config.watch, isolate: config.isolate },
|
|
518
|
-
(cleanup) => this.onCleanupWorkerContext(cleanup),
|
|
518
|
+
(cleanup) => this.onCleanupWorkerContext?.(cleanup),
|
|
519
519
|
);
|
|
520
520
|
}
|
|
521
521
|
}
|
|
@@ -534,7 +534,7 @@ This trades module isolation for startup cost: application singletons, config
|
|
|
534
534
|
mutations, mocks and module side effects can survive between files. Reset your
|
|
535
535
|
own state in `resetBetweenFiles`, restore mocks, and keep connector selection
|
|
536
536
|
consistent. Give each worker a distinct database/schema using
|
|
537
|
-
`process.env.VITEST_POOL_ID` when preparing test connections. Never drop a shared
|
|
537
|
+
`process.env.VITEST_POOL_ID` (1-based since Vitest 5) when preparing test connections. Never drop a shared
|
|
538
538
|
database from this hook while other workers are using it. The framework does not
|
|
539
539
|
create a memory replica set or a cross-worker lock; those belong to the application's
|
|
540
540
|
global setup. A shared HTTP server and a single replica set can contend on I/O,
|
|
@@ -552,3 +552,94 @@ Root components and providers execute in both realms. Move server-only work into
|
|
|
552
552
|
one `#vessel` around `{children}`. Client navigation still swaps layout/page
|
|
553
553
|
children while preserving the document root. Remove temporary explicit form IDs
|
|
554
554
|
only when the application does not otherwise depend on those names.
|
|
555
|
+
|
|
556
|
+
## 5.35 to 5.36 per-page API routes
|
|
557
|
+
|
|
558
|
+
A page document now carries only the named API routes its client code can reach. Before 5.36 every page carried every named route, so a browser call to a name the build cannot see worked on every page by accident. Check each `apiHref()` and `useSubmitForm({ route })` call:
|
|
559
|
+
|
|
560
|
+
- A name written as a string literal, a constant or a template with a static prefix is found automatically.
|
|
561
|
+
- A name computed at run time, or taken from a module the build cannot follow, now throws on the page that calls it. Declare the names in that page's setup file, for example `export const config = { apiRoutes: ["orders.*"] };`, a layout or root `config` covers every page that renders it. Or call the endpoint with an explicit `path` and `method`.
|
|
562
|
+
- `npx warlock check` reports the call sites that need a declaration as `WL9019`.
|
|
563
|
+
|
|
564
|
+
Routes registered with `clientVisible: false` are never embedded, and a browser call to one is an error (`WL9020`). Keep endpoints the browser must not reach on that option. Authorization is unchanged: the server still decides each request.
|
|
565
|
+
|
|
566
|
+
To measure a page's inline payload, set `build.budgets.payload` (default `64kb`). An over-budget route logs one line per process and never fails a request.
|
|
567
|
+
|
|
568
|
+
## 5.35 to 5.36 translation payloads
|
|
569
|
+
|
|
570
|
+
A page's hydration payload now carries only the translations its client code
|
|
571
|
+
can reach, in route-dictionary (`locales.json`) and global-registry (`extend()`)
|
|
572
|
+
apps alike. The groups every page uses (default `app` and `common`) arrive once
|
|
573
|
+
per locale in a hashed, cached file that the page preloads. Nothing in a page's
|
|
574
|
+
render changes: server output is the full dictionary, as before.
|
|
575
|
+
|
|
576
|
+
Run `warlock build`, then `warlock check`. A `WL9104` warning marks a page that
|
|
577
|
+
renders a translation key computed at runtime. Declare the keys it renders in
|
|
578
|
+
the page's setup file, for example
|
|
579
|
+
`export const config = { translations: ["statuses.*", "members.title"] }`, so the
|
|
580
|
+
page ships only what it uses.
|
|
581
|
+
|
|
582
|
+
In development, a warning names a key the page reads but its payload lacks, and
|
|
583
|
+
resolves it from the full table so the page keeps working. In production the
|
|
584
|
+
key renders as its own text and the miss is reported once through
|
|
585
|
+
`onClientError` with `kind: "translations"`. Declare the key, or set
|
|
586
|
+
`build: { translations: { prune: false } }` in the web connector options while migrating to
|
|
587
|
+
restore the previous per-page payload.
|
|
588
|
+
|
|
589
|
+
Global-registry apps are covered too. Their shared table is served at
|
|
590
|
+
`/__warlock/translations/<locale>.<hash>.json`, which is cached immutable in
|
|
591
|
+
production. A direct `trans()` call from `@mongez/localization` is not checked
|
|
592
|
+
for a pruning miss; the calls through `useTrans()` are.
|
|
593
|
+
|
|
594
|
+
## 5.35 to 5.36 Vite 8 and Vitest 5
|
|
595
|
+
|
|
596
|
+
`@warlock.js/web` requires Vite 8 (`^8.0.0`) and `@vitejs/plugin-react` 6, and the
|
|
597
|
+
test kit requires Vitest 5. Vite 8 bundles with Rolldown and transforms
|
|
598
|
+
TypeScript with Oxc. Vite 7 is not supported; plugin-react 6 needs Vite 8, so the
|
|
599
|
+
family moves together. Vite 8 needs Node.js 20.19 or 22.12 and newer, Vitest 5
|
|
600
|
+
needs 22.12 or newer; Warlock already requires 22.15.
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
npx warlock upgrade --from 5.35 --to 5.36.0 --dry-run
|
|
604
|
+
npx warlock upgrade --from 5.35 --to 5.36.0
|
|
605
|
+
npm install
|
|
606
|
+
npx warlock check
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
The `vite-8-vitest-5` codemod:
|
|
610
|
+
|
|
611
|
+
- raises `vite` (8.3.3), `vitest` and `@vitest/*` (5.0.3) and `@vitejs/plugin-react`
|
|
612
|
+
(6.1.2) in `dependencies`, `devDependencies`, `overrides` and `resolutions`,
|
|
613
|
+
keeping `^`, `~` or exact style;
|
|
614
|
+
- rewrites a generated `src/test-runner.ts` from `VitestTestRunner` in
|
|
615
|
+
`vitest/runners` to `TestRunner` from `vitest`, and calls
|
|
616
|
+
`this.onCleanupWorkerContext?.(cleanup)` (the hook is absent in vm pools);
|
|
617
|
+
- lists, without editing, imports of the removed `vitest/coverage`,
|
|
618
|
+
`vitest/reporters`, `vitest/environments`, `vitest/snapshot`, `vitest/suite`,
|
|
619
|
+
`vitest/mocker` and `vitest/internal/module-runner` entry points, other names
|
|
620
|
+
imported from `vitest/runners`, a top-level `esbuild` option in a Vite or
|
|
621
|
+
Vitest config, and object-form `output.manualChunks`.
|
|
622
|
+
|
|
623
|
+
Do these by hand when the codemod lists them:
|
|
624
|
+
|
|
625
|
+
- Move a config's `esbuild: { jsx: "automatic" }` to
|
|
626
|
+
`oxc: { jsx: { runtime: "automatic" } }`. `esbuild.target` is not converted, and
|
|
627
|
+
Oxc does not lower native decorators: keep `lowerStage3Decorators()` from
|
|
628
|
+
`@warlock.js/core/vite` first in `plugins` for decorated modules, as in the
|
|
629
|
+
generated test config.
|
|
630
|
+
- Replace object `manualChunks` with
|
|
631
|
+
`build.rolldownOptions.output.codeSplitting.groups`; `build.rollupOptions` still
|
|
632
|
+
works but is deprecated in favour of `build.rolldownOptions`.
|
|
633
|
+
- A custom bundler `external` list that names Node builtins is still refused by the
|
|
634
|
+
client build.
|
|
635
|
+
|
|
636
|
+
Vitest 5 changes defaults that can alter a green suite: `clearMocks` is true,
|
|
637
|
+
`json` and `junit` reporters write to `.vitest/` instead of stdout, `-t` joins a
|
|
638
|
+
test's names with `>`, `VITEST_POOL_ID` and `VITEST_WORKER_ID` start at 1, nested
|
|
639
|
+
`vi.mock`/`vi.hoisted` calls throw, and an unawaited `resolves` or `rejects`
|
|
640
|
+
assertion fails the test. Vitest also no longer searches parent directories for a
|
|
641
|
+
config file; run it from the package that owns the config. Official guides:
|
|
642
|
+
https://vite.dev/guide/migration and https://vitest.dev/guide/migration.
|
|
643
|
+
|
|
644
|
+
A project that uses `@warlock.js/ai`'s custom matchers needs Vitest 5 for their
|
|
645
|
+
TypeScript types.
|