@stacksjs/stx 0.2.171 → 0.2.173
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/dist/cli.js +925 -834
- package/dist/codemod.d.ts +1 -24
- package/dist/codemod.js +56 -9
- package/dist/craft.js +264 -185
- package/dist/production-builder.js +1 -0
- package/dist/pwa.js +210 -131
- package/dist/render.js +5 -18
- package/dist/script-validation.d.ts +0 -7
- package/dist/script-validation.js +6 -4
- package/dist/server-bindings.js +45 -1
- package/dist/signals.js +93 -15
- package/dist/ssg.js +223 -144
- package/dist/stx-virtual-ts.d.ts +13 -0
- package/dist/stx-virtual-ts.js +36 -7
- package/dist/typecheck.d.ts +1 -13
- package/dist/typecheck.js +98 -4
- package/dist/variable-extractor.js +1 -1
- package/package.json +5 -5
package/dist/stx-virtual-ts.d.ts
CHANGED
|
@@ -108,6 +108,19 @@ export declare function splitTopLevelImports(code: string): { imports: string, b
|
|
|
108
108
|
* checked against the server block, or do not and they are `any`.
|
|
109
109
|
*/
|
|
110
110
|
export declare function clientPayloadDeclarations(serverCode: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Declarations at the TOP LEVEL of a block, ignoring anything nested.
|
|
113
|
+
*
|
|
114
|
+
* `collectBlockDeclarations` matches `^\s*const …` with leading whitespace
|
|
115
|
+
* allowed, so it also returns bindings local to a function. For most callers
|
|
116
|
+
* that is harmless. For the bridge warning it is not: a `const state` inside a
|
|
117
|
+
* helper never crosses to the client, and naming it told the author to declare
|
|
118
|
+
* a payload entry that would do nothing (stacksjs/stx#1908).
|
|
119
|
+
*
|
|
120
|
+
* Nested braces are blanked first, so only depth-zero declarations survive to
|
|
121
|
+
* be matched by the shared patterns.
|
|
122
|
+
*/
|
|
123
|
+
export declare function collectTopLevelDeclarations(code: string): string[];
|
|
111
124
|
/**
|
|
112
125
|
* Server bindings a client block reaches for without a declared payload.
|
|
113
126
|
*
|
package/dist/stx-virtual-ts.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { stripCommentsAndLiterals } from "./strip-literals.js";
|
|
2
2
|
import { STX_RUNTIME_GLOBALS } from "./runtime-globals.js";
|
|
3
3
|
const SCRIPT_RE = /<script(\s[^>]*)?>([\s\S]*?)<\/script>/gi;
|
|
4
|
+
function blankCommentContents(source) {
|
|
5
|
+
const blank = (match) => match.replace(/[^\n]/g, " ");
|
|
6
|
+
return source.replace(/<!--[\s\S]*?-->/g, blank).replace(/\{\{--[\s\S]*?--\}\}/g, blank);
|
|
7
|
+
}
|
|
4
8
|
export function extractScriptBlocks(source) {
|
|
5
|
-
const blocks = [];
|
|
9
|
+
const blocks = [], scannable = blankCommentContents(source);
|
|
6
10
|
SCRIPT_RE.lastIndex = 0;
|
|
7
11
|
let match;
|
|
8
|
-
while ((match = SCRIPT_RE.exec(
|
|
9
|
-
const attrs = (match[1] || "").trim(),
|
|
12
|
+
while ((match = SCRIPT_RE.exec(scannable)) !== null) {
|
|
13
|
+
const attrs = (match[1] || "").trim(), openLength = (match[0].match(/^<script(?:\s[^>]*)?>/i)?.[0] ?? "<script>").length, closeLength = (match[0].match(/<\/script\s*>$/i)?.[0] ?? "</script>").length, bodyStart = match.index + openLength, bodyEnd = match.index + match[0].length - closeLength, body = source.slice(bodyStart, bodyEnd);
|
|
10
14
|
if (/\bsrc\s*=/.test(attrs))
|
|
11
15
|
continue;
|
|
12
16
|
const typeMatch = attrs.match(/\btype\s*=\s*["']([^"']+)["']/i);
|
|
13
17
|
if (typeMatch && !/^(?:module|text\/(?:javascript|typescript))$/i.test(typeMatch[1]))
|
|
14
18
|
continue;
|
|
15
|
-
const kind = /\bserver\b/.test(attrs) ? "server" : /\bclient\b/.test(attrs) ? "client" : "plain",
|
|
19
|
+
const kind = /\bserver\b/.test(attrs) ? "server" : /\bclient\b/.test(attrs) ? "client" : "plain", startLine = lineAt(source, bodyStart);
|
|
16
20
|
blocks.push({ kind, code: body, startLine, attrs });
|
|
17
21
|
}
|
|
18
22
|
return blocks;
|
|
@@ -108,7 +112,9 @@ const NON_EXPRESSION_ATTRS = new Set([
|
|
|
108
112
|
"x-teleport",
|
|
109
113
|
"v-slot",
|
|
110
114
|
"x-hydrate",
|
|
111
|
-
":key"
|
|
115
|
+
":key",
|
|
116
|
+
"x-tooltip",
|
|
117
|
+
"x-tooltip-position"
|
|
112
118
|
]);
|
|
113
119
|
export function extractTemplateExpressions(source) {
|
|
114
120
|
const masked = maskNonTemplateRegions(source), found = [], interpolation = /(@?)\{\{([\s\S]*?)\}\}|(@?)\{!!([\s\S]*?)!!\}/g;
|
|
@@ -313,11 +319,34 @@ export function clientPayloadDeclarations(serverCode) {
|
|
|
313
319
|
return parts.join(`
|
|
314
320
|
`);
|
|
315
321
|
}
|
|
322
|
+
export function collectTopLevelDeclarations(code) {
|
|
323
|
+
const chars = stripCommentsAndLiterals(code).split("");
|
|
324
|
+
let depth = 0;
|
|
325
|
+
for (let i = 0;i < chars.length; i++) {
|
|
326
|
+
const c = chars[i];
|
|
327
|
+
if (c === "{" || c === "(" || c === "[") {
|
|
328
|
+
depth++;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (c === "}" || c === ")" || c === "]") {
|
|
332
|
+
depth = Math.max(0, depth - 1);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (depth > 0 && c !== `
|
|
336
|
+
`)
|
|
337
|
+
chars[i] = " ";
|
|
338
|
+
}
|
|
339
|
+
return collectBlockDeclarations(chars.join(""));
|
|
340
|
+
}
|
|
316
341
|
export function scrapedBridgeNames(serverCode, clientCode) {
|
|
317
342
|
if (extractClientPayloadNames(serverCode) !== null)
|
|
318
343
|
return [];
|
|
319
|
-
const searchable = stripCommentsAndLiterals(clientCode);
|
|
320
|
-
return
|
|
344
|
+
const searchable = stripCommentsAndLiterals(clientCode), provided = new Set(STX_RUNTIME_GLOBALS);
|
|
345
|
+
return collectTopLevelDeclarations(serverCode).filter((name) => {
|
|
346
|
+
if (provided.has(name))
|
|
347
|
+
return !1;
|
|
348
|
+
if (collectBlockDeclarations(clientCode).includes(name))
|
|
349
|
+
return !1;
|
|
321
350
|
return new RegExp(`(?<![.#\\w$])${name}(?![\\w$])`).test(searchable);
|
|
322
351
|
});
|
|
323
352
|
}
|
package/dist/typecheck.d.ts
CHANGED
|
@@ -29,19 +29,6 @@ export declare function sourcePathFor(virtualPath: string): string;
|
|
|
29
29
|
* than hand tsc a path to nothing.
|
|
30
30
|
*/
|
|
31
31
|
export declare function findRuntimeTypeDeclarations(): string | null;
|
|
32
|
-
/**
|
|
33
|
-
* Type-check `.stx` files.
|
|
34
|
-
*
|
|
35
|
-
* Runs the `tsc` BINARY over generated virtual files rather than driving the
|
|
36
|
-
* compiler API. TypeScript 7 ships as a Go port whose npm package exposes only
|
|
37
|
-
* `version` — no `createProgram`, no `ScriptTarget` — so an API-based checker
|
|
38
|
-
* would work on TS 5 and break on the version this repo actually resolves.
|
|
39
|
-
* Shelling out works across both.
|
|
40
|
-
*
|
|
41
|
-
* TypeScript is not a declared dependency: anyone running a type-check already
|
|
42
|
-
* has it, and making the package depend on a compiler to serve a page would be
|
|
43
|
-
* the wrong trade.
|
|
44
|
-
*/
|
|
45
32
|
export declare function typecheckStxFiles(files: string[], options?: TypecheckOptions): Promise<TypecheckResult>;
|
|
46
33
|
/** Render diagnostics the way a CLI should: file:line:col, then the message. */
|
|
47
34
|
export declare function formatTypecheckDiagnostics(diagnostics: TypecheckDiagnostic[]): string;
|
|
@@ -64,6 +51,7 @@ export declare interface TypecheckOptions {
|
|
|
64
51
|
}
|
|
65
52
|
export declare interface TypecheckResult {
|
|
66
53
|
diagnostics: TypecheckDiagnostic[]
|
|
54
|
+
failure?: string
|
|
67
55
|
checkedFiles: string[]
|
|
68
56
|
blockCount: number
|
|
69
57
|
expressionCount: number
|
package/dist/typecheck.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { STX_RUNTIME_GLOBALS } from "./runtime-globals.js";
|
|
4
4
|
import { stateDir } from "./state-dir.js";
|
|
@@ -39,6 +39,89 @@ export function findRuntimeTypeDeclarations() {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
const TSC_LINE_RE = /^(.*?)\((\d+),(\d+)\):\s+(error|warning)\s+TS(\d+):\s+(.*)$/;
|
|
42
|
+
function readProjectPathAliases(startDir) {
|
|
43
|
+
let dir = path.resolve(startDir), configPath;
|
|
44
|
+
for (;; ) {
|
|
45
|
+
const candidate = path.join(dir, "tsconfig.json");
|
|
46
|
+
if (existsSync(candidate)) {
|
|
47
|
+
configPath = candidate;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const parent = path.dirname(dir);
|
|
51
|
+
if (parent === dir)
|
|
52
|
+
break;
|
|
53
|
+
dir = parent;
|
|
54
|
+
}
|
|
55
|
+
if (!configPath)
|
|
56
|
+
return {};
|
|
57
|
+
const stripJsonc = (text) => {
|
|
58
|
+
let out = "", inString = !1, escaped = !1;
|
|
59
|
+
for (let i = 0;i < text.length; i++) {
|
|
60
|
+
const c = text[i];
|
|
61
|
+
if (inString) {
|
|
62
|
+
out += c;
|
|
63
|
+
if (escaped)
|
|
64
|
+
escaped = !1;
|
|
65
|
+
else if (c === "\\")
|
|
66
|
+
escaped = !0;
|
|
67
|
+
else if (c === '"')
|
|
68
|
+
inString = !1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (c === '"') {
|
|
72
|
+
inString = !0;
|
|
73
|
+
out += c;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (c === "/" && text[i + 1] === "/") {
|
|
77
|
+
while (i < text.length && text[i] !== `
|
|
78
|
+
`)
|
|
79
|
+
i++;
|
|
80
|
+
out += `
|
|
81
|
+
`;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (c === "/" && text[i + 1] === "*") {
|
|
85
|
+
i += 2;
|
|
86
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/"))
|
|
87
|
+
i++;
|
|
88
|
+
i++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
out += c;
|
|
92
|
+
}
|
|
93
|
+
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
94
|
+
}, seen = new Set;
|
|
95
|
+
let current = configPath, paths, baseUrl, owner = configPath;
|
|
96
|
+
while (current && !seen.has(current)) {
|
|
97
|
+
seen.add(current);
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = JSON.parse(stripJsonc(readFileSync(current, "utf8")));
|
|
101
|
+
} catch {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
const co = parsed?.compilerOptions ?? {};
|
|
105
|
+
if (!paths && co.paths && typeof co.paths === "object") {
|
|
106
|
+
paths = co.paths;
|
|
107
|
+
owner = current;
|
|
108
|
+
}
|
|
109
|
+
if (!baseUrl && typeof co.baseUrl === "string")
|
|
110
|
+
baseUrl = path.resolve(path.dirname(current), co.baseUrl);
|
|
111
|
+
if (paths && baseUrl)
|
|
112
|
+
break;
|
|
113
|
+
current = typeof parsed?.extends === "string" ? path.resolve(path.dirname(current), parsed.extends.endsWith(".json") ? parsed.extends : `${parsed.extends}.json`) : void 0;
|
|
114
|
+
}
|
|
115
|
+
if (!paths)
|
|
116
|
+
return {};
|
|
117
|
+
const base = baseUrl ?? path.dirname(owner), absolute = {};
|
|
118
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
119
|
+
if (!Array.isArray(targets))
|
|
120
|
+
continue;
|
|
121
|
+
absolute[pattern] = targets.map((t) => typeof t === "string" && !path.isAbsolute(t) ? path.resolve(base, t) : t);
|
|
122
|
+
}
|
|
123
|
+
return { paths: absolute };
|
|
124
|
+
}
|
|
42
125
|
export async function typecheckStxFiles(files, options = {}) {
|
|
43
126
|
const checkClient = options.client !== !1, checkServer = options.server !== !1, checkTemplates = options.templates !== !1, virtualFiles = new Map, scrapedByFile = new Map, checkedFiles = [];
|
|
44
127
|
let blockCount = 0, expressionCount = 0;
|
|
@@ -80,7 +163,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
80
163
|
}
|
|
81
164
|
if (virtualFiles.size === 0)
|
|
82
165
|
return { diagnostics: [], checkedFiles, blockCount: 0, expressionCount: 0 };
|
|
83
|
-
const ambient =
|
|
166
|
+
const ambient = (options.extraLibs ?? []).map((lib) => path.resolve(lib));
|
|
84
167
|
if (runtimeTypes)
|
|
85
168
|
ambient.push(runtimeTypes);
|
|
86
169
|
const globalsDts = `${stateDir()}/typecheck/__stx_globals.d.ts`, globalDecls = [
|
|
@@ -112,6 +195,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
112
195
|
resolveJsonModule: !0,
|
|
113
196
|
lib: ["ESNext", "DOM", "DOM.Iterable"],
|
|
114
197
|
types: [],
|
|
198
|
+
...readProjectPathAliases(files.length > 0 ? path.dirname(path.resolve(files[0])) : process.cwd()),
|
|
115
199
|
...options.compilerOptions ?? {}
|
|
116
200
|
},
|
|
117
201
|
files: [...writtenToOrigin.keys()].map((name) => `${workDir}/${name}`).concat(globalsDts, ...ambient)
|
|
@@ -120,12 +204,18 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
120
204
|
const proc = Bun.spawnSync(["bun", "x", "tsc", "-p", tsconfigPath, "--pretty", "false"], {
|
|
121
205
|
stdout: "pipe",
|
|
122
206
|
stderr: "pipe"
|
|
123
|
-
}), output = new TextDecoder().decode(proc.stdout) + new TextDecoder().decode(proc.stderr), diagnostics = [];
|
|
207
|
+
}), output = new TextDecoder().decode(proc.stdout) + new TextDecoder().decode(proc.stderr), diagnostics = [], exitCode = proc.exitCode ?? 0;
|
|
208
|
+
let printedDiagnostics = 0;
|
|
209
|
+
const ambientBasenames = new Set(ambient.map((lib) => lib.split("/").pop()));
|
|
210
|
+
let ambientDiagnostics = 0;
|
|
124
211
|
for (const rawLine of output.split(`
|
|
125
212
|
`)) {
|
|
126
213
|
const m = rawLine.match(TSC_LINE_RE);
|
|
127
214
|
if (!m)
|
|
128
215
|
continue;
|
|
216
|
+
printedDiagnostics++;
|
|
217
|
+
if (ambientBasenames.has(m[1].split("/").pop()))
|
|
218
|
+
ambientDiagnostics++;
|
|
129
219
|
const [, filePart, lineStr, colStr, category, code, message] = m, entry = writtenToOrigin.get(filePart.split("/").pop());
|
|
130
220
|
if (!entry)
|
|
131
221
|
continue;
|
|
@@ -173,7 +263,11 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
173
263
|
return !0;
|
|
174
264
|
});
|
|
175
265
|
unique.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column);
|
|
176
|
-
|
|
266
|
+
const brokenLib = ambientDiagnostics > 0 && unique.length === 0, failure = brokenLib || exitCode !== 0 && printedDiagnostics === 0 ? `${brokenLib ? "a file passed with --lib has errors of its own, so nothing in your code was checked" : `tsc exited ${exitCode} without reporting any diagnostic, so nothing was checked`}. ` + "A syntax error in an ambient declaration file does this \u2014 skipLibCheck suppresses " + `SEMANTIC diagnostics only. tsc said:
|
|
267
|
+
${output.trim().split(`
|
|
268
|
+
`).slice(0, 12).join(`
|
|
269
|
+
`) || "(no output)"}` : void 0;
|
|
270
|
+
return { diagnostics: unique, checkedFiles, blockCount, expressionCount, failure };
|
|
177
271
|
}
|
|
178
272
|
export function formatTypecheckDiagnostics(diagnostics) {
|
|
179
273
|
return diagnostics.map((d) => {
|
|
@@ -676,7 +676,7 @@ export function convertToCommonJS(scriptContent, filePath) {
|
|
|
676
676
|
};
|
|
677
677
|
if (defaultImportMatch) {
|
|
678
678
|
const [, name, source] = defaultImportMatch, resolved = resolveSource(source);
|
|
679
|
-
convertedLines.push(`const ${name} = await import('${resolved}')`);
|
|
679
|
+
convertedLines.push(`const ${name} = (await import('${resolved}')).default`);
|
|
680
680
|
convertedLines.push(`module.exports.${name} = ${name};`);
|
|
681
681
|
} else if (namedImportMatch) {
|
|
682
682
|
const [, names, source] = namedImportMatch, resolved = resolveSource(source), specifiers = [], exported = [], isStxRuntime = STX_RUNTIME_SPECIFIERS.has(source);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.173",
|
|
5
5
|
"description": "A performant UI Framework. Powered by Bun.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -117,12 +117,12 @@
|
|
|
117
117
|
"dependencies": {
|
|
118
118
|
"@cwcss/crosswind": "^0.2.14",
|
|
119
119
|
"@stacksjs/clapp": "^0.2.0",
|
|
120
|
-
"@stacksjs/desktop": "0.2.
|
|
121
|
-
"@stacksjs/sanitizer": "0.2.
|
|
120
|
+
"@stacksjs/desktop": "0.2.173",
|
|
121
|
+
"@stacksjs/sanitizer": "0.2.173",
|
|
122
122
|
"@stacksjs/ts-i18n": "^0.1.11",
|
|
123
|
-
"bun-plugin-stx": "0.2.
|
|
123
|
+
"bun-plugin-stx": "0.2.173",
|
|
124
124
|
"bunfig": "^0.15.17",
|
|
125
|
-
"stx-router": "0.2.
|
|
125
|
+
"stx-router": "0.2.173",
|
|
126
126
|
"ts-broadcasting": "^0.0.9",
|
|
127
127
|
"ts-images": "^0.1.9",
|
|
128
128
|
"ts-syntax-highlighter": "^0.2.1"
|