ferrovue 0.12.0 → 0.13.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.
- package/README.md +8 -8
- package/dist/attrs.d.ts +4 -0
- package/dist/attrs.js +8 -0
- package/dist/blocks.js +6 -17
- package/dist/calls.js +150 -330
- package/dist/children.js +64 -19
- package/dist/classes.js +2 -5
- package/dist/client.d.ts +1 -1
- package/dist/client.js +2 -2
- package/dist/component.js +76 -23
- package/dist/conditional.d.ts +3 -0
- package/dist/conditional.js +201 -0
- package/dist/conformance.js +4 -10
- package/dist/constants.js +93 -15
- package/dist/dynamic.js +9 -4
- package/dist/enums.d.ts +20 -0
- package/dist/enums.js +126 -13
- package/dist/errors.d.ts +16 -16
- package/dist/errors.js +16 -16
- package/dist/expr.js +241 -772
- package/dist/fallthrough.d.ts +8 -0
- package/dist/fallthrough.js +39 -31
- package/dist/functions.js +3 -1
- package/dist/globals.d.ts +7 -0
- package/dist/globals.js +223 -0
- package/dist/lists.d.ts +7 -2
- package/dist/lists.js +100 -9
- package/dist/logical.d.ts +3 -0
- package/dist/logical.js +215 -0
- package/dist/loops.js +7 -3
- package/dist/members.d.ts +5 -0
- package/dist/members.js +343 -0
- package/dist/model.d.ts +43 -1
- package/dist/model.js +50 -2
- package/dist/narrowing.d.ts +14 -4
- package/dist/narrowing.js +11 -8
- package/dist/numbers.d.ts +2 -0
- package/dist/numbers.js +38 -1
- package/dist/objects.d.ts +2 -0
- package/dist/objects.js +22 -0
- package/dist/operators.d.ts +5 -0
- package/dist/operators.js +186 -0
- package/dist/plugins/head.js +14 -24
- package/dist/plugins/loaders.d.ts +5 -0
- package/dist/plugins/loaders.js +115 -8
- package/dist/plugins/router-link.js +5 -12
- package/dist/plugins/scoped.js +7 -13
- package/dist/plugins/stores.js +2 -2
- package/dist/problems.d.ts +8 -0
- package/dist/problems.js +25 -0
- package/dist/revive.js +6 -2
- package/dist/rust.d.ts +1 -1
- package/dist/rust.js +41 -14
- package/dist/script.d.ts +0 -1
- package/dist/script.js +335 -238
- package/dist/slots.d.ts +2 -0
- package/dist/slots.js +11 -0
- package/dist/ssr.js +3 -2
- package/dist/statements.js +11 -12
- package/dist/strings.d.ts +25 -0
- package/dist/strings.js +240 -1
- package/dist/styles.js +12 -15
- package/dist/template.js +97 -11
- package/dist/testing-browser.js +27 -48
- package/dist/typescript.d.ts +7 -0
- package/dist/typescript.js +162 -4
- package/dist/vite.d.ts +0 -2
- package/dist/vite.js +9 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ the configuration at your project root yourself:
|
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
60
|
-
"$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.
|
|
60
|
+
"$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.13.0/schema.json",
|
|
61
61
|
"components": "client/components",
|
|
62
62
|
"out": "src/generated"
|
|
63
63
|
}
|
|
@@ -173,7 +173,7 @@ The crate's features:
|
|
|
173
173
|
|
|
174
174
|
- `router` and `i18n`, both on by default: vue-router and vue-i18n. An application with neither
|
|
175
175
|
routes nor translations can leave them out, and build neither `ferrovue-router` nor
|
|
176
|
-
`ferrovue-i18n`: `ferrovue = { version = "0.
|
|
176
|
+
`ferrovue-i18n`: `ferrovue = { version = "0.13", default-features = false }`.
|
|
177
177
|
- `axum` and `actix-web`: `Html` is an `IntoResponse` or a `Responder`, so a handler responds with
|
|
178
178
|
the component as `text/html; charset=utf-8`. `into_html(props)` and `into_island(props)` take the
|
|
179
179
|
props by value, so a handler that builds them returns the page:
|
|
@@ -223,10 +223,10 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
|
|
|
223
223
|
|
|
224
224
|
| Area | Supported |
|
|
225
225
|
|---|---|
|
|
226
|
-
| Prop types | `string`, `number` (integers, `i64`, written and computed as JavaScript does, exact within ±2⁵³), `Float` from `ferrovue/types` (fractions, `f64`), `boolean`, unions of two or more string literals (`"sm" \| "md"`, an alias, or written in place), each a Rust enum generated for it (`Size::Md`, `"md".parse::<Size>()`, written and serialised as its literal; see the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#unions-of-string-literals) guide), or an enum of the application's own that `unions` names for the alias; a list of such a union, a list of its enum (`Size[]` is `Vec<Size>`); `T \| undefined`, `T \| null` (an `Option` written as `null`), arrays (`T[]`, `Array<T>`, `readonly T[]`), interfaces and object type aliases (recursive ones too), dictionaries (`Record<string, T>`, `{ [key: string]: T }`, `ferrovue::props::Record` in Rust, which keeps JavaScript's order of keys), another component's exported `Props`, `TrustedHtml`, `InlineHtml`, `Date` (a prop, optional or nullable too, and inside lists, dictionaries and interfaces: `ferrovue::date::JsDate`, or `chrono::DateTime<Utc>` with the `chrono` feature) |
|
|
226
|
+
| Prop types | `string`, `number` (integers, `i64`, written and computed as JavaScript does, exact within ±2⁵³), `Float` from `ferrovue/types` (fractions, `f64`), `boolean`, unions of two or more string literals (`"sm" \| "md"`, an alias, or written in place), each a Rust enum generated for it (`Size::Md`, `"md".parse::<Size>()`, written and serialised as its literal; see the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#unions-of-string-literals) guide), or an enum of the application's own that `unions` names for the alias; a list of such a union, a list of its enum (`Size[]` is `Vec<Size>`); `T \| undefined`, `T \| null` (an `Option` written as `null`), arrays (`T[]`, `Array<T>`, `readonly T[]`), interfaces and object type aliases (recursive ones too), object types written in place, mapped types and `Record`s over a union of names, indexed types (`Card["size"]`), tuples (`[label: string, n: number]`, a tuple struct written as an array), dictionaries (`Record<string, T>`, `{ [key: string]: T }`, `ferrovue::props::Record` in Rust, which keeps JavaScript's order of keys), another component's exported `Props`, `TrustedHtml`, `InlineHtml`, `Date` (a prop, optional or nullable too, and inside lists, dictionaries and interfaces: `ferrovue::date::JsDate`, or `chrono::DateTime<Utc>` with the `chrono` feature) |
|
|
227
227
|
| Shared types | Interfaces and type aliases imported from `.ts` files (generated once, into `types.rs`; imported by a relative path or through an alias the project declares, in its tsconfig.json's `compilerOptions.paths` or, with the Vite plugin, Vite's `resolve.alias`: `@/lib/format`), from a store's file, or from another component's `.vue` file; objects of a shared type can be passed between components. A TypeScript `enum` of string members is a Rust enum, as a union of string literals is, its variants named after its members; one of numbers is a number |
|
|
228
|
-
| Constants | Constants imported from `.ts` files, object constants declared in `<script setup>`, and `enum`s (imported, or declared in the component, with members set to such constants or to earlier members), evaluated when the component is compiled: strings, numbers, booleans, `null`, arithmetic and joined strings of them as JavaScript evaluates them (`60 * MINUTE`, `` `${BASE}/feed` ``, `"#" + 1.5`, and `**` where it gives an exact integer within ±2⁵³), lists of them, objects of them read field by field (`LABELS.save`, `Tone.Loud`, `Rank[5]`), and lists of objects of them, which become a `const` in `types.rs` (`{ value, label }` items get a type named after the constant, `OptionsItem`, unless the constant is typed with an interface: `OPTIONS: Option[]`; a field that is `null` in some items is `T | null`); lookup tables: an object constant (`as const`, `Record<Size, T>` or untyped, keys computed from enum members too) read by a key whose type is a union of string or integer literals or an `enum` (a prop, a `computed`, a `v-for` item's field, a `?:` of literals), compiled to a `match` over the members (over the variants of a union's enum, with no catch-all arm; a table of a union's members, `Record<Tone, Tone>`, reaches a union prop as a `match` from variant to variant), read on by field (`THEME[tone].fg`) in text, attributes, `:class` and `:style`, with `undefined` for a member the object lacks (`ICONS[kind] ?? "none"`, `ICONS[kind]?.label`, `v-if="ICONS[kind]"`, `kind && ICONS[kind]`); a free-text key tested with `Object.hasOwn(ROLES, key)`, against the object's own keys, which narrows the key for `ROLES[key]` in what follows; `Object.keys()` of an object constant; a constant `new Map([[key, value], …])` of string or integer keys, read with `.get(key)` (`undefined` for a key it lacks) and `.has(key)`. See the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#constants) guide |
|
|
229
|
-
| Props | `withDefaults`, destructured props with defaults and new names (`const { size = "md", label: text } = defineProps<…>()`), the props object (`const props = defineProps<…>()`, then `props.label` in the template or in script code), optional booleans (Vue casts an absent one to `false`), `defineModel` (named, required, with defaults), components with no props. A generic component (`<script setup generic="T extends Item">`) renders each type parameter as its constraint, which is all the template can rely on |
|
|
228
|
+
| Constants | Constants imported from `.ts` files, object constants declared in `<script setup>`, and `enum`s (imported, or declared in the component, with members set to such constants or to earlier members), evaluated when the component is compiled: strings, numbers, booleans, `null`, arithmetic and joined strings of them as JavaScript evaluates them (`60 * MINUTE`, `` `${BASE}/feed` ``, `"#" + 1.5`, and `**` where it gives an exact integer within ±2⁵³), lists of them, objects of them read field by field (`LABELS.save`, `Tone.Loud`, `Rank[5]`), and lists of objects of them, which become a `const` in `types.rs` (`{ value, label }` items get a type named after the constant, `OptionsItem`, unless the constant is typed with an interface: `OPTIONS: Option[]`; a field that is `null` in some items is `T | null`); lookup tables: an object constant (`as const`, `Record<Size, T>` or untyped, keys computed from enum members too) read by a key whose type is a union of string or integer literals or an `enum` (a prop, a `computed`, a `v-for` item's field, a `?:` of literals, or built from one by `.toUpperCase()`, `.toLowerCase()` or a template literal), or by a constant number (`O[HALF]` reads `O["0.5"]`), compiled to a `match` over the members (over the variants of a union's enum, with no catch-all arm; a table of a union's members, `Record<Tone, Tone>`, reaches a union prop as a `match` from variant to variant), read on by field (`THEME[tone].fg`) in text, attributes, `:class` and `:style`, with `undefined` for a member the object lacks (`ICONS[kind] ?? "none"`, `ICONS[kind]?.label`, `v-if="ICONS[kind]"`, `kind && ICONS[kind]`); a free-text key tested with `Object.hasOwn(ROLES, key)`, against the object's own keys, which narrows the key for `ROLES[key]` in what follows; `Object.keys()` of an object constant; a constant `new Map([[key, value], …])` of string or integer keys, read with `.get(key)` (`undefined` for a key it lacks) and `.has(key)`. See the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#constants) guide |
|
|
229
|
+
| Props | Props declared in place, by an interface of the component, or by one imported from a `.ts` file by a relative path (`defineProps<CardProps>()`), extended or joined with `&`; `withDefaults`, destructured props with defaults and new names (`const { size = "md", label: text } = defineProps<…>()`), the props object (`const props = defineProps<…>()`, then `props.label` in the template or in script code), optional booleans (Vue casts an absent one to `false`), `defineModel` (named, required, with defaults), components with no props. A generic component (`<script setup generic="T extends Item">`) renders each type parameter as its constraint, which is all the template can rely on |
|
|
230
230
|
| Setup | `ref`/`shallowRef`, `computed` (an expression, or a body of statements: see Statements; one that may throw, through `.repeat()`, `.padStart()`, a date, `$t` or a helper, is evaluated where the render first reads it, once, as Vue does), refs that start empty, typed by their type argument (`ref<Row[]>([])`, `ref<Row>()`, `ref<string \| undefined>(undefined)`), refs of a union of string literals, held as its enum (`ref<Choice>(props.tone ?? "")`, a member, a value of the union, a lookup table's value, or a `?:` or `??` of them), plain `const`/`let`, `.value` in script code, helpers with Rust twins, functions (see Functions), a plain `<script>` block beside setup. Lifecycle hooks, `watch` (not `immediate`), `defineEmits`, `defineSlots`, `defineOptions`, `defineExpose` and template refs (`ref(null)`, `useTemplateRef`) are client-only, as is a function only an event handler calls. The template may name them only from event handlers, which the server drops |
|
|
231
231
|
| Statements | in a `computed`'s body and in a list method's callback (`.map()`, `.filter()`, `.find()`, `.some()`, `.reduce()`, a `.toSorted()` comparator and the others): `const` and `let` of any value the template may compute, destructured too (`const { name, posts: count } = author`); `if` / `else if` / `else` and early `return`, narrowing what follows as TypeScript narrows it (`if (!user) return ""; return user.name`, `if (!a \|\| !a.title) return "-"`); `switch` over a string or a number, with stacked `case`s, `default` and `break`; assignments to the body's `let`s (`=`, `+=`, `-=`, `*=`, `++`, `--` and the others), with the type TypeScript gives them; `for (const x of list)` over a list, `list.entries()`, `Object.keys()` or `Object.entries()`, `for (const ch of text)` over a string by its characters (a character beyond U+FFFF is one), and counting loops `for (let i = 0; i < n; i++)` to an integer bound, with `break`, `continue`, nested loops and `return` inside them, carrying strings, numbers and booleans from one item to the next (`let total = 0; for (const r of rows) total += r.n;`, thousands separators, the largest of a list). Each value is evaluated once, where JavaScript evaluates it, and only on the path that reaches it. `while`, `do…while` and `for…in` loops, assignments to props, refs, `const`s, parameters and fields, `try`, `throw`, `var` and a path that ends without `return` are refused. See the crate's [`script_logic`](https://docs.rs/ferrovue/latest/ferrovue/guide/script_logic/index.html) guide |
|
|
232
232
|
| Functions | functions the template or a `computed` calls: declared in `<script setup>` (`function f(a: T): R { … }`, `const f = (a: T) => …`) or exported from a `.ts` file the component imports, whose bodies hold what a `computed`'s may (see Statements), arrow functions of their own included (`const pad = (k: number) => …`, reading the body's names) and read props, refs, `computed`s, constants and other such functions (a `.ts` file's own constants, functions and imports); parameters by name, optional (`x?: T`), with defaults (`x = 1`, also after an earlier parameter: ``plural = `${word}s` ``) and as object patterns. Each call is translated where it is written, its arguments evaluated once and in order, with the types they have there, so one function serves an integer and a `Float`. A function imported from a `.ts` file needs no `helpers` entry. Recursion, `...rest`, `async` and generators are refused. See the crate's [`script_logic`](https://docs.rs/ferrovue/latest/ferrovue/guide/script_logic/index.html#functions) guide |
|
|
@@ -234,7 +234,7 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
|
|
|
234
234
|
| Loaders | A top-level `await` in `<script setup>` of a loader declared with `defineLoader`, whose arguments are strings, numbers and booleans and whose result is any type a prop may have, dates inside it included: the compiler writes a `Loaders` trait the Rust app implements, and the render stops where Vue's would until the result is in, starting siblings' loaders together. The results travel in the page for the client to hydrate from, inside a `<Suspense>` `mountPage` adds. See the crate's [`loaders`](https://docs.rs/ferrovue/latest/ferrovue/guide/loaders/index.html) guide |
|
|
235
235
|
| Provide and inject | `provide` and `inject` resolved through the component tree, slot content seeing the providers of the component that renders it, as Vue does: keys that are string literals or `InjectionKey` symbols exported from `.ts` files; values that are props, refs, `computed`s, constants or objects of them (an interface's fields, `reactive()` unwrapping refs); defaults, factory defaults (`inject(k, () => …, true)`), object defaults built as the key's interface (`inject(k, { size: "md" })`), and keys holding functions, which only event handlers may call. A page rendered from Rust passes the generated `Provides`, empty or holding what the client app gives `app.provide`. See the crate's [`provide_inject`](https://docs.rs/ferrovue/latest/ferrovue/guide/provide_inject/index.html) guide |
|
|
236
236
|
| Text | `{{ }}` of strings, integers and booleans; `+`, `-`, `*`, `/`, `%`, with integers and fractions as JavaScript computes them; `<`, `>`, `<=`, `>=` (between numbers, or between strings by UTF-16 code unit), `===`, `!==`; unary `-`, `!`; `??`, `\|\|`, `&&`, `?:` (whose branches may differ in type where the value is only written out: in `{{ }}`, a string-valued attribute and a template literal, each branch is written as JavaScript writes it, `{{ n > 0 ? n : "none" }}`); template literals and `String()` of strings, numbers, booleans, values that may be absent (`undefined` or `null`, as JavaScript writes them) and lists (their items joined by `,`); `typeof`; optional chaining `a?.b`, `a?.[i]` and chains that go on past it (`a?.b.c`, `s?.trim().length`, `list?.at(-1)?.name`), with every property and method the value has when present, `undefined` as a whole once `a` is absent; `.length`; `.toString()`, `.toFixed()`, `.toPrecision()` and `.toExponential()` (with no argument or a literal one), `Math.max`/`min` (of a spread list too, `Math.max(...xs)`)/`abs`/`round`/`floor`/`ceil`/`trunc`/`sqrt`/`sign`, the constants of `Math` and `Number`, `**` and `Math.pow()` of an integer and a whole exponent known when compiling, exact within ±2⁵³; `Number()`, `parseInt()` (no radix, 10 or 16), `parseFloat()`; `Number.isNaN()`, `Number.isFinite()`, `Number.isInteger()` and `Number.isSafeInteger()` of a number, present or optional, and `isNaN()` and `isFinite()` of a present number; `JSON.stringify()` of strings, numbers, booleans and lists of those; `encodeURIComponent()` and `encodeURI()`; array literals |
|
|
237
|
-
| Strings | `.trim()`, `.trimStart()`, `.trimEnd()`, `.trimLeft()`, `.trimRight()`, `.toUpperCase()`, `.toLowerCase()`, `.includes()`, `.startsWith()`, `.endsWith()`, `.indexOf()` and `.lastIndexOf()`, with a position or without, `.slice()`, `.substring()`, `.substr()`, `.at()`, `.charAt()`, `.charCodeAt()`, `.codePointAt()`, `.concat()`, `.split()` with a limit or without, `.replace()` and `.replaceAll()` with string patterns (`$&`, `` $` ``, `$'`, `$$` read as JavaScript reads them), `.padStart()`, `.padEnd()`, `.repeat()`, `s[i]`; indices and lengths count UTF-16 code units, as JavaScript's do; a string known to hold ASCII alone (`String(n)`, `n.toFixed(2)`, literals and what is built of them) may be cut into characters and joined again, as a thousands separator is (`String(n).split("").reduce(…)`, slices) (see the crate's [`strings`](https://docs.rs/ferrovue/latest/ferrovue/guide/strings/index.html) guide) |
|
|
237
|
+
| Strings | `.trim()`, `.trimStart()`, `.trimEnd()`, `.trimLeft()`, `.trimRight()`, `.toUpperCase()`, `.toLowerCase()`, `.includes()`, `.startsWith()`, `.endsWith()`, `.indexOf()` and `.lastIndexOf()`, with a position or without, `.slice()`, `.substring()`, `.substr()`, `.at()`, `.charAt()`, `.charCodeAt()`, `.codePointAt()`, `.concat()`, `.split()` with a limit or without, `.replace()` and `.replaceAll()` with string patterns (`$&`, `` $` ``, `$'`, `$$` read as JavaScript reads them), regular expression literals of a subset (characters, classes, `^`, `$`, `\b`, quantifiers, the flags `g` and `i`) in `.replace()`, `.replaceAll()`, `.split()` and `.test()`, `.padStart()`, `.padEnd()`, `.repeat()`, `s[i]`; indices and lengths count UTF-16 code units, as JavaScript's do; a string known to hold ASCII alone (`String(n)`, `n.toFixed(2)`, literals and what is built of them) may be cut into characters and joined again, as a thousands separator is (`String(n).split("").reduce(…)`, slices) (see the crate's [`strings`](https://docs.rs/ferrovue/latest/ferrovue/guide/strings/index.html) guide) |
|
|
238
238
|
| Dates | `new Date(s)` and `Date.parse(s)` of an ISO string (a date alone, or a date and time with a `Z` or an offset, as ECMAScript's date time string format specifies), `new Date(ms)`, `Date.UTC(…)` of one to seven parts, `.toISOString()`, `.toUTCString()`, `.toJSON()`, `JSON.stringify()`, `.getTime()`, `.valueOf()` and the `.getUTC…()` methods, and `<`, `<=`, `>` and `>=` between two dates, invalid dates included, as JavaScript computes them, a date's validity tested with `Number.isNaN(d.getTime())`, and dates chosen with `?:`, `??` and `||` (a date is always truthy, an invalid one too), of a date built in the template or of a `Date` prop, which an island's `data-props` carries as its ISO string and the client turns back into a `Date` before hydrating, and which may be handed to a child's `Date` prop. A date inside a list (`Date[]`, `(Date \| null)[]`), a dictionary (`Record<string, Date>`) or an interface of the props or of a loader's result (`rows: Row[]` with `at: Date`) is read as a prop's date is, walked with `v-for` and the list methods that do not compare or write dates as text, built in an object literal, and turned back into a `Date` in the browser from a plan the compiler writes beside the props (`data-dates`) and the loaders' results. Prices and formatted dates are written in Rust and passed as strings, or filled into a `$t` message. See the crate's [`dates_and_prices`](https://docs.rs/ferrovue/latest/ferrovue/guide/dates_and_prices/index.html) guide |
|
|
239
239
|
| Conditions | `v-if` / `v-else-if` / `v-else`, `?:`, `&&` and `||`, with optional and nullable values narrowed as TypeScript narrows them: `v-if="user"`, `user !== undefined`, `user !== null`, `user != null`, `typeof user !== "undefined"`, and `!user`, `user === undefined` or `user === null` for the `v-else`; a test narrows every operand after it in a chain (`n != null && n > 1 && n < 9`, `n == null \|\| n < 0 \|\| n > 9`, `n != null && n > 1 ? n * 2 : 0`), and `a && b` gives `undefined` where `a`, whose present values are all truthy, is absent (`kind && ICONS[kind]`); a string of a union of string literals narrowed the same way, by `===` and `!==` with a literal, by its truth and by `||`, so that a `ref<Tone | "">` reaches a child's `Tone` prop as `tone \|\| undefined`, `tone === '' ? undefined : tone`, under `v-if="tone !== ''"` or through a `computed` annotated `Tone \| undefined` |
|
|
240
240
|
| Lists | `v-for` over arrays (of strings, numbers, objects or child props), array literals and number ranges (`n in 5`); with an index; with destructured items (`{ id, name } in rows`); nested; on `<template>`. `.includes()` (a number looked for in a list of numbers whether either is a `number` or a `Float`, as JavaScript compares them), `.join()`, and `.filter()`, `.map()`, `.some()`, `.every()`, `.find()`, `.findIndex()`, `.findLast()`, `.findLastIndex()` with an arrow function of the item (a name, or destructured) and its index, whose body is an expression or a body of statements (see Statements), narrowing inside as outside, or with `Boolean`; `.filter(Boolean)` of a list or an array literal whose items may be `null` or `undefined`, which keeps the ones present and truthy as a list of the values (`[name, title, city].filter(Boolean).join(" · ")`); `Boolean(x)`; `.slice()`; an item by its index, `list[i]` with an integer `i` and `list.at(i)` (from the end when negative), `undefined` outside the list and narrowed as other optional values are (`v-if="rows[0]"`); `.indexOf()` and `.lastIndexOf()` of a string, number or boolean, compared with `===` (`NaN` found nowhere, `-0` found as `0`); `.concat()` of lists of one item type (numbers whether `number` or `Float`); `.reduce()` and `.reduceRight()` with an initial value, into a string, a number or a boolean; `.toReversed()`, and `.toSorted()` by `String(x)` as JavaScript sorts without a comparator (`[10, 9, 1]` to `[1, 10, 9]`) or by a comparator `(a, b) => a - b`, `a.k - b.k` of integers or `?:` over `<` and `>` of integers or strings, stable as JavaScript's; `[...list].sort(…)` and `[...list].reverse()`; spread in array literals (`[...a, x, ...b]`) of one item type; `.flat()` of lists of lists, to a literal depth or `Infinity`, and `.flatMap()` with an arrow function that gives a list (flattened one level) or a value (kept as `.map()` keeps it); chained and nested; a computed list read by `v-for`, `.join()`, `.length`, `.includes()`, kept by `computed`, or handed to a child |
|
|
@@ -246,11 +246,11 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
|
|
|
246
246
|
| `v-bind()` in `<style>` | variables bound to props, setup values, `computed`s and constants, quoted expressions included (`v-bind('size + "px"')`), in a scoped or global `<style>`: written into the root's `style` after its own and what a parent passes, on each root of a fragment, and handed to a root that is a component, as Vue's server writes them; `undefined` and `null` as `initial`, an empty string as a space; named as plugin-vue names them for production or development (`isProduction`) |
|
|
247
247
|
| CSS modules | `<style module>` and `<style module="name">`, read as `$style.x`, `name.x`, `$style['x-y']` and through `useCssModule()`, each class a constant in the generated code, when `cssModules` mirrors Vite's `css.modules.generateScopedName`: computed as postcss-modules computes it in Vite (`[local]`, `[name]`, `[hash:base64:5]`, `[path]` and the rest), composed classes, `@value`s and keyframes included |
|
|
248
248
|
| Components | imported child components, `v-bind` of a child's own `Props`, listeners in any form (`@x`, `v-on="obj"`, `@[name]`), which the server skips, `v-model` on a child's `defineModel`, recursion; props named in `kebab-case` or `camelCase`; props named like listeners (`on1`, `onSale`), left out when falsy where Vue's `mergeProps` joins a child's props, as Vue leaves them out; `defineAsyncComponent(() => import("./X.vue"))` (or `{ loader: … }`), rendered as the component it loads, which Vue's server renderer waits for |
|
|
249
|
-
| `<component :is>` | over a closed set of choices, compiled to a `match`: an imported component (`:is="Card"`), an HTML element's name (`is="h2"`), a `computed` or `?:` choosing among them, a prop typed as a union of string literals (`as: "h1" \| "h2"`, a `match` over its enum's variants), and an object of imported components (or element names) declared in setup or imported from a `.ts` file, read by such a prop (`ICONS[name]`); props, fallthrough attributes, slots and scope ids reach each choice as they reach a static child; also inside `<KeepAlive>` and `<Transition>`. See the crate's [`generated_code`](https://docs.rs/ferrovue/latest/ferrovue/guide/generated_code/index.html#component-is) guide |
|
|
249
|
+
| `<component :is>` | over a closed set of choices, compiled to a `match`: an imported component (`:is="Card"`), an HTML element's name (`is="h2"`), a `computed` or `?:` choosing among them, a prop or a `defineModel` typed as a union of string literals (`as: "h1" \| "h2"`, a `match` over its enum's variants), and an object of imported components (or element names) declared in setup or imported from a `.ts` file, read by such a prop (`ICONS[name]`); props, fallthrough attributes, slots and scope ids reach each choice as they reach a static child; also inside `<KeepAlive>` and `<Transition>`. See the crate's [`generated_code`](https://docs.rs/ferrovue/latest/ferrovue/guide/generated_code/index.html#component-is) guide |
|
|
250
250
|
| Escape hatches | `<ClientOnly>` from `ferrovue`, whose default slot the server never renders, so anything may go in it; components listed in `twins`, rendered by Rust functions of yours. See the crate's [`errors_and_limits`](https://docs.rs/ferrovue/latest/ferrovue/guide/errors_and_limits/index.html#client-only-content) guide |
|
|
251
251
|
| Fallthrough attributes | what a parent passes a child beyond its props (static and bound attributes, `class`, `style`, `data-*`, `aria-*`, booleans, `undefined`): onto its single root, merged with the root's own class and style and replacing its other attributes where they stand, as Vue's `mergeProps` merges them; none for two roots; on through a root that is a component, or a `<RouterLink>`; with `inheritAttrs: false`, onto the elements or components that bind `v-bind="$attrs"` or a `useAttrs()` binding, before or after their own; beside scope ids. Listeners are dropped, as Vue's server drops them. See the crate's [`generated_code`](https://docs.rs/ferrovue/latest/ferrovue/guide/generated_code/index.html#fallthrough-attributes) guide |
|
|
252
252
|
| Slots | default and named slots, fallbacks, `$slots.name` tests and the same through `useSlots()`, scoped slots (`<slot :item="x">` and `#item="{ item }"` or `v-slot="props"`), whose props a parent can hand to its own children |
|
|
253
|
-
| Forms | `v-model` on text inputs, checkboxes, radios, `<select>` and `<textarea>` (renders the initial state); a `<textarea>`, or an `<input>` with `v-model
|
|
253
|
+
| Forms | `v-model` on text inputs, checkboxes, radios, `<select>` and `<textarea>` (renders the initial state); a `<textarea>`, or an `<input>` with `v-model` of a literal `type`, at the component's root where no parent passes it `value` or `type` (FV0424 otherwise) |
|
|
254
254
|
| Built-ins | `<Transition>`, `<TransitionGroup>`, `<KeepAlive>`, `<Suspense>` (synchronous content), `<Teleport>` (to any target, nested, disabled: see below), `v-text`, `v-once`, `v-pre`, `v-memo`, custom directives listed in `clientDirectives`, on an element with no content when its attributes are bound by name and it is not the component's root (FV0425) |
|
|
255
255
|
| Vue Router | `<RouterLink>` (resolved by name or imported) with a string `to` or `{ name, params, query, hash }` / `{ path, query, hash }`, `active-class`, `exact-active-class`, `aria-current-value`, `replace`; vue-router's own encoding and active-link matching, nested routes included (a parent link is active on its children's pages, exact only on its own); a history base; optional parameters (`:id?`); routes built from a folder of pages, which the Vite plugin also gives the client as `ferrovue/routes`. `useRoute()` and `$route`: `path`, `fullPath`, `hash`, `name`, `params`, and `query` (a value written once, without `=`, or repeated, exactly as vue-router parses it; `typeof route.query.q === "string"` narrows one to a single string). `<RouterView>`, at the top and in nested route components: each takes the page it shows as a slot |
|
|
256
256
|
| Pinia | option stores with a typed `state`, and setup stores (`defineStore(id, () => { … })`) whose returned refs are typed by `ref<T>()` or their initial literal; read through `useX()` or `storeToRefs`, in the template or in `computed`; getters that are an expression of the state, and a setup store's computeds, which may read each other |
|
package/dist/attrs.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type N, type Scope, type Val } from "./model.ts";
|
|
2
|
+
import { type ErrorCode } from "./errors.ts";
|
|
2
3
|
import { Emitter } from "./emitter.ts";
|
|
3
4
|
export declare function display(e: Emitter, v: Val): boolean;
|
|
4
5
|
export declare function interpolate(s: Scope, e: Emitter, v: Val, n: N): void;
|
|
@@ -17,4 +18,7 @@ export declare function renderAttrs(s: Scope, e: Emitter, n: N): void;
|
|
|
17
18
|
export declare function mergeProps(s: Scope, n: N): N;
|
|
18
19
|
export declare function attrEntries(s: Scope, obj: N, side: "vnode" | "own"): string[];
|
|
19
20
|
export declare function attrOf(s: Scope, key: string, n: N, side: "vnode" | "own"): string;
|
|
21
|
+
/** A binding that is a string, `null` or `undefined`, as an attribute, refused with `code` and `what`
|
|
22
|
+
* when it is anything else. */
|
|
23
|
+
export declare function stringAttr(s: Scope, n: N, code: ErrorCode, what: string): string;
|
|
20
24
|
export declare function valueAttr(s: Scope, v: Val, n: N): string;
|
package/dist/attrs.js
CHANGED
|
@@ -329,6 +329,14 @@ export function attrOf(s, key, n, side) {
|
|
|
329
329
|
return "fv::attrs::Attr::Undefined";
|
|
330
330
|
return valueAttr(s, attrShown(s, key, n), n);
|
|
331
331
|
}
|
|
332
|
+
/** A binding that is a string, `null` or `undefined`, as an attribute, refused with `code` and `what`
|
|
333
|
+
* when it is anything else. */
|
|
334
|
+
export function stringAttr(s, n, code, what) {
|
|
335
|
+
const v = expr(s, n);
|
|
336
|
+
if (v.ty.k === "str" || nothing(v.ty) || (v.ty.k === "opt" && v.ty.of.k === "str"))
|
|
337
|
+
return valueAttr(s, v, n);
|
|
338
|
+
return fail(s.comp, code, what, n);
|
|
339
|
+
}
|
|
332
340
|
export function valueAttr(s, v, n) {
|
|
333
341
|
switch (v.ty.k) {
|
|
334
342
|
case "str":
|
package/dist/blocks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { fail, nothing, UNION_STR, unionStr } from "./model.js";
|
|
1
|
+
import { fail, nothing, patternNames, UNION_STR, unionStr } from "./model.js";
|
|
2
2
|
import { GenError } from "./errors.js";
|
|
3
|
+
import { enumListVal } from "./enums.js";
|
|
3
4
|
import { ctx } from "./context.js";
|
|
4
5
|
import { fresh, jsHint } from "./names.js";
|
|
5
6
|
import { expr, typedBy } from "./expr.js";
|
|
@@ -30,6 +31,10 @@ export function bindingOf(v) {
|
|
|
30
31
|
const optional = v.ty.k === "opt";
|
|
31
32
|
return { init: bare(v.variant), read: (at) => ({ code: optional ? `${at}.map(${UNION_STR})` : unionStr(at), ty: v.ty, variant: at, ...lone }), owned: false };
|
|
32
33
|
}
|
|
34
|
+
if (v.variants?.of === v.code && v.ty.k === "list") {
|
|
35
|
+
const { def } = v.variants;
|
|
36
|
+
return { init: `${v.variants.iter}.collect::<Vec<_>>()`, read: (at) => ({ ...enumListVal(at, v.ty, def), ...lone }), owned: true };
|
|
37
|
+
}
|
|
33
38
|
if (v.owned)
|
|
34
39
|
return { init: bare(v.code), read: (at) => ({ code: v.ty.k === "opt" ? `${at}.as_ref()` : at, ty: v.ty, ...lone }), owned: true };
|
|
35
40
|
if (v.slice)
|
|
@@ -64,22 +69,6 @@ function declared(st) {
|
|
|
64
69
|
return st.declarations.flatMap((d) => patternNames(d.id));
|
|
65
70
|
return [];
|
|
66
71
|
}
|
|
67
|
-
function patternNames(p) {
|
|
68
|
-
switch (p?.type) {
|
|
69
|
-
case "Identifier":
|
|
70
|
-
return [p.name];
|
|
71
|
-
case "ObjectPattern":
|
|
72
|
-
return p.properties.flatMap((q) => patternNames(q.type === "RestElement" ? q.argument : q.value));
|
|
73
|
-
case "ArrayPattern":
|
|
74
|
-
return p.elements.flatMap((q) => (q ? patternNames(q) : []));
|
|
75
|
-
case "AssignmentPattern":
|
|
76
|
-
return patternNames(p.left);
|
|
77
|
-
case "RestElement":
|
|
78
|
-
return patternNames(p.argument);
|
|
79
|
-
default:
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
72
|
class Renamer {
|
|
84
73
|
comp;
|
|
85
74
|
constructor(comp) {
|