@decocms/blocks-cli 7.31.6 → 7.32.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/blocks-cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.32.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deco codegen (generate-blocks, generate-schema, generate-invoke) and Fresh-to-TanStack migration tooling",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"lint:unused": "knip"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@decocms/blocks": "7.
|
|
34
|
+
"@decocms/blocks": "7.32.0",
|
|
35
35
|
"ts-morph": "^27.0.0",
|
|
36
36
|
"tsx": "^4.22.5"
|
|
37
37
|
},
|
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { extractPlatform } from "./phase-analyze";
|
|
5
|
+
import { extractGoogleFonts, extractPlatform } from "./phase-analyze";
|
|
6
6
|
|
|
7
7
|
// `extractPlatform` runs four strategies in order:
|
|
8
8
|
// 1. deno.json imports referencing apps/{platform}/
|
|
@@ -16,6 +16,68 @@ import { extractPlatform } from "./phase-analyze";
|
|
|
16
16
|
// list so Strategy 2 fires first and short-circuits before Strategy 3 can
|
|
17
17
|
// over-match.
|
|
18
18
|
|
|
19
|
+
describe("extractGoogleFonts", () => {
|
|
20
|
+
let tmp: string;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "extract-gfonts-"));
|
|
24
|
+
fs.mkdirSync(path.join(tmp, "routes"), { recursive: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("extracts preconnects and stylesheet from _app.tsx", () => {
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
path.join(tmp, "routes", "_app.tsx"),
|
|
34
|
+
`<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
35
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
|
36
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" />`,
|
|
37
|
+
);
|
|
38
|
+
const result = extractGoogleFonts(tmp);
|
|
39
|
+
expect(result.preconnects).toEqual([
|
|
40
|
+
"https://fonts.googleapis.com",
|
|
41
|
+
"https://fonts.gstatic.com",
|
|
42
|
+
]);
|
|
43
|
+
expect(result.stylesheets).toEqual([
|
|
44
|
+
"https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap",
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("deduplicates URLs present in both _app.tsx and _layout.tsx", () => {
|
|
49
|
+
const sharedUrl = "https://fonts.googleapis.com/css2?family=Roboto&display=swap";
|
|
50
|
+
fs.writeFileSync(
|
|
51
|
+
path.join(tmp, "routes", "_app.tsx"),
|
|
52
|
+
`<link rel="stylesheet" href="${sharedUrl}" />`,
|
|
53
|
+
);
|
|
54
|
+
fs.writeFileSync(
|
|
55
|
+
path.join(tmp, "routes", "_layout.tsx"),
|
|
56
|
+
`<link rel="stylesheet" href="${sharedUrl}" />`,
|
|
57
|
+
);
|
|
58
|
+
const result = extractGoogleFonts(tmp);
|
|
59
|
+
expect(result.stylesheets).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("extracts Google Fonts @import from tailwind.css", () => {
|
|
63
|
+
fs.writeFileSync(
|
|
64
|
+
path.join(tmp, "tailwind.css"),
|
|
65
|
+
`@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');`,
|
|
66
|
+
);
|
|
67
|
+
const result = extractGoogleFonts(tmp);
|
|
68
|
+
expect(result.stylesheets).toEqual([
|
|
69
|
+
"https://fonts.googleapis.com/css2?family=Poppins&display=swap",
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns empty arrays when no Google Fonts are declared", () => {
|
|
74
|
+
fs.writeFileSync(path.join(tmp, "routes", "_app.tsx"), `<link rel="icon" href="/favicon.ico" />`);
|
|
75
|
+
const result = extractGoogleFonts(tmp);
|
|
76
|
+
expect(result.preconnects).toHaveLength(0);
|
|
77
|
+
expect(result.stylesheets).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
19
81
|
describe("extractPlatform", () => {
|
|
20
82
|
let tmp: string;
|
|
21
83
|
|
|
@@ -418,6 +418,50 @@ function scanDir(
|
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
export function extractGoogleFonts(
|
|
422
|
+
sourceDir: string,
|
|
423
|
+
): { preconnects: string[]; stylesheets: string[] } {
|
|
424
|
+
const preconnects: string[] = [];
|
|
425
|
+
const stylesheets: string[] = [];
|
|
426
|
+
const seen = new Set<string>();
|
|
427
|
+
|
|
428
|
+
const hrefRe = /href=["'](https:\/\/fonts\.(?:googleapis|gstatic)\.com[^"']*)["']/g;
|
|
429
|
+
|
|
430
|
+
// Read _app.tsx and _layout.tsx for <link> declarations
|
|
431
|
+
for (const candidate of ["routes/_app.tsx", "routes/_layout.tsx"]) {
|
|
432
|
+
const fp = path.join(sourceDir, candidate);
|
|
433
|
+
if (!fs.existsSync(fp)) continue;
|
|
434
|
+
const content = fs.readFileSync(fp, "utf-8");
|
|
435
|
+
for (const m of content.matchAll(hrefRe)) {
|
|
436
|
+
const href = m[1];
|
|
437
|
+
if (seen.has(href)) continue;
|
|
438
|
+
seen.add(href);
|
|
439
|
+
if (href.includes("/css")) {
|
|
440
|
+
stylesheets.push(href);
|
|
441
|
+
} else {
|
|
442
|
+
preconnects.push(href);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Also check tailwind.css for @import url(https://fonts.googleapis.com/...)
|
|
448
|
+
for (const cssCandidate of ["tailwind.css", "styles/app.css", "styles/global.css"]) {
|
|
449
|
+
const fp = path.join(sourceDir, cssCandidate);
|
|
450
|
+
if (!fs.existsSync(fp)) continue;
|
|
451
|
+
const content = fs.readFileSync(fp, "utf-8");
|
|
452
|
+
const importRe = /@import\s+url\(['"]?(https:\/\/fonts\.googleapis\.com[^'")\s]+)/g;
|
|
453
|
+
for (const m of content.matchAll(importRe)) {
|
|
454
|
+
const href = m[1];
|
|
455
|
+
if (!seen.has(href)) {
|
|
456
|
+
seen.add(href);
|
|
457
|
+
stylesheets.push(href);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return { preconnects, stylesheets };
|
|
463
|
+
}
|
|
464
|
+
|
|
421
465
|
function extractGtmId(sourceDir: string): string | null {
|
|
422
466
|
const appPath = path.join(sourceDir, "routes", "_app.tsx");
|
|
423
467
|
if (!fs.existsSync(appPath)) return null;
|
|
@@ -627,6 +671,20 @@ export function analyze(ctx: MigrationContext): void {
|
|
|
627
671
|
ctx.platform = extractPlatform(ctx.sourceDir);
|
|
628
672
|
ctx.vtexAccount = ctx.platform === "vtex" ? extractVtexAccount(ctx.sourceDir) : null;
|
|
629
673
|
ctx.gtmId = extractGtmId(ctx.sourceDir);
|
|
674
|
+
ctx.googleFonts = extractGoogleFonts(ctx.sourceDir);
|
|
675
|
+
// If no font URLs were extracted but the file mentions googleapis (likely a
|
|
676
|
+
// JSX expression like href={URL_CONST}), flag it for manual review.
|
|
677
|
+
if (ctx.googleFonts.preconnects.length === 0 && ctx.googleFonts.stylesheets.length === 0) {
|
|
678
|
+
const appPath = path.join(ctx.sourceDir, "routes", "_app.tsx");
|
|
679
|
+
if (fs.existsSync(appPath) && fs.readFileSync(appPath, "utf-8").includes("fonts.googleapis")) {
|
|
680
|
+
ctx.manualReviewItems.push({
|
|
681
|
+
file: "routes/_app.tsx",
|
|
682
|
+
reason:
|
|
683
|
+
"Google Fonts reference found but could not be extracted automatically (may use a JSX expression or variable). Add the font preconnect and stylesheet links manually to src/routes/__root.tsx.",
|
|
684
|
+
severity: "warning",
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
630
688
|
|
|
631
689
|
// Extract theme colors and font from CMS
|
|
632
690
|
const theme = extractThemeFromCms(ctx.sourceDir);
|
|
@@ -51,17 +51,26 @@ function generateRoot(ctx: MigrationContext, siteTitle: string, vtexAccount: str
|
|
|
51
51
|
preconnects.push(` { rel: "preconnect", href: "https://${vtexAccount}.vtexassets.com", crossOrigin: "anonymous" as const },`);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
const googleFontLinks = [
|
|
55
|
+
...ctx.googleFonts.preconnects.map((href) =>
|
|
56
|
+
` { rel: "preconnect", href: "${href}"${href.includes("gstatic") ? ', crossOrigin: "anonymous" as const' : ""} },`
|
|
57
|
+
),
|
|
58
|
+
...ctx.googleFonts.stylesheets.map((href) =>
|
|
59
|
+
` { rel: "stylesheet", href: "${href}" },`
|
|
60
|
+
),
|
|
61
|
+
];
|
|
62
|
+
|
|
55
63
|
const fontPreloads = fonts.map((f) =>
|
|
56
64
|
` { rel: "preload", href: "${f}", as: "font", type: "font/woff2", crossOrigin: "anonymous" as const },`
|
|
57
65
|
);
|
|
58
66
|
|
|
59
|
-
// DNS prefetch for common third-party services
|
|
60
67
|
const dnsPrefetch: string[] = [];
|
|
61
68
|
if (isVtex) {
|
|
62
69
|
dnsPrefetch.push(` { rel: "dns-prefetch", href: "https://sp.vtex.com" },`);
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
const linkLines = [...preconnects, ...googleFontLinks, ...fontPreloads].filter(Boolean);
|
|
73
|
+
|
|
65
74
|
return `import { createRootRoute } from "@tanstack/react-router";
|
|
66
75
|
import { DecoRootLayout } from "@decocms/tanstack";
|
|
67
76
|
// @ts-ignore Vite ?url import
|
|
@@ -83,8 +92,7 @@ export const Route = createRootRoute({
|
|
|
83
92
|
{ property: "og:locale", content: "pt_BR" },
|
|
84
93
|
],
|
|
85
94
|
links: [
|
|
86
|
-
${
|
|
87
|
-
${fontPreloads.join("\n")}
|
|
95
|
+
${linkLines.join("\n")}
|
|
88
96
|
{ rel: "stylesheet", href: appCss },
|
|
89
97
|
{ rel: "icon", href: "/favicon.ico" },
|
|
90
98
|
${dnsPrefetch.join("\n")}
|
package/scripts/migrate/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface MigrationContext {
|
|
|
156
156
|
themeColors: Record<string, string>;
|
|
157
157
|
/** Font family from CMS config */
|
|
158
158
|
fontFamily: string | null;
|
|
159
|
+
/** Google Fonts URLs extracted from routes/_app.tsx or CSS @import */
|
|
160
|
+
googleFonts: { preconnects: string[]; stylesheets: string[] };
|
|
159
161
|
|
|
160
162
|
/**
|
|
161
163
|
* Statically-extracted content of the source site's `tailwind.config.ts`
|
|
@@ -226,6 +228,7 @@ export function createContext(
|
|
|
226
228
|
discoveredNpmDeps: {},
|
|
227
229
|
themeColors: {},
|
|
228
230
|
fontFamily: null,
|
|
231
|
+
googleFonts: { preconnects: [], stylesheets: [] },
|
|
229
232
|
tailwindConfig: {
|
|
230
233
|
colors: {},
|
|
231
234
|
fontFamily: {},
|