ferrovue 0.9.0 → 0.10.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 (76) hide show
  1. package/README.md +36 -22
  2. package/dist/args.js +1 -1
  3. package/dist/attrs.d.ts +2 -0
  4. package/dist/attrs.js +12 -4
  5. package/dist/blocks.d.ts +32 -0
  6. package/dist/blocks.js +476 -0
  7. package/dist/calls.js +12 -3
  8. package/dist/children.js +10 -3
  9. package/dist/cli.js +2 -6
  10. package/dist/client-render.d.ts +4 -3
  11. package/dist/client-render.js +17 -4
  12. package/dist/client.d.ts +12 -0
  13. package/dist/client.js +28 -24
  14. package/dist/compiler.d.ts +14 -4
  15. package/dist/compiler.js +184 -35
  16. package/dist/component.js +9 -3
  17. package/dist/conformance.d.ts +1 -1
  18. package/dist/constants.d.ts +26 -3
  19. package/dist/constants.js +176 -20
  20. package/dist/context.d.ts +15 -1
  21. package/dist/context.js +8 -2
  22. package/dist/diagnostics.d.ts +11 -1
  23. package/dist/diagnostics.js +33 -4
  24. package/dist/dynamic.js +7 -2
  25. package/dist/editor.d.ts +2 -2
  26. package/dist/editor.js +14 -9
  27. package/dist/emitter.d.ts +1 -0
  28. package/dist/emitter.js +12 -0
  29. package/dist/errors.d.ts +422 -7
  30. package/dist/errors.js +1399 -335
  31. package/dist/expr.d.ts +11 -0
  32. package/dist/expr.js +140 -33
  33. package/dist/fixture.d.ts +4 -1
  34. package/dist/fixture.js +10 -3
  35. package/dist/functions.d.ts +14 -0
  36. package/dist/functions.js +172 -0
  37. package/dist/hydration.js +1 -1
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +1 -0
  40. package/dist/inputs.d.ts +2 -2
  41. package/dist/inputs.js +1 -1
  42. package/dist/lists.d.ts +5 -1
  43. package/dist/lists.js +173 -29
  44. package/dist/loads.d.ts +27 -0
  45. package/dist/loads.js +81 -0
  46. package/dist/loops.js +10 -3
  47. package/dist/model.d.ts +50 -3
  48. package/dist/model.js +162 -6
  49. package/dist/narrowing.d.ts +8 -0
  50. package/dist/narrowing.js +28 -4
  51. package/dist/page-json.d.ts +1 -0
  52. package/dist/page-json.js +14 -0
  53. package/dist/plugin.d.ts +2 -1
  54. package/dist/plugins/client-only.js +1 -1
  55. package/dist/plugins/head.js +2 -1
  56. package/dist/plugins/index.js +2 -1
  57. package/dist/plugins/loaders.d.ts +35 -0
  58. package/dist/plugins/loaders.js +349 -0
  59. package/dist/plugins/provide.js +5 -4
  60. package/dist/plugins/router-link.js +2 -2
  61. package/dist/plugins/stores.js +7 -7
  62. package/dist/plugins/twins.js +1 -1
  63. package/dist/resolve.d.ts +32 -0
  64. package/dist/resolve.js +246 -0
  65. package/dist/rust.d.ts +1 -1
  66. package/dist/rust.js +22 -4
  67. package/dist/schema.d.ts +1 -1
  68. package/dist/script.d.ts +1 -1
  69. package/dist/script.js +64 -54
  70. package/dist/settle.js +8 -0
  71. package/dist/template.js +162 -137
  72. package/dist/typescript.d.ts +0 -1
  73. package/dist/typescript.js +14 -22
  74. package/dist/vite.js +3 -3
  75. package/dist/volar.js +1 -1
  76. package/package.json +1 -1
package/README.md CHANGED
@@ -18,13 +18,6 @@ HTML with no JavaScript runtime, and the browser hydrates that HTML with the sam
18
18
  - **Exact or refused.** What ferrovue cannot reproduce exactly is a compile error that names the
19
19
  file, line and construct. A component that compiles renders as Vue renders it.
20
20
 
21
- ferrovue is at 0.8, before 1.0: a minor release may change the configuration, the generated code or
22
- the crates' API. The [CHANGELOG](https://github.com/Kinsmir/ferrovue/blob/main/CHANGELOG.md) says
23
- what changed, and the guide's upgrade pages
24
- ([0.8 to 0.9](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_9/index.html),
25
- [0.7 to 0.8](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_8/index.html)) say how
26
- to move a project.
27
-
28
21
  ```text
29
22
  Button.vue ──(ferrovue compiler, Node)──▶ button.rs ──(your Rust server)──▶ HTML ──▶ Vue hydrates it
30
23
  ```
@@ -114,9 +107,10 @@ pnpm ferrovue --watch # regenerates when components or configs change
114
107
  ```
115
108
 
116
109
  `-c, --config <path>` reads another configuration file. `--watch` regenerates on a change to what a
117
- run reads (the configuration, the components, stores, locale messages, pages or routes file, and
118
- the `.ts` files components import types from). A Vite project can use the plugin instead, which
119
- does the same in its dev server and before a build:
110
+ run reads (the configuration, the components, stores, locale messages, pages or routes file, the
111
+ `.ts` files components import types from, and the tsconfig files their aliases come from). A Vite
112
+ project can use the plugin instead, which does the same in its dev server and before a build, and
113
+ also resolves imports through the build's `resolve.alias`:
120
114
 
121
115
  ```ts
122
116
  // vite.config.ts
@@ -131,7 +125,9 @@ by default).
131
125
 
132
126
  Every error carries a stable code, as `error[FV1805]: components/Card.vue:4:17: …`, documented in
133
127
  the [`error_codes`](https://docs.rs/ferrovue/latest/ferrovue/guide/error_codes/index.html) guide.
134
- `--format json` writes the same errors as JSON with the exit status unchanged; the
128
+ A run reports every error it finds, each with a `= help:` line where the way around it is short,
129
+ then a line that counts them, and writes nothing while any remains. `--format json` writes the same
130
+ errors as JSON with the exit status unchanged; the
135
131
  [quick start](https://docs.rs/ferrovue/latest/ferrovue/guide/quick_start/index.html#editor-and-ci-diagnostics)
136
132
  has a VS Code problem matcher that puts them in the Problems panel.
137
133
 
@@ -222,17 +218,20 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
222
218
  | Area | Supported |
223
219
  |---|---|
224
220
  | Prop types | `string`, `number` (integers, `i64`, written and computed as JavaScript does, exact within ±2⁵³), `Float` from `ferrovue/types` (fractions, `f64`), `boolean`, string-literal unions (`"sm" \| "md"`), `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::Record` in Rust, which keeps JavaScript's order of keys), another component's exported `Props`, `TrustedHtml`, `InlineHtml`, `Date` (a component's own prop, optional or nullable too: `ferrovue::JsDate`, or `chrono::DateTime<Utc>` with the `chrono` feature) |
225
- | Shared types | Interfaces and type aliases imported from `.ts` files (generated once, into `types.rs`), 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` whose members are literals is the type of its values: a string, or a number |
226
- | Constants | Constants imported from `.ts` files and `enum`s (imported, or declared in the component), evaluated when the component is compiled: strings, numbers, booleans, `null`, 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`). See the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#constants) guide |
221
+ | 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` whose members are literals is the type of its values: a string, or a number |
222
+ | Constants | Constants imported from `.ts` files, object constants declared in `<script setup>`, and `enum`s (imported, or declared in the component), evaluated when the component is compiled: strings, numbers, booleans, `null`, 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, 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]"`). See the crate's [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#constants) guide |
227
223
  | Props | `withDefaults`, destructured props with defaults (`const { size = "md" } = 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
- | Setup | `ref`/`shallowRef`, `computed` (an expression or a single `return`; 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)`), plain `const`/`let`, `.value` in script code, helpers with Rust twins, a plain `<script>` block beside setup. Lifecycle hooks, `watch` (not `immediate`), `defineEmits`, `defineSlots`, `defineOptions`, `defineExpose`, template refs (`ref(null)`, `useTemplateRef`) and functions are client-only. The template may name them only from event handlers, which the server drops |
224
+ | 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)`), 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 |
225
+ | 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` never assigned again, 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`. Each `const` is evaluated once, where JavaScript evaluates it, and only on the path that reaches it. Loops, assignments, `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 |
226
+ | 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) 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 |
227
+ | 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: 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 |
229
228
  | 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 |
230
- | 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 `-`, `!`; `??`, `\|\|`, `&&`, `?:`; 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 |
229
+ | 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 |
231
230
  | 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()`; indices and lengths count UTF-16 code units, as JavaScript's do (see the crate's [`strings`](https://docs.rs/ferrovue/latest/ferrovue/guide/strings/index.html) guide) |
232
231
  | 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. 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 |
233
232
  | 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` |
234
- | 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, 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; chained and nested; a computed list read by `v-for`, `.join()`, `.length`, `.includes()`, kept by `computed`, or handed to a child |
235
- | Dictionaries | `v-for="(value, key, index) in r"` over a `Record<string, T>`, and `([key, value], index) in Object.entries(r)`, in JavaScript's order: keys that are array indices first, in numeric order, then the others as the props give them; `Object.keys(r)`, `Object.values(r)`, `Object.entries(r).length`; handed to a child |
233
+ | 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 |
234
+ | Dictionaries | `v-for="(value, key, index) in r"` over a `Record<string, T>`, and `([key, value], index) in Object.entries(r)`, in JavaScript's order: keys that are array indices first, in numeric order, then the others as the props give them; `Object.keys(r)`, `Object.values(r)` (of a record of lists too, which `.flat()` joins), and `Object.entries(r)` read by the list methods, whose arrow functions destructure each pair (`([key, value]) => …`, `([, value]) => …`, `([key, { id }]) => …`) or read its parts (`pair[0]`, `pair[1]`), and walked by `v-for="[key, value] in Object.entries(r).filter(…)"`, kept by `computed` and sorted by a comparator of either part; handed to a child |
236
235
  | Attributes | static and bound attributes, boolean attributes, `:hidden`, `data-*` and `aria-*`, `v-bind` objects |
237
236
  | `class` | strings, arrays, objects (`{ active: on }`, computed keys), `cond && "x"`, `cond ? "x" : null`, merged with a static `class` |
238
237
  | `style` | objects (camelCase or kebab-case keys, `--custom` properties), arrays of objects, strings, merged with a static `style`, and `v-show`; later values override earlier ones as in Vue. A global `<style>` block is allowed |
@@ -273,8 +272,9 @@ The crate's guide, `ferrovue::guide` on docs.rs, covers each topic in depth:
273
272
  `<style scoped>` ids that match `@vitejs/plugin-vue`, `v-bind()` in `<style>`, CSS modules
274
273
  - [Routing](https://docs.rs/ferrovue/latest/ferrovue/guide/routing/index.html),
275
274
  [translating with vue-i18n](https://docs.rs/ferrovue/latest/ferrovue/guide/i18n/index.html),
276
- [Pinia](https://docs.rs/ferrovue/latest/ferrovue/guide/pinia/index.html) and
277
- [provide and inject](https://docs.rs/ferrovue/latest/ferrovue/guide/provide_inject/index.html)
275
+ [Pinia](https://docs.rs/ferrovue/latest/ferrovue/guide/pinia/index.html),
276
+ [provide and inject](https://docs.rs/ferrovue/latest/ferrovue/guide/provide_inject/index.html) and
277
+ [loaders](https://docs.rs/ferrovue/latest/ferrovue/guide/loaders/index.html)
278
278
  - [Teleports](https://docs.rs/ferrovue/latest/ferrovue/guide/teleports/index.html) and
279
279
  [the page head](https://docs.rs/ferrovue/latest/ferrovue/guide/head/index.html) (`useHead`,
280
280
  `useSeoMeta`)
@@ -294,7 +294,12 @@ The crate's guide, `ferrovue::guide` on docs.rs, covers each topic in depth:
294
294
  - [Errors and limits](https://docs.rs/ferrovue/latest/ferrovue/guide/errors_and_limits/index.html),
295
295
  with helpers, `<ClientOnly>` and Rust twins, and every
296
296
  [error code](https://docs.rs/ferrovue/latest/ferrovue/guide/error_codes/index.html)
297
- - Upgrading a project [from 0.8 to 0.9](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_9/index.html)
297
+ - [Porting an existing app](https://docs.rs/ferrovue/latest/ferrovue/guide/porting/index.html)
298
+ from a Node server that renders with Vue, and the
299
+ [support policy](https://docs.rs/ferrovue/latest/ferrovue/guide/support/index.html): supported
300
+ versions, fixes, deprecations and where to report a problem
301
+ - Upgrading a project [from 0.9 to 0.10](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_10/index.html),
302
+ [from 0.8 to 0.9](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_9/index.html)
298
303
  and [from 0.7 to 0.8](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_8/index.html)
299
304
 
300
305
  [`examples/fullstack`](https://github.com/Kinsmir/ferrovue/blob/main/examples/fullstack/README.md)
@@ -354,10 +359,19 @@ the fixtures from it. A weekly CI job re-records them from the newest patch each
354
359
  hydrates the committed HTML with it, so a patch that changes what Vue writes or hydrates is caught
355
360
  before it reaches you; a ferrovue patch follows when one does.
356
361
 
362
+ ### Upgrading
363
+
364
+ Until 1.0, versions are 0.MINOR.PATCH: raising MINOR (0.9.2 to 0.10.0) may change the
365
+ configuration, the generated code and the crates' API, and raising PATCH (0.10.0 to 0.10.1) breaks
366
+ none of them. The npm package and the crates share one version, so upgrade them together and
367
+ generate your components again. The
368
+ [CHANGELOG](https://github.com/Kinsmir/ferrovue/blob/main/CHANGELOG.md) lists what each release
369
+ changes, and the [guides](#guides) have an upgrade page for each MINOR release.
370
+
357
371
  ### Deprecations
358
372
 
359
- What ferrovue stops supporting is deprecated for at least one minor release and removed in the next
360
- major release. A deprecated crate item carries `#[deprecated]`; a deprecated configuration key warns
373
+ What ferrovue stops supporting is deprecated for at least one minor release before it is removed:
374
+ in a later minor release before 1.0, and only in a major release from 1.0. A deprecated crate item carries `#[deprecated]`; a deprecated configuration key warns
361
375
  with `warning[FV1117]` and a command-line option with `warning[FV1118]`, naming the replacement, and the JSON Schema marks a deprecated key for editors.
362
376
  Each release's CHANGELOG lists what it deprecates under "Deprecated".
363
377
 
package/dist/args.js CHANGED
@@ -60,7 +60,7 @@ export function parseArgs(argv, flags = FLAGS, commands = COMMANDS) {
60
60
  return { error: `option '${name}' takes no value` };
61
61
  }
62
62
  if (flag.deprecated) {
63
- args.warnings.push(new GenError("FV1118", `'${name}' is deprecated since ${flag.deprecated.since}, and goes in the next major release: use ${flag.deprecated.use}`));
63
+ args.warnings.push(new GenError("FV1118", `'${name}' is deprecated since ${flag.deprecated.since}, and goes in a later minor release: use ${flag.deprecated.use}`));
64
64
  }
65
65
  args.options.set(longName(flag), value);
66
66
  args.spelt.set(longName(flag), name);
package/dist/attrs.d.ts CHANGED
@@ -5,6 +5,8 @@ export declare function interpolate(s: Scope, e: Emitter, v: Val, n: N): void;
5
5
  export declare function attrValue(e: Emitter, v: Val): void;
6
6
  export declare function renderAttr(s: Scope, e: Emitter, key: string, v: Val, n: N): void;
7
7
  export declare function keepsAttrCase(tag: string): boolean;
8
+ /** The value bound to the attribute \`key\`, shown as text unless the attribute is a boolean one. */
9
+ export declare function attrShown(s: Scope, key: string, n: N, tag?: string | null): Val;
8
10
  export declare function renderDynamicAttr(s: Scope, e: Emitter, key: string, v: Val, n: N, tag?: string | null): void;
9
11
  export declare const IGNORED_PROPS: Set<string>;
10
12
  export declare function directiveProps(s: Scope, n: N): boolean;
package/dist/attrs.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { escapeHtml, hyphenate, isBooleanAttr, isSSRSafeAttrName, isSVGTag, propsToAttrMap } from "@vue/shared";
2
2
  import { fail, nothing, GenError, rustStr } from "./model.js";
3
3
  import { CONFIG_FILE, ctx } from "./context.js";
4
- import { describeTy, expr } from "./expr.js";
4
+ import { describeTy, expr, shown } from "./expr.js";
5
5
  import { known, truthy } from "./narrowing.js";
6
6
  import { asCow, isTemporary, unquote } from "./strings.js";
7
7
  import { atom, bare, condition, receiver, strArg } from "./parens.js";
@@ -78,8 +78,16 @@ export function renderAttr(s, e, key, v, n) {
78
78
  export function keepsAttrCase(tag) {
79
79
  return tag.indexOf("-") > 0 || isSVGTag(tag);
80
80
  }
81
+ function attrName(key, tag) {
82
+ return tag !== null && keepsAttrCase(tag) ? key : (propsToAttrMap[key] ?? key.toLowerCase());
83
+ }
84
+ /** The value bound to the attribute \`key\`, shown as text unless the attribute is a boolean one. */
85
+ export function attrShown(s, key, n, tag = null) {
86
+ const name = attrName(key, tag);
87
+ return isBooleanAttr(name) || name === "hidden" ? expr(s, n) : shown(s, n, "attr");
88
+ }
81
89
  export function renderDynamicAttr(s, e, key, v, n, tag = null) {
82
- const name = tag !== null && keepsAttrCase(tag) ? key : (propsToAttrMap[key] ?? key.toLowerCase());
90
+ const name = attrName(key, tag);
83
91
  if (!isSSRSafeAttrName(name))
84
92
  fail(s.comp, "FV0404", `unsafe attribute name \`${name}\``, n);
85
93
  renderable(s, key, v, n);
@@ -259,7 +267,7 @@ function objectAttrs(s, e, n, merged) {
259
267
  e.lit(`"`);
260
268
  }
261
269
  else {
262
- renderDynamicAttr(s, e, key, expr(s, p.value), p);
270
+ renderDynamicAttr(s, e, key, attrShown(s, key, p.value), p);
263
271
  }
264
272
  }
265
273
  }
@@ -318,7 +326,7 @@ export function attrOf(s, key, n, side) {
318
326
  return styleAttr(s, n);
319
327
  if (n.type === "NullLiteral")
320
328
  return "fv::Attr::Undefined";
321
- return valueAttr(s, expr(s, n), n);
329
+ return valueAttr(s, attrShown(s, key, n), n);
322
330
  }
323
331
  export function valueAttr(s, v, n) {
324
332
  switch (v.ty.k) {
@@ -0,0 +1,32 @@
1
+ import { type Component, type N, type Scope, type Val } from "./model.ts";
2
+ /** Runtime calls that panic where JavaScript throws, or user code that may. */
3
+ export declare const PANICS: RegExp;
4
+ export declare function panicky(code: string): boolean;
5
+ /** How a value is held in a Rust `let`: what the `let` is given, how the name reads back, and
6
+ * whether the `let` owns what a read borrows (a `String`, a `Cow`, a `Vec`). */
7
+ export declare function bindingOf(v: Val): {
8
+ init: string;
9
+ read: (at: string) => Val;
10
+ owned: boolean;
11
+ };
12
+ /** How many times a body's statements may be translated again after an `if` or a `case` that
13
+ * falls off its end: each such path continues with the statements after it, translated on its own,
14
+ * so sequential `switch`es of `break`ing cases multiply them. */
15
+ export declare const PATH_BUDGET = 4096;
16
+ /** The expression a function's body computes: the expression of an arrow function, or a block of
17
+ * statements in the subset ferrovue translates, written as one expression. */
18
+ export declare function bodyExpr(comp: Component, fn: N): N;
19
+ /** `p` (a name, or an object or array pattern of names) bound to `value` around `body`. */
20
+ export declare function destructured(comp: Component, p: N, value: N, body: N): N;
21
+ /** `fn` with an expression for its body, where it is an arrow or function expression with a block. */
22
+ export declare function expressionBodied(comp: Component, fn: N): N;
23
+ /** `const name = init` around `body`: the value `body` computes with `name` bound once, as a Rust
24
+ * `let` in a block that hands out nothing borrowed from it. */
25
+ export declare function letIn(s: Scope, n: N): Val;
26
+ /** `v` bound once to `name` for `body`, which is given the scope that reads it. */
27
+ export declare function bound(s: Scope, local: string, v: Val, then: (inner: Scope) => Val): Val;
28
+ /** Whether a desugared `if` whose test is `a && b` or `a || b` should be split, so that a side
29
+ * that narrows a value narrows what follows it, as TypeScript narrows it. */
30
+ export declare function splits(s: Scope, test: N): boolean;
31
+ /** `a && b ? x : y` as `a ? (b ? x : y) : y`, and `a || b ? x : y` as `a ? x : (b ? x : y)`. */
32
+ export declare function split(n: N): N;