ferrovue 0.11.0 → 0.12.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 (101) hide show
  1. package/README.md +20 -17
  2. package/dist/args.d.ts +1 -1
  3. package/dist/args.js +1 -1
  4. package/dist/attrs.js +16 -14
  5. package/dist/blocks.js +3 -2
  6. package/dist/calls.js +21 -21
  7. package/dist/children.js +47 -12
  8. package/dist/classes.js +9 -9
  9. package/dist/cli.js +3 -1
  10. package/dist/client.d.ts +28 -7
  11. package/dist/client.js +84 -4
  12. package/dist/compiler.d.ts +11 -6
  13. package/dist/compiler.js +8 -5
  14. package/dist/component.js +9 -3
  15. package/dist/config.d.ts +128 -0
  16. package/dist/config.js +32 -0
  17. package/dist/conformance.d.ts +6 -43
  18. package/dist/conformance.js +4 -14
  19. package/dist/constants.js +116 -20
  20. package/dist/context.d.ts +9 -126
  21. package/dist/context.js +22 -31
  22. package/dist/date-plans.d.ts +11 -0
  23. package/dist/date-plans.js +10 -0
  24. package/dist/diagnostics.d.ts +3 -3
  25. package/dist/diagnostics.js +1 -1
  26. package/dist/dynamic.js +10 -7
  27. package/dist/editor.js +3 -2
  28. package/dist/enums.d.ts +36 -5
  29. package/dist/enums.js +200 -23
  30. package/dist/error-areas.d.ts +5 -0
  31. package/dist/error-areas.js +24 -0
  32. package/dist/errors.d.ts +75 -23
  33. package/dist/errors.js +82 -41
  34. package/dist/expr.d.ts +2 -1
  35. package/dist/expr.js +73 -27
  36. package/dist/file-routes.d.ts +2 -15
  37. package/dist/file-routes.js +2 -1
  38. package/dist/files.d.ts +3 -3
  39. package/dist/files.js +2 -1
  40. package/dist/functions.js +16 -3
  41. package/dist/inputs.d.ts +1 -1
  42. package/dist/inputs.js +1 -0
  43. package/dist/link-router.d.ts +1 -2
  44. package/dist/link-router.js +1 -2
  45. package/dist/lists.js +20 -19
  46. package/dist/loads.d.ts +6 -25
  47. package/dist/loads.js +1 -84
  48. package/dist/loops.js +19 -6
  49. package/dist/model.d.ts +47 -37
  50. package/dist/model.js +89 -41
  51. package/dist/narrowing.d.ts +8 -0
  52. package/dist/narrowing.js +51 -4
  53. package/dist/numbers.js +2 -2
  54. package/dist/objects.js +8 -4
  55. package/dist/page-routes.d.ts +1 -2
  56. package/dist/peer.d.ts +3 -0
  57. package/dist/peer.js +17 -0
  58. package/dist/plugin.d.ts +1 -1
  59. package/dist/plugin.js +1 -0
  60. package/dist/plugins/client-only.js +2 -2
  61. package/dist/plugins/css-modules.js +2 -1
  62. package/dist/plugins/dates.js +9 -9
  63. package/dist/plugins/head.js +23 -23
  64. package/dist/plugins/i18n.js +4 -4
  65. package/dist/plugins/loaders.js +22 -21
  66. package/dist/plugins/router-link.js +12 -12
  67. package/dist/plugins/router.d.ts +1 -1
  68. package/dist/plugins/router.js +14 -12
  69. package/dist/plugins/scoped.d.ts +1 -1
  70. package/dist/plugins/scoped.js +2 -2
  71. package/dist/plugins/stores.js +2 -1
  72. package/dist/plugins/teleport.js +5 -5
  73. package/dist/plugins/twins.d.ts +1 -1
  74. package/dist/plugins/twins.js +6 -4
  75. package/dist/resolve.d.ts +0 -10
  76. package/dist/rust.d.ts +1 -1
  77. package/dist/rust.js +41 -34
  78. package/dist/schema.js +2 -1
  79. package/dist/script.js +49 -4
  80. package/dist/slots.js +6 -5
  81. package/dist/strings.d.ts +2 -2
  82. package/dist/strings.js +24 -22
  83. package/dist/styles.js +16 -22
  84. package/dist/template.js +19 -12
  85. package/dist/{fixture.d.ts → testing-browser.d.ts} +26 -13
  86. package/dist/{hydration.js → testing-browser.js} +102 -1
  87. package/dist/testing.d.ts +42 -5
  88. package/dist/testing.js +14 -4
  89. package/dist/types.d.ts +1 -1
  90. package/dist/typescript.js +9 -7
  91. package/dist/vhtml.js +1 -1
  92. package/dist/vite.d.ts +14 -2
  93. package/dist/vite.js +3 -1
  94. package/package.json +22 -6
  95. package/schema.json +13 -5
  96. package/dist/fixture.js +0 -122
  97. package/dist/hydration.d.ts +0 -26
  98. package/dist/index.d.ts +0 -4
  99. package/dist/index.js +0 -3
  100. /package/dist/{routes.d.ts → route-records.d.ts} +0 -0
  101. /package/dist/{routes.js → route-records.js} +0 -0
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![npm](https://img.shields.io/npm/v/ferrovue.svg)](https://www.npmjs.com/package/ferrovue)
5
5
  [![docs.rs](https://img.shields.io/docsrs/ferrovue)](https://docs.rs/ferrovue)
6
6
  [![CI](https://github.com/Kinsmir/ferrovue/actions/workflows/ci.yml/badge.svg)](https://github.com/Kinsmir/ferrovue/actions/workflows/ci.yml)
7
+ [![Mutation score](https://img.shields.io/endpoint?url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FKinsmir%2Fferrovue%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/Kinsmir/ferrovue/main)
7
8
  [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#licence)
8
9
 
9
10
  Documentation: [docs.rs/ferrovue](https://docs.rs/ferrovue) (API and guide) · [kinsmir.github.io/ferrovue](https://kinsmir.github.io/ferrovue/) (guide and overview)
@@ -34,7 +35,7 @@ element structure, attribute order, whitespace and fragment markers.
34
35
  | | What it is | Published as |
35
36
  |---|---|---|
36
37
  | `packages/ferrovue` | The compiler, the `ferrovue` command, the Vite plugin and the browser client (TypeScript) | npm: [`ferrovue`](https://www.npmjs.com/package/ferrovue) |
37
- | `crates/ferrovue` | The runtime the generated Rust calls: escaping, slots, islands, `<RouterLink>` matching. It re-exports `ferrovue-router`, `ferrovue-i18n` and `ferrovue-core`, released with it | crates.io: [`ferrovue`](https://crates.io/crates/ferrovue) ([API docs](https://docs.rs/ferrovue)) |
38
+ | `crates/ferrovue` | The runtime the generated Rust calls: escaping, slots, islands, `<RouterLink>` matching. It holds `ferrovue-core`, `ferrovue-router` and `ferrovue-i18n`, released with it, as its modules `core`, `router` and `i18n` | crates.io: [`ferrovue`](https://crates.io/crates/ferrovue) ([API docs](https://docs.rs/ferrovue)) |
38
39
 
39
40
  ## Quick start
40
41
 
@@ -56,7 +57,7 @@ the configuration at your project root yourself:
56
57
 
57
58
  ```json
58
59
  {
59
- "$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.8.0/schema.json",
60
+ "$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.12.0/schema.json",
60
61
  "components": "client/components",
61
62
  "out": "src/generated"
62
63
  }
@@ -75,8 +76,9 @@ read `$schema`. Offline, the copy in the installed package works as well:
75
76
  | `routes` | no | JSON file listing the app's routes: each a vue-router path, or `{ "path", "name", "children" }`. Or `{ "pages": "client/pages" }`: a folder of pages, whose file names give the routes as vue-router's file-based routing reads them (`index.vue`, `[id].vue`, `[[id]].vue`, `[...path].vue`, `(group)` folders, a `name.vue` beside `name/` as its layout), each page compiled as a component. Needed for `<RouterLink>`, `<RouterView>` and `useRoute()` |
76
77
  | `router` | no | Instead of `routes`: `{ routes, base?, linkActiveClass?, linkExactActiveClass? }`, matching `createWebHistory(base)` and `createRouter`'s options |
77
78
  | `stores` | no | Directory of Pinia option stores whose state components may read |
78
- | `trustedHtml` | no | Rust type of a `TrustedHtml` prop (needed for `v-html`): `ferrovue::BasicHtml` (text with a few formatting tags, built in), `ferrovue::InlineHtml` (inline formatting tags only, built in, which makes every `TrustedHtml` prop inline HTML), `ferrovue::Sanitised` (HTML cleaned by ammonia, the `ammonia` feature), or a type of your own such as `crate::html::CleanHtml` |
79
- | `dateType` | no | Rust type of a `Date` prop: `ferrovue::JsDate` (the default), `chrono::DateTime<chrono::Utc>` (the `chrono` feature), or a type of your own that implements `ferrovue::JsTime`. See [`dates_and_prices`](https://docs.rs/ferrovue/latest/ferrovue/guide/dates_and_prices/index.html#date-props) |
79
+ | `trustedHtml` | no | Rust type of a `TrustedHtml` prop (needed for `v-html`): `ferrovue::html::BasicHtml` (text with a few formatting tags, built in), `ferrovue::html::InlineHtml` (inline formatting tags only, built in, which makes every `TrustedHtml` prop inline HTML), `ferrovue::html::Sanitised` (HTML cleaned by ammonia, the `ammonia` feature), or a type of your own such as `crate::html::CleanHtml` |
80
+ | `dateType` | no | Rust type of a `Date` prop: `ferrovue::date::JsDate` (the default), `chrono::DateTime<chrono::Utc>` (the `chrono` feature), or a type of your own that implements `ferrovue::date::JsTime`. See [`dates_and_prices`](https://docs.rs/ferrovue/latest/ferrovue/guide/dates_and_prices/index.html#date-props) |
81
+ | `unions` | no | Rust enums of your own for unions of string literals, by `type` alias: `{ "Tone": "crate::domain::Tone" }`. Generated code uses the enum, which implements `ferrovue::props::Union` and has the variants ferrovue would generate, and generates none for the union. See [`props`](https://docs.rs/ferrovue/latest/ferrovue/guide/props/index.html#unions-mapped-to-existing-enums) |
80
82
  | `helpers` | no | `{ module, functions }`: functions a template may call, each mapped to a Rust twin |
81
83
  | `twins` | no | Components ferrovue does not compile, each rendered by a Rust function of yours: `{ "VBtn": { "rust": "crate::ui::v_btn", "props": { "label": "string" }, "slots": ["default"] } }`. See [Rust twins](https://docs.rs/ferrovue/latest/ferrovue/guide/errors_and_limits/index.html#rust-twins) |
82
84
  | `i18n` | no | vue-i18n: `{ messages, locale?, fallbackLocale? }`, the directory of locale files (`en.json`, `nl.json`), the default locale and the fallbacks |
@@ -171,7 +173,7 @@ The crate's features:
171
173
 
172
174
  - `router` and `i18n`, both on by default: vue-router and vue-i18n. An application with neither
173
175
  routes nor translations can leave them out, and build neither `ferrovue-router` nor
174
- `ferrovue-i18n`: `ferrovue = { version = "0.8", default-features = false }`.
176
+ `ferrovue-i18n`: `ferrovue = { version = "0.12", default-features = false }`.
175
177
  - `axum` and `actix-web`: `Html` is an `IntoResponse` or a `Responder`, so a handler responds with
176
178
  the component as `text/html; charset=utf-8`. `into_html(props)` and `into_island(props)` take the
177
179
  props by value, so a handler that builds them returns the page:
@@ -184,12 +186,12 @@ The crate's features:
184
186
 
185
187
  - `stream`, which `axum` and `actix-web` turn on: `HtmlStream`, a page sent in the order its parts
186
188
  are ready.
187
- - `maud`: `ferrovue::Html` implements `maud::Render`, so `(greeting::html(&props))` goes straight
189
+ - `maud`: `ferrovue::html::Html` implements `maud::Render`, so `(greeting::html(&props))` goes straight
188
190
  into a `maud::html!` page.
189
191
  - `dioxus`: `{greeting::island(&props)}` in a Dioxus 0.7 `rsx!` is the island element itself, with
190
192
  exactly the markup Vue hydrates inside it (`examples/dioxus`).
191
- - `ammonia`: `ferrovue::Sanitised`, HTML cleaned by [ammonia](https://docs.rs/ammonia), for
192
- `v-html`. `ferrovue::BasicHtml` and `ferrovue::InlineHtml` are built in; the
193
+ - `ammonia`: `ferrovue::html::Sanitised`, HTML cleaned by [ammonia](https://docs.rs/ammonia), for
194
+ `v-html`. `ferrovue::html::BasicHtml` and `ferrovue::html::InlineHtml` are built in; the
193
195
  [`escaping`](https://docs.rs/ferrovue/latest/ferrovue/guide/escaping/index.html) guide describes
194
196
  all three.
195
197
 
@@ -221,11 +223,11 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
221
223
 
222
224
  | Area | Supported |
223
225
  |---|---|
224
- | 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), `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 prop, optional or nullable too, and inside lists, dictionaries and interfaces: `ferrovue::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), 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) |
225
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 |
226
- | 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`, 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), 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 |
227
- | 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 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 |
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 |
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 |
229
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 |
230
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 |
231
233
  | Object literals | object literals built as the Rust struct of the interface where they go: a `computed` annotated with it (`computed<Card>(…)`, `computed((): Card => …)`, `ComputedRef<Card>`), a function's return type or parameter, a `.map()` callback's return type or the list type its result is given (`computed<Row[]>`, a child's `Row[]` prop: `:rows="items.map((x) => ({ … }))"`), a child's prop typed with an interface (`:item="{ id, label }"`, `v-bind="{ item: { … } }"`), an annotated `const`, `satisfies`; shorthand keys, computed keys the compiler knows (a constant, an enum member), nested literals and array literals of them, a spread of the same type before the other keys (`{ ...card, title }`), optional fields left out or `undefined`, `null` for a `T \| null` field, a member of a field's union of string literals as its enum's variant (`{ size: "md" }`). An untyped literal is refused with a message that says how to type it. See the crate's [`script_logic`](https://docs.rs/ferrovue/latest/ferrovue/guide/script_logic/index.html#object-literals) guide |
@@ -234,7 +236,7 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
234
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 |
235
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) |
236
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 |
237
- | 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]`) |
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` |
238
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 |
239
241
  | 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), `Object.hasOwn(r, key)`, 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 |
240
242
  | Attributes | static and bound attributes, boolean attributes, `:hidden`, `data-*` and `aria-*`, `v-bind` objects |
@@ -245,7 +247,7 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
245
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 |
246
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 |
247
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 |
248
- | Escape hatches | `<ClientOnly>` from `ferrovue/client`, 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 |
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 |
249
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 |
250
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 |
251
253
  | Forms | `v-model` on text inputs, checkboxes, radios, `<select>` and `<textarea>` (renders the initial state); a `<textarea>`, or an `<input>` with `v-model`, sits inside another element when it would be the component's root (FV0424) |
@@ -253,8 +255,8 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
253
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 |
254
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 |
255
257
  | vue-i18n | `$t` and `useI18n()`'s `t` and `locale`: named and list values, plurals by vue-i18n's rule, literals, linked messages with `upper`/`lower`/`capitalize`, nested and flat keys, fallback locales, a missing key shown as itself. Messages are parsed at build time by vue-i18n's own compiler |
256
- | `v-html` | only on a `TrustedHtml` prop (`import type { TrustedHtml } from "ferrovue/types"`), or an `InlineHtml` one (inline tags only, `ferrovue::InlineHtml` on the server, with no `trustedHtml` needed), the only HTML it may write inside a `<p>`; never on a table's structure, a `<select>` or `<optgroup>`, or an SVG or MathML element that holds no HTML. See the crate's [`escaping`](https://docs.rs/ferrovue/latest/ferrovue/guide/escaping/index.html#where-v-html-may-go) guide |
257
- | Page head | `useHead` (and `useServerHead`) from `@unhead/vue` 3: `title`, `titleTemplate`, `base`, `meta`, `link`, `script`, `style`, `noscript`, `htmlAttrs` and `bodyAttrs`, with values the server computes (props, setup bindings, getters such as `() => props.title`, `list.map(x => ({ … }))` for a list of tags), `key`, `tagPosition`, `tagPriority` and `tagDuplicateStrategy`; `useSeoMeta` (and `useServerSeoMeta`) of string, number, boolean and `null` values. Collected into a `ferrovue::Head` in the order Vue's server runs them, and written as unhead's `renderSSRHead` writes them: its tag order, deduplication, title template and escaping. See the crate's [`head`](https://docs.rs/ferrovue/latest/ferrovue/guide/head/index.html) guide |
258
+ | `v-html` | only on a `TrustedHtml` prop (`import type { TrustedHtml } from "ferrovue/types"`), or an `InlineHtml` one (inline tags only, `ferrovue::html::InlineHtml` on the server, with no `trustedHtml` needed), the only HTML it may write inside a `<p>`; never on a table's structure, a `<select>` or `<optgroup>`, or an SVG or MathML element that holds no HTML. See the crate's [`escaping`](https://docs.rs/ferrovue/latest/ferrovue/guide/escaping/index.html#where-v-html-may-go) guide |
259
+ | Page head | `useHead` (and `useServerHead`) from `@unhead/vue` 3: `title`, `titleTemplate`, `base`, `meta`, `link`, `script`, `style`, `noscript`, `htmlAttrs` and `bodyAttrs`, with values the server computes (props, setup bindings, getters such as `() => props.title`, `list.map(x => ({ … }))` for a list of tags), `key`, `tagPosition`, `tagPriority` and `tagDuplicateStrategy`; `useSeoMeta` (and `useServerSeoMeta`) of string, number, boolean and `null` values. Collected into a `ferrovue::head::Head` in the order Vue's server runs them, and written as unhead's `renderSSRHead` writes them: its tag order, deduplication, title template and escaping. See the crate's [`head`](https://docs.rs/ferrovue/latest/ferrovue/guide/head/index.html) guide |
258
260
 
259
261
  Anything else is refused at compile time, with an error that names the file, line and column,
260
262
  quotes the line and puts a caret under the construct. The
@@ -305,7 +307,8 @@ The crate's guide, `ferrovue::guide` on docs.rs, covers each topic in depth:
305
307
  from a Node server that renders with Vue, and the
306
308
  [support policy](https://docs.rs/ferrovue/latest/ferrovue/guide/support/index.html): supported
307
309
  versions, fixes, deprecations and where to report a problem
308
- - Upgrading a project [from 0.10 to 0.11](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_11/index.html),
310
+ - Upgrading a project [from 0.11 to 0.12](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_12/index.html),
311
+ [from 0.10 to 0.11](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_11/index.html),
309
312
  [from 0.9 to 0.10](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_10/index.html),
310
313
  [from 0.8 to 0.9](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_9/index.html)
311
314
  and [from 0.7 to 0.8](https://docs.rs/ferrovue/latest/ferrovue/guide/upgrading_to_0_8/index.html)
package/dist/args.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GenError } from "./model.ts";
1
+ import { GenError } from "./errors.ts";
2
2
  import type { Deprecation } from "./schema.ts";
3
3
  /** A command-line option: its names, short one first, the value it takes, and its line in `--help`. */
4
4
  export interface Flag {
package/dist/args.js CHANGED
@@ -1,4 +1,4 @@
1
- import { GenError } from "./model.js";
1
+ import { GenError } from "./errors.js";
2
2
  /** The commands `ferrovue` takes, with their lines in `--help`. */
3
3
  export const COMMANDS = {
4
4
  init: "scaffold starter ferrovue.config.json and components",
package/dist/attrs.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { escapeHtml, hyphenate, isBooleanAttr, isSSRSafeAttrName, isSVGTag, propsToAttrMap } from "@vue/shared";
2
- import { fail, nothing, GenError, rustStr } from "./model.js";
2
+ import { fail, nothing, rustStr } from "./model.js";
3
+ import { GenError } from "./errors.js";
3
4
  import { asWritten } from "./numbers.js";
4
- import { CONFIG_FILE, ctx } from "./context.js";
5
+ import { CONFIG_FILE } from "./config.js";
6
+ import { ctx } from "./context.js";
5
7
  import { describeTy, expr, shown } from "./expr.js";
6
8
  import { known, truthy } from "./narrowing.js";
7
9
  import { asCow, isTemporary, unquote } from "./strings.js";
@@ -20,10 +22,10 @@ export function display(e, v) {
20
22
  else if (v.ty.k === "bool" && v.konst !== undefined)
21
23
  e.lit(String(v.konst));
22
24
  else if (v.ty.k === "str")
23
- e.stmt(`fv::escape_into(out, ${strArg(v.code)});`);
25
+ e.stmt(`fv::core::escape_into(out, ${strArg(v.code)});`);
24
26
  else if (v.ty.k === "int" || v.ty.k === "float") {
25
27
  const w = asWritten(v);
26
- e.stmt(`fv::${w.ty.k === "int" ? "push_int" : "push_number"}(out, ${bare(w.code)});`);
28
+ e.stmt(`fv::core::${w.ty.k === "int" ? "push_int" : "push_number"}(out, ${bare(w.code)});`);
27
29
  e.expect(NUMBER_BYTES);
28
30
  }
29
31
  else if (v.ty.k === "bool")
@@ -188,7 +190,7 @@ export function renderAttrs(s, e, n) {
188
190
  else
189
191
  e.close(" else {");
190
192
  if (parts.length === 1 && parts[0] === n) {
191
- e.stmt(`fv::passed_attrs_into(out, ${s.fallthrough}.list(), ${isAttrs(n) && s.attrs !== null ? s.attrs : '""'});`);
193
+ e.stmt(`fv::attrs::passed_attrs_into(out, ${s.fallthrough}.list(), ${isAttrs(n) && s.attrs !== null ? s.attrs : '""'});`);
192
194
  e.close();
193
195
  return;
194
196
  }
@@ -209,7 +211,7 @@ export function renderAttrs(s, e, n) {
209
211
  fail(s.comp, "FV0407", "attributes must be an object literal", p);
210
212
  }
211
213
  const ids = idsAt >= 0 && s.attrs !== null ? s.attrs : '""';
212
- e.stmt(`fv::attrs_into(out, &[${sources.join(", ")}], ${idsAt >= 0 ? idsAt : sources.length}, ${ids});`);
214
+ e.stmt(`fv::attrs::attrs_into(out, &[${sources.join(", ")}], ${idsAt >= 0 ? idsAt : sources.length}, ${ids});`);
213
215
  e.close();
214
216
  }
215
217
  function staticAttrs(s, e, n) {
@@ -324,27 +326,27 @@ export function attrOf(s, key, n, side) {
324
326
  if (key === "style")
325
327
  return styleAttr(s, n);
326
328
  if (n.type === "NullLiteral")
327
- return "fv::Attr::Undefined";
329
+ return "fv::attrs::Attr::Undefined";
328
330
  return valueAttr(s, attrShown(s, key, n), n);
329
331
  }
330
332
  export function valueAttr(s, v, n) {
331
333
  switch (v.ty.k) {
332
334
  case "str":
333
- return isTemporary(v) ? `fv::Attr::Str(${asCow(v)})` : `fv::Attr::str(${strArg(v.code)})`;
335
+ return isTemporary(v) ? `fv::attrs::Attr::Str(${asCow(v)})` : `fv::attrs::Attr::str(${strArg(v.code)})`;
334
336
  case "int":
335
- return `fv::Attr::Int(${bare(v.code)})`;
337
+ return `fv::attrs::Attr::Int(${bare(v.code)})`;
336
338
  case "float":
337
- return `fv::Attr::Float(${bare(v.code)})`;
339
+ return `fv::attrs::Attr::Float(${bare(v.code)})`;
338
340
  case "bool":
339
- return `fv::Attr::Bool(${bare(v.code)})`;
341
+ return `fv::attrs::Attr::Bool(${bare(v.code)})`;
340
342
  case "undef":
341
343
  case "null":
342
- return "fv::Attr::Undefined";
344
+ return "fv::attrs::Attr::Undefined";
343
345
  case "opt": {
344
346
  const of = v.ty.of.k;
345
- const make = of === "str" ? "fv::Attr::str" : of === "int" ? "fv::Attr::Int" : of === "float" ? "fv::Attr::Float" : of === "bool" ? "fv::Attr::Bool" : null;
347
+ const make = of === "str" ? "fv::attrs::Attr::str" : of === "int" ? "fv::attrs::Attr::Int" : of === "float" ? "fv::attrs::Attr::Float" : of === "bool" ? "fv::attrs::Attr::Bool" : null;
346
348
  if (make !== null)
347
- return `${atom(v.code)}.map_or(fv::Attr::Undefined, ${make})`;
349
+ return `${atom(v.code)}.map_or(fv::attrs::Attr::Undefined, ${make})`;
348
350
  break;
349
351
  }
350
352
  default:
package/dist/blocks.js CHANGED
@@ -1,4 +1,5 @@
1
- import { fail, GenError, nothing } from "./model.js";
1
+ import { fail, nothing, UNION_STR, unionStr } from "./model.js";
2
+ import { GenError } from "./errors.js";
2
3
  import { ctx } from "./context.js";
3
4
  import { fresh, jsHint } from "./names.js";
4
5
  import { expr, typedBy } from "./expr.js";
@@ -27,7 +28,7 @@ export function bindingOf(v) {
27
28
  const lone = v.lone ? { lone: true } : {};
28
29
  if (v.variant !== undefined) {
29
30
  const optional = v.ty.k === "opt";
30
- return { init: bare(v.variant), read: (at) => ({ code: optional ? `${at}.map(|v| v.as_str())` : `${at}.as_str()`, ty: v.ty, variant: at, ...lone }), owned: false };
31
+ return { init: bare(v.variant), read: (at) => ({ code: optional ? `${at}.map(${UNION_STR})` : unionStr(at), ty: v.ty, variant: at, ...lone }), owned: false };
31
32
  }
32
33
  if (v.owned)
33
34
  return { init: bare(v.code), read: (at) => ({ code: v.ty.k === "opt" ? `${at}.as_ref()` : at, ty: v.ty, ...lone }), owned: true };
package/dist/calls.js CHANGED
@@ -62,7 +62,7 @@ export function call(s, n) {
62
62
  if (a.ty.k === "str")
63
63
  return a;
64
64
  if (isNumber(a.ty))
65
- return { code: `&*fv::Js(${bare(asWritten(a).code)}).to_string()`, ty: asciiTy() };
65
+ return { code: `&*fv::core::Js(${bare(asWritten(a).code)}).to_string()`, ty: asciiTy() };
66
66
  if (a.ty.k === "bool")
67
67
  return { code: `if ${condition(a.code)} { "true" } else { "false" }`, ty: asciiTy() };
68
68
  return formatted(comp, [stringOf(comp, a, args[0], "`String()`", "FV0723")], n, "`String()`");
@@ -75,12 +75,12 @@ export function call(s, n) {
75
75
  if (a.lone)
76
76
  fail(comp, "FV0706", `${lonely(name)}; it throws a \`URIError\` on a half`, n);
77
77
  const text = a.ty.k === "str" ? a : formatted(comp, [stringOf(comp, a, args[0], name, "FV0723")], n, name);
78
- return { code: `&*fv::js_${callee.name === "encodeURI" ? "encode_uri" : "encode_uri_component"}(${strArg(text.code)})`, ty: STR };
78
+ return { code: `&*fv::core::js_${callee.name === "encodeURI" ? "encode_uri" : "encode_uri_component"}(${strArg(text.code)})`, ty: STR };
79
79
  }
80
80
  if (callee.name === "Number" && args.length === 1) {
81
81
  const a = expr(s, args[0]);
82
82
  if (a.ty.k === "str")
83
- return { code: `fv::js_number(${strArg(a.code)})`, ty: FLOAT };
83
+ return { code: `fv::strings::js_number(${strArg(a.code)})`, ty: FLOAT };
84
84
  if (isNumber(a.ty))
85
85
  return a;
86
86
  if (a.ty.k === "bool")
@@ -92,12 +92,12 @@ export function call(s, n) {
92
92
  if (a.ty.k !== "str")
93
93
  fail(comp, "FV0702", `\`${callee.name}()\` takes a string`, args[0]);
94
94
  if (callee.name === "parseFloat")
95
- return { code: `fv::js_parse_float(${strArg(a.code)})`, ty: FLOAT };
95
+ return { code: `fv::strings::js_parse_float(${strArg(a.code)})`, ty: FLOAT };
96
96
  const radix = args[1];
97
97
  if (radix && !(radix.type === "NumericLiteral" && (radix.value === 10 || radix.value === 16))) {
98
98
  fail(comp, "FV0703", "`parseInt()` takes a radix of 10 or 16, written as a literal", radix);
99
99
  }
100
- return { code: `fv::js_parse_int(${strArg(a.code)}, ${radix ? radix.value : 0})`, ty: FLOAT };
100
+ return { code: `fv::strings::js_parse_int(${strArg(a.code)}, ${radix ? radix.value : 0})`, ty: FLOAT };
101
101
  }
102
102
  if ((callee.name === "isNaN" || callee.name === "isFinite") && global)
103
103
  return numberTest(s, callee.name, args, n, true);
@@ -157,7 +157,7 @@ export function call(s, n) {
157
157
  if ((method === "max" || method === "min") && vals.length >= 2) {
158
158
  if (ints && !vals.some(isCast))
159
159
  return { code: vals.slice(1).reduce((acc, v) => `${atom(acc)}.${method}(${bare(v.code)})`, vals[0].code), ty: INT };
160
- const extreme = vals.slice(1).reduce((acc, v) => `fv::js_${method}(${bare(acc)}, ${bare(asF64(v))})`, asF64(vals[0]));
160
+ const extreme = vals.slice(1).reduce((acc, v) => `fv::core::js_${method}(${bare(acc)}, ${bare(asF64(v))})`, asF64(vals[0]));
161
161
  if (ints)
162
162
  return intFromF64(extreme, vals.find(isCast)?.at ?? refusalAt(comp, n));
163
163
  return { code: extreme, ty: FLOAT };
@@ -167,7 +167,7 @@ export function call(s, n) {
167
167
  if (method === "abs")
168
168
  return ints ? intFromF64(`${receiver(asF64(x))}.abs()`, refusalAt(comp, n)) : { code: `${receiver(x.code)}.abs()`, ty: FLOAT };
169
169
  if (method === "round")
170
- return { code: `fv::js_round(${bare(asF64(x))})`, ty: FLOAT };
170
+ return { code: `fv::core::js_round(${bare(asF64(x))})`, ty: FLOAT };
171
171
  if (method === "floor" || method === "ceil" || method === "trunc")
172
172
  return { code: `${receiver(asF64(x))}.${method}()`, ty: FLOAT };
173
173
  if (method === "sqrt")
@@ -175,7 +175,7 @@ export function call(s, n) {
175
175
  if (method === "sign") {
176
176
  if (x.num !== undefined)
177
177
  return numberVal(Math.sign(x.num), ints);
178
- const sign = `fv::js_sign(${bare(asF64(x))})`;
178
+ const sign = `fv::core::js_sign(${bare(asF64(x))})`;
179
179
  return ints ? intFromF64(sign, null) : { code: sign, ty: FLOAT };
180
180
  }
181
181
  }
@@ -203,14 +203,14 @@ export function call(s, n) {
203
203
  const same = strTy(isAscii(target.ty));
204
204
  switch (args.length === 0 ? method : "") {
205
205
  case "trim":
206
- return { code: `fv::js_trim(${strArg(target.code)})`, ty: same, ...lone };
206
+ return { code: `fv::strings::js_trim(${strArg(target.code)})`, ty: same, ...lone };
207
207
  case "trimStart":
208
- return { code: `fv::js_trim_start(${strArg(target.code)})`, ty: same, ...lone };
208
+ return { code: `fv::strings::js_trim_start(${strArg(target.code)})`, ty: same, ...lone };
209
209
  case "trimEnd":
210
210
  case "trimRight":
211
- return { code: `fv::js_trim_end(${strArg(target.code)})`, ty: same, ...lone };
211
+ return { code: `fv::strings::js_trim_end(${strArg(target.code)})`, ty: same, ...lone };
212
212
  case "trimLeft":
213
- return { code: `fv::js_trim_start(${strArg(target.code)})`, ty: same, ...lone };
213
+ return { code: `fv::strings::js_trim_start(${strArg(target.code)})`, ty: same, ...lone };
214
214
  case "toUpperCase":
215
215
  return { code: `&*${receiver(target.code)}.to_uppercase()`, ty: same, ...lone };
216
216
  case "toLowerCase":
@@ -231,15 +231,15 @@ export function call(s, n) {
231
231
  fail(comp, code, `\`.${method}()\` takes a literal ${what}, from ${low} to 100`, n);
232
232
  const x = bare(asF64(target));
233
233
  if (method === "toFixed")
234
- return { code: `&*fv::js_to_fixed(${x}, ${given ?? 0})`, ty: asciiTy() };
234
+ return { code: `&*fv::core::js_to_fixed(${x}, ${given ?? 0})`, ty: asciiTy() };
235
235
  if (method === "toExponential")
236
- return { code: `&*fv::js_to_exponential(${x}, ${given === undefined ? "None" : `Some(${given})`})`, ty: asciiTy() };
236
+ return { code: `&*fv::core::js_to_exponential(${x}, ${given === undefined ? "None" : `Some(${given})`})`, ty: asciiTy() };
237
237
  if (given === undefined)
238
- return { code: `&*fv::Js(${bare(asWritten(target).code)}).to_string()`, ty: asciiTy() };
239
- return { code: `&*fv::js_to_precision(${x}, ${given})`, ty: asciiTy() };
238
+ return { code: `&*fv::core::Js(${bare(asWritten(target).code)}).to_string()`, ty: asciiTy() };
239
+ return { code: `&*fv::core::js_to_precision(${x}, ${given})`, ty: asciiTy() };
240
240
  }
241
241
  if (isNumber(target.ty) && method === "toString" && args.length === 0) {
242
- return { code: `&*fv::Js(${bare(asWritten(target).code)}).to_string()`, ty: asciiTy() };
242
+ return { code: `&*fv::core::Js(${bare(asWritten(target).code)}).to_string()`, ty: asciiTy() };
243
243
  }
244
244
  if (target.owned)
245
245
  refuseInPlace(comp, `\`.${method}()\``, n);
@@ -266,7 +266,7 @@ export function call(s, n) {
266
266
  const sep = args.length ? expr(s, args[0]) : { code: '","', ty: STR };
267
267
  if (sep.ty.k !== "str")
268
268
  fail(comp, "FV0802", "`.join()` takes a string", args[0]);
269
- const each = of.k === "str" ? `${atom(target.code)}.iter().map(|v| &**v)` : `${atom(target.code)}.iter().map(|v| fv::Js(*v).to_string())`;
269
+ const each = of.k === "str" ? `${atom(target.code)}.iter().map(|v| &**v)` : `${atom(target.code)}.iter().map(|v| fv::core::Js(*v).to_string())`;
270
270
  return { code: `&*${each}.collect::<Vec<_>>().join(${strArg(sep.code)})`, ty: strTy(writesAscii(target.ty) && isAscii(sep.ty)), ...loneOf(sep) };
271
271
  }
272
272
  }
@@ -349,11 +349,11 @@ function json(s, v, n) {
349
349
  const one = (code, ty) => {
350
350
  switch (ty.k) {
351
351
  case "str":
352
- return `fv::js_json_string(${code === "v" ? "&v" : strArg(code)})`;
352
+ return `fv::strings::js_json_string(${code === "v" ? "&v" : strArg(code)})`;
353
353
  case "int":
354
- return `fv::Js(${bare(code)}).to_string()`;
354
+ return `fv::core::Js(${bare(code)}).to_string()`;
355
355
  case "float":
356
- return `fv::js_json_number(${bare(code)})`;
356
+ return `fv::strings::js_json_number(${bare(code)})`;
357
357
  case "bool":
358
358
  return `(if ${condition(code)} { "true" } else { "false" }).to_owned()`;
359
359
  default:
package/dist/children.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { isSSRSafeAttrName, propsToAttrMap } from "@vue/shared";
2
- import { absence, camelize, declares, enumOf, fail, nothing, REFUSALS, refusalAt, rustStr, sameTy, snake, takesAttrs, variantPath } from "./model.js";
2
+ import { absence, camelize, declares, enumItems, enumOf, fail, literalsOf, nothing, REFUSALS, refusalAt, rustStr, sameTy, snake, takesAttrs, variantPath } from "./model.js";
3
3
  import { ctx } from "./context.js";
4
4
  import { known } from "./narrowing.js";
5
- import { intoEnum, knownLiteral } from "./enums.js";
5
+ import { enumItemVal, intoEnum, knownLiteral, refuseUnproven } from "./enums.js";
6
6
  import { bareDates, markHome } from "./typescript.js";
7
7
  import { items } from "./lists.js";
8
8
  import { describeTy, expr, fieldVal, holdsNothing, typedBy } from "./expr.js";
@@ -254,10 +254,10 @@ function childAttrsArg(s, child, parts, merges, fallthrough, ids, n) {
254
254
  return ids;
255
255
  }
256
256
  if (!sources.length)
257
- return ids === null ? "&fv::Attrs::NONE" : `&fv::Attrs::scoped(${ids})`;
257
+ return ids === null ? "&fv::attrs::Attrs::NONE" : `&fv::attrs::Attrs::scoped(${ids})`;
258
258
  if (sources.length === 1 && !merges)
259
- return `&fv::Attrs::new(${sources[0]}, ${ids ?? '""'})`;
260
- return `&fv::Attrs::merged(&[${sources.join(", ")}], ${ids ?? '""'})`;
259
+ return `&fv::attrs::Attrs::new(${sources[0]}, ${ids ?? '""'})`;
260
+ return `&fv::attrs::Attrs::merged(&[${sources.join(", ")}], ${ids ?? '""'})`;
261
261
  }
262
262
  export function fieldInit(name, value) {
263
263
  return value === name ? name : `${name}: ${bare(value)}`;
@@ -271,7 +271,7 @@ export function extraParams(c, unread = () => false) {
271
271
  export function callChild(s, e, child, propsCode, slots, attrs, later = false) {
272
272
  const m = `super::${child.module}`;
273
273
  const scoped = child.inherits || takesAttrs(child);
274
- const trailing = paramsOf(child).map((p) => `, ${p.name}`).join("") + (scoped ? `, ${attrs ?? (takesAttrs(child) ? "&fv::Attrs::NONE" : '""')}` : "");
274
+ const trailing = paramsOf(child).map((p) => `, ${p.name}`).join("") + (scoped ? `, ${attrs ?? (takesAttrs(child) ? "&fv::attrs::Attrs::NONE" : '""')}` : "");
275
275
  const render = scoped ? "try_render_scoped" : "try_render";
276
276
  const around = later ? ctx.plugins.flatMap((p) => p.loadedLater?.(child) ?? []) : [];
277
277
  if (around.length) {
@@ -377,19 +377,19 @@ export function callWith(s, e, child, m, head, slotsTy, trailing, slots, vnode =
377
377
  e.close("),");
378
378
  }
379
379
  else if (sid !== null) {
380
- e.open(`${field}: Some(fv::Slot::slotted(&|out: &mut String${sidParam}| -> bool`);
380
+ e.open(`${field}: Some(fv::render::Slot::slotted(&|out: &mut String${sidParam}| -> bool`);
381
381
  const opened = e.lines.length - 1;
382
382
  content(inner);
383
383
  unread(opened, sid);
384
384
  e.close(")),");
385
385
  }
386
386
  else if (staticallyFilled(body)) {
387
- e.open(`${field}: Some(fv::Slot::new(&|out: &mut String|`);
387
+ e.open(`${field}: Some(fv::render::Slot::new(&|out: &mut String|`);
388
388
  statements({ ...inner, fill: false }, e, body);
389
389
  e.close(")),");
390
390
  }
391
391
  else {
392
- e.open(`${field}: Some(fv::Slot::markup(&|out: &mut String| -> bool`);
392
+ e.open(`${field}: Some(fv::render::Slot::markup(&|out: &mut String| -> bool`);
393
393
  e.stmt("let mut filled = false;");
394
394
  statements({ ...inner, fill: true }, e, body);
395
395
  e.stmt("filled");
@@ -402,6 +402,32 @@ function datesInto(of, at) {
402
402
  const date = `|fv_t| ${REFUSALS}.date(fv_t, ${at})`;
403
403
  return of.k === "date" ? `.map(${date})` : `.map(|v| v.map(${date}))`;
404
404
  }
405
+ function enumListInto(comp, v, want, node) {
406
+ const refuse = () => fail(comp, "FV0511", `the prop is ${describeTy(want)}, and the value given is ${describeTy(v.ty)}`, node);
407
+ if (want.k === "opt") {
408
+ if (v.ty.k === "list")
409
+ return `Some(${enumListInto(comp, v, want.of, node)})`;
410
+ if (v.ty.k !== "opt" || v.ty.of.k !== "list" || absence(v.ty) !== absence(want))
411
+ return refuse();
412
+ if (v.enums === undefined)
413
+ return `${atom(v.code)}.map(|v| ${enumListInto(comp, { code: "v", ty: v.ty.of, slice: true }, want.of, node)})`;
414
+ return `${atom(v.enums.place)}.as_ref().map(|v| ${enumListInto(comp, { ...v, code: "v", ty: v.ty.of, enums: { ...v.enums, place: "v" } }, want.of, node)})`;
415
+ }
416
+ if (want.k !== "list" || v.ty.k !== "list")
417
+ return refuse();
418
+ if (v.ty.of.k === "undef")
419
+ return "Vec::new()";
420
+ const of = v.ty.of;
421
+ if (v.enums !== undefined) {
422
+ const item = ownInto(comp, enumItemVal("v", of, v.enums.def), want.of, node);
423
+ return item === "v" ? `${atom(v.enums.place)}.to_vec()` : `${atom(v.enums.place)}.iter().map(|&v| ${item}).collect()`;
424
+ }
425
+ if (of.k !== "str" && (of.k !== "opt" || of.of.k !== "str"))
426
+ return refuse();
427
+ const each = of.k === "str" ? { code: v.iter !== undefined ? "&*v" : "&**v", ty: of } : { code: v.iter !== undefined ? "v" : "v.as_deref()", ty: of };
428
+ const iter = v.iter ?? `${atom(v.code)}.iter()`;
429
+ return `${iter}.map(|v| ${ownInto(comp, each, want.of, node)}).collect()`;
430
+ }
405
431
  export function ownInto(comp, v, want, node) {
406
432
  holdsNothing(comp, v, want, node, "the prop");
407
433
  if (v.owned && sameTy(v.ty, want))
@@ -410,13 +436,22 @@ export function ownInto(comp, v, want, node) {
410
436
  return `Some(${bare(v.code)})`;
411
437
  if (want.k === "opt" && nothing(v.ty))
412
438
  return "None";
439
+ if (enumItems(want) !== undefined)
440
+ return enumListInto(comp, v, want, node);
413
441
  const def = enumOf(want);
414
442
  if (def !== undefined) {
415
- const into = (v.ty.k === "opt" ? v.ty.of : v.ty).k === "str" ? intoEnum(comp, v, def) : null;
443
+ const into = (v.ty.k === "opt" ? v.ty.of : v.ty).k === "str" ? intoEnum(comp, v, def, node) : null;
416
444
  if (into === null) {
445
+ refuseUnproven(v);
417
446
  const literal = knownLiteral(v);
418
- const given = literal !== undefined ? `is ${JSON.stringify(literal)}` : (v.ty.k === "opt" ? v.ty.of : v.ty).k === "str" ? "is a string that may be any other" : `is ${describeTy(v.ty)}`;
419
- fail(comp, "FV0515", `the prop is one of ${def.literals.map((l) => JSON.stringify(l)).join(" | ")}, the members of \`${def.name}\`, and the value given ${given}: pass one of them, or a value typed as the union`, node);
447
+ const inner = v.ty.k === "opt" ? v.ty.of : v.ty;
448
+ const outside = literal === undefined && inner.k === "str" ? literalsOf(inner)?.filter((l) => typeof l !== "string" || !def.literals.includes(l)) : undefined;
449
+ const union = `the prop is one of ${def.literals.map((l) => JSON.stringify(l)).join(" | ")}, the members of \`${def.name}\``;
450
+ if (outside !== undefined && outside.length > 0) {
451
+ fail(comp, "FV0515", `${union}, and the value given may be ${outside.map((l) => JSON.stringify(l)).join(" or ")}: narrow it to the members, by \`===\` or \`!==\` with a literal, by its truth or by \`||\`, in \`?:\`, \`&&\` or \`v-if\`, or pass a value typed as the union`, node);
452
+ }
453
+ const given = literal !== undefined ? `is ${JSON.stringify(literal)}` : inner.k === "str" ? "is a string that may be any other" : `is ${describeTy(v.ty)}`;
454
+ fail(comp, "FV0515", `${union}, and the value given ${given}: pass one of them, or a value typed as the union`, node);
420
455
  }
421
456
  return want.k === "opt" && v.ty.k !== "opt" ? `Some(${bare(into)})` : into;
422
457
  }
package/dist/classes.js CHANGED
@@ -36,7 +36,7 @@ export function classItems(s, n) {
36
36
  keys.push(key);
37
37
  return `(${bare(cond(s, p.value))}, ${bare(key.code)})`;
38
38
  });
39
- return [{ code: `&*fv::class_object(&[${entries.join(", ")}])` }];
39
+ return [{ code: `&*fv::attrs::class_object(&[${entries.join(", ")}])` }];
40
40
  }
41
41
  return n.properties.flatMap((p) => {
42
42
  if (p.type !== "ObjectProperty")
@@ -52,7 +52,7 @@ export function classItems(s, n) {
52
52
  const k = expr(s, p.key);
53
53
  if (k.ty.k !== "str")
54
54
  fail(s.comp, "FV1002", "a computed class name is a string", p.key);
55
- name = { code: `fv::js_trim(${strArg(k.code)})` };
55
+ name = { code: `fv::strings::js_trim(${strArg(k.code)})` };
56
56
  }
57
57
  const v = expr(s, p.value);
58
58
  const on = known(v);
@@ -115,7 +115,7 @@ export function renderClass(s, e, n, after = false) {
115
115
  }
116
116
  if (i < items.length) {
117
117
  const rest = items.slice(i).map((it) => ("lit" in it ? rustStr(it.lit) : it.code));
118
- e.stmt(`fv::class_into(out, ${wrote}, &[${rest.join(", ")}]);`);
118
+ e.stmt(`fv::attrs::class_into(out, ${wrote}, &[${rest.join(", ")}]);`);
119
119
  }
120
120
  }
121
121
  const UNWRITTEN = new Set(["on", "if", "else-if", "else", "for", "slot", "once", "memo", "cloak", "pre"]);
@@ -202,23 +202,23 @@ export function literalClass(n) {
202
202
  export function classAttr(s, n, side) {
203
203
  switch (n.type) {
204
204
  case "StringLiteral":
205
- return `fv::Attr::str(${rustStr(n.value)})`;
205
+ return `fv::attrs::Attr::str(${rustStr(n.value)})`;
206
206
  case "NullLiteral":
207
- return 'fv::Attr::str("")';
207
+ return 'fv::attrs::Attr::str("")';
208
208
  case "ArrayExpression":
209
209
  case "ObjectExpression": {
210
210
  const items = classItems(s, n);
211
211
  const names = items.every((it) => "lit" in it)
212
212
  ? rustStr(items.map((it) => ("lit" in it ? it.lit : "")).join(" "))
213
- : `fv::class_names(&[${items.map((it) => ("lit" in it ? rustStr(it.lit) : it.code)).join(", ")}])`;
213
+ : `fv::attrs::class_names(&[${items.map((it) => ("lit" in it ? rustStr(it.lit) : it.code)).join(", ")}])`;
214
214
  const literal = names.startsWith('"');
215
215
  if (side === "vnode")
216
- return literal ? `fv::Attr::str(${names})` : `fv::Attr::from(${names})`;
217
- return `fv::Attr::Names(${literal ? `std::borrow::Cow::Borrowed(${names})` : `${names}.into()`})`;
216
+ return literal ? `fv::attrs::Attr::str(${names})` : `fv::attrs::Attr::from(${names})`;
217
+ return `fv::attrs::Attr::Names(${literal ? `std::borrow::Cow::Borrowed(${names})` : `${names}.into()`})`;
218
218
  }
219
219
  case "LogicalExpression":
220
220
  if (n.operator === "&&") {
221
- return narrowed(s, n.left, true, (inner) => classAttr(inner, n.right, side), () => 'fv::Attr::str("")') ?? `if ${condition(cond(s, n.left))} { ${classAttr(s, n.right, side)} } else { fv::Attr::str("") }`;
221
+ return narrowed(s, n.left, true, (inner) => classAttr(inner, n.right, side), () => 'fv::attrs::Attr::str("")') ?? `if ${condition(cond(s, n.left))} { ${classAttr(s, n.right, side)} } else { fv::attrs::Attr::str("") }`;
222
222
  }
223
223
  break;
224
224
  case "ConditionalExpression":
package/dist/cli.js CHANGED
@@ -2,7 +2,9 @@
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, watch, writeFileSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { relativePath } from "./paths.js";
5
- import { CONFIG_FILE, generate, isGenerated, loadConfig, VERSION, write } from "./compiler.js";
5
+ import { CONFIG_FILE, loadConfig } from "./config.js";
6
+ import {} from "./errors.js";
7
+ import { generate, isGenerated, VERSION, write } from "./compiler.js";
6
8
  import { helpText, parseArgs } from "./args.js";
7
9
  import { unifiedDiff } from "./diff.js";
8
10
  import { diagnose, diagnoseAll, formatRefusal, formatWarning, isRefusal } from "./diagnostics.js";