@decocms/blocks-cli 7.21.0 → 7.22.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/package.json +4 -3
- package/scripts/lib/upgrade-6-to-7.test.ts +145 -0
- package/scripts/lib/upgrade-6-to-7.ts +157 -0
- package/scripts/migrate/analyzers/tailwind-config.test.ts +149 -0
- package/scripts/migrate/analyzers/tailwind-config.ts +283 -0
- package/scripts/migrate/css-compile-check.test.ts +115 -0
- package/scripts/migrate/css-compile-check.ts +137 -0
- package/scripts/migrate/phase-analyze.ts +13 -0
- package/scripts/migrate/phase-cleanup.test.ts +1 -0
- package/scripts/migrate/phase-compile.test.ts +48 -0
- package/scripts/migrate/phase-compile.ts +38 -0
- package/scripts/migrate/phase-report.test.ts +103 -0
- package/scripts/migrate/phase-report.ts +64 -4
- package/scripts/migrate/phase-verify-legacy-specifiers.test.ts +134 -0
- package/scripts/migrate/phase-verify.test.ts +1 -0
- package/scripts/migrate/phase-verify.ts +24 -0
- package/scripts/migrate/post-cleanup/migration-artifacts.test.ts +262 -0
- package/scripts/migrate/post-cleanup/rules.ts +321 -0
- package/scripts/migrate/post-cleanup/runner.test.ts +2 -0
- package/scripts/migrate/templates/app-css.test.ts +150 -0
- package/scripts/migrate/templates/app-css.ts +142 -52
- package/scripts/migrate/templates/hooks.test.ts +1 -0
- package/scripts/migrate/templates/package-json.ts +5 -0
- package/scripts/migrate/templates/server-entry-vtex-invoke.test.ts +51 -0
- package/scripts/migrate/templates/server-entry.ts +53 -1
- package/scripts/migrate/transforms/color-oklch.test.ts +83 -0
- package/scripts/migrate/transforms/color-oklch.ts +122 -0
- package/scripts/migrate/transforms/css.test.ts +109 -0
- package/scripts/migrate/transforms/css.ts +186 -0
- package/scripts/migrate/transforms/tailwind-renames.test.ts +37 -0
- package/scripts/migrate/transforms/tailwind-renames.ts +224 -0
- package/scripts/migrate/transforms/tailwind.test.ts +74 -0
- package/scripts/migrate/transforms/tailwind.ts +27 -123
- package/scripts/migrate/types.ts +18 -0
- package/scripts/tailwind-lint.ts +9 -54
- package/scripts/upgrade-6-to-7.test.ts +80 -0
- package/scripts/upgrade-6-to-7.ts +169 -0
|
@@ -25,6 +25,7 @@ import { execSync } from "node:child_process";
|
|
|
25
25
|
import * as fs from "node:fs";
|
|
26
26
|
import * as path from "node:path";
|
|
27
27
|
import { gray, green, red, yellow } from "./colors";
|
|
28
|
+
import { checkCssCompiles } from "./css-compile-check";
|
|
28
29
|
import type { MigrationContext } from "./types";
|
|
29
30
|
import { logPhase } from "./types";
|
|
30
31
|
|
|
@@ -48,6 +49,15 @@ export interface CompileResult {
|
|
|
48
49
|
skipped: boolean;
|
|
49
50
|
typecheck: { ran: boolean; passed: boolean; output?: string };
|
|
50
51
|
build: { ran: boolean; passed: boolean; output?: string };
|
|
52
|
+
/**
|
|
53
|
+
* Real Tailwind v4 compilation of src/styles/app.css via the site's own
|
|
54
|
+
* `@tailwindcss/cli` install. Catches the exact failure mode of
|
|
55
|
+
* decocms/blocks#369 / skill gotcha #48 (dropped custom theme tokens ->
|
|
56
|
+
* "Cannot apply unknown utility class") during migration instead of at
|
|
57
|
+
* runtime. `unmatchedClassWarnings` is informational only — it never
|
|
58
|
+
* fails the build (see css-compile-check.ts for why).
|
|
59
|
+
*/
|
|
60
|
+
css: { ran: boolean; passed: boolean; output?: string; unmatchedClassWarnings: string[] };
|
|
51
61
|
/** True iff compile decided the migration should fail (only in strict mode). */
|
|
52
62
|
shouldFail: boolean;
|
|
53
63
|
}
|
|
@@ -96,6 +106,7 @@ export function compile(
|
|
|
96
106
|
skipped: false,
|
|
97
107
|
typecheck: { ran: false, passed: false },
|
|
98
108
|
build: { ran: false, passed: false },
|
|
109
|
+
css: { ran: false, passed: false, unmatchedClassWarnings: [] },
|
|
99
110
|
shouldFail: false,
|
|
100
111
|
};
|
|
101
112
|
|
|
@@ -136,6 +147,32 @@ export function compile(
|
|
|
136
147
|
console.log("");
|
|
137
148
|
}
|
|
138
149
|
|
|
150
|
+
// CSS compile check — real Tailwind v4 build of src/styles/app.css.
|
|
151
|
+
console.log(" Running: CSS compile check (npx @tailwindcss/cli)...");
|
|
152
|
+
const cssResult = checkCssCompiles(ctx, runner);
|
|
153
|
+
result.css.ran = cssResult.ran;
|
|
154
|
+
result.css.passed = cssResult.passed;
|
|
155
|
+
result.css.unmatchedClassWarnings = cssResult.unmatchedClassWarnings;
|
|
156
|
+
|
|
157
|
+
if (!cssResult.ran) {
|
|
158
|
+
console.log(` ${gray("–")} Skipped (no src/styles/app.css)`);
|
|
159
|
+
} else if (cssResult.passed) {
|
|
160
|
+
console.log(` ${green("✓")} CSS compiled`);
|
|
161
|
+
if (cssResult.unmatchedClassWarnings.length > 0) {
|
|
162
|
+
console.log(
|
|
163
|
+
` ${yellow("⚠")} ${cssResult.unmatchedClassWarnings.length} class(es) used in src/ produced no CSS output (dropped theme token or typo?):`,
|
|
164
|
+
);
|
|
165
|
+
console.log(` ${cssResult.unmatchedClassWarnings.slice(0, 20).join(", ")}`);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
result.css.output = cssResult.output;
|
|
169
|
+
const output = truncate(cssResult.output ?? "", MAX_OUTPUT_LINES);
|
|
170
|
+
const icon = opts.strict ? red("✗") : yellow("⚠");
|
|
171
|
+
console.log(` ${icon} CSS compile failed:\n`);
|
|
172
|
+
console.log(output);
|
|
173
|
+
console.log("");
|
|
174
|
+
}
|
|
175
|
+
|
|
139
176
|
// Optional Vite build (heavier, opt-in).
|
|
140
177
|
if (opts.withBuild) {
|
|
141
178
|
console.log(" Running: Vite build (npx vite build)...");
|
|
@@ -158,6 +195,7 @@ export function compile(
|
|
|
158
195
|
// Decide whether to fail the migration.
|
|
159
196
|
const anyFailed =
|
|
160
197
|
(result.typecheck.ran && !result.typecheck.passed) ||
|
|
198
|
+
(result.css.ran && !result.css.passed) ||
|
|
161
199
|
(result.build.ran && !result.build.passed);
|
|
162
200
|
|
|
163
201
|
if (anyFailed && opts.strict) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { report } from "./phase-report";
|
|
6
|
+
import { createContext } from "./types";
|
|
7
|
+
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "phase-report-test-"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("report — CSS Migration section", () => {
|
|
19
|
+
it("summarizes tokens ported from tailwind.config.ts", () => {
|
|
20
|
+
const ctx = createContext(tmpDir);
|
|
21
|
+
ctx.tailwindConfig = {
|
|
22
|
+
colors: { perola: "#F5F0E8", "brand-500": "#B10200" },
|
|
23
|
+
fontFamily: { "bebas-neue": "Bebas Neue, sans-serif" },
|
|
24
|
+
screens: {},
|
|
25
|
+
safelist: ["bg-red-500"],
|
|
26
|
+
safelistPatterns: [],
|
|
27
|
+
plugins: [],
|
|
28
|
+
reviewItems: [],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
report(ctx);
|
|
32
|
+
|
|
33
|
+
const content = fs.readFileSync(path.join(tmpDir, "MIGRATION_REPORT.md"), "utf-8");
|
|
34
|
+
expect(content).toContain("## CSS Migration");
|
|
35
|
+
expect(content).toContain("2 color(s), 1 font stack(s), 0 breakpoint(s)");
|
|
36
|
+
expect(content).toContain("1 safelist entrie(s)");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("lists CSS-specific manual review items separately from unrelated ones", () => {
|
|
40
|
+
const ctx = createContext(tmpDir);
|
|
41
|
+
ctx.manualReviewItems = [
|
|
42
|
+
{
|
|
43
|
+
file: "src/styles/app.css",
|
|
44
|
+
reason: "safelist pattern /^grid-cols-/ cannot be auto-converted",
|
|
45
|
+
severity: "warning",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
file: "src/components/Foo.tsx",
|
|
49
|
+
reason: "useComponent(...) call site detected — needs manual React conversion",
|
|
50
|
+
severity: "error",
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
report(ctx);
|
|
55
|
+
|
|
56
|
+
const content = fs.readFileSync(path.join(tmpDir, "MIGRATION_REPORT.md"), "utf-8");
|
|
57
|
+
const cssSection = content.split("## CSS Migration")[1].split("## Always Check")[0];
|
|
58
|
+
expect(cssSection).toContain("safelist pattern");
|
|
59
|
+
expect(cssSection).not.toContain("useComponent");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("reports no CSS findings when none exist", () => {
|
|
63
|
+
const ctx = createContext(tmpDir);
|
|
64
|
+
report(ctx);
|
|
65
|
+
const content = fs.readFileSync(path.join(tmpDir, "MIGRATION_REPORT.md"), "utf-8");
|
|
66
|
+
expect(content).toContain("No CSS-specific findings from this run.");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("does not miscategorize an unrelated finding whose reason merely contains 'collapse' as a substring of another word", () => {
|
|
70
|
+
const ctx = createContext(tmpDir);
|
|
71
|
+
ctx.manualReviewItems = [
|
|
72
|
+
{
|
|
73
|
+
file: "src/components/Nav.tsx",
|
|
74
|
+
reason: "the mobile nav state collapsed unexpectedly during the smoke test — needs manual QA",
|
|
75
|
+
severity: "warning",
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
report(ctx);
|
|
80
|
+
|
|
81
|
+
const content = fs.readFileSync(path.join(tmpDir, "MIGRATION_REPORT.md"), "utf-8");
|
|
82
|
+
const cssSection = content.split("## CSS Migration")[1].split("## Always Check")[0];
|
|
83
|
+
expect(cssSection).toContain("No CSS-specific findings from this run.");
|
|
84
|
+
expect(cssSection).not.toContain("collapsed unexpectedly");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("still categorizes a genuine DaisyUI collapse finding as CSS-specific", () => {
|
|
88
|
+
const ctx = createContext(tmpDir);
|
|
89
|
+
ctx.manualReviewItems = [
|
|
90
|
+
{
|
|
91
|
+
file: "src/components/Filters.tsx",
|
|
92
|
+
reason: "DaisyUI .collapse usage found — its expand/collapse chain breaks under Tailwind v4.",
|
|
93
|
+
severity: "warning",
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
report(ctx);
|
|
98
|
+
|
|
99
|
+
const content = fs.readFileSync(path.join(tmpDir, "MIGRATION_REPORT.md"), "utf-8");
|
|
100
|
+
const cssSection = content.split("## CSS Migration")[1].split("## Always Check")[0];
|
|
101
|
+
expect(cssSection).toContain("DaisyUI .collapse usage found");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import type { MigrationContext } from "./types";
|
|
3
|
+
import type { MigrationContext, ReviewItem } from "./types";
|
|
4
4
|
import { logPhase } from "./types";
|
|
5
5
|
|
|
6
|
+
// Word-only keywords are matched with \b boundaries so a generic English
|
|
7
|
+
// word like "collapse" doesn't misfire on an unrelated finding's reason
|
|
8
|
+
// text (e.g. a JS/UX note that happens to mention "collapse" in a
|
|
9
|
+
// non-DaisyUI sense). Path-/symbol-like keywords (containing non-word
|
|
10
|
+
// characters) are matched as plain substrings since \b doesn't apply to them.
|
|
11
|
+
const CSS_REVIEW_KEYWORDS = [
|
|
12
|
+
"tailwind.config.ts",
|
|
13
|
+
"safelist",
|
|
14
|
+
"oklch",
|
|
15
|
+
"css-styling.md",
|
|
16
|
+
"@theme",
|
|
17
|
+
"@apply",
|
|
18
|
+
"@utility",
|
|
19
|
+
"DaisyUI",
|
|
20
|
+
"collapse",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const CSS_REVIEW_KEYWORD_MATCHERS = CSS_REVIEW_KEYWORDS.map((k) =>
|
|
24
|
+
/^\w+$/.test(k) ? new RegExp(`\\b${k}\\b`, "i") : k,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function isCssReviewItem(item: ReviewItem): boolean {
|
|
28
|
+
return (
|
|
29
|
+
item.file.endsWith("app.css") ||
|
|
30
|
+
item.file === "tailwind.config.ts" ||
|
|
31
|
+
CSS_REVIEW_KEYWORD_MATCHERS.some((k) =>
|
|
32
|
+
typeof k === "string" ? item.reason.includes(k) : k.test(item.reason),
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
6
37
|
const FRAMEWORK_FINDINGS = [
|
|
7
38
|
"Session/analytics SDK is boilerplate duplicated across all sites — should be a single framework function",
|
|
8
39
|
"GTM event system (useGTMEvent, data-gtm-* listeners) is universal pattern — should be in @decocms/blocks",
|
|
@@ -129,6 +160,37 @@ export function report(ctx: MigrationContext): void {
|
|
|
129
160
|
lines.push("");
|
|
130
161
|
}
|
|
131
162
|
|
|
163
|
+
// CSS Migration — replaces the old generic "DaisyUI/Tailwind" checkboxes
|
|
164
|
+
// with what was actually ported/detected for THIS site, so a human
|
|
165
|
+
// doesn't have to re-derive it from scratch.
|
|
166
|
+
lines.push("## CSS Migration");
|
|
167
|
+
lines.push("");
|
|
168
|
+
const twColorCount = Object.keys(ctx.tailwindConfig.colors).length;
|
|
169
|
+
const twFontCount = Object.keys(ctx.tailwindConfig.fontFamily).length;
|
|
170
|
+
const twScreenCount = Object.keys(ctx.tailwindConfig.screens).length;
|
|
171
|
+
const twSafelistCount = ctx.tailwindConfig.safelist.length;
|
|
172
|
+
lines.push(
|
|
173
|
+
`- **tailwind.config.ts porting**: ${twColorCount} color(s), ${twFontCount} font stack(s), ${twScreenCount} breakpoint(s) ported into \`src/styles/app.css\` \`@theme\`; ${twSafelistCount} safelist entrie(s) ported via \`@source inline(...)\`.`,
|
|
174
|
+
);
|
|
175
|
+
lines.push(
|
|
176
|
+
"- **Real CSS compile check**: runs in the compile phase (`npx @tailwindcss/cli`) against `src/styles/app.css` — catches unknown-utility-class errors before they ship. Re-run any time with `bun run tailwind:lint`.",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const cssReviewItems = ctx.manualReviewItems.filter(isCssReviewItem);
|
|
180
|
+
if (cssReviewItems.length > 0) {
|
|
181
|
+
lines.push("");
|
|
182
|
+
lines.push(`**${cssReviewItems.length} CSS-specific finding(s) requiring manual review:**`);
|
|
183
|
+
lines.push("");
|
|
184
|
+
for (const item of cssReviewItems) {
|
|
185
|
+
const icon = item.severity === "error" ? "🔴" : item.severity === "warning" ? "🟡" : "🔵";
|
|
186
|
+
lines.push(`${icon} **\`${item.file}\`**: ${item.reason}`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push("- No CSS-specific findings from this run.");
|
|
191
|
+
}
|
|
192
|
+
lines.push("");
|
|
193
|
+
|
|
132
194
|
// Always-present manual review items
|
|
133
195
|
lines.push("## Always Check (site-specific)");
|
|
134
196
|
lines.push("");
|
|
@@ -136,9 +198,7 @@ export function report(ctx: MigrationContext): void {
|
|
|
136
198
|
lines.push("- [ ] `src/setup/section-loaders.ts` — verify extracted loaders work correctly");
|
|
137
199
|
lines.push("- [ ] `src/hooks/useCart.ts` — wire to actual server functions for your platform");
|
|
138
200
|
lines.push("- [ ] `src/worker-entry.ts` — verify CSP, proxy, and segment builder");
|
|
139
|
-
lines.push("- [ ] DaisyUI v4
|
|
140
|
-
lines.push("- [ ] Tailwind v3 to v4: verify all utility classes still work");
|
|
141
|
-
lines.push("- [ ] Check `src/styles/app.css` theme colors match the original design");
|
|
201
|
+
lines.push("- [ ] See the **CSS Migration** section above for DaisyUI v4→v5 / Tailwind v3→v4 findings specific to this site");
|
|
142
202
|
lines.push("- [ ] Run `npm run generate:blocks` and `npm run generate:schema` after migration");
|
|
143
203
|
lines.push("");
|
|
144
204
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { checks } from "./phase-verify";
|
|
6
|
+
import type { MigrationContext } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* #367: blocks-cli must never leave the frozen pre-7.x-split package
|
|
10
|
+
* specifiers (@decocms/start@6.30.0, @decocms/apps@5.4.0) in a migrated
|
|
11
|
+
* site's src/ — including via a transform hand-off marker (jsx.ts emits
|
|
12
|
+
* `@decocms/start/hooks` as a same-run marker that phase-cleanup.ts is
|
|
13
|
+
* supposed to resolve to `@decocms/blocks/hooks`) that leaks past cleanup
|
|
14
|
+
* if the pipeline order ever changes.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function makeCtx(sourceDir: string): MigrationContext {
|
|
18
|
+
return {
|
|
19
|
+
sourceDir,
|
|
20
|
+
siteName: "test-site",
|
|
21
|
+
platform: "custom",
|
|
22
|
+
vtexAccount: null,
|
|
23
|
+
gtmId: null,
|
|
24
|
+
importMap: {},
|
|
25
|
+
discoveredNpmDeps: {},
|
|
26
|
+
themeColors: {},
|
|
27
|
+
fontFamily: null,
|
|
28
|
+
files: [],
|
|
29
|
+
sectionMetas: [],
|
|
30
|
+
islandClassifications: [],
|
|
31
|
+
islandWrapperTargets: new Map(),
|
|
32
|
+
loaderInventory: [],
|
|
33
|
+
scaffoldedFiles: [],
|
|
34
|
+
transformedFiles: [],
|
|
35
|
+
deletedFiles: [],
|
|
36
|
+
movedFiles: [],
|
|
37
|
+
manualReviewItems: [],
|
|
38
|
+
frameworkFindings: [],
|
|
39
|
+
dryRun: false,
|
|
40
|
+
verbose: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const legacySpecifierCheck = checks.find(
|
|
45
|
+
(c) => c.name === "No frozen pre-split package specifiers in src/ (@decocms/start, @decocms/apps/*)",
|
|
46
|
+
);
|
|
47
|
+
if (!legacySpecifierCheck) {
|
|
48
|
+
throw new Error("verify check not found — name changed?");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function runCheck(ctx: MigrationContext): { ok: boolean; output: string } {
|
|
52
|
+
const lines: string[] = [];
|
|
53
|
+
const spy = vi.spyOn(console, "log").mockImplementation((...args: unknown[]) => {
|
|
54
|
+
lines.push(args.map((a) => String(a)).join(" "));
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
const ok = legacySpecifierCheck!.fn(ctx);
|
|
58
|
+
return { ok, output: lines.join("\n") };
|
|
59
|
+
} finally {
|
|
60
|
+
spy.mockRestore();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("verify check: 'No frozen pre-split package specifiers in src/' (#367)", () => {
|
|
65
|
+
let tmp: string;
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "verify-legacy-specifiers-"));
|
|
69
|
+
fs.mkdirSync(path.join(tmp, "src", "hooks"), { recursive: true });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("passes when src/ only imports from the current split packages", () => {
|
|
77
|
+
fs.writeFileSync(
|
|
78
|
+
path.join(tmp, "src", "hooks", "useUser.ts"),
|
|
79
|
+
`import { SectionRenderer } from "@decocms/blocks/hooks";\n` +
|
|
80
|
+
`import { invoke } from "@decocms/blocks/sdk/invoke";\n` +
|
|
81
|
+
`export const x = 1;\n`,
|
|
82
|
+
);
|
|
83
|
+
const { ok, output } = runCheck(makeCtx(tmp));
|
|
84
|
+
expect(ok).toBe(true);
|
|
85
|
+
expect(output).toBe("");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("fails when a `@decocms/start/hooks` hand-off marker leaked past cleanup", () => {
|
|
89
|
+
fs.writeFileSync(
|
|
90
|
+
path.join(tmp, "src", "hooks", "useUser.ts"),
|
|
91
|
+
`import { SectionRenderer } from "@decocms/start/hooks";\nexport const x = 1;\n`,
|
|
92
|
+
);
|
|
93
|
+
const { ok, output } = runCheck(makeCtx(tmp));
|
|
94
|
+
expect(ok).toBe(false);
|
|
95
|
+
expect(output).toContain("@decocms/start");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("fails on a bare `@decocms/start` import (no subpath)", () => {
|
|
99
|
+
fs.writeFileSync(
|
|
100
|
+
path.join(tmp, "src", "hooks", "setup.ts"),
|
|
101
|
+
`import "@decocms/start";\nexport const x = 1;\n`,
|
|
102
|
+
);
|
|
103
|
+
const { ok } = runCheck(makeCtx(tmp));
|
|
104
|
+
expect(ok).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("fails on a `@decocms/apps/vtex/...` monolith subpath import", () => {
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
path.join(tmp, "src", "hooks", "old.ts"),
|
|
110
|
+
`import { getUser } from "@decocms/apps/vtex/loaders/user";\nexport const x = 1;\n`,
|
|
111
|
+
);
|
|
112
|
+
const { ok, output } = runCheck(makeCtx(tmp));
|
|
113
|
+
expect(ok).toBe(false);
|
|
114
|
+
expect(output).toContain("@decocms/apps/");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("does not flag the current @decocms/apps-vtex split package", () => {
|
|
118
|
+
fs.writeFileSync(
|
|
119
|
+
path.join(tmp, "src", "hooks", "useUser.ts"),
|
|
120
|
+
`import { getUser } from "@decocms/apps-vtex/loaders/user";\nexport const x = 1;\n`,
|
|
121
|
+
);
|
|
122
|
+
const { ok } = runCheck(makeCtx(tmp));
|
|
123
|
+
expect(ok).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("ignores commented-out references", () => {
|
|
127
|
+
fs.writeFileSync(
|
|
128
|
+
path.join(tmp, "src", "hooks", "Docs.ts"),
|
|
129
|
+
`// import { x } from "@decocms/start/hooks"\nexport const x = 1;\n`,
|
|
130
|
+
);
|
|
131
|
+
const { ok } = runCheck(makeCtx(tmp));
|
|
132
|
+
expect(ok).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -28,6 +28,7 @@ function makeCtx(sourceDir: string): MigrationContext {
|
|
|
28
28
|
importMap: {},
|
|
29
29
|
discoveredNpmDeps: {},
|
|
30
30
|
themeColors: {},
|
|
31
|
+
tailwindConfig: { colors: {}, fontFamily: {}, screens: {}, safelist: [], safelistPatterns: [], plugins: [], reviewItems: [] },
|
|
31
32
|
fontFamily: null,
|
|
32
33
|
files: [],
|
|
33
34
|
sectionMetas: [],
|
|
@@ -410,6 +410,30 @@ export const checks: Check[] = [
|
|
|
410
410
|
return true;
|
|
411
411
|
},
|
|
412
412
|
},
|
|
413
|
+
{
|
|
414
|
+
// Fleet-wide regression guard, complementing the scaffolder-only check
|
|
415
|
+
// in templates/no-legacy-packages.test.ts (#367): the frozen
|
|
416
|
+
// pre-7.x-split packages (@decocms/start@6.30.0, @decocms/apps@5.4.0)
|
|
417
|
+
// must never appear in a migrated site's src/, even via a transform
|
|
418
|
+
// hand-off marker that leaked past cleanup (see jsx.ts's
|
|
419
|
+
// `@decocms/start/hooks` same-run hand-off to phase-cleanup.ts).
|
|
420
|
+
name: "No frozen pre-split package specifiers in src/ (@decocms/start, @decocms/apps/*)",
|
|
421
|
+
severity: "error",
|
|
422
|
+
fn: (ctx) => {
|
|
423
|
+
const srcDir = path.join(ctx.sourceDir, "src");
|
|
424
|
+
if (!fs.existsSync(srcDir)) return true;
|
|
425
|
+
const bad = [
|
|
426
|
+
...findFilesWithPattern(srcDir, /(?:from|import)\s+["']@decocms\/start(?:\/|["'])/),
|
|
427
|
+
...findFilesWithPattern(srcDir, /from\s+["']@decocms\/apps\//),
|
|
428
|
+
];
|
|
429
|
+
const unique = [...new Set(bad)];
|
|
430
|
+
if (unique.length > 0) {
|
|
431
|
+
console.log(` Still references frozen @decocms/start or @decocms/apps/* specifiers: ${unique.join(", ")}`);
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
},
|
|
436
|
+
},
|
|
413
437
|
{
|
|
414
438
|
name: "Setup infrastructure is complete",
|
|
415
439
|
severity: "error",
|