ferrovue 0.3.0 → 0.4.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.
Files changed (85) hide show
  1. package/README.md +32 -13
  2. package/dist/attrs.d.ts +2 -52
  3. package/dist/attrs.js +18 -448
  4. package/dist/calls.d.ts +4 -0
  5. package/dist/calls.js +219 -0
  6. package/dist/children.d.ts +9 -0
  7. package/dist/children.js +273 -0
  8. package/dist/classes.d.ts +13 -0
  9. package/dist/classes.js +176 -0
  10. package/dist/cli.js +0 -17
  11. package/dist/client.js +0 -12
  12. package/dist/compiler.js +25 -90
  13. package/dist/component.js +10 -86
  14. package/dist/context.d.ts +4 -66
  15. package/dist/context.js +2 -33
  16. package/dist/diff.d.ts +0 -3
  17. package/dist/diff.js +0 -9
  18. package/dist/emitter.d.ts +0 -8
  19. package/dist/emitter.js +0 -12
  20. package/dist/expr.d.ts +0 -70
  21. package/dist/expr.js +27 -890
  22. package/dist/fallthrough.d.ts +9 -0
  23. package/dist/fallthrough.js +78 -0
  24. package/dist/fixture.d.ts +2 -1
  25. package/dist/fixture.js +37 -22
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +1 -0
  28. package/dist/islands.d.ts +1 -0
  29. package/dist/islands.js +1 -0
  30. package/dist/lists.d.ts +0 -11
  31. package/dist/lists.js +4 -39
  32. package/dist/loops.d.ts +3 -0
  33. package/dist/loops.js +164 -0
  34. package/dist/model.d.ts +14 -143
  35. package/dist/model.js +27 -12
  36. package/dist/narrowing.d.ts +19 -0
  37. package/dist/narrowing.js +126 -0
  38. package/dist/numbers.d.ts +8 -0
  39. package/dist/numbers.js +73 -0
  40. package/dist/parens.d.ts +0 -27
  41. package/dist/parens.js +0 -44
  42. package/dist/plugin.d.ts +84 -0
  43. package/dist/plugin.js +26 -0
  44. package/dist/plugins/i18n.d.ts +23 -0
  45. package/dist/{i18n.js → plugins/i18n.js} +76 -19
  46. package/dist/plugins/index.d.ts +2 -0
  47. package/dist/plugins/index.js +8 -0
  48. package/dist/plugins/router-link.d.ts +8 -0
  49. package/dist/{router.js → plugins/router-link.js} +19 -61
  50. package/dist/plugins/router.d.ts +42 -0
  51. package/dist/plugins/router.js +256 -0
  52. package/dist/plugins/scoped.d.ts +12 -0
  53. package/dist/plugins/scoped.js +125 -0
  54. package/dist/plugins/shared-types.d.ts +2 -0
  55. package/dist/plugins/shared-types.js +7 -0
  56. package/dist/plugins/stores.d.ts +37 -0
  57. package/dist/plugins/stores.js +377 -0
  58. package/dist/plugins/teleport.d.ts +5 -0
  59. package/dist/plugins/teleport.js +55 -0
  60. package/dist/rust.d.ts +1 -7
  61. package/dist/rust.js +38 -160
  62. package/dist/script.d.ts +0 -6
  63. package/dist/script.js +13 -128
  64. package/dist/slots.d.ts +13 -0
  65. package/dist/slots.js +160 -0
  66. package/dist/strings.d.ts +13 -0
  67. package/dist/strings.js +208 -0
  68. package/dist/styles.d.ts +14 -0
  69. package/dist/styles.js +181 -0
  70. package/dist/template.d.ts +1 -42
  71. package/dist/template.js +14 -738
  72. package/dist/testing.d.ts +1 -0
  73. package/dist/testing.js +1 -8
  74. package/dist/types.d.ts +2 -0
  75. package/dist/typescript.d.ts +0 -13
  76. package/dist/typescript.js +8 -41
  77. package/dist/vite.d.ts +1 -0
  78. package/dist/vite.js +1 -23
  79. package/package.json +7 -2
  80. package/dist/i18n.d.ts +0 -27
  81. package/dist/router.d.ts +0 -17
  82. package/dist/scoped.d.ts +0 -24
  83. package/dist/scoped.js +0 -177
  84. package/dist/stores.d.ts +0 -6
  85. package/dist/stores.js +0 -229
@@ -0,0 +1,14 @@
1
+ import { type N, type Scope } from "./model.ts";
2
+ import { Emitter } from "./emitter.ts";
3
+ export declare function mergedStyle(s: Scope, e: Emitter, n: N): void;
4
+ export declare function styleAttr(s: Scope, n: N): string;
5
+ export interface StyleItem {
6
+ cond: string | null;
7
+ entries: {
8
+ key: string;
9
+ css: string;
10
+ value: N;
11
+ }[];
12
+ }
13
+ export declare function styleItems(s: Scope, n: N, when: string | null): StyleItem[];
14
+ export declare function renderStyle(s: Scope, e: Emitter, n: N): void;
package/dist/styles.js ADDED
@@ -0,0 +1,181 @@
1
+ import { escapeHtml, hyphenate, parseStringStyle } from "@vue/shared";
2
+ import { fail, rustStr } from "./model.js";
3
+ import { expr } from "./expr.js";
4
+ import { cond } from "./narrowing.js";
5
+ import { condition, logical, not, strArg } from "./parens.js";
6
+ import { Emitter } from "./emitter.js";
7
+ import { display, valueAttr } from "./attrs.js";
8
+ export function mergedStyle(s, e, n) {
9
+ if (n.type === "StringLiteral") {
10
+ renderStyle(s, e, { type: "ArrayExpression", elements: [n] });
11
+ return;
12
+ }
13
+ if (n.fvMerged || ["ObjectExpression", "ArrayExpression", "ConditionalExpression", "LogicalExpression", "NullLiteral"].includes(n.type)) {
14
+ renderStyle(s, e, n);
15
+ return;
16
+ }
17
+ const v = expr(s, n);
18
+ if (v.ty.k === "str")
19
+ e.stmt(`fv::style_text_into(out, ${strArg(v.code)});`);
20
+ else if (v.ty.k === "opt" && v.ty.of.k === "str") {
21
+ e.open(`if let Some(v) = ${v.code}`);
22
+ e.stmt("fv::style_text_into(out, v);");
23
+ e.close();
24
+ }
25
+ else if (v.ty.k !== "undef")
26
+ fail(s.comp, "a style binding is a string, an object or an array", n);
27
+ }
28
+ export function styleAttr(s, n) {
29
+ switch (n.type) {
30
+ case "StringLiteral":
31
+ return `fv::Attr::str(${rustStr(n.value)})`;
32
+ case "NullLiteral":
33
+ return "fv::Attr::Undefined";
34
+ case "ObjectExpression": {
35
+ const entries = n.properties.map((p) => {
36
+ if (p.type !== "ObjectProperty" || p.computed)
37
+ fail(s.comp, "a style object holds plain `property: value` pairs", p);
38
+ const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
39
+ if (/^\d+$/.test(key) || key.startsWith(":"))
40
+ fail(s.comp, `style property \`${key}\``, p);
41
+ return `(${rustStr(key)}, ${valueAttr(s, expr(s, p.value), p.value)})`;
42
+ });
43
+ return `fv::Attr::style([${entries.join(", ")}])`;
44
+ }
45
+ case "ArrayExpression":
46
+ return `fv::Attr::styles([${n.elements.map((el) => (el ? styleAttr(s, el) : "fv::Attr::Undefined")).join(", ")}])`;
47
+ case "LogicalExpression":
48
+ if (n.operator === "&&")
49
+ return `if ${condition(cond(s, n.left))} { ${styleAttr(s, n.right)} } else { fv::Attr::Undefined }`;
50
+ break;
51
+ case "ConditionalExpression":
52
+ return `if ${condition(cond(s, n.test))} { ${styleAttr(s, n.consequent)} } else { ${styleAttr(s, n.alternate)} }`;
53
+ }
54
+ const v = expr(s, n);
55
+ if (v.ty.k === "str" || v.ty.k === "undef" || (v.ty.k === "opt" && v.ty.of.k === "str"))
56
+ return valueAttr(s, v, n);
57
+ return fail(s.comp, "a style binding is a string, an object or an array", n);
58
+ }
59
+ export function styleItems(s, n, when) {
60
+ if (when === "false")
61
+ return [];
62
+ if (when === "true")
63
+ when = null;
64
+ const both = (a, b) => (a === null ? b : logical(a, "&&", b));
65
+ switch (n.type) {
66
+ case "NullLiteral":
67
+ return [];
68
+ case "ArrayExpression":
69
+ return n.elements.flatMap((el) => (el ? styleItems(s, el, when) : []));
70
+ case "StringLiteral":
71
+ return [{ cond: when, entries: Object.entries(parseStringStyle(n.value)).map(([key, v]) => ({ key, css: key, value: { type: "StringLiteral", value: v } })) }];
72
+ case "ConditionalExpression": {
73
+ const t = cond(s, n.test);
74
+ return [...styleItems(s, n.consequent, both(when, t)), ...styleItems(s, n.alternate, both(when, not(t)))];
75
+ }
76
+ case "LogicalExpression":
77
+ if (n.operator === "&&")
78
+ return styleItems(s, n.right, both(when, cond(s, n.left)));
79
+ break;
80
+ case "ObjectExpression":
81
+ return [{
82
+ cond: when,
83
+ entries: n.properties.map((p) => {
84
+ if (p.type !== "ObjectProperty" || p.computed)
85
+ fail(s.comp, "a style object holds plain `property: value` pairs", p);
86
+ const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
87
+ if (/^\d+$/.test(key) || key.startsWith(":"))
88
+ fail(s.comp, `style property \`${key}\``, p);
89
+ return { key, css: key.startsWith("--") ? key : hyphenate(key), value: p.value };
90
+ }),
91
+ }];
92
+ }
93
+ return fail(s.comp, "a style binding is an object, an array of objects, or a string on its own", n);
94
+ }
95
+ export function renderStyle(s, e, n) {
96
+ if (n.type === "StringLiteral") {
97
+ e.lit(escapeHtml(n.value));
98
+ return;
99
+ }
100
+ if (n.type !== "ObjectExpression" && n.type !== "ArrayExpression" && n.type !== "ConditionalExpression" && n.type !== "LogicalExpression" && n.type !== "NullLiteral") {
101
+ const v = expr(s, n);
102
+ if (v.ty.k === "str")
103
+ e.stmt(`fv::escape_into(out, ${strArg(v.code)});`);
104
+ else if (v.ty.k === "opt" && v.ty.of.k === "str") {
105
+ e.open(`if let Some(v) = ${v.code}`);
106
+ e.stmt("fv::escape_into(out, v);");
107
+ e.close();
108
+ }
109
+ else if (v.ty.k !== "undef")
110
+ fail(s.comp, "a style binding is a string, an object or an array", n);
111
+ return;
112
+ }
113
+ const items = styleItems(s, n, null);
114
+ const order = [];
115
+ const sets = new Map();
116
+ for (const it of items) {
117
+ for (const en of it.entries) {
118
+ if (!sets.has(en.key)) {
119
+ order.push(en.key);
120
+ sets.set(en.key, []);
121
+ }
122
+ sets.get(en.key).push({ cond: it.cond, css: en.css, value: en.value });
123
+ }
124
+ }
125
+ const write = (css, value) => {
126
+ if (value.type === "StringLiteral" || value.type === "NumericLiteral") {
127
+ e.lit(escapeHtml(`${css}:${String(value.value)};`));
128
+ return;
129
+ }
130
+ const v = expr(s, value);
131
+ const one = (w) => {
132
+ if (w.ty.k === "str" || w.ty.k === "int" || w.ty.k === "float") {
133
+ e.lit(`${escapeHtml(css)}:`);
134
+ display(e, w);
135
+ e.lit(";");
136
+ }
137
+ else if (w.ty.k === "opt") {
138
+ e.open(`if let Some(v) = ${w.code}`);
139
+ one({ code: "v", ty: w.ty.of });
140
+ e.close();
141
+ }
142
+ };
143
+ one(v);
144
+ };
145
+ const flat = items.flatMap((it, i) => it.entries.map((en) => ({ key: en.key, item: i, cond: it.cond })));
146
+ for (const key of order) {
147
+ const at = flat.filter((f) => f.key === key);
148
+ if (at.length < 2 || at[0].cond === null)
149
+ continue;
150
+ const first = flat.indexOf(at[0]);
151
+ const again = flat.indexOf(at[at.length - 1]);
152
+ const between = flat.slice(first + 1, again).some((f) => order.indexOf(f.key) > order.indexOf(key));
153
+ if (between)
154
+ fail(s.comp, `the place of style property \`${key}\` would depend on a condition; set it unconditionally first`, n);
155
+ }
156
+ for (const key of order) {
157
+ const chain = [];
158
+ for (const set of sets.get(key).toReversed()) {
159
+ chain.push(set);
160
+ if (set.cond === null)
161
+ break;
162
+ }
163
+ if (chain.length === 1 && chain[0].cond === null) {
164
+ write(chain[0].css, chain[0].value);
165
+ continue;
166
+ }
167
+ chain.forEach((set, i) => {
168
+ if (set.cond === null) {
169
+ e.close(" else {");
170
+ }
171
+ else if (i === 0) {
172
+ e.open(`if ${condition(set.cond)}`);
173
+ }
174
+ else {
175
+ e.close(` else if ${condition(set.cond)} {`);
176
+ }
177
+ write(set.css, set.value);
178
+ });
179
+ e.close();
180
+ }
181
+ }
@@ -1,48 +1,7 @@
1
- import { type Component, type N, type Scope, type Ty, type Val } from "./model.ts";
1
+ import { type N, type Scope } from "./model.ts";
2
2
  import { Emitter } from "./emitter.ts";
3
- /** One `${...}` inside a pushed template literal. */
4
3
  export declare function slot(s: Scope, e: Emitter, n: N): void;
5
- /** `isComment` in `@vue/server-renderer`: a chunk that is only comments and whitespace. */
6
4
  export declare function isComment(text: string): boolean;
7
- /** Whether a `_push` argument is content to `ssrRenderSlot` rather than only comments — `"run"`
8
- * when that depends on the values it interpolates (`${of1}<!--[-->` is a comment when `of1` writes
9
- * nothing). Vue asks it of each pushed string, so the generated code asks `fv::is_comment` of what
10
- * that push wrote. */
11
5
  export declare function pushesContent(n: N): boolean | "run";
12
6
  export declare function push(s: Scope, e: Emitter, n: N): void;
13
- export declare function renderChild(s: Scope, e: Emitter, n: N): void;
14
- /** `name: value` in a struct literal, or the name alone when the value is a variable of that name. */
15
- export declare function fieldInit(name: string, value: string): string;
16
- /** Whether a component takes a `Slots` argument. */
17
- export declare function takesSlots(c: Component): boolean;
18
- /** The arguments after `props` that a component's `render` takes. */
19
- export declare function extraParams(c: Component): string;
20
- /** `render(out, props[, slots][, route])` for a child, with the slot content this template gives
21
- * it as closures; `render_scoped`, with the scope ids its root is handed last — and the attributes
22
- * it is passed, as `fv::Attrs` — for a child that may be handed some. */
23
- export declare function callChild(s: Scope, e: Emitter, child: Component, propsCode: string, slots: N, attrs: string | null): void;
24
- /** The statements a parent's slot content pushes: the `if (_push)` half of its `_withCtx`. */
25
- export declare function slotBody(s: Scope, value: N): N[];
26
- /** A parent's slot content: what it pushes, and the parameter its scoped slot props are bound to
27
- * (`_` when it takes none). */
28
- export declare function slotContent(s: Scope, value: N): {
29
- body: N[];
30
- param: N;
31
- };
32
- /** Whether slot content always pushes something that is not a comment: a push of content outside
33
- * any `if` or loop. Then nothing has to be decided at run time. */
34
- export declare function staticallyFilled(body: N[]): boolean;
35
- /** `RowSlotProps`, `RowSlot`: the names of a scoped slot's props struct and content type. */
36
- export declare function slotTypeName(slotName: string, suffix: string): string;
37
- /** Whether a scoped slot prop's Rust type borrows, and so needs the props' lifetime `'v`. */
38
- export declare function slotFieldBorrows(ty: Ty): boolean;
39
- /** A scoped slot prop's Rust type: a scalar copied, anything else borrowed from the render for `'v`. */
40
- export declare function slotFieldTy(ty: Ty, comp: Component): string;
41
- /** A value an outlet passes, as the scoped slot prop field holds it. */
42
- export declare function slotFieldValue(s: Scope, v: Val, n: N): string;
43
- /** `<slot name="x" :prop="…">fallback</slot>`: `ssrRenderSlot`, with the props a scoped slot passes. */
44
- export declare function slotOutlet(s: Scope, e: Emitter, c: N): void;
45
- /** A value into a child's props field, whose strings are `Cow`s. */
46
- export declare function ownInto(comp: Component, v: Val, want: Ty, node: N): string;
47
7
  export declare function statements(s: Scope, e: Emitter, body: N[]): void;
48
- export declare function list(s: Scope, e: Emitter, c: N): void;