@stacksjs/stx 0.2.170 → 0.2.172
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 +1263 -1168
- package/dist/client-script-bundler.js +2 -1
- package/dist/client-script.d.ts +1 -12
- package/dist/client-script.js +2 -149
- package/dist/codemod.d.ts +1 -24
- package/dist/codemod.js +61 -9
- package/dist/compression.d.ts +11 -4
- package/dist/compression.js +52 -13
- package/dist/craft.js +489 -415
- package/dist/expressions.d.ts +0 -21
- package/dist/expressions.js +12 -1
- package/dist/find-body-tag.js +29 -17
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -0
- package/dist/page-action.d.ts +83 -0
- package/dist/page-action.js +64 -0
- package/dist/production-builder.js +1 -0
- package/dist/production-server.js +10 -3
- package/dist/pwa.js +398 -324
- package/dist/reactive-tracking.d.ts +81 -0
- package/dist/reactive-tracking.js +49 -0
- package/dist/reactivity.js +29 -23
- package/dist/render.js +5 -18
- package/dist/runtime-injection.js +25 -39
- package/dist/script-validation.d.ts +0 -7
- package/dist/script-validation.js +6 -4
- package/dist/signals-api.d.ts +18 -0
- package/dist/signals-api.js +26 -41
- package/dist/signals.js +93 -15
- package/dist/ssg.d.ts +8 -0
- package/dist/ssg.js +515 -441
- package/dist/state-management.js +2 -2
- package/dist/strip-literals.d.ts +40 -0
- package/dist/strip-literals.js +149 -0
- package/dist/stx-virtual-ts.d.ts +103 -1
- package/dist/stx-virtual-ts.js +124 -7
- package/dist/template-compiler.js +4 -2
- package/dist/template-hydrator.d.ts +1 -1
- package/dist/template-hydrator.js +13 -1
- package/dist/typecheck.d.ts +2 -14
- package/dist/typecheck.js +133 -11
- package/dist/types/config-types.d.ts +10 -3
- package/dist/variable-extractor.js +1 -1
- package/package.json +5 -5
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";
|
|
@@ -6,15 +6,20 @@ import {
|
|
|
6
6
|
buildVirtualTypeScript,
|
|
7
7
|
extractScriptBlocks,
|
|
8
8
|
resolvePosition,
|
|
9
|
-
|
|
9
|
+
clientPayloadDeclarations,
|
|
10
|
+
scrapedBridgeNames,
|
|
11
|
+
serverContextDeclarations,
|
|
12
|
+
substituteInterpolationsInPlace
|
|
10
13
|
} from "./stx-virtual-ts.js";
|
|
11
14
|
export {
|
|
12
15
|
extractScriptBlocks,
|
|
13
16
|
extractTemplateExpressions
|
|
14
17
|
} from "./stx-virtual-ts.js";
|
|
15
|
-
export function buildVirtualSource(block) {
|
|
16
|
-
|
|
17
|
-
`.repeat(block.startLine - 1) : "")
|
|
18
|
+
export function buildVirtualSource(block, serverCode = "") {
|
|
19
|
+
const leadingNewlines = block.startLine > 1 ? `
|
|
20
|
+
`.repeat(block.startLine - 1) : "", code = substituteInterpolationsInPlace(block.code), payload = block.kind === "client" || block.kind === "plain" ? clientPayloadDeclarations(serverCode) : "";
|
|
21
|
+
return `${leadingNewlines + code}
|
|
22
|
+
${payload}
|
|
18
23
|
export {}
|
|
19
24
|
`;
|
|
20
25
|
}
|
|
@@ -34,8 +39,91 @@ export function findRuntimeTypeDeclarations() {
|
|
|
34
39
|
return null;
|
|
35
40
|
}
|
|
36
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
|
+
}
|
|
37
125
|
export async function typecheckStxFiles(files, options = {}) {
|
|
38
|
-
const checkClient = options.client !== !1, checkServer = options.server !== !1, checkTemplates = options.templates !== !1, virtualFiles = new Map, checkedFiles = [];
|
|
126
|
+
const checkClient = options.client !== !1, checkServer = options.server !== !1, checkTemplates = options.templates !== !1, virtualFiles = new Map, scrapedByFile = new Map, checkedFiles = [];
|
|
39
127
|
let blockCount = 0, expressionCount = 0;
|
|
40
128
|
const runtimeTypes = findRuntimeTypeDeclarations();
|
|
41
129
|
for (const file of files) {
|
|
@@ -45,7 +133,14 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
45
133
|
if (b.kind === "client")
|
|
46
134
|
return checkClient;
|
|
47
135
|
return !1;
|
|
48
|
-
}),
|
|
136
|
+
}), serverCode = extractScriptBlocks(source).filter((block) => block.kind === "server").map((block) => block.code).join(`
|
|
137
|
+
`);
|
|
138
|
+
if (checkClient && serverCode) {
|
|
139
|
+
const clientBlocks = blocks.filter((block) => block.kind === "client"), names = [...new Set(clientBlocks.flatMap((block) => scrapedBridgeNames(serverCode, block.code)))];
|
|
140
|
+
if (names.length > 0)
|
|
141
|
+
scrapedByFile.set(file, { names, line: clientBlocks[0].startLine });
|
|
142
|
+
}
|
|
143
|
+
const templateBuffer = checkTemplates ? buildVirtualTypeScript(source, { runtimeGlobals: !runtimeTypes }) : null, expressions = templateBuffer ? [...templateBuffer.lineMap.values()].filter((m) => m.expression).length : 0;
|
|
49
144
|
if (blocks.length === 0 && expressions === 0)
|
|
50
145
|
continue;
|
|
51
146
|
checkedFiles.push(file);
|
|
@@ -53,7 +148,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
53
148
|
expressionCount += expressions;
|
|
54
149
|
blocks.forEach((block, i) => {
|
|
55
150
|
virtualFiles.set(virtualPathFor(file, block.kind, i), {
|
|
56
|
-
source: buildVirtualSource(block),
|
|
151
|
+
source: buildVirtualSource(block, serverCode),
|
|
57
152
|
origin: file,
|
|
58
153
|
kind: block.kind
|
|
59
154
|
});
|
|
@@ -68,7 +163,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
68
163
|
}
|
|
69
164
|
if (virtualFiles.size === 0)
|
|
70
165
|
return { diagnostics: [], checkedFiles, blockCount: 0, expressionCount: 0 };
|
|
71
|
-
const ambient =
|
|
166
|
+
const ambient = (options.extraLibs ?? []).map((lib) => path.resolve(lib));
|
|
72
167
|
if (runtimeTypes)
|
|
73
168
|
ambient.push(runtimeTypes);
|
|
74
169
|
const globalsDts = `${stateDir()}/typecheck/__stx_globals.d.ts`, globalDecls = [
|
|
@@ -100,6 +195,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
100
195
|
resolveJsonModule: !0,
|
|
101
196
|
lib: ["ESNext", "DOM", "DOM.Iterable"],
|
|
102
197
|
types: [],
|
|
198
|
+
...readProjectPathAliases(files.length > 0 ? path.dirname(path.resolve(files[0])) : process.cwd()),
|
|
103
199
|
...options.compilerOptions ?? {}
|
|
104
200
|
},
|
|
105
201
|
files: [...writtenToOrigin.keys()].map((name) => `${workDir}/${name}`).concat(globalsDts, ...ambient)
|
|
@@ -108,12 +204,18 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
108
204
|
const proc = Bun.spawnSync(["bun", "x", "tsc", "-p", tsconfigPath, "--pretty", "false"], {
|
|
109
205
|
stdout: "pipe",
|
|
110
206
|
stderr: "pipe"
|
|
111
|
-
}), 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;
|
|
112
211
|
for (const rawLine of output.split(`
|
|
113
212
|
`)) {
|
|
114
213
|
const m = rawLine.match(TSC_LINE_RE);
|
|
115
214
|
if (!m)
|
|
116
215
|
continue;
|
|
216
|
+
printedDiagnostics++;
|
|
217
|
+
if (ambientBasenames.has(m[1].split("/").pop()))
|
|
218
|
+
ambientDiagnostics++;
|
|
117
219
|
const [, filePart, lineStr, colStr, category, code, message] = m, entry = writtenToOrigin.get(filePart.split("/").pop());
|
|
118
220
|
if (!entry)
|
|
119
221
|
continue;
|
|
@@ -122,6 +224,8 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
122
224
|
const at = resolvePosition(entry.virtual, line, column);
|
|
123
225
|
if (!at?.expression)
|
|
124
226
|
continue;
|
|
227
|
+
if (Number(code) === 2774)
|
|
228
|
+
continue;
|
|
125
229
|
line = at.line;
|
|
126
230
|
column = at.column;
|
|
127
231
|
expression = at.expression.code.trim();
|
|
@@ -137,6 +241,20 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
137
241
|
expression
|
|
138
242
|
});
|
|
139
243
|
}
|
|
244
|
+
if (checkClient)
|
|
245
|
+
for (const [file, scraped] of scrapedByFile) {
|
|
246
|
+
if (scraped.names.length === 0)
|
|
247
|
+
continue;
|
|
248
|
+
diagnostics.push({
|
|
249
|
+
file,
|
|
250
|
+
line: scraped.line,
|
|
251
|
+
column: 1,
|
|
252
|
+
code: 0,
|
|
253
|
+
message: `${scraped.names.length === 1 ? "1 value reaches" : `${scraped.names.length} values reach`} this block on the implicit bridge and ${scraped.names.length === 1 ? "is" : "are"} untyped: ${scraped.names.join(", ")}. Declare them with defineClientPayload({ ${scraped.names.join(", ")} }) in the server block to have them checked.`,
|
|
254
|
+
category: "warning",
|
|
255
|
+
blockKind: "client"
|
|
256
|
+
});
|
|
257
|
+
}
|
|
140
258
|
const seen = new Set, unique = diagnostics.filter((d) => {
|
|
141
259
|
const key = `${d.file}:${d.line}:${d.column}:${d.code}:${d.message}`;
|
|
142
260
|
if (seen.has(key))
|
|
@@ -145,7 +263,11 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
145
263
|
return !0;
|
|
146
264
|
});
|
|
147
265
|
unique.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column);
|
|
148
|
-
|
|
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 };
|
|
149
271
|
}
|
|
150
272
|
export function formatTypecheckDiagnostics(diagnostics) {
|
|
151
273
|
return diagnostics.map((d) => {
|
|
@@ -344,6 +344,16 @@ export interface StrictModeConfig {
|
|
|
344
344
|
failOnViolation?: boolean;
|
|
345
345
|
/** Pattern strings to allow through validation (matched against message or regex source) */
|
|
346
346
|
allowPatterns?: string[];
|
|
347
|
+
/**
|
|
348
|
+
* What to do when a `<script client>` block does not bundle.
|
|
349
|
+
*
|
|
350
|
+
* `'warn'` (default) ships the ORIGINAL, unbundled source so the page still
|
|
351
|
+
* renders — right for a dev server, and why the error overlay exists.
|
|
352
|
+
* `'error'` refuses instead, for anyone who would rather nothing rendered
|
|
353
|
+
* than a page whose bindings quietly do nothing. `stx build` fails on a
|
|
354
|
+
* bundle error either way. See #1884.
|
|
355
|
+
*/
|
|
356
|
+
bundlerFallback?: 'warn' | 'error';
|
|
347
357
|
}
|
|
348
358
|
/**
|
|
349
359
|
* Real-time broadcasting configuration.
|
|
@@ -655,9 +665,6 @@ export interface StxConfig {
|
|
|
655
665
|
* rendered than a page whose bindings quietly do nothing. `stx build` fails on
|
|
656
666
|
* a bundle error either way. See #1884.
|
|
657
667
|
*/
|
|
658
|
-
strict?: {
|
|
659
|
-
bundlerFallback?: 'warn' | 'error';
|
|
660
|
-
};
|
|
661
668
|
/** Directory to store cached templates, defaults to `cache` inside `stateDir` */
|
|
662
669
|
cachePath: string;
|
|
663
670
|
/** Custom directives registered by the user */
|
|
@@ -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.172",
|
|
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.172",
|
|
121
|
+
"@stacksjs/sanitizer": "0.2.172",
|
|
122
122
|
"@stacksjs/ts-i18n": "^0.1.11",
|
|
123
|
-
"bun-plugin-stx": "0.2.
|
|
123
|
+
"bun-plugin-stx": "0.2.172",
|
|
124
124
|
"bunfig": "^0.15.17",
|
|
125
|
-
"stx-router": "0.2.
|
|
125
|
+
"stx-router": "0.2.172",
|
|
126
126
|
"ts-broadcasting": "^0.0.9",
|
|
127
127
|
"ts-images": "^0.1.9",
|
|
128
128
|
"ts-syntax-highlighter": "^0.2.1"
|