ferrovue 0.15.1 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/config.d.ts +3 -2
- package/dist/whitespace.d.ts +9 -8
- package/dist/whitespace.js +37 -22
- package/package.json +1 -1
- package/schema.json +2 -2
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ the configuration at your project root yourself:
|
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
60
|
-
"$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.15.
|
|
60
|
+
"$schema": "https://cdn.jsdelivr.net/npm/ferrovue@0.15.2/schema.json",
|
|
61
61
|
"components": "client/components",
|
|
62
62
|
"out": "src/generated"
|
|
63
63
|
}
|
|
@@ -242,7 +242,7 @@ ferrovue compiles `<script setup lang="ts">` components, and components with no
|
|
|
242
242
|
| 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 |
|
|
243
243
|
| 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 |
|
|
244
244
|
| HTML comments | left out, as Vue's production build leaves them out, or kept with `comments` or `"isProduction": false`, as on the dev server: written as Vue writes them, at the root, between roots, inside elements and in slot content |
|
|
245
|
-
| Whitespace | text condensed as Vue condenses it, or with `"whitespace": "trim-edges"` also trimmed where it meets
|
|
245
|
+
| Whitespace | text condensed as Vue condenses it, or with `"whitespace": "trim-edges"` also trimmed where it meets a block or inline-block element's tags across a line break and spaced between inline elements and components across one, the client compiled alike with `trimEdges`: in elements, components and `<template v-slot>`, keeping `<pre>`, `<textarea>`, `v-pre` and a static `white-space: pre*` style. See [`generated_code`](https://docs.rs/ferrovue/latest/ferrovue/guide/generated_code/index.html#whitespace) |
|
|
246
246
|
| Attributes | static and bound attributes, boolean attributes, `:hidden`, `data-*` and `aria-*`, `v-bind` objects |
|
|
247
247
|
| `class` | strings, arrays, objects (`{ active: on }`, computed keys), `cond && "x"`, `cond ? "x" : null`, merged with a static `class` |
|
|
248
248
|
| `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 |
|
package/dist/config.d.ts
CHANGED
|
@@ -104,8 +104,9 @@ export interface Config {
|
|
|
104
104
|
* `false` and left out when it is `true`. */
|
|
105
105
|
comments?: boolean;
|
|
106
106
|
/** How the text of templates is spaced: `"condense"` (the default) as Vue spaces it, or
|
|
107
|
-
* `"trim-edges"`, which also trims the whitespace holding a line break where text meets
|
|
108
|
-
* element's tags and renders a line break between two inline elements as a
|
|
107
|
+
* `"trim-edges"`, which also trims the whitespace holding a line break where text meets a block
|
|
108
|
+
* or inline-block element's tags and renders a line break between two inline elements as a
|
|
109
|
+
* space. The client
|
|
109
110
|
* compiles the same way with `trimEdges` from `ferrovue/whitespace` in `@vitejs/plugin-vue`'s
|
|
110
111
|
* `template.compilerOptions.nodeTransforms`. */
|
|
111
112
|
whitespace?: WhitespaceMode;
|
package/dist/whitespace.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/** A node transform for `@vitejs/plugin-vue`'s `template.compilerOptions.nodeTransforms` that
|
|
2
|
-
* trims the whitespace at the start and end of
|
|
3
|
-
* tags, when that whitespace holds a line break, as `"whitespace": "trim-edges"`
|
|
4
|
-
* Rust render it.
|
|
5
|
-
*
|
|
6
|
-
* one space, as a formatter
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
2
|
+
* trims the whitespace at the start and end of a block or inline-block element's text, where it
|
|
3
|
+
* meets the element's tags, when that whitespace holds a line break, as `"whitespace": "trim-edges"`
|
|
4
|
+
* makes ferrovue's Rust render it. An inline element's edges keep Vue's one space, and a space
|
|
5
|
+
* typed on the same line as the tag stays. A line break between two inline elements, components,
|
|
6
|
+
* `<slot>`s or `<template>`s, such as `<span>` and `<a>`, renders as one space, as a formatter
|
|
7
|
+
* reads it, also at the template's root and across a comment.
|
|
8
|
+
* It trims the content of block and inline-block elements, components and `<template v-slot>`, and
|
|
9
|
+
* leaves `<pre>`, `<textarea>`, `v-pre` and an element whose `style` sets `white-space: pre`,
|
|
10
|
+
* `pre-wrap`, `pre-line` or `break-spaces` as they are, with everything inside them. */
|
|
10
11
|
export declare function trimEdges(node: object): void;
|
package/dist/whitespace.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const ROOT = 0;
|
|
2
2
|
const ELEMENT = 1;
|
|
3
3
|
const TEXT = 2;
|
|
4
|
+
const COMMENT = 3;
|
|
4
5
|
const PLAIN = 0;
|
|
5
6
|
const COMPONENT = 1;
|
|
6
7
|
const SLOT = 2;
|
|
@@ -17,6 +18,8 @@ const SPACELESS_TAGS = new Set([
|
|
|
17
18
|
]
|
|
18
19
|
.join(" ")
|
|
19
20
|
.split(" "));
|
|
21
|
+
const INLINE_BLOCK_TAGS = new Set(["audio", "button", "meter", "object", "progress", "select", "video"]);
|
|
22
|
+
const COMMENTS = /<!--[\s\S]*?(?:-->|$)/g;
|
|
20
23
|
const KEPT_TAGS = new Set(["pre", "textarea"]);
|
|
21
24
|
const PRE_STYLE = /(?:^|;)\s*white-space\s*:\s*(?:pre|pre-wrap|pre-line|break-spaces)\s*(?:!important\s*)?(?:;|$)/i;
|
|
22
25
|
const OPEN_TAG = /^<[^\s/>]+(?:[^"'>]|"[^"]*"|'[^']*')*/;
|
|
@@ -33,7 +36,11 @@ function keeps(el) {
|
|
|
33
36
|
return V_PRE.test(open.replace(QUOTED, '""'));
|
|
34
37
|
}
|
|
35
38
|
function trims(el) {
|
|
36
|
-
if (el.tagType === PLAIN
|
|
39
|
+
if (el.tagType === PLAIN) {
|
|
40
|
+
const tag = el.tag.toLowerCase();
|
|
41
|
+
return el.ns !== HTML || SPACELESS_TAGS.has(tag) || INLINE_BLOCK_TAGS.has(tag);
|
|
42
|
+
}
|
|
43
|
+
if (el.tagType === COMPONENT)
|
|
37
44
|
return true;
|
|
38
45
|
return el.tagType === TEMPLATE && el.props.some((p) => p.type === 7 && p.name === "slot");
|
|
39
46
|
}
|
|
@@ -64,22 +71,26 @@ function inline(node) {
|
|
|
64
71
|
const tag = node.tag.toLowerCase();
|
|
65
72
|
return (node.ns === HTML || tag === "svg" || tag === "math") && !SPACELESS_TAGS.has(tag);
|
|
66
73
|
}
|
|
67
|
-
function spaceInline(
|
|
68
|
-
|
|
69
|
-
return;
|
|
70
|
-
const children = el.children;
|
|
74
|
+
function spaceInline(node, source, start) {
|
|
75
|
+
const children = node.children;
|
|
71
76
|
const spaced = [];
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
let prev;
|
|
78
|
+
let after = 0;
|
|
79
|
+
children.forEach((child) => {
|
|
80
|
+
if (child.type === COMMENT) {
|
|
81
|
+
spaced.push(child);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
74
84
|
if (prev !== undefined && inline(prev) && inline(child)) {
|
|
75
|
-
const
|
|
76
|
-
const gap = el.loc.source.slice(prev.loc.end.offset - start, child.loc.start.offset - start);
|
|
85
|
+
const gap = source.slice(prev.loc.end.offset - start, child.loc.start.offset - start).replace(COMMENTS, "");
|
|
77
86
|
if (BLANK.test(gap) && LINE_BREAK.test(gap))
|
|
78
|
-
spaced.
|
|
87
|
+
spaced.splice(after, 0, { type: TEXT, content: " ", loc: { source: gap, start: prev.loc.end, end: child.loc.start } });
|
|
79
88
|
}
|
|
80
89
|
spaced.push(child);
|
|
90
|
+
prev = child;
|
|
91
|
+
after = spaced.length;
|
|
81
92
|
});
|
|
82
|
-
|
|
93
|
+
node.children = spaced;
|
|
83
94
|
}
|
|
84
95
|
function walk(node) {
|
|
85
96
|
for (const child of node.children ?? []) {
|
|
@@ -87,22 +98,26 @@ function walk(node) {
|
|
|
87
98
|
continue;
|
|
88
99
|
if (trims(child))
|
|
89
100
|
trimChildren(child);
|
|
90
|
-
|
|
101
|
+
if (child.ns === HTML)
|
|
102
|
+
spaceInline(child, child.loc.source, child.loc.start.offset);
|
|
91
103
|
walk(child);
|
|
92
104
|
}
|
|
93
105
|
}
|
|
94
106
|
/** A node transform for `@vitejs/plugin-vue`'s `template.compilerOptions.nodeTransforms` that
|
|
95
|
-
* trims the whitespace at the start and end of
|
|
96
|
-
* tags, when that whitespace holds a line break, as `"whitespace": "trim-edges"`
|
|
97
|
-
* Rust render it.
|
|
98
|
-
*
|
|
99
|
-
* one space, as a formatter
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
107
|
+
* trims the whitespace at the start and end of a block or inline-block element's text, where it
|
|
108
|
+
* meets the element's tags, when that whitespace holds a line break, as `"whitespace": "trim-edges"`
|
|
109
|
+
* makes ferrovue's Rust render it. An inline element's edges keep Vue's one space, and a space
|
|
110
|
+
* typed on the same line as the tag stays. A line break between two inline elements, components,
|
|
111
|
+
* `<slot>`s or `<template>`s, such as `<span>` and `<a>`, renders as one space, as a formatter
|
|
112
|
+
* reads it, also at the template's root and across a comment.
|
|
113
|
+
* It trims the content of block and inline-block elements, components and `<template v-slot>`, and
|
|
114
|
+
* leaves `<pre>`, `<textarea>`, `v-pre` and an element whose `style` sets `white-space: pre`,
|
|
115
|
+
* `pre-wrap`, `pre-line` or `break-spaces` as they are, with everything inside them. */
|
|
103
116
|
export function trimEdges(node) {
|
|
104
117
|
const n = node;
|
|
105
|
-
if (n.type
|
|
106
|
-
|
|
118
|
+
if (n.type !== ROOT)
|
|
119
|
+
return;
|
|
120
|
+
spaceInline(n, n.source ?? n.loc.source, n.source === undefined ? n.loc.start.offset : 0);
|
|
121
|
+
walk(n);
|
|
107
122
|
}
|
|
108
123
|
Object.defineProperty(trimEdges, MARK, { value: "trim-edges" });
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -311,8 +311,8 @@
|
|
|
311
311
|
"type": "boolean"
|
|
312
312
|
},
|
|
313
313
|
"whitespace": {
|
|
314
|
-
"description": "How the text of templates is spaced: `\"condense\"` (the default) as Vue spaces it, or `\"trim-edges\"`, which also trims the whitespace holding a line break where text meets
|
|
315
|
-
"markdownDescription": "How the text of templates is spaced: `\"condense\"` (the default) as Vue spaces it, or `\"trim-edges\"`, which also trims the whitespace holding a line break where text meets
|
|
314
|
+
"description": "How the text of templates is spaced: `\"condense\"` (the default) as Vue spaces it, or `\"trim-edges\"`, which also trims the whitespace holding a line break where text meets a block or inline-block element's tags and renders a line break between two inline elements as a space. The client compiles the same way with `trimEdges` from `ferrovue/whitespace` in `@vitejs/plugin-vue`'s `template.compilerOptions.nodeTransforms`.",
|
|
315
|
+
"markdownDescription": "How the text of templates is spaced: `\"condense\"` (the default) as Vue spaces it, or `\"trim-edges\"`, which also trims the whitespace holding a line break where text meets a block or inline-block element's tags and renders a line break between two inline elements as a space. The client compiles the same way with `trimEdges` from `ferrovue/whitespace` in `@vitejs/plugin-vue`'s `template.compilerOptions.nodeTransforms`.",
|
|
316
316
|
"type": "string",
|
|
317
317
|
"enum": [
|
|
318
318
|
"condense",
|