@decocms/blocks-cli 7.55.2 → 7.56.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.56.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": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"lint:unused": "knip"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@decocms/blocks": "7.
|
|
36
|
+
"@decocms/blocks": "7.56.0",
|
|
37
37
|
"ts-morph": "^27.0.0",
|
|
38
38
|
"tsx": "^4.22.5"
|
|
39
39
|
},
|
|
@@ -57,6 +57,43 @@ describe("transformCtxCompat", () => {
|
|
|
57
57
|
expect(r.content).toContain("ctx?: AppContext");
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
// The scanner walks raw characters, so without a literal/comment guard it
|
|
61
|
+
// rewrote `ctx.` inside strings too — patching the call site correctly while
|
|
62
|
+
// silently corrupting the message next to it.
|
|
63
|
+
it("does not rewrite ctx inside string literals", () => {
|
|
64
|
+
const r = transformCtxCompat(withLoader(" console.log('ctx.device:', ctx.device);"));
|
|
65
|
+
expect(r.content).toContain("'ctx.device:'");
|
|
66
|
+
expect(r.content).toContain("console.log('ctx.device:', ctx?.device)");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("does not rewrite ctx inside double-quoted strings or escaped quotes", () => {
|
|
70
|
+
const r = transformCtxCompat(
|
|
71
|
+
withLoader(' const s = "he said \\"ctx.device\\""; return ctx.device;'),
|
|
72
|
+
);
|
|
73
|
+
expect(r.content).toContain('"he said \\"ctx.device\\""');
|
|
74
|
+
expect(r.content).toContain("return ctx?.device");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("does not rewrite ctx inside comments", () => {
|
|
78
|
+
const r = transformCtxCompat(
|
|
79
|
+
withLoader(" // don't touch ctx.device here\n /* nor ctx.invoke */\n return ctx.device;"),
|
|
80
|
+
);
|
|
81
|
+
expect(r.content).toContain("// don't touch ctx.device here");
|
|
82
|
+
expect(r.content).toContain("/* nor ctx.invoke */");
|
|
83
|
+
expect(r.content).toContain("return ctx?.device");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("does not rewrite template text but DOES rewrite ${} interpolation", () => {
|
|
87
|
+
const r = transformCtxCompat(withLoader(" const u = `ctx.device is ${ctx.vtex.account}`;"));
|
|
88
|
+
// literal text stays, the interpolated expression is real code
|
|
89
|
+
expect(r.content).toContain("`ctx.device is ${ctx?.vtex?.account}`");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("handles nested template literals", () => {
|
|
93
|
+
const r = transformCtxCompat(withLoader(" const u = `a${`b${ctx.device}`}c`;"));
|
|
94
|
+
expect(r.content).toContain("`a${`b${ctx?.device}`}c`");
|
|
95
|
+
});
|
|
96
|
+
|
|
60
97
|
it("does not match identifiers that merely contain ctx", () => {
|
|
61
98
|
const src = withLoader(" const c = canvasCtx.foo;\n const d = a.ctx.bar;");
|
|
62
99
|
const r = transformCtxCompat(src);
|
|
@@ -112,7 +112,99 @@ export function transformCtxCompat(content: string): TransformResult {
|
|
|
112
112
|
let i = 0;
|
|
113
113
|
let count = 0;
|
|
114
114
|
|
|
115
|
+
// Contexts we are inside of, innermost last. Without this the scanner walks
|
|
116
|
+
// raw characters and happily rewrites `ctx.` inside string literals and
|
|
117
|
+
// comments: `console.log('ctx.device:', ctx.device)` became
|
|
118
|
+
// `console.log('ctx?.device:', ctx?.device)` — the call site correctly
|
|
119
|
+
// patched, the message silently corrupted.
|
|
120
|
+
//
|
|
121
|
+
// A template literal pushes "template" (its text is data), and a `${` inside
|
|
122
|
+
// one pushes "expr" (real code, where rewriting must resume) — so
|
|
123
|
+
// `` `${ctx.device}` `` is still rewritten while `` `ctx.device` `` is not.
|
|
124
|
+
//
|
|
125
|
+
// Regex literals are deliberately NOT tracked: telling `/re/` from division
|
|
126
|
+
// needs real parsing, and guessing wrong would consume live code. A `ctx.`
|
|
127
|
+
// inside a regex in a loader is vanishingly rare next to that risk.
|
|
128
|
+
const stack: ("template" | "expr")[] = [];
|
|
129
|
+
|
|
115
130
|
while (i < content.length) {
|
|
131
|
+
const inTemplateText = stack[stack.length - 1] === "template";
|
|
132
|
+
|
|
133
|
+
if (inTemplateText) {
|
|
134
|
+
if (content[i] === "\\") {
|
|
135
|
+
out += content.slice(i, i + 2);
|
|
136
|
+
i += 2;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (content[i] === "`") {
|
|
140
|
+
stack.pop();
|
|
141
|
+
out += content[i];
|
|
142
|
+
i += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (content.startsWith("${", i)) {
|
|
146
|
+
stack.push("expr");
|
|
147
|
+
out += "${";
|
|
148
|
+
i += 2;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
out += content[i];
|
|
152
|
+
i += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Line comment — copy to end of line.
|
|
157
|
+
if (content.startsWith("//", i)) {
|
|
158
|
+
const nl = content.indexOf("\n", i);
|
|
159
|
+
const end = nl === -1 ? content.length : nl;
|
|
160
|
+
out += content.slice(i, end);
|
|
161
|
+
i = end;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Block comment — copy to the closing delimiter.
|
|
166
|
+
if (content.startsWith("/*", i)) {
|
|
167
|
+
const close = content.indexOf("*/", i + 2);
|
|
168
|
+
const end = close === -1 ? content.length : close + 2;
|
|
169
|
+
out += content.slice(i, end);
|
|
170
|
+
i = end;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Single/double quoted string — copy to the unescaped closing quote.
|
|
175
|
+
if (content[i] === "'" || content[i] === '"') {
|
|
176
|
+
const quote = content[i];
|
|
177
|
+
let j = i + 1;
|
|
178
|
+
while (j < content.length) {
|
|
179
|
+
if (content[j] === "\\") {
|
|
180
|
+
j += 2;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (content[j] === quote || content[j] === "\n") {
|
|
184
|
+
j += 1;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
j += 1;
|
|
188
|
+
}
|
|
189
|
+
out += content.slice(i, j);
|
|
190
|
+
i = j;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (content[i] === "`") {
|
|
195
|
+
stack.push("template");
|
|
196
|
+
out += content[i];
|
|
197
|
+
i += 1;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (content[i] === "}" && stack[stack.length - 1] === "expr") {
|
|
202
|
+
stack.pop();
|
|
203
|
+
out += content[i];
|
|
204
|
+
i += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
116
208
|
const isCtxToken =
|
|
117
209
|
content.startsWith("ctx", i) &&
|
|
118
210
|
!isIdentChar(content[i - 1]) &&
|