@weave-framework/cli 0.2.53 → 0.2.107
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/README.md +21 -0
- package/dist/cli.js +158 -25
- package/dist/styles.d.ts +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @weave-framework/cli
|
|
2
|
+
|
|
3
|
+
The Weave CLI — `weave build`, `weave dev` (watch + live-reload), `weave check`, `weave routes`.
|
|
4
|
+
|
|
5
|
+
Part of **[Weave](https://weave-framework.github.io/weave/)** — a fine-grained reactive, signal-native UI framework: no Virtual DOM, zero third-party runtime dependencies.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D @weave-framework/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Scaffolded apps already include it (with scripts wired up):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm create weave@latest my-app
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
📚 **Guides + full API reference:** [weave-framework.github.io/weave](https://weave-framework.github.io/weave/)
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -741,10 +741,11 @@ function rewrite(expr, scope, ctxRef = "ctx") {
|
|
|
741
741
|
const name = expr.slice(i, j);
|
|
742
742
|
const isProperty = lastNonSpace(out) === ".";
|
|
743
743
|
const binding = scope.get(name);
|
|
744
|
-
|
|
744
|
+
const prev = lastNonSpace(out);
|
|
745
|
+
const next = firstNonSpaceFrom(expr, j);
|
|
746
|
+
const isObjectKey = (prev === "{" || prev === ",") && next === ":";
|
|
747
|
+
if (binding && !isProperty && !isObjectKey) {
|
|
745
748
|
if (binding.kind !== "local") {
|
|
746
|
-
const prev = lastNonSpace(out);
|
|
747
|
-
const next = firstNonSpaceFrom(expr, j);
|
|
748
749
|
if ((prev === "{" || prev === ",") && (next === "," || next === "}")) insert(`${name}: `);
|
|
749
750
|
}
|
|
750
751
|
if (binding.kind === "ctx") {
|
|
@@ -943,8 +944,11 @@ function freeIdentifiers(expr) {
|
|
|
943
944
|
let j = i + 1;
|
|
944
945
|
while (j < n && ID_CHAR.test(expr[j])) j++;
|
|
945
946
|
const name = expr.slice(i, j);
|
|
946
|
-
const
|
|
947
|
-
|
|
947
|
+
const prev = lastNonSpace(expr.slice(0, i));
|
|
948
|
+
const next = firstNonSpaceFrom(expr, j);
|
|
949
|
+
const isProperty = prev === ".";
|
|
950
|
+
const isObjectKey = (prev === "{" || prev === ",") && next === ":";
|
|
951
|
+
if (!isProperty && !isObjectKey && !NON_CTX.has(name) && !params.has(name)) out.add(name);
|
|
948
952
|
i = j;
|
|
949
953
|
continue;
|
|
950
954
|
}
|
|
@@ -978,6 +982,8 @@ var Gen = class {
|
|
|
978
982
|
// @weave-framework/runtime/dom helpers
|
|
979
983
|
usedCore = /* @__PURE__ */ new Set();
|
|
980
984
|
// @weave-framework/runtime primitives (computed, …)
|
|
985
|
+
usedComponents = /* @__PURE__ */ new Set();
|
|
986
|
+
// PascalCase child tags referenced in module mode
|
|
981
987
|
templates = [];
|
|
982
988
|
tplN = 0;
|
|
983
989
|
fnN = 0;
|
|
@@ -989,8 +995,14 @@ var Gen = class {
|
|
|
989
995
|
this.usedCore.add(name);
|
|
990
996
|
return this.mode === "function" ? `rt.${name}` : name;
|
|
991
997
|
}
|
|
992
|
-
/**
|
|
998
|
+
/**
|
|
999
|
+
* Reference a child component: from the `_c` map in function mode, bare (imported)
|
|
1000
|
+
* in module mode. In module mode the bare identifier must be in the emitted module's
|
|
1001
|
+
* scope — the loader resolves each recorded tag to a real `import` (see the plugin's
|
|
1002
|
+
* child-import injection), or the component's own `<script>` imports it explicitly.
|
|
1003
|
+
*/
|
|
993
1004
|
Comp(name) {
|
|
1005
|
+
this.usedComponents.add(name);
|
|
994
1006
|
return this.mode === "function" ? `_c.${name}` : name;
|
|
995
1007
|
}
|
|
996
1008
|
tpl(html) {
|
|
@@ -1008,15 +1020,23 @@ function compileTemplate(input, options = {}) {
|
|
|
1008
1020
|
const gen = new Gen(mode, options.scopeAttr, options.hostAttr);
|
|
1009
1021
|
const ast = parseTemplate(input);
|
|
1010
1022
|
const render = compileFragment(gen, ast, ctxScope(options.scope ?? []), "render", "ctx, slots", true);
|
|
1023
|
+
const components = [...gen.usedComponents];
|
|
1011
1024
|
if (mode === "function") {
|
|
1012
1025
|
const body = [...gen.templates, render, "return render(ctx, {});"].join("\n");
|
|
1013
|
-
return { code: body };
|
|
1026
|
+
return { code: body, components };
|
|
1014
1027
|
}
|
|
1015
1028
|
const domImport = `import { ${[...gen.used].sort().join(", ")} } from ${JSON.stringify(runtimeImport)};`;
|
|
1016
1029
|
const coreImport = gen.usedCore.size ? `import { ${[...gen.usedCore].sort().join(", ")} } from "@weave-framework/runtime";
|
|
1017
1030
|
` : "";
|
|
1018
1031
|
const code = [domImport + "\n" + coreImport, ...gen.templates, `export default ${render}`].join("\n");
|
|
1019
|
-
return { code };
|
|
1032
|
+
return { code, components };
|
|
1033
|
+
}
|
|
1034
|
+
function pascalToKebab(tag) {
|
|
1035
|
+
return tag.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
1036
|
+
}
|
|
1037
|
+
function childImportCandidates(tag) {
|
|
1038
|
+
const k = pascalToKebab(tag);
|
|
1039
|
+
return [`../${k}/${k}`, `./${k}`, `./${k}/${k}`];
|
|
1020
1040
|
}
|
|
1021
1041
|
function compileFragment(gen, nodes, scope, name, param = "", isHost = false) {
|
|
1022
1042
|
const top = trimTop(nodes);
|
|
@@ -1852,7 +1872,7 @@ function compileComponent(src, opts = {}) {
|
|
|
1852
1872
|
renderBody,
|
|
1853
1873
|
`export default defineComponent(${setupArg});`
|
|
1854
1874
|
].filter(Boolean).join("\n\n");
|
|
1855
|
-
return { code, css, hash };
|
|
1875
|
+
return { code, css, hash, components: compiled.components };
|
|
1856
1876
|
}
|
|
1857
1877
|
function parseSfcLoc(source) {
|
|
1858
1878
|
const script = locateBlock(source, "script");
|
|
@@ -1916,7 +1936,7 @@ function extractBlock(source, tag) {
|
|
|
1916
1936
|
}
|
|
1917
1937
|
|
|
1918
1938
|
// packages/compiler/src/sources.ts
|
|
1919
|
-
var DECL = /export\s+const\s+(template|styles)\s*(?::[
|
|
1939
|
+
var DECL = /export\s+const\s+(template|styles)\s*(?::[^=\n]+)?=\s*/g;
|
|
1920
1940
|
function extractSources(script) {
|
|
1921
1941
|
let template;
|
|
1922
1942
|
let templateRange;
|
|
@@ -1948,10 +1968,30 @@ function extractSources(script) {
|
|
|
1948
1968
|
function parseLiteral(src, i, kind) {
|
|
1949
1969
|
i = skipWs(src, i);
|
|
1950
1970
|
const c = src[i];
|
|
1951
|
-
if (c === '"' || c === "'" || c === "`") return
|
|
1971
|
+
if (c === '"' || c === "'" || c === "`") return parseConcat(src, i);
|
|
1952
1972
|
if (c === "[") return parseArray(src, i);
|
|
1953
1973
|
throw new Error(`weave: \`${kind}\` must be a static string${kind === "styles" ? " or array of strings" : ""}`);
|
|
1954
1974
|
}
|
|
1975
|
+
function parseConcat(src, i) {
|
|
1976
|
+
const first = parseString(src, i);
|
|
1977
|
+
let value = first.value;
|
|
1978
|
+
let end = first.end;
|
|
1979
|
+
let single = true;
|
|
1980
|
+
for (; ; ) {
|
|
1981
|
+
const plus = skipWs(src, end);
|
|
1982
|
+
if (src[plus] !== "+") break;
|
|
1983
|
+
const nextStart = skipWs(src, plus + 1);
|
|
1984
|
+
const c = src[nextStart];
|
|
1985
|
+
if (c !== '"' && c !== "'" && c !== "`") {
|
|
1986
|
+
throw new Error("weave: `template`/`styles` must be a static string \u2014 `+` may only join string literals");
|
|
1987
|
+
}
|
|
1988
|
+
const next = parseString(src, nextStart);
|
|
1989
|
+
value += next.value;
|
|
1990
|
+
end = next.end;
|
|
1991
|
+
single = false;
|
|
1992
|
+
}
|
|
1993
|
+
return single ? { value, end, innerStart: first.innerStart, innerEnd: first.innerEnd } : { value, end };
|
|
1994
|
+
}
|
|
1955
1995
|
function parseString(src, i) {
|
|
1956
1996
|
const quote = src[i];
|
|
1957
1997
|
const innerStart = i + 1;
|
|
@@ -1983,7 +2023,7 @@ function parseArray(src, i) {
|
|
|
1983
2023
|
j++;
|
|
1984
2024
|
continue;
|
|
1985
2025
|
}
|
|
1986
|
-
const str =
|
|
2026
|
+
const str = parseConcat(src, j);
|
|
1987
2027
|
items.push(str.value);
|
|
1988
2028
|
j = str.end;
|
|
1989
2029
|
}
|
|
@@ -2028,15 +2068,18 @@ function langFromExt(file) {
|
|
|
2028
2068
|
if (file.endsWith(".sass")) return "sass";
|
|
2029
2069
|
return "css";
|
|
2030
2070
|
}
|
|
2071
|
+
function pkgImporters(sass) {
|
|
2072
|
+
return [new sass.NodePackageImporter()];
|
|
2073
|
+
}
|
|
2031
2074
|
async function compileStyleFile(path) {
|
|
2032
2075
|
if (langFromExt(path) === "css") return readFile(path, "utf8");
|
|
2033
2076
|
const sass = await import("sass");
|
|
2034
|
-
return sass.compile(path).css;
|
|
2077
|
+
return sass.compile(path, { importers: pkgImporters(sass) }).css;
|
|
2035
2078
|
}
|
|
2036
2079
|
async function compileStyleFileTracked(path) {
|
|
2037
2080
|
if (langFromExt(path) === "css") return { css: await readFile(path, "utf8"), files: [path] };
|
|
2038
2081
|
const sass = await import("sass");
|
|
2039
|
-
const result = sass.compile(path);
|
|
2082
|
+
const result = sass.compile(path, { importers: pkgImporters(sass) });
|
|
2040
2083
|
const files = result.loadedUrls.filter((u) => u.protocol === "file:").map((u) => fileURLToPath(u));
|
|
2041
2084
|
return { css: result.css, files };
|
|
2042
2085
|
}
|
|
@@ -2045,15 +2088,24 @@ async function compileStyleSource(source, lang, fromDir) {
|
|
|
2045
2088
|
const sass = await import("sass");
|
|
2046
2089
|
return sass.compileString(source, {
|
|
2047
2090
|
syntax: lang === "sass" ? "indented" : "scss",
|
|
2048
|
-
loadPaths: fromDir ? [fromDir] : []
|
|
2091
|
+
loadPaths: fromDir ? [fromDir] : [],
|
|
2092
|
+
importers: pkgImporters(sass)
|
|
2049
2093
|
}).css;
|
|
2050
2094
|
}
|
|
2051
2095
|
|
|
2052
2096
|
// packages/cli/src/plugin.ts
|
|
2097
|
+
function styleId(css) {
|
|
2098
|
+
let h = 5381;
|
|
2099
|
+
for (let i = 0; i < css.length; i++) h = Math.imul(h, 33) ^ css.charCodeAt(i) | 0;
|
|
2100
|
+
return "w-css-" + (h >>> 0).toString(36);
|
|
2101
|
+
}
|
|
2053
2102
|
function cssInjector(css) {
|
|
2054
2103
|
if (!css) return "";
|
|
2104
|
+
const id = styleId(css);
|
|
2055
2105
|
return `
|
|
2056
|
-
;(()=>{const
|
|
2106
|
+
;(()=>{const id=${JSON.stringify(
|
|
2107
|
+
id
|
|
2108
|
+
)};if(document.getElementById(id))return;const s=document.createElement("style");s.id=id;s.textContent=${JSON.stringify(
|
|
2057
2109
|
css
|
|
2058
2110
|
)};document.head.appendChild(s);})();
|
|
2059
2111
|
`;
|
|
@@ -2100,6 +2152,78 @@ async function resolveStyles(decl, tsPath, dir, styleLang) {
|
|
|
2100
2152
|
const compiled = await compileStyleFileTracked(siblingStyle);
|
|
2101
2153
|
return { css: compiled.css, files: compiled.files };
|
|
2102
2154
|
}
|
|
2155
|
+
function stripComments(code) {
|
|
2156
|
+
let out = "";
|
|
2157
|
+
let i = 0;
|
|
2158
|
+
const n = code.length;
|
|
2159
|
+
while (i < n) {
|
|
2160
|
+
const c = code[i];
|
|
2161
|
+
const d = code[i + 1];
|
|
2162
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
2163
|
+
const quote = c;
|
|
2164
|
+
out += c;
|
|
2165
|
+
i++;
|
|
2166
|
+
while (i < n) {
|
|
2167
|
+
const ch = code[i];
|
|
2168
|
+
if (ch === "\\") {
|
|
2169
|
+
out += ch + (code[i + 1] ?? "");
|
|
2170
|
+
i += 2;
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
out += ch;
|
|
2174
|
+
i++;
|
|
2175
|
+
if (ch === quote) break;
|
|
2176
|
+
}
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
if (c === "/" && d === "/") {
|
|
2180
|
+
while (i < n && code[i] !== "\n") i++;
|
|
2181
|
+
continue;
|
|
2182
|
+
}
|
|
2183
|
+
if (c === "/" && d === "*") {
|
|
2184
|
+
i += 2;
|
|
2185
|
+
while (i < n && !(code[i] === "*" && code[i + 1] === "/")) i++;
|
|
2186
|
+
i += 2;
|
|
2187
|
+
continue;
|
|
2188
|
+
}
|
|
2189
|
+
out += c;
|
|
2190
|
+
i++;
|
|
2191
|
+
}
|
|
2192
|
+
return out;
|
|
2193
|
+
}
|
|
2194
|
+
function importsBinding(script, name) {
|
|
2195
|
+
if (!script) return false;
|
|
2196
|
+
const code = stripComments(script);
|
|
2197
|
+
const word = new RegExp(`\\b${name}\\b`);
|
|
2198
|
+
const IMPORT = /import\s+([^;]*?)\s+from\s+['"][^'"]+['"]/g;
|
|
2199
|
+
let m;
|
|
2200
|
+
while ((m = IMPORT.exec(code)) !== null) {
|
|
2201
|
+
if (word.test(m[1])) return true;
|
|
2202
|
+
}
|
|
2203
|
+
return false;
|
|
2204
|
+
}
|
|
2205
|
+
function resolveChildModule(tag, dir) {
|
|
2206
|
+
for (const cand of childImportCandidates(tag)) {
|
|
2207
|
+
for (const ext of [".ts", ".weave"]) {
|
|
2208
|
+
if (existsSync(resolve(dir, cand + ext))) return cand;
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
return null;
|
|
2212
|
+
}
|
|
2213
|
+
function injectChildImports(code, components, dir, script, filename) {
|
|
2214
|
+
const imports = [];
|
|
2215
|
+
for (const tag of components) {
|
|
2216
|
+
if (importsBinding(script, tag)) continue;
|
|
2217
|
+
const cand = resolveChildModule(tag, dir);
|
|
2218
|
+
if (cand === null) {
|
|
2219
|
+
throw new Error(
|
|
2220
|
+
`weave: ${filename} composes <${tag}> but no import for it was found. Import it in the component's script, or place its module at ${childImportCandidates(tag).map((c) => `${c}.ts`).join(" / ")} (relative to the component).`
|
|
2221
|
+
);
|
|
2222
|
+
}
|
|
2223
|
+
imports.push(`import ${tag} from ${JSON.stringify(cand + ".js")};`);
|
|
2224
|
+
}
|
|
2225
|
+
return imports.length ? imports.join("\n") + "\n" + code : code;
|
|
2226
|
+
}
|
|
2103
2227
|
function weave(state, options = {}) {
|
|
2104
2228
|
const styleLang = options.styleLang ?? "css";
|
|
2105
2229
|
const dev2 = options.dev ?? false;
|
|
@@ -2118,8 +2242,9 @@ function weave(state, options = {}) {
|
|
|
2118
2242
|
const source = await readFile2(args.path, "utf8");
|
|
2119
2243
|
const src = parseSfc(source);
|
|
2120
2244
|
const styles = src.styles ? await compileStyleSource(src.styles, styleLang, dirname(args.path)) : void 0;
|
|
2121
|
-
const { code, css } = compileComponent({ ...src, styles }, { filename: args.path });
|
|
2122
|
-
|
|
2245
|
+
const { code, css, components } = compileComponent({ ...src, styles }, { filename: args.path });
|
|
2246
|
+
const wired = injectChildImports(code, components, dirname(args.path), src.script, args.path);
|
|
2247
|
+
return emit2(wired, css, dirname(args.path));
|
|
2123
2248
|
});
|
|
2124
2249
|
build2.onLoad({ filter: /\.ts$/ }, async (args) => {
|
|
2125
2250
|
if (args.path.includes("node_modules")) return void 0;
|
|
@@ -2142,11 +2267,12 @@ function weave(state, options = {}) {
|
|
|
2142
2267
|
dir,
|
|
2143
2268
|
styleLang
|
|
2144
2269
|
);
|
|
2145
|
-
const { code, css } = compileComponent(
|
|
2270
|
+
const { code, css, components } = compileComponent(
|
|
2146
2271
|
{ script: decl.script, template: template.text, styles: styles.css },
|
|
2147
2272
|
{ filename: args.path }
|
|
2148
2273
|
);
|
|
2149
|
-
|
|
2274
|
+
const wired = injectChildImports(code, components, dir, decl.script, args.path);
|
|
2275
|
+
return { ...emit2(wired, css, dir), watchFiles: [...template.files, ...styles.files] };
|
|
2150
2276
|
});
|
|
2151
2277
|
}
|
|
2152
2278
|
};
|
|
@@ -2309,7 +2435,7 @@ async function build(config) {
|
|
|
2309
2435
|
import { context } from "esbuild";
|
|
2310
2436
|
import { createServer } from "node:http";
|
|
2311
2437
|
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile4 } from "node:fs/promises";
|
|
2312
|
-
import { join as join3, extname, relative as relative2, sep as sep2 } from "node:path";
|
|
2438
|
+
import { join as join3, extname, relative as relative2, sep as sep2, isAbsolute } from "node:path";
|
|
2313
2439
|
var RELOAD_PATH = "/__weave_reload";
|
|
2314
2440
|
var MIME = {
|
|
2315
2441
|
".html": "text/html; charset=utf-8",
|
|
@@ -2336,7 +2462,7 @@ async function devInMemory(config) {
|
|
|
2336
2462
|
const css = (await Promise.all(config.styles.map(compileStyleFile))).join("\n");
|
|
2337
2463
|
if (css)
|
|
2338
2464
|
banner = {
|
|
2339
|
-
js: `(()=>{const s=document.createElement("style");s.textContent=${JSON.stringify(
|
|
2465
|
+
js: `(()=>{const id="w-global-styles";if(document.getElementById(id))return;const s=document.createElement("style");s.id=id;s.textContent=${JSON.stringify(
|
|
2340
2466
|
css
|
|
2341
2467
|
)};document.head.appendChild(s);})();`
|
|
2342
2468
|
};
|
|
@@ -2401,8 +2527,15 @@ async function handleRequest(req, res, config, outputs, clients) {
|
|
|
2401
2527
|
return;
|
|
2402
2528
|
}
|
|
2403
2529
|
if (extname(url)) {
|
|
2530
|
+
const target = join3(config.servedir, url);
|
|
2531
|
+
const rel = relative2(config.servedir, target);
|
|
2532
|
+
if (rel === ".." || rel.startsWith(".." + sep2) || isAbsolute(rel)) {
|
|
2533
|
+
res.writeHead(403);
|
|
2534
|
+
res.end("Forbidden");
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2404
2537
|
try {
|
|
2405
|
-
const buf = await readFile4(
|
|
2538
|
+
const buf = await readFile4(target);
|
|
2406
2539
|
res.writeHead(200, { "content-type": mime(url) });
|
|
2407
2540
|
res.end(buf);
|
|
2408
2541
|
} catch {
|
|
@@ -2604,7 +2737,7 @@ function generateRoutes(dir, opts = {}) {
|
|
|
2604
2737
|
import { build as esbuildBuild } from "esbuild";
|
|
2605
2738
|
import { existsSync as existsSync4 } from "node:fs";
|
|
2606
2739
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2607
|
-
import { resolve as resolve2, dirname as dirname2, join as join5, isAbsolute } from "node:path";
|
|
2740
|
+
import { resolve as resolve2, dirname as dirname2, join as join5, isAbsolute as isAbsolute2 } from "node:path";
|
|
2608
2741
|
function defineConfig(config) {
|
|
2609
2742
|
return config;
|
|
2610
2743
|
}
|
|
@@ -2648,7 +2781,7 @@ async function importConfigModule(file) {
|
|
|
2648
2781
|
return mod.default ?? mod;
|
|
2649
2782
|
}
|
|
2650
2783
|
function resolveConfig(raw, root) {
|
|
2651
|
-
const abs = (p) =>
|
|
2784
|
+
const abs = (p) => isAbsolute2(p) ? p : resolve2(root, p);
|
|
2652
2785
|
if (!raw.root && !raw.entry) {
|
|
2653
2786
|
throw new Error("weave: config must declare either `root` (generated bootstrap) or `entry` (hand-written)");
|
|
2654
2787
|
}
|
package/dist/styles.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export type StyleLang = 'css' | 'scss' | 'sass';
|
|
11
11
|
/** The style language implied by a file extension (defaults to plain CSS). */
|
|
12
12
|
export declare function langFromExt(file: string): StyleLang;
|
|
13
|
-
/** Compile a style FILE to CSS — `@use`/`@import` resolve relative to it. */
|
|
13
|
+
/** Compile a style FILE to CSS — `@use`/`@import` resolve relative to it (+ `pkg:` packages). */
|
|
14
14
|
export declare function compileStyleFile(path: string): Promise<string>;
|
|
15
15
|
/**
|
|
16
16
|
* Like {@link compileStyleFile} but also reports every file the compile pulled in —
|