@weave-framework/cli 0.2.0 → 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 +343 -81
- 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
|
@@ -411,13 +411,7 @@ var Parser = class {
|
|
|
411
411
|
return this.src[this.pos];
|
|
412
412
|
}
|
|
413
413
|
readInterp() {
|
|
414
|
-
this.
|
|
415
|
-
const start = this.pos;
|
|
416
|
-
const end = this.src.indexOf("}}", this.pos);
|
|
417
|
-
if (end === -1) throw new ParseError("Unclosed {{ interpolation");
|
|
418
|
-
this.pos = end + 2;
|
|
419
|
-
const raw = this.src.slice(start, end);
|
|
420
|
-
return { expr: raw.trim(), offset: start + (raw.length - raw.trimStart().length) };
|
|
414
|
+
return this.readDoubleBracedExpr();
|
|
421
415
|
}
|
|
422
416
|
skipComment() {
|
|
423
417
|
const end = this.src.indexOf("-->", this.pos);
|
|
@@ -684,19 +678,76 @@ function rewrite(expr, scope, ctxRef = "ctx") {
|
|
|
684
678
|
};
|
|
685
679
|
while (i < n) {
|
|
686
680
|
const c = expr[i];
|
|
687
|
-
if (c === '"' || c === "'"
|
|
681
|
+
if (c === '"' || c === "'") {
|
|
688
682
|
const end = scanString(expr, i);
|
|
689
683
|
copy(i, expr.slice(i, end));
|
|
690
684
|
i = end;
|
|
691
685
|
continue;
|
|
692
686
|
}
|
|
687
|
+
if (c === "`") {
|
|
688
|
+
copy(i, "`");
|
|
689
|
+
let k = i + 1;
|
|
690
|
+
while (k < n) {
|
|
691
|
+
const ch = expr[k];
|
|
692
|
+
if (ch === "\\") {
|
|
693
|
+
copy(k, expr.slice(k, k + 2));
|
|
694
|
+
k += 2;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (ch === "`") {
|
|
698
|
+
copy(k, "`");
|
|
699
|
+
k++;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
if (ch === "$" && expr[k + 1] === "{") {
|
|
703
|
+
copy(k, "${");
|
|
704
|
+
k += 2;
|
|
705
|
+
const exprStart = k;
|
|
706
|
+
let depth = 1;
|
|
707
|
+
while (k < n && depth > 0) {
|
|
708
|
+
const mc = expr[k];
|
|
709
|
+
if (mc === '"' || mc === "'" || mc === "`") {
|
|
710
|
+
k = scanString(expr, k);
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
if (mc === "{") depth++;
|
|
714
|
+
else if (mc === "}") {
|
|
715
|
+
depth--;
|
|
716
|
+
if (depth === 0) break;
|
|
717
|
+
}
|
|
718
|
+
k++;
|
|
719
|
+
}
|
|
720
|
+
const sub = rewrite(expr.slice(exprStart, k), scope, ctxRef);
|
|
721
|
+
if (sub.reactive) reactive = true;
|
|
722
|
+
flush();
|
|
723
|
+
const genStart = out.length;
|
|
724
|
+
for (const s of sub.segments) segments.push({ src: exprStart + s.src, gen: genStart + s.gen, len: s.len });
|
|
725
|
+
out += sub.code;
|
|
726
|
+
if (expr[k] === "}") {
|
|
727
|
+
copy(k, "}");
|
|
728
|
+
k++;
|
|
729
|
+
}
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
copy(k, ch);
|
|
733
|
+
k++;
|
|
734
|
+
}
|
|
735
|
+
i = k;
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
693
738
|
if (ID_START.test(c)) {
|
|
694
739
|
let j = i + 1;
|
|
695
740
|
while (j < n && ID_CHAR.test(expr[j])) j++;
|
|
696
741
|
const name = expr.slice(i, j);
|
|
697
742
|
const isProperty = lastNonSpace(out) === ".";
|
|
698
743
|
const binding = scope.get(name);
|
|
699
|
-
|
|
744
|
+
const prev = lastNonSpace(out);
|
|
745
|
+
const next = firstNonSpaceFrom(expr, j);
|
|
746
|
+
const isObjectKey = (prev === "{" || prev === ",") && next === ":";
|
|
747
|
+
if (binding && !isProperty && !isObjectKey) {
|
|
748
|
+
if (binding.kind !== "local") {
|
|
749
|
+
if ((prev === "{" || prev === ",") && (next === "," || next === "}")) insert(`${name}: `);
|
|
750
|
+
}
|
|
700
751
|
if (binding.kind === "ctx") {
|
|
701
752
|
insert(`${ctxRef}.`);
|
|
702
753
|
copy(i, name);
|
|
@@ -736,6 +787,12 @@ function lastNonSpace(s) {
|
|
|
736
787
|
}
|
|
737
788
|
return "";
|
|
738
789
|
}
|
|
790
|
+
function firstNonSpaceFrom(s, from) {
|
|
791
|
+
for (let i = from; i < s.length; i++) {
|
|
792
|
+
if (!/\s/.test(s[i])) return s[i];
|
|
793
|
+
}
|
|
794
|
+
return "";
|
|
795
|
+
}
|
|
739
796
|
var NON_CTX = /* @__PURE__ */ new Set([
|
|
740
797
|
// literals / keywords
|
|
741
798
|
"true",
|
|
@@ -842,16 +899,56 @@ function freeIdentifiers(expr) {
|
|
|
842
899
|
const n = expr.length;
|
|
843
900
|
while (i < n) {
|
|
844
901
|
const c = expr[i];
|
|
845
|
-
if (c === '"' || c === "'"
|
|
902
|
+
if (c === '"' || c === "'") {
|
|
846
903
|
i = scanString(expr, i);
|
|
847
904
|
continue;
|
|
848
905
|
}
|
|
906
|
+
if (c === "`") {
|
|
907
|
+
let k = i + 1;
|
|
908
|
+
while (k < n) {
|
|
909
|
+
const ch = expr[k];
|
|
910
|
+
if (ch === "\\") {
|
|
911
|
+
k += 2;
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (ch === "`") {
|
|
915
|
+
k++;
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
if (ch === "$" && expr[k + 1] === "{") {
|
|
919
|
+
k += 2;
|
|
920
|
+
const start = k;
|
|
921
|
+
let depth = 1;
|
|
922
|
+
while (k < n && depth > 0) {
|
|
923
|
+
const mc = expr[k];
|
|
924
|
+
if (mc === '"' || mc === "'" || mc === "`") {
|
|
925
|
+
k = scanString(expr, k);
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
if (mc === "{") depth++;
|
|
929
|
+
else if (mc === "}") {
|
|
930
|
+
depth--;
|
|
931
|
+
if (depth === 0) break;
|
|
932
|
+
}
|
|
933
|
+
k++;
|
|
934
|
+
}
|
|
935
|
+
for (const id of freeIdentifiers(expr.slice(start, k))) out.add(id);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
k++;
|
|
939
|
+
}
|
|
940
|
+
i = k;
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
849
943
|
if (ID_START.test(c)) {
|
|
850
944
|
let j = i + 1;
|
|
851
945
|
while (j < n && ID_CHAR.test(expr[j])) j++;
|
|
852
946
|
const name = expr.slice(i, j);
|
|
853
|
-
const
|
|
854
|
-
|
|
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);
|
|
855
952
|
i = j;
|
|
856
953
|
continue;
|
|
857
954
|
}
|
|
@@ -885,6 +982,8 @@ var Gen = class {
|
|
|
885
982
|
// @weave-framework/runtime/dom helpers
|
|
886
983
|
usedCore = /* @__PURE__ */ new Set();
|
|
887
984
|
// @weave-framework/runtime primitives (computed, …)
|
|
985
|
+
usedComponents = /* @__PURE__ */ new Set();
|
|
986
|
+
// PascalCase child tags referenced in module mode
|
|
888
987
|
templates = [];
|
|
889
988
|
tplN = 0;
|
|
890
989
|
fnN = 0;
|
|
@@ -896,8 +995,14 @@ var Gen = class {
|
|
|
896
995
|
this.usedCore.add(name);
|
|
897
996
|
return this.mode === "function" ? `rt.${name}` : name;
|
|
898
997
|
}
|
|
899
|
-
/**
|
|
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
|
+
*/
|
|
900
1004
|
Comp(name) {
|
|
1005
|
+
this.usedComponents.add(name);
|
|
901
1006
|
return this.mode === "function" ? `_c.${name}` : name;
|
|
902
1007
|
}
|
|
903
1008
|
tpl(html) {
|
|
@@ -915,15 +1020,23 @@ function compileTemplate(input, options = {}) {
|
|
|
915
1020
|
const gen = new Gen(mode, options.scopeAttr, options.hostAttr);
|
|
916
1021
|
const ast = parseTemplate(input);
|
|
917
1022
|
const render = compileFragment(gen, ast, ctxScope(options.scope ?? []), "render", "ctx, slots", true);
|
|
1023
|
+
const components = [...gen.usedComponents];
|
|
918
1024
|
if (mode === "function") {
|
|
919
1025
|
const body = [...gen.templates, render, "return render(ctx, {});"].join("\n");
|
|
920
|
-
return { code: body };
|
|
1026
|
+
return { code: body, components };
|
|
921
1027
|
}
|
|
922
1028
|
const domImport = `import { ${[...gen.used].sort().join(", ")} } from ${JSON.stringify(runtimeImport)};`;
|
|
923
1029
|
const coreImport = gen.usedCore.size ? `import { ${[...gen.usedCore].sort().join(", ")} } from "@weave-framework/runtime";
|
|
924
1030
|
` : "";
|
|
925
1031
|
const code = [domImport + "\n" + coreImport, ...gen.templates, `export default ${render}`].join("\n");
|
|
926
|
-
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}`];
|
|
927
1040
|
}
|
|
928
1041
|
function compileFragment(gen, nodes, scope, name, param = "", isHost = false) {
|
|
929
1042
|
const top = trimTop(nodes);
|
|
@@ -1123,6 +1236,7 @@ function compileFragment(gen, nodes, scope, name, param = "", isHost = false) {
|
|
|
1123
1236
|
html += "<!---->";
|
|
1124
1237
|
const anchorVar = nodeExpr(path);
|
|
1125
1238
|
const props = [];
|
|
1239
|
+
const eventKeys = [];
|
|
1126
1240
|
for (const attr of node.attrs) {
|
|
1127
1241
|
switch (attr.type) {
|
|
1128
1242
|
case "static":
|
|
@@ -1131,13 +1245,17 @@ function compileFragment(gen, nodes, scope, name, param = "", isHost = false) {
|
|
|
1131
1245
|
case "attr":
|
|
1132
1246
|
props.push(`get ${propKey(attr.name)}() { return ${rewrite(attr.expr, sc).code}; }`);
|
|
1133
1247
|
break;
|
|
1134
|
-
case "event":
|
|
1135
|
-
|
|
1248
|
+
case "event": {
|
|
1249
|
+
const k = onProp(attr.name);
|
|
1250
|
+
props.push(`${propKey(k)}: ${rewrite(attr.expr, sc).code}`);
|
|
1251
|
+
eventKeys.push(k);
|
|
1136
1252
|
break;
|
|
1253
|
+
}
|
|
1137
1254
|
default:
|
|
1138
1255
|
throw new Error(`'${attr.type}' binding on <${node.tag}> is not supported yet (M5: props + on:event only)`);
|
|
1139
1256
|
}
|
|
1140
1257
|
}
|
|
1258
|
+
if (eventKeys.length) props.push(`'$events': [${eventKeys.map((k) => JSON.stringify(k)).join(", ")}]`);
|
|
1141
1259
|
const groups = /* @__PURE__ */ new Map();
|
|
1142
1260
|
for (const child of node.children) {
|
|
1143
1261
|
let target = child;
|
|
@@ -1620,89 +1738,120 @@ function skipString(s, start) {
|
|
|
1620
1738
|
|
|
1621
1739
|
// packages/compiler/src/infer.ts
|
|
1622
1740
|
var FOR_VARS = ["$index", "$count", "$first", "$last", "$even", "$odd"];
|
|
1741
|
+
function childLists(node) {
|
|
1742
|
+
switch (node.type) {
|
|
1743
|
+
case "element":
|
|
1744
|
+
case "snippet":
|
|
1745
|
+
case "key":
|
|
1746
|
+
return [node.children];
|
|
1747
|
+
case "if":
|
|
1748
|
+
return node.branches.map((b) => b.children);
|
|
1749
|
+
case "for":
|
|
1750
|
+
return node.empty ? [node.children, node.empty] : [node.children];
|
|
1751
|
+
case "switch":
|
|
1752
|
+
return node.cases.map((c) => c.children);
|
|
1753
|
+
case "defer":
|
|
1754
|
+
return node.placeholder ? [node.children, node.placeholder] : [node.children];
|
|
1755
|
+
case "await": {
|
|
1756
|
+
const lists = [];
|
|
1757
|
+
if (node.pending) lists.push(node.pending);
|
|
1758
|
+
if (node.then) lists.push(node.then.children);
|
|
1759
|
+
if (node.catch) lists.push(node.catch.children);
|
|
1760
|
+
return lists;
|
|
1761
|
+
}
|
|
1762
|
+
default:
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1623
1766
|
function inferCtxNames(nodes) {
|
|
1624
1767
|
const used = /* @__PURE__ */ new Set();
|
|
1625
|
-
const
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1768
|
+
const snippetNames = /* @__PURE__ */ new Set();
|
|
1769
|
+
const collectSnippets = (list) => {
|
|
1770
|
+
for (const node of list) {
|
|
1771
|
+
if (node.type === "snippet") snippetNames.add(node.name);
|
|
1772
|
+
for (const cl of childLists(node)) collectSnippets(cl);
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
collectSnippets(nodes);
|
|
1776
|
+
const add = (expr, declared) => {
|
|
1777
|
+
if (!expr) return;
|
|
1778
|
+
for (const id of freeIdentifiers(expr)) {
|
|
1779
|
+
if (!declared.has(id) && !snippetNames.has(id)) used.add(id);
|
|
1780
|
+
}
|
|
1628
1781
|
};
|
|
1629
|
-
const walk2 = (list) => {
|
|
1782
|
+
const walk2 = (list, parentDeclared) => {
|
|
1783
|
+
let declared = parentDeclared;
|
|
1630
1784
|
for (const node of list) {
|
|
1631
1785
|
switch (node.type) {
|
|
1632
1786
|
case "text":
|
|
1633
1787
|
break;
|
|
1634
1788
|
case "interp":
|
|
1635
|
-
add(node.expr);
|
|
1789
|
+
add(node.expr, declared);
|
|
1636
1790
|
break;
|
|
1637
1791
|
case "let":
|
|
1638
|
-
add(node.expr);
|
|
1639
|
-
declared.add(node.name);
|
|
1792
|
+
add(node.expr, declared);
|
|
1793
|
+
declared = new Set(declared).add(node.name);
|
|
1640
1794
|
break;
|
|
1641
1795
|
case "element":
|
|
1642
1796
|
for (const attr of node.attrs) {
|
|
1643
|
-
if (attr.type === "use") add(attr.name);
|
|
1644
|
-
if (attr.type === "transition") add(attr.name);
|
|
1645
|
-
if (attr.type !== "static") add(attr.expr);
|
|
1797
|
+
if (attr.type === "use") add(attr.name, declared);
|
|
1798
|
+
if (attr.type === "transition") add(attr.name, declared);
|
|
1799
|
+
if (attr.type !== "static") add(attr.expr, declared);
|
|
1646
1800
|
}
|
|
1647
|
-
walk2(node.children);
|
|
1801
|
+
walk2(node.children, declared);
|
|
1648
1802
|
break;
|
|
1649
1803
|
case "if":
|
|
1650
1804
|
for (const br of node.branches) {
|
|
1651
|
-
add(br.cond);
|
|
1652
|
-
|
|
1653
|
-
walk2(br.children);
|
|
1805
|
+
add(br.cond, declared);
|
|
1806
|
+
walk2(br.children, br.alias ? new Set(declared).add(br.alias) : declared);
|
|
1654
1807
|
}
|
|
1655
1808
|
break;
|
|
1656
|
-
case "for":
|
|
1657
|
-
add(node.list);
|
|
1658
|
-
add(node.
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
walk2(node.children);
|
|
1662
|
-
if (node.empty) walk2(node.empty);
|
|
1809
|
+
case "for": {
|
|
1810
|
+
add(node.list, declared);
|
|
1811
|
+
const inner = new Set(declared).add(node.item);
|
|
1812
|
+
for (const v of FOR_VARS) inner.add(v);
|
|
1813
|
+
add(node.track, inner);
|
|
1814
|
+
walk2(node.children, inner);
|
|
1815
|
+
if (node.empty) walk2(node.empty, declared);
|
|
1663
1816
|
break;
|
|
1817
|
+
}
|
|
1664
1818
|
case "switch":
|
|
1665
|
-
add(node.expr);
|
|
1819
|
+
add(node.expr, declared);
|
|
1666
1820
|
for (const c of node.cases) {
|
|
1667
|
-
add(c.test);
|
|
1668
|
-
walk2(c.children);
|
|
1821
|
+
add(c.test, declared);
|
|
1822
|
+
walk2(c.children, declared);
|
|
1669
1823
|
}
|
|
1670
1824
|
break;
|
|
1671
1825
|
case "defer":
|
|
1672
|
-
if (node.trigger.kind === "when") add(node.trigger.expr);
|
|
1673
|
-
if (node.trigger.kind === "timer") add(node.trigger.ms);
|
|
1674
|
-
walk2(node.children);
|
|
1675
|
-
if (node.placeholder) walk2(node.placeholder);
|
|
1826
|
+
if (node.trigger.kind === "when") add(node.trigger.expr, declared);
|
|
1827
|
+
if (node.trigger.kind === "timer") add(node.trigger.ms, declared);
|
|
1828
|
+
walk2(node.children, declared);
|
|
1829
|
+
if (node.placeholder) walk2(node.placeholder, declared);
|
|
1676
1830
|
break;
|
|
1677
1831
|
case "await":
|
|
1678
|
-
add(node.expr);
|
|
1679
|
-
if (node.pending) walk2(node.pending);
|
|
1680
|
-
if (node.then)
|
|
1681
|
-
|
|
1682
|
-
walk2(node.then.children);
|
|
1683
|
-
}
|
|
1684
|
-
if (node.catch) {
|
|
1685
|
-
if (node.catch.alias) declared.add(node.catch.alias);
|
|
1686
|
-
walk2(node.catch.children);
|
|
1687
|
-
}
|
|
1832
|
+
add(node.expr, declared);
|
|
1833
|
+
if (node.pending) walk2(node.pending, declared);
|
|
1834
|
+
if (node.then) walk2(node.then.children, node.then.alias ? new Set(declared).add(node.then.alias) : declared);
|
|
1835
|
+
if (node.catch) walk2(node.catch.children, node.catch.alias ? new Set(declared).add(node.catch.alias) : declared);
|
|
1688
1836
|
break;
|
|
1689
|
-
case "snippet":
|
|
1690
|
-
declared
|
|
1691
|
-
for (const p of node.params)
|
|
1692
|
-
walk2(node.children);
|
|
1837
|
+
case "snippet": {
|
|
1838
|
+
const inner = new Set(declared);
|
|
1839
|
+
for (const p of node.params) inner.add(p);
|
|
1840
|
+
walk2(node.children, inner);
|
|
1693
1841
|
break;
|
|
1842
|
+
}
|
|
1694
1843
|
case "render":
|
|
1695
|
-
add(node.expr);
|
|
1844
|
+
add(node.expr, declared);
|
|
1696
1845
|
break;
|
|
1697
1846
|
case "key":
|
|
1698
|
-
add(node.expr);
|
|
1699
|
-
walk2(node.children);
|
|
1847
|
+
add(node.expr, declared);
|
|
1848
|
+
walk2(node.children, declared);
|
|
1700
1849
|
break;
|
|
1701
1850
|
}
|
|
1702
1851
|
}
|
|
1703
1852
|
};
|
|
1704
|
-
walk2(nodes);
|
|
1705
|
-
return [...used].
|
|
1853
|
+
walk2(nodes, /* @__PURE__ */ new Set());
|
|
1854
|
+
return [...used].sort();
|
|
1706
1855
|
}
|
|
1707
1856
|
|
|
1708
1857
|
// packages/compiler/src/component.ts
|
|
@@ -1723,7 +1872,7 @@ function compileComponent(src, opts = {}) {
|
|
|
1723
1872
|
renderBody,
|
|
1724
1873
|
`export default defineComponent(${setupArg});`
|
|
1725
1874
|
].filter(Boolean).join("\n\n");
|
|
1726
|
-
return { code, css, hash };
|
|
1875
|
+
return { code, css, hash, components: compiled.components };
|
|
1727
1876
|
}
|
|
1728
1877
|
function parseSfcLoc(source) {
|
|
1729
1878
|
const script = locateBlock(source, "script");
|
|
@@ -1787,7 +1936,7 @@ function extractBlock(source, tag) {
|
|
|
1787
1936
|
}
|
|
1788
1937
|
|
|
1789
1938
|
// packages/compiler/src/sources.ts
|
|
1790
|
-
var DECL = /export\s+const\s+(template|styles)\s*(?::[
|
|
1939
|
+
var DECL = /export\s+const\s+(template|styles)\s*(?::[^=\n]+)?=\s*/g;
|
|
1791
1940
|
function extractSources(script) {
|
|
1792
1941
|
let template;
|
|
1793
1942
|
let templateRange;
|
|
@@ -1819,10 +1968,30 @@ function extractSources(script) {
|
|
|
1819
1968
|
function parseLiteral(src, i, kind) {
|
|
1820
1969
|
i = skipWs(src, i);
|
|
1821
1970
|
const c = src[i];
|
|
1822
|
-
if (c === '"' || c === "'" || c === "`") return
|
|
1971
|
+
if (c === '"' || c === "'" || c === "`") return parseConcat(src, i);
|
|
1823
1972
|
if (c === "[") return parseArray(src, i);
|
|
1824
1973
|
throw new Error(`weave: \`${kind}\` must be a static string${kind === "styles" ? " or array of strings" : ""}`);
|
|
1825
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
|
+
}
|
|
1826
1995
|
function parseString(src, i) {
|
|
1827
1996
|
const quote = src[i];
|
|
1828
1997
|
const innerStart = i + 1;
|
|
@@ -1854,7 +2023,7 @@ function parseArray(src, i) {
|
|
|
1854
2023
|
j++;
|
|
1855
2024
|
continue;
|
|
1856
2025
|
}
|
|
1857
|
-
const str =
|
|
2026
|
+
const str = parseConcat(src, j);
|
|
1858
2027
|
items.push(str.value);
|
|
1859
2028
|
j = str.end;
|
|
1860
2029
|
}
|
|
@@ -1899,15 +2068,18 @@ function langFromExt(file) {
|
|
|
1899
2068
|
if (file.endsWith(".sass")) return "sass";
|
|
1900
2069
|
return "css";
|
|
1901
2070
|
}
|
|
2071
|
+
function pkgImporters(sass) {
|
|
2072
|
+
return [new sass.NodePackageImporter()];
|
|
2073
|
+
}
|
|
1902
2074
|
async function compileStyleFile(path) {
|
|
1903
2075
|
if (langFromExt(path) === "css") return readFile(path, "utf8");
|
|
1904
2076
|
const sass = await import("sass");
|
|
1905
|
-
return sass.compile(path).css;
|
|
2077
|
+
return sass.compile(path, { importers: pkgImporters(sass) }).css;
|
|
1906
2078
|
}
|
|
1907
2079
|
async function compileStyleFileTracked(path) {
|
|
1908
2080
|
if (langFromExt(path) === "css") return { css: await readFile(path, "utf8"), files: [path] };
|
|
1909
2081
|
const sass = await import("sass");
|
|
1910
|
-
const result = sass.compile(path);
|
|
2082
|
+
const result = sass.compile(path, { importers: pkgImporters(sass) });
|
|
1911
2083
|
const files = result.loadedUrls.filter((u) => u.protocol === "file:").map((u) => fileURLToPath(u));
|
|
1912
2084
|
return { css: result.css, files };
|
|
1913
2085
|
}
|
|
@@ -1916,15 +2088,24 @@ async function compileStyleSource(source, lang, fromDir) {
|
|
|
1916
2088
|
const sass = await import("sass");
|
|
1917
2089
|
return sass.compileString(source, {
|
|
1918
2090
|
syntax: lang === "sass" ? "indented" : "scss",
|
|
1919
|
-
loadPaths: fromDir ? [fromDir] : []
|
|
2091
|
+
loadPaths: fromDir ? [fromDir] : [],
|
|
2092
|
+
importers: pkgImporters(sass)
|
|
1920
2093
|
}).css;
|
|
1921
2094
|
}
|
|
1922
2095
|
|
|
1923
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
|
+
}
|
|
1924
2102
|
function cssInjector(css) {
|
|
1925
2103
|
if (!css) return "";
|
|
2104
|
+
const id = styleId(css);
|
|
1926
2105
|
return `
|
|
1927
|
-
;(()=>{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(
|
|
1928
2109
|
css
|
|
1929
2110
|
)};document.head.appendChild(s);})();
|
|
1930
2111
|
`;
|
|
@@ -1971,6 +2152,78 @@ async function resolveStyles(decl, tsPath, dir, styleLang) {
|
|
|
1971
2152
|
const compiled = await compileStyleFileTracked(siblingStyle);
|
|
1972
2153
|
return { css: compiled.css, files: compiled.files };
|
|
1973
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
|
+
}
|
|
1974
2227
|
function weave(state, options = {}) {
|
|
1975
2228
|
const styleLang = options.styleLang ?? "css";
|
|
1976
2229
|
const dev2 = options.dev ?? false;
|
|
@@ -1989,8 +2242,9 @@ function weave(state, options = {}) {
|
|
|
1989
2242
|
const source = await readFile2(args.path, "utf8");
|
|
1990
2243
|
const src = parseSfc(source);
|
|
1991
2244
|
const styles = src.styles ? await compileStyleSource(src.styles, styleLang, dirname(args.path)) : void 0;
|
|
1992
|
-
const { code, css } = compileComponent({ ...src, styles }, { filename: args.path });
|
|
1993
|
-
|
|
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));
|
|
1994
2248
|
});
|
|
1995
2249
|
build2.onLoad({ filter: /\.ts$/ }, async (args) => {
|
|
1996
2250
|
if (args.path.includes("node_modules")) return void 0;
|
|
@@ -2013,11 +2267,12 @@ function weave(state, options = {}) {
|
|
|
2013
2267
|
dir,
|
|
2014
2268
|
styleLang
|
|
2015
2269
|
);
|
|
2016
|
-
const { code, css } = compileComponent(
|
|
2270
|
+
const { code, css, components } = compileComponent(
|
|
2017
2271
|
{ script: decl.script, template: template.text, styles: styles.css },
|
|
2018
2272
|
{ filename: args.path }
|
|
2019
2273
|
);
|
|
2020
|
-
|
|
2274
|
+
const wired = injectChildImports(code, components, dir, decl.script, args.path);
|
|
2275
|
+
return { ...emit2(wired, css, dir), watchFiles: [...template.files, ...styles.files] };
|
|
2021
2276
|
});
|
|
2022
2277
|
}
|
|
2023
2278
|
};
|
|
@@ -2180,7 +2435,7 @@ async function build(config) {
|
|
|
2180
2435
|
import { context } from "esbuild";
|
|
2181
2436
|
import { createServer } from "node:http";
|
|
2182
2437
|
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile4 } from "node:fs/promises";
|
|
2183
|
-
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";
|
|
2184
2439
|
var RELOAD_PATH = "/__weave_reload";
|
|
2185
2440
|
var MIME = {
|
|
2186
2441
|
".html": "text/html; charset=utf-8",
|
|
@@ -2207,7 +2462,7 @@ async function devInMemory(config) {
|
|
|
2207
2462
|
const css = (await Promise.all(config.styles.map(compileStyleFile))).join("\n");
|
|
2208
2463
|
if (css)
|
|
2209
2464
|
banner = {
|
|
2210
|
-
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(
|
|
2211
2466
|
css
|
|
2212
2467
|
)};document.head.appendChild(s);})();`
|
|
2213
2468
|
};
|
|
@@ -2272,8 +2527,15 @@ async function handleRequest(req, res, config, outputs, clients) {
|
|
|
2272
2527
|
return;
|
|
2273
2528
|
}
|
|
2274
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
|
+
}
|
|
2275
2537
|
try {
|
|
2276
|
-
const buf = await readFile4(
|
|
2538
|
+
const buf = await readFile4(target);
|
|
2277
2539
|
res.writeHead(200, { "content-type": mime(url) });
|
|
2278
2540
|
res.end(buf);
|
|
2279
2541
|
} catch {
|
|
@@ -2475,7 +2737,7 @@ function generateRoutes(dir, opts = {}) {
|
|
|
2475
2737
|
import { build as esbuildBuild } from "esbuild";
|
|
2476
2738
|
import { existsSync as existsSync4 } from "node:fs";
|
|
2477
2739
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2478
|
-
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";
|
|
2479
2741
|
function defineConfig(config) {
|
|
2480
2742
|
return config;
|
|
2481
2743
|
}
|
|
@@ -2519,7 +2781,7 @@ async function importConfigModule(file) {
|
|
|
2519
2781
|
return mod.default ?? mod;
|
|
2520
2782
|
}
|
|
2521
2783
|
function resolveConfig(raw, root) {
|
|
2522
|
-
const abs = (p) =>
|
|
2784
|
+
const abs = (p) => isAbsolute2(p) ? p : resolve2(root, p);
|
|
2523
2785
|
if (!raw.root && !raw.entry) {
|
|
2524
2786
|
throw new Error("weave: config must declare either `root` (generated bootstrap) or `entry` (hand-written)");
|
|
2525
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 —
|