@decocms/blocks-cli 7.1.1 → 7.2.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 +2 -2
- package/scripts/generate-invoke.test.ts +10 -13
- package/scripts/generate-invoke.ts +19 -15
- package/scripts/generate-schema.ts +21 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/blocks-cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.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": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"lint:unused": "knip"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@decocms/blocks": "7.
|
|
37
|
+
"@decocms/blocks": "7.2.0",
|
|
38
38
|
"ts-morph": "^27.0.0",
|
|
39
39
|
"tsx": "^4.19.0"
|
|
40
40
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Integration test for scripts/generate-invoke.ts.
|
|
3
3
|
*
|
|
4
|
-
* The generator scans
|
|
4
|
+
* The generator scans an `invoke.ts` file from @decocms/apps-vtex and emits a
|
|
5
5
|
* site-local `src/server/invoke.gen.ts` with top-level `createServerFn`
|
|
6
6
|
* declarations. The piece we care most about locking is the Set-Cookie
|
|
7
7
|
* bridge: every handler must call `forwardResponseCookies()` after the
|
|
@@ -81,20 +81,17 @@ describe("generate-invoke.ts — output shape", () => {
|
|
|
81
81
|
|
|
82
82
|
beforeAll(() => {
|
|
83
83
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "gen-invoke-"));
|
|
84
|
-
|
|
84
|
+
// Mirrors @decocms/apps-vtex's actual layout post-migration: invoke.ts
|
|
85
|
+
// sits at the package root (no more vtex/ nesting — the old vtex/
|
|
86
|
+
// directory in apps-start IS this package's root now).
|
|
87
|
+
appsDir = path.join(tmp, "apps-vtex");
|
|
85
88
|
siteDir = path.join(tmp, "site");
|
|
86
|
-
fs.mkdirSync(path.join(appsDir, "
|
|
89
|
+
fs.mkdirSync(path.join(appsDir, "actions"), { recursive: true });
|
|
87
90
|
fs.mkdirSync(path.join(siteDir, "src", "server"), { recursive: true });
|
|
88
|
-
fs.writeFileSync(path.join(appsDir, "
|
|
89
|
-
fs.writeFileSync(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
fs.writeFileSync(
|
|
94
|
-
path.join(appsDir, "vtex", "actions", "session.ts"),
|
|
95
|
-
FIXTURE_ACTIONS_SESSION_TS,
|
|
96
|
-
);
|
|
97
|
-
fs.writeFileSync(path.join(appsDir, "vtex", "types.ts"), FIXTURE_TYPES_TS);
|
|
91
|
+
fs.writeFileSync(path.join(appsDir, "invoke.ts"), FIXTURE_INVOKE_TS);
|
|
92
|
+
fs.writeFileSync(path.join(appsDir, "actions", "checkout.ts"), FIXTURE_ACTIONS_CHECKOUT_TS);
|
|
93
|
+
fs.writeFileSync(path.join(appsDir, "actions", "session.ts"), FIXTURE_ACTIONS_SESSION_TS);
|
|
94
|
+
fs.writeFileSync(path.join(appsDir, "types.ts"), FIXTURE_TYPES_TS);
|
|
98
95
|
outFile = path.join(siteDir, "src", "server", "invoke.gen.ts");
|
|
99
96
|
|
|
100
97
|
const result = spawnSync(
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
/**
|
|
3
|
-
* Scans @decocms/apps
|
|
3
|
+
* Scans @decocms/apps-vtex's invoke.ts and generates a site-local invoke file
|
|
4
4
|
* with top-level createServerFn declarations.
|
|
5
5
|
*
|
|
6
6
|
* TanStack Start's compiler only transforms createServerFn().handler() when
|
|
7
7
|
* the call is at module top-level (assigned to a const). The factory pattern
|
|
8
|
-
* used in @decocms/apps
|
|
8
|
+
* used in @decocms/apps-vtex/invoke.ts causes the "fast path" in the compiler
|
|
9
9
|
* to skip the .handler() calls because they're inside a function body.
|
|
10
10
|
*
|
|
11
11
|
* This script generates an equivalent file where each server function is a
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Env / CLI:
|
|
18
18
|
* --out-file override output (default: src/server/invoke.gen.ts)
|
|
19
|
-
* --apps-dir override @decocms/apps location (default: auto-resolve from node_modules)
|
|
19
|
+
* --apps-dir override @decocms/apps-vtex location (default: auto-resolve from node_modules)
|
|
20
20
|
*/
|
|
21
21
|
import fs from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
@@ -35,19 +35,23 @@ function resolveAppsDir(): string {
|
|
|
35
35
|
const explicit = arg("apps-dir", "");
|
|
36
36
|
if (explicit) return path.resolve(cwd, explicit);
|
|
37
37
|
|
|
38
|
-
// Try common locations
|
|
38
|
+
// Try common locations. Both point at the directory that contains
|
|
39
|
+
// invoke.ts directly at its root: the installed @decocms/apps-vtex
|
|
40
|
+
// package (no more vtex/ nesting — that directory IS the package root
|
|
41
|
+
// now), or a raw apps-start checkout's vtex/ subdirectory as a legacy
|
|
42
|
+
// fallback for anyone still developing against the pre-split monorepo.
|
|
39
43
|
const candidates = [
|
|
40
|
-
path.resolve(cwd, "node_modules/@decocms/apps"),
|
|
41
|
-
path.resolve(cwd, "../apps-start"),
|
|
44
|
+
path.resolve(cwd, "node_modules/@decocms/apps-vtex"),
|
|
45
|
+
path.resolve(cwd, "../apps-start/vtex"),
|
|
42
46
|
];
|
|
43
47
|
for (const c of candidates) {
|
|
44
|
-
if (fs.existsSync(path.join(c, "
|
|
48
|
+
if (fs.existsSync(path.join(c, "invoke.ts"))) return c;
|
|
45
49
|
}
|
|
46
|
-
throw new Error("Could not find @decocms/apps. Use --apps-dir to specify its location.");
|
|
50
|
+
throw new Error("Could not find @decocms/apps-vtex. Use --apps-dir to specify its location.");
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
const appsDir = resolveAppsDir();
|
|
50
|
-
const invokeFile = path.join(appsDir, "
|
|
54
|
+
const invokeFile = path.join(appsDir, "invoke.ts");
|
|
51
55
|
|
|
52
56
|
if (!fs.existsSync(invokeFile)) {
|
|
53
57
|
console.error(`invoke.ts not found at: ${invokeFile}`);
|
|
@@ -60,7 +64,7 @@ if (!fs.existsSync(invokeFile)) {
|
|
|
60
64
|
|
|
61
65
|
interface ActionDef {
|
|
62
66
|
name: string;
|
|
63
|
-
/** The import source for the action function (e.g., "@decocms/apps
|
|
67
|
+
/** The import source for the action function (e.g., "@decocms/apps-vtex/actions/checkout") */
|
|
64
68
|
importSource: string;
|
|
65
69
|
/** The imported function name (e.g., "addItemsToCart") */
|
|
66
70
|
importedFn: string;
|
|
@@ -85,7 +89,7 @@ for (const imp of sourceFile.getImportDeclarations()) {
|
|
|
85
89
|
const localName = named.getName();
|
|
86
90
|
const importedName = named.getAliasNode()?.getText() || localName;
|
|
87
91
|
importMap.set(localName, {
|
|
88
|
-
source: source.startsWith("./") ? `@decocms/apps
|
|
92
|
+
source: source.startsWith("./") ? `@decocms/apps-vtex/${source.slice(2)}` : source,
|
|
89
93
|
importedName: localName,
|
|
90
94
|
});
|
|
91
95
|
}
|
|
@@ -100,7 +104,7 @@ for (const imp of sourceFile.getImportDeclarations()) {
|
|
|
100
104
|
const localName = named.getName();
|
|
101
105
|
const source = imp.getModuleSpecifierValue();
|
|
102
106
|
typeImportMap.set(localName, {
|
|
103
|
-
source: source.startsWith("./") ? `@decocms/apps
|
|
107
|
+
source: source.startsWith("./") ? `@decocms/apps-vtex/${source.slice(2)}` : source,
|
|
104
108
|
importedName: localName,
|
|
105
109
|
});
|
|
106
110
|
}
|
|
@@ -111,7 +115,7 @@ for (const imp of sourceFile.getImportDeclarations()) {
|
|
|
111
115
|
for (const named of imp.getNamedImports()) {
|
|
112
116
|
const localName = named.getName();
|
|
113
117
|
typeImportMap.set(localName, {
|
|
114
|
-
source: source.startsWith("./") ? `@decocms/apps
|
|
118
|
+
source: source.startsWith("./") ? `@decocms/apps-vtex/${source.slice(2)}` : source,
|
|
115
119
|
importedName: localName,
|
|
116
120
|
});
|
|
117
121
|
}
|
|
@@ -376,7 +380,7 @@ for (const action of actions) {
|
|
|
376
380
|
|
|
377
381
|
if (action.importedFn) {
|
|
378
382
|
// Emit the wrapper body verbatim. The arrow function in
|
|
379
|
-
// @decocms/apps
|
|
383
|
+
// @decocms/apps-vtex/invoke.ts is the contract that maps the external
|
|
380
384
|
// invoke shape (what storefront callers send) to the internal action
|
|
381
385
|
// shape (what vtex/actions/* expects). Most wrappers are direct
|
|
382
386
|
// pass-throughs (`actionFn(data)`) but some adapt the payload
|
|
@@ -450,7 +454,7 @@ for (const action of actions) {
|
|
|
450
454
|
out += `} as const;
|
|
451
455
|
|
|
452
456
|
// Re-export OrderForm type (commonly imported from invoke by site components)
|
|
453
|
-
export type { OrderForm } from "@decocms/apps
|
|
457
|
+
export type { OrderForm } from "@decocms/apps-vtex/types";
|
|
454
458
|
|
|
455
459
|
// ---------------------------------------------------------------------------
|
|
456
460
|
// Default invoke object — import this if you don't need site extensions
|
|
@@ -839,14 +839,22 @@ function generateMeta(): MetaResponse {
|
|
|
839
839
|
}
|
|
840
840
|
|
|
841
841
|
// ---------------------------------------------------------------------------
|
|
842
|
-
// App loaders pass: walk @decocms/apps
|
|
843
|
-
// each .ts file as a CMS loader. CMS keys are derived
|
|
844
|
-
// (e.g. "vtex/loaders/intelligentSearch/productList.ts")
|
|
842
|
+
// App loaders pass: walk each @decocms/apps-<namespace> package's src/loaders/
|
|
843
|
+
// directory and register each .ts file as a CMS loader. CMS keys are derived
|
|
844
|
+
// from the file path (e.g. "vtex/loaders/intelligentSearch/productList.ts") —
|
|
845
|
+
// this key format is a CMS decofile convention, independent of the npm
|
|
846
|
+
// package layout, so it stays "<namespace>/loaders/..." even though the
|
|
847
|
+
// namespace now maps to its own "@decocms/apps-<namespace>" package rather
|
|
848
|
+
// than a subdirectory of one monolithic "@decocms/apps" package.
|
|
845
849
|
//
|
|
846
850
|
// Only apps installed in src/apps/ are scanned. An app bridge file that
|
|
847
|
-
// re-exports from "@decocms/apps
|
|
851
|
+
// re-exports from "@decocms/apps-{namespace}/mod" signals the namespace.
|
|
848
852
|
// ---------------------------------------------------------------------------
|
|
849
|
-
|
|
853
|
+
|
|
854
|
+
/** Absolute path to the installed @decocms/apps-<namespace> package, if present. */
|
|
855
|
+
function getAppPkgDir(namespace: string): string {
|
|
856
|
+
return path.resolve(root, `node_modules/@decocms/apps-${namespace}`);
|
|
857
|
+
}
|
|
850
858
|
|
|
851
859
|
/** Detect installed app namespaces from src/apps/ bridge files. */
|
|
852
860
|
function detectInstalledAppNamespaces(): Set<string> {
|
|
@@ -855,7 +863,7 @@ function generateMeta(): MetaResponse {
|
|
|
855
863
|
if (!fs.existsSync(siteAppsDir)) return namespaces;
|
|
856
864
|
|
|
857
865
|
const appFiles = findTsxFiles(siteAppsDir);
|
|
858
|
-
const re = /["']@decocms\/apps
|
|
866
|
+
const re = /["']@decocms\/apps-([^/]+)\/mod["']/;
|
|
859
867
|
for (const filePath of appFiles) {
|
|
860
868
|
try {
|
|
861
869
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -869,13 +877,13 @@ function generateMeta(): MetaResponse {
|
|
|
869
877
|
// Discover app loader files via filesystem walk (scoped to installed apps)
|
|
870
878
|
function discoverAppLoaders(): Array<{ cmsKey: string; sourceFile: string; namespace: string }> {
|
|
871
879
|
const result: Array<{ cmsKey: string; sourceFile: string; namespace: string }> = [];
|
|
872
|
-
if (!fs.existsSync(appsPkgDir)) return result;
|
|
873
880
|
|
|
874
881
|
const installed = detectInstalledAppNamespaces();
|
|
875
882
|
if (installed.size === 0) return result;
|
|
876
883
|
|
|
877
884
|
for (const namespace of installed) {
|
|
878
|
-
const
|
|
885
|
+
const pkgDir = getAppPkgDir(namespace);
|
|
886
|
+
const loadersDir = path.join(pkgDir, "src", "loaders");
|
|
879
887
|
if (!fs.existsSync(loadersDir)) continue;
|
|
880
888
|
|
|
881
889
|
const files = fs.readdirSync(loadersDir, { recursive: true }) as string[];
|
|
@@ -888,7 +896,9 @@ function generateMeta(): MetaResponse {
|
|
|
888
896
|
if (rel.includes("__tests__") || rel.includes("__test__") || rel.endsWith(".test.ts")) continue;
|
|
889
897
|
|
|
890
898
|
const cmsKey = `${namespace}/loaders/${rel.replace(/\\/g, "/")}`;
|
|
891
|
-
|
|
899
|
+
// Absolute — each namespace now resolves against its own package dir,
|
|
900
|
+
// not a single shared "appsPkgDir" (see absSourceFile below).
|
|
901
|
+
const sourceFile = path.join(loadersDir, rel);
|
|
892
902
|
result.push({ cmsKey, sourceFile, namespace });
|
|
893
903
|
}
|
|
894
904
|
}
|
|
@@ -908,9 +918,9 @@ function generateMeta(): MetaResponse {
|
|
|
908
918
|
|
|
909
919
|
const installedNs = detectInstalledAppNamespaces();
|
|
910
920
|
console.log(`Installed app namespaces: ${[...installedNs].join(", ") || "(none)"}`);
|
|
911
|
-
console.log(`Scanning app loaders from
|
|
921
|
+
console.log(`Scanning app loaders from ${[...installedNs].map((ns) => `@decocms/apps-${ns}`).join(", ") || "(none)"} (${appLoaders.length} files)...`);
|
|
912
922
|
for (const appLoader of appLoaders) {
|
|
913
|
-
const absSourceFile =
|
|
923
|
+
const absSourceFile = appLoader.sourceFile;
|
|
914
924
|
|
|
915
925
|
try {
|
|
916
926
|
// Resolve the real path to de-duplicate re-exports pointing to the same file
|