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,42 @@
1
+ import { type Component } from "../model.ts";
2
+ import { type Plugin } from "../plugin.ts";
3
+ declare module "../model.ts" {
4
+ interface PluginTys {
5
+ route: {
6
+ k: "route";
7
+ };
8
+ params: {
9
+ k: "params";
10
+ };
11
+ queryobj: {
12
+ k: "queryobj";
13
+ };
14
+ query: {
15
+ k: "query";
16
+ };
17
+ }
18
+ }
19
+ export interface RouteDef {
20
+ path: string;
21
+ name?: string;
22
+ children?: RouteDef[];
23
+ fullPath: string;
24
+ }
25
+ export declare function allRoutes(routes: RouteDef[]): RouteDef[];
26
+ interface RouterRun {
27
+ routes: RouteDef[] | null;
28
+ file: string | null;
29
+ base: string;
30
+ linkActive: string;
31
+ linkExactActive: string;
32
+ readers: Set<Component>;
33
+ views: Set<Component>;
34
+ }
35
+ interface RouterScope {
36
+ useRoute: string | null;
37
+ components: Map<string, "RouterLink" | "RouterView">;
38
+ }
39
+ export declare function readRoutes(root: string, file: string): RouteDef[];
40
+ export declare function routesSource(routes: RouteDef[], file: string): string;
41
+ export declare const router: Plugin<RouterRun, RouterScope>;
42
+ export {};
@@ -0,0 +1,256 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { BOOL, fail, GenError, opt, rustStr, STR } from "../model.js";
4
+ import { CONFIG_FILE } from "../context.js";
5
+ import { expr } from "../expr.js";
6
+ import { boolOf, pathOf } from "../narrowing.js";
7
+ import { atom, bare, not, strArg } from "../parens.js";
8
+ import { runOf, scopeOf } from "../plugin.js";
9
+ import { header } from "../rust.js";
10
+ import { scopeIdOf } from "./scoped.js";
11
+ import { routerLink } from "./router-link.js";
12
+ export function allRoutes(routes) {
13
+ return routes.flatMap((r) => [r, ...allRoutes(r.children ?? [])]);
14
+ }
15
+ function typeofString(n) {
16
+ if (n.type !== "BinaryExpression" || (n.operator !== "===" && n.operator !== "!=="))
17
+ return null;
18
+ const isTypeof = (m) => m.type === "UnaryExpression" && m.operator === "typeof";
19
+ const isString = (m) => m.type === "StringLiteral" && m.value === "string";
20
+ const target = isTypeof(n.left) && isString(n.right) ? n.left.argument : isTypeof(n.right) && isString(n.left) ? n.right.argument : null;
21
+ return target ? { target, negated: n.operator === "!==" } : null;
22
+ }
23
+ function theRoute(s, n) {
24
+ if (!runOf(router).routes)
25
+ fail(s.comp, `reading the route needs \`routes\` in ${CONFIG_FILE}`, n);
26
+ runOf(router).readers.add(s.comp);
27
+ return { code: "fv_route", ty: { k: "route" } };
28
+ }
29
+ function routeField(s, base, prop, n) {
30
+ if (base.ty.k === "params") {
31
+ return { code: `fv_route.param(${rustStr(prop)})`, ty: opt(STR) };
32
+ }
33
+ if (base.ty.k === "queryobj")
34
+ return { code: `fv_route.query(${rustStr(prop)})`, ty: { k: "query" } };
35
+ switch (prop) {
36
+ case "path":
37
+ return { code: "fv_route.path()", ty: STR };
38
+ case "hash":
39
+ return { code: "fv_route.hash()", ty: STR };
40
+ case "name":
41
+ return { code: "fv_route.name()", ty: opt(STR) };
42
+ case "params":
43
+ return { code: "fv_route", ty: { k: "params" } };
44
+ case "query":
45
+ return { code: "fv_route", ty: { k: "queryobj" } };
46
+ case "fullPath":
47
+ return { code: "fv_route.full_path()", ty: STR };
48
+ }
49
+ return fail(s.comp, `\`route.${prop}\` is not available on the server: \`path\`, \`fullPath\`, \`hash\`, \`name\`, \`params\` and \`query\` are`, n);
50
+ }
51
+ export function readRoutes(root, file) {
52
+ const raw = JSON.parse(readFileSync(join(root, file), "utf8"));
53
+ if (!Array.isArray(raw))
54
+ throw new GenError(`${file} lists the routes in an array`);
55
+ const read = (list, parent) => list.map((r) => {
56
+ const o = (typeof r === "string" ? { path: r } : r);
57
+ if (typeof o?.path !== "string" || (o.name !== undefined && typeof o.name !== "string") || (o.children !== undefined && !Array.isArray(o.children))) {
58
+ throw new GenError(`${file}: a route is a path, or \`{ "path": "…", "name": "…", "children": [ … ] }\``);
59
+ }
60
+ const fullPath = parent === null || o.path.startsWith("/") ? o.path : `${parent}${parent.endsWith("/") || o.path === "" ? "" : "/"}${o.path}`;
61
+ const def = { path: o.path, fullPath };
62
+ if (o.name !== undefined)
63
+ def.name = o.name;
64
+ if (o.children)
65
+ def.children = read(o.children, fullPath);
66
+ return def;
67
+ });
68
+ return read(raw, null);
69
+ }
70
+ function routeDefs(routes, depth) {
71
+ const pad = " ".repeat(depth);
72
+ return routes
73
+ .map((r) => {
74
+ const name = r.name === undefined ? "None" : `Some(${rustStr(r.name)})`;
75
+ const children = r.children?.length ? `&[\n${routeDefs(r.children, depth + 1)}\n${pad}]` : "&[]";
76
+ return `${pad}ferrovue::RouteDef { path: ${rustStr(r.path)}, name: ${name}, children: ${children} },`;
77
+ })
78
+ .join("\n");
79
+ }
80
+ export function routesSource(routes, file) {
81
+ return `${header(file, "the routes file")}
82
+ //! The app's routes: what \`<RouterLink>\` resolves against and \`useRoute()\` reads.
83
+
84
+ /// Each route: its vue-router path, its name if it has one, and the routes nested in it.
85
+ pub const ROUTES: &[ferrovue::RouteDef<'static>] = &[
86
+ ${routeDefs(routes, 1)}
87
+ ];
88
+
89
+ /// Every route's full path, nested ones included.
90
+ pub const PATHS: &[&str] = &[
91
+ ${allRoutes(routes).map((r) => ` ${rustStr(r.fullPath)},`).join("\n")}
92
+ ];
93
+
94
+ /// The history's base, which every link's \`href\` starts with.
95
+ pub const BASE: &str = ${rustStr(runOf(router).base)};
96
+
97
+ /// The router these routes make: build it once, and resolve each request's location with
98
+ /// [\`ferrovue::Router::at\`].
99
+ pub fn router() -> ferrovue::Router {
100
+ ferrovue::Router::tree(ROUTES).with_base(BASE)
101
+ }
102
+ `;
103
+ }
104
+ export const router = {
105
+ name: "router",
106
+ configure(config, root) {
107
+ const r = config.router ?? (config.routes ? { routes: config.routes } : null);
108
+ return {
109
+ routes: r ? readRoutes(root, r.routes) : null,
110
+ file: r?.routes ?? null,
111
+ base: r?.base ?? "",
112
+ linkActive: r?.linkActiveClass ?? "router-link-active",
113
+ linkExactActive: r?.linkExactActiveClass ?? "router-link-exact-active",
114
+ readers: new Set(),
115
+ views: new Set(),
116
+ };
117
+ },
118
+ compiled(comp, code, script) {
119
+ const imported = (exported) => script.some((st) => st.type === "ImportDeclaration" && st.source.value === "vue-router" &&
120
+ st.specifiers.some((sp) => sp.type === "ImportSpecifier" && (sp.imported.name ?? sp.imported.value) === exported));
121
+ const run = runOf(router);
122
+ if (code.includes('_resolveComponent("RouterLink")') || imported("RouterLink") || code.includes("_ctx.$route"))
123
+ run.readers.add(comp);
124
+ if (code.includes('_resolveComponent("RouterView")') || imported("RouterView"))
125
+ run.views.add(comp);
126
+ },
127
+ scope: () => ({ useRoute: null, components: new Map() }),
128
+ scriptImport(s, st, from) {
129
+ if (from !== "vue-router")
130
+ return false;
131
+ const own = scopeOf(router, s);
132
+ for (const sp of st.specifiers) {
133
+ const name = sp.type === "ImportSpecifier" ? (sp.imported.name ?? sp.imported.value) : null;
134
+ if (name === "useRoute")
135
+ own.useRoute = sp.local.name;
136
+ else if (name === "RouterLink" || name === "RouterView")
137
+ own.components.set(sp.local.name, name);
138
+ else
139
+ s.clientOnly.set(sp.local.name, `\`${name}\` from vue-router does not run on the server`);
140
+ }
141
+ return true;
142
+ },
143
+ scriptBinding(s, d) {
144
+ const { useRoute } = scopeOf(router, s);
145
+ const init = d.init;
146
+ if (useRoute === null || d.id.type !== "Identifier" || init?.type !== "CallExpression" || init.callee.type !== "Identifier" || init.callee.name !== useRoute)
147
+ return false;
148
+ s.setup.set(d.id.name, theRoute(s, d));
149
+ return true;
150
+ },
151
+ global: (s, name, n) => (name === "$route" ? theRoute(s, n) : null),
152
+ member(s, base, prop, n, computed) {
153
+ if (base.ty.k === "params" || base.ty.k === "queryobj" || (base.ty.k === "route" && !computed))
154
+ return routeField(s, base, prop, n);
155
+ return null;
156
+ },
157
+ equality(s, n) {
158
+ const tested = typeofString(n);
159
+ if (!tested)
160
+ return null;
161
+ const v = expr(s, tested.target);
162
+ if (v.ty.k !== "query")
163
+ fail(s.comp, '`typeof` tests a query value only, as `typeof route.query.q === "string"`', n);
164
+ const one = `${atom(v.code)}.attr_value().is_some()`;
165
+ return boolOf(tested.negated ? not(one) : one);
166
+ },
167
+ presence(s, n) {
168
+ const t = typeofString(n);
169
+ if (!t)
170
+ return undefined;
171
+ const path = pathOf(t.target);
172
+ const v = expr(s, t.target);
173
+ if (path === null || v.ty.k !== "query")
174
+ return null;
175
+ const option = `${atom(v.code)}.attr_value()`;
176
+ return { path, of: STR, negated: t.negated, truthy: false, option, present: `${option}.is_some()`, pattern: (name) => `let Some(${name}) = ${option}` };
177
+ },
178
+ values: {
179
+ describe: (ty) => (ty.k === "query" ? "a query value" : undefined),
180
+ truthy: (v) => (v.ty.k === "query" ? `${atom(v.code)}.truthy()` : undefined),
181
+ interpolate(e, v) {
182
+ if (v.ty.k !== "query")
183
+ return false;
184
+ e.stmt(`${atom(v.code)}.write_display(out);`);
185
+ return true;
186
+ },
187
+ unbindable: (ty) => ty.k === "query"
188
+ ? { what: "a query value, which is an array when its key is repeated", fix: 'narrow it to one string, as `typeof route.query.q === "string" ? route.query.q : ""`' }
189
+ : undefined,
190
+ nullish(s, a, b, n) {
191
+ if (a.ty.k !== "query")
192
+ return undefined;
193
+ if (b.ty.k !== "str")
194
+ fail(s.comp, "`??` after a query value takes a string", n);
195
+ return { code: `${atom(a.code)}.or(${bare(b.code)})`, ty: a.ty };
196
+ },
197
+ equals(a, b) {
198
+ if (a.ty.k === "query" && b.ty.k === "str")
199
+ return `${atom(a.code)}.is(${strArg(b.code)})`;
200
+ if (b.ty.k === "query" && a.ty.k === "str")
201
+ return `${atom(b.code)}.is(${strArg(a.code)})`;
202
+ if (a.ty.k === "query" && b.ty.k === "undef")
203
+ return `${atom(a.code)}.is_undefined()`;
204
+ if (b.ty.k === "query" && a.ty.k === "undef")
205
+ return `${atom(b.code)}.is_undefined()`;
206
+ return undefined;
207
+ },
208
+ isArray: (v) => (v.ty.k === "query" ? { code: `${atom(v.code)}.is_array()`, ty: BOOL } : undefined),
209
+ },
210
+ resolveComponent(s, local, name) {
211
+ if (name !== "RouterLink" && name !== "RouterView")
212
+ return false;
213
+ scopeOf(router, s).components.set(local, name);
214
+ return true;
215
+ },
216
+ component(s, e, n) {
217
+ const target = n.arguments[0];
218
+ const { components } = scopeOf(router, s);
219
+ const routed = target.type === "Identifier"
220
+ ? components.get(target.name)
221
+ : target.type === "MemberExpression" && target.object.name === "$setup"
222
+ ? components.get(target.computed ? target.property.value : target.property.name)
223
+ : undefined;
224
+ if (routed === "RouterLink")
225
+ routerLink(s, e, n);
226
+ else if (routed === "RouterView") {
227
+ if (scopeIdOf(s.comp) !== null)
228
+ fail(s.comp, "`<RouterView>` in a component with `<style scoped>` gives the page this component's id, which the server's page does not carry", n);
229
+ e.stmt("fv_slots.router_view.render_to(out);");
230
+ }
231
+ else
232
+ return false;
233
+ return true;
234
+ },
235
+ child(s, child, n) {
236
+ if (runOf(router).views.has(child)) {
237
+ fail(s.comp, `${child.name} holds \`<RouterView>\`: the server renders it at the top, never as a child`, n);
238
+ }
239
+ },
240
+ slotFields: (comp) => (runOf(router).views.has(comp) ? [{ js: "routerView", rust: "router_view", doc: "The page `<RouterView>` shows." }] : []),
241
+ params: [
242
+ {
243
+ name: "fv_route",
244
+ ty: "&fv::Route<'_>",
245
+ pageTy: "&'p fv::Route<'p>",
246
+ reads: (c) => runOf(router).readers.has(c),
247
+ test: { lines: ["let router = route_table::router();", "let route = router.at(&fixture.route);"], arg: "&route", fixture: true },
248
+ fixtureField: ' #[serde(rename = "$route", default = "Fixture::root")]\n route: String,',
249
+ fixtureDefault: ' fn root() -> String {\n "/".to_owned()\n }',
250
+ },
251
+ ],
252
+ modules() {
253
+ const { routes, file } = runOf(router);
254
+ return routes ? [["route_table.rs", routesSource(routes, file)]] : [];
255
+ },
256
+ };
@@ -0,0 +1,12 @@
1
+ import { type Component } from "../model.ts";
2
+ import { type ScopeIdMode } from "../context.ts";
3
+ import { type Plugin } from "../plugin.ts";
4
+ interface ScopedRun {
5
+ mode: ScopeIdMode;
6
+ viteRoot: string;
7
+ ids: Map<Component, string>;
8
+ slotted: Set<Component>;
9
+ }
10
+ export declare function scopeIdOf(comp: Component): string | null;
11
+ export declare const scoped: Plugin<ScopedRun>;
12
+ export {};
@@ -0,0 +1,125 @@
1
+ import { parse as parseJs } from "@babel/parser";
2
+ import { createHash } from "node:crypto";
3
+ import { relative, resolve as resolvePath, sep } from "node:path";
4
+ import { fail, rustStr } from "../model.js";
5
+ import {} from "../context.js";
6
+ import { runOf } from "../plugin.js";
7
+ export function scopeIdOf(comp) {
8
+ return runOf(scoped).ids.get(comp) ?? null;
9
+ }
10
+ function scopeHash(file, source) {
11
+ const { mode, viteRoot } = runOf(scoped);
12
+ const path = relative(viteRoot, resolvePath(file)).split(sep).join("/");
13
+ return createHash("sha256")
14
+ .update(mode === "filepath" ? path : path + source)
15
+ .digest("hex")
16
+ .slice(0, 8);
17
+ }
18
+ function flowOf(comp, ssr, children) {
19
+ const calls = [];
20
+ const outlets = [];
21
+ const self = new Set();
22
+ const resolve = (target) => {
23
+ if (target?.type === "MemberExpression" && target.object.name === "$setup") {
24
+ return children.get(target.computed ? target.property.value : target.property.name) ?? null;
25
+ }
26
+ return target?.type === "Identifier" && self.has(target.name) ? comp.name : null;
27
+ };
28
+ const mentions = (n, name) => !!n && typeof n === "object" && (n.type === "Identifier" ? n.name === name : Object.values(n).some((v) => mentions(v, name)));
29
+ const walk = (n, within) => {
30
+ if (!n || typeof n !== "object")
31
+ return;
32
+ if (Array.isArray(n)) {
33
+ for (const x of n)
34
+ walk(x, within);
35
+ return;
36
+ }
37
+ if (n.type === "VariableDeclarator" && n.init?.type === "CallExpression" && n.init.callee.name === "_resolveComponent") {
38
+ if (n.init.arguments[0]?.value === comp.name && n.init.arguments[1])
39
+ self.add(n.id.name);
40
+ }
41
+ if (n.type === "CallExpression" && n.callee.type === "Identifier") {
42
+ const a = n.arguments;
43
+ if (n.callee.name === "_ssrRenderComponent") {
44
+ const child = resolve(a[0]);
45
+ if (child)
46
+ calls.push({ child, passesAttrs: mentions(a[1], "_attrs"), within: a[4] ? within : null });
47
+ walk(a[2], child);
48
+ return;
49
+ }
50
+ if (n.callee.name === "_ssrRenderSlot" || n.callee.name === "_ssrRenderSlotInner") {
51
+ const id = a[6];
52
+ const passes = !id || id.type === "NullLiteral" ? "none" : id.type === "Identifier" && id.name === "_scopeId" ? "forward" : "id";
53
+ outlets.push({ passes, within });
54
+ }
55
+ }
56
+ for (const [key, v] of Object.entries(n))
57
+ if (key !== "loc" && key !== "start" && key !== "end")
58
+ walk(v, within);
59
+ };
60
+ walk(parseJs(ssr, { sourceType: "module" }).program, null);
61
+ return { calls, outlets };
62
+ }
63
+ function scopeFlow(read) {
64
+ const flows = read.map((r) => ({ comp: r.comp, ...flowOf(r.comp, r.ssr, r.children) }));
65
+ const byName = new Map(read.map((r) => [r.comp.name, r.comp]));
66
+ const passes = (name) => (name !== null && byName.get(name)?.passesSlotIds) ?? false;
67
+ for (let changed = true; changed;) {
68
+ changed = false;
69
+ for (const f of flows) {
70
+ if (!f.comp.passesSlotIds && f.outlets.some((o) => o.passes === "id" || (o.passes === "forward" && passes(o.within)))) {
71
+ f.comp.passesSlotIds = changed = true;
72
+ }
73
+ }
74
+ }
75
+ for (let changed = true; changed;) {
76
+ changed = false;
77
+ for (const f of flows) {
78
+ for (const c of f.calls) {
79
+ const child = byName.get(c.child);
80
+ if (!child || child.inherits)
81
+ continue;
82
+ if (scopeIdOf(f.comp) !== null || (c.passesAttrs && f.comp.inherits && child.inheritAttrs) || passes(c.within)) {
83
+ child.inherits = changed = true;
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ function childIds(s, child, passesAttrs, inSlot, n) {
90
+ const base = passesAttrs && child.inheritAttrs ? s.attrs : null;
91
+ const own = scopeIdOf(s.comp);
92
+ const slotted = inSlot ? s.sid : null;
93
+ let code;
94
+ if (base === null && slotted === null)
95
+ code = own === null ? null : rustStr(` ${own}`);
96
+ else if (own === null && slotted === null)
97
+ code = base;
98
+ else
99
+ code = `&fv::scope_attrs(${base ?? '""'}, ${own === null ? '""' : rustStr(own)}, ${slotted ?? '""'})`;
100
+ if (code !== null && !child.inherits)
101
+ fail(s.comp, `${child.name} is handed scope ids its render does not take`, n);
102
+ return code;
103
+ }
104
+ export const scoped = {
105
+ name: "scoped",
106
+ configure: (config, root) => ({
107
+ mode: config.scopeId ?? "filepath-source",
108
+ viteRoot: resolvePath(root, config.viteRoot ?? "."),
109
+ ids: new Map(),
110
+ slotted: new Set(),
111
+ }),
112
+ sfc(comp, descriptor, file, source) {
113
+ const run = runOf(scoped);
114
+ if (descriptor.styles.some((st) => st.scoped))
115
+ run.ids.set(comp, `data-v-${scopeHash(file, source)}`);
116
+ if (descriptor.slotted)
117
+ run.slotted.add(comp);
118
+ },
119
+ templateOptions(comp) {
120
+ const id = scopeIdOf(comp);
121
+ return { id: id ?? comp.name, scoped: id !== null, slotted: runOf(scoped).slotted.has(comp) };
122
+ },
123
+ analyse: (read) => scopeFlow(read),
124
+ childIds,
125
+ };
@@ -0,0 +1,2 @@
1
+ import { type Plugin } from "../plugin.ts";
2
+ export declare const sharedTypes: Plugin;
@@ -0,0 +1,7 @@
1
+ import { ctx } from "../context.js";
2
+ import { typesSource } from "../rust.js";
3
+ import {} from "../plugin.js";
4
+ export const sharedTypes = {
5
+ name: "types",
6
+ modules: () => (ctx.typeStructs.size ? [["types.rs", typesSource()]] : []),
7
+ };
@@ -0,0 +1,37 @@
1
+ import { type Component, type N, type Struct } from "../model.ts";
2
+ import { type Plugin } from "../plugin.ts";
3
+ declare module "../model.ts" {
4
+ interface StructTy {
5
+ store?: true;
6
+ }
7
+ }
8
+ export interface StoreGetter {
9
+ param: string | null;
10
+ body: N;
11
+ file: string;
12
+ setup?: true;
13
+ }
14
+ export interface Store {
15
+ hook: string;
16
+ id: string;
17
+ field: string;
18
+ state: string;
19
+ module: string;
20
+ getters: Map<string, StoreGetter>;
21
+ }
22
+ interface StoresRun {
23
+ dir: string | null;
24
+ stores: Map<string, Store>;
25
+ structs: Map<string, Struct>;
26
+ files: Map<string, string>;
27
+ readers: Set<Component>;
28
+ inProgress: Set<string>;
29
+ }
30
+ interface StoresScope {
31
+ hooks: Map<string, Store>;
32
+ storeToRefs: string | null;
33
+ values: Map<string, Store>;
34
+ }
35
+ export declare function storeHome(file: string): Component;
36
+ export declare const piniaStores: Plugin<StoresRun, StoresScope>;
37
+ export {};