@microsoft/fast-build 0.3.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -2
- package/bin/fast.js +170 -21
- package/package.json +3 -2
- package/wasm/microsoft_fast_build.d.ts +24 -12
- package/wasm/microsoft_fast_build.js +62 -40
- package/wasm/microsoft_fast_build_bg.wasm +0 -0
- package/wasm/microsoft_fast_build_bg.wasm.d.ts +2 -2
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ import { render } from '@microsoft/fast-build';
|
|
|
25
25
|
|
|
26
26
|
## CLI usage
|
|
27
27
|
|
|
28
|
-
Once installed, the `fast` binary is available. Use the `build` subcommand to render an HTML template with
|
|
28
|
+
Once installed, the `fast` binary is available. Use the `build` subcommand to render an HTML template with an optional JSON state file:
|
|
29
29
|
|
|
30
30
|
```shell
|
|
31
31
|
fast build [options]
|
|
@@ -36,9 +36,11 @@ fast build [options]
|
|
|
36
36
|
| Option | Default | Description |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `--entry="<path>"` | `index.html` | Entry HTML template to render |
|
|
39
|
-
| `--state="<path>"` | `
|
|
39
|
+
| `--state="<path>"` | `{}` when omitted | JSON file containing the template state. If omitted, no state file is loaded and rendering uses an empty state object. If `--state` is provided, the file must exist. |
|
|
40
40
|
| `--output="<path>"` | `output.html` | Where to write the rendered HTML |
|
|
41
41
|
| `--templates="<glob>"` | _(none)_ | Glob pattern(s) for custom element template HTML files. Separate multiple patterns with commas. A warning is printed if not provided or if no files match a pattern. |
|
|
42
|
+
| `--attribute-name-strategy="<strategy>"` | `camelCase` | Strategy for mapping HTML attribute names to state property names on custom elements. `"camelCase"` converts dashes to camelCase (e.g. `foo-bar` → `fooBar`). `"none"` preserves dashes (e.g. `foo-bar` → `foo-bar`). See [Attribute name strategy](#attribute-name-strategy). |
|
|
43
|
+
| `--config="<path>"` | `fast-build.config.json` | Path to a JSON configuration file. If omitted, `fast-build.config.json` in the current directory is used when present. CLI arguments take precedence over config values. See [Configuration file](#configuration-file). |
|
|
42
44
|
|
|
43
45
|
### Example
|
|
44
46
|
|
|
@@ -79,6 +81,18 @@ Produces `output.html`:
|
|
|
79
81
|
</html>
|
|
80
82
|
```
|
|
81
83
|
|
|
84
|
+
### Missing or omitted state
|
|
85
|
+
|
|
86
|
+
State is optional. When `--state` is omitted and no `state` path is provided by config, `fast build` does not look for `state.json`; rendering uses an empty object (`{}`). An explicit `--state` path, or a `state` path from config, must exist or the command exits with an error.
|
|
87
|
+
|
|
88
|
+
> **Breaking change:** Earlier versions implicitly loaded `state.json` from the current working directory when `--state` was omitted. To use a state file, pass `--state=state.json` or set `"state": "state.json"` in `fast-build.config.json`.
|
|
89
|
+
|
|
90
|
+
When a binding value is not present in state:
|
|
91
|
+
|
|
92
|
+
- Content bindings render nothing, including unresolved dot paths: `<p>{{foo.bar}}</p>` becomes `<p></p>`.
|
|
93
|
+
- Attribute bindings omit the entire attribute, including unresolved dot paths: `<div class="{{foo.bar}}"></div>` becomes `<div></div>`.
|
|
94
|
+
- `<f-repeat>` treats a missing list binding, including unresolved dot paths, as an empty array and renders zero iterations. If the binding is present but is not an array, rendering still errors.
|
|
95
|
+
|
|
82
96
|
### Custom element templates
|
|
83
97
|
|
|
84
98
|
Pass a glob pattern (or comma-separated list of patterns) to `--templates` to expand custom elements into [Declarative Shadow DOM](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom):
|
|
@@ -105,6 +119,56 @@ Template files must use the following format:
|
|
|
105
119
|
|
|
106
120
|
If an `<f-template>` element has no `name` attribute, a warning is printed and it is ignored. Exact file paths (no wildcards) are also accepted as patterns, making it possible to register a single template file.
|
|
107
121
|
|
|
122
|
+
Any `shadowroot*` attributes on `<f-template>` are forwarded to the rendered Declarative Shadow DOM `<template>`. The CLI normalizes `shadowrootmode` and legacy `shadowroot` for compatibility: when neither has a non-empty value, it emits `shadowrootmode="open" shadowroot="open"`; when exactly one has a non-empty value, that value is mirrored to the other; when both have explicit non-empty values, both are preserved as authored, even if they conflict.
|
|
123
|
+
|
|
124
|
+
### Attribute name strategy
|
|
125
|
+
|
|
126
|
+
The `--attribute-name-strategy` option controls how HTML attribute names on custom elements are mapped to state property names in their shadow templates.
|
|
127
|
+
|
|
128
|
+
| Strategy | Behaviour | Template binding |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `camelCase` (default) | Dashed attribute names converted to camelCase | `foo-bar` → `{{fooBar}}` |
|
|
131
|
+
| `none` | Attribute names lowercased as-is, dashes preserved | `foo-bar` → `{{foo-bar}}` |
|
|
132
|
+
|
|
133
|
+
The `camelCase` strategy only affects "plain" custom element attributes. It does **not** change:
|
|
134
|
+
- `data-*` attributes (always use `dataset.*` grouping)
|
|
135
|
+
- `aria-*` attributes (always use ARIA reflection lookup)
|
|
136
|
+
- HTML global attributes with known property names (e.g. `tabindex` → `tabIndex`)
|
|
137
|
+
|
|
138
|
+
```shell
|
|
139
|
+
fast build \
|
|
140
|
+
--templates="./components/**/*.html" \
|
|
141
|
+
--attribute-name-strategy=camelCase \
|
|
142
|
+
--entry=index.html \
|
|
143
|
+
--state=state.json \
|
|
144
|
+
--output=output.html
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Configuration file
|
|
148
|
+
|
|
149
|
+
Instead of passing every option on the command line, you can place a `fast-build.config.json` file alongside your project files:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"entry": "index.html",
|
|
154
|
+
"state": "state.json",
|
|
155
|
+
"output": "output.html",
|
|
156
|
+
"templates": "./components/**/*.html"
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The CLI automatically loads `fast-build.config.json` from the current directory when it exists. To use a different file, pass `--config`:
|
|
161
|
+
|
|
162
|
+
```shell
|
|
163
|
+
fast build --config=configs/my-build.json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Precedence:** CLI arguments always override config file values. For example, `--output=other.html` will override the `output` value in the config file.
|
|
167
|
+
|
|
168
|
+
**Path resolution:** File paths in the config file (`entry`, `state`, `output`, `templates`) are resolved relative to the config file's directory, not the current working directory. This ensures the config works correctly regardless of where the CLI is invoked.
|
|
169
|
+
|
|
170
|
+
All keys are optional. Only the following keys are allowed: `entry`, `state`, `output`, `templates`, `attribute-name-strategy`. Unknown keys or non-string values produce an error. If `state` is omitted, rendering uses `{}`; if `state` is present, the referenced file must exist.
|
|
171
|
+
|
|
108
172
|
## Template syntax
|
|
109
173
|
|
|
110
174
|
Template syntax follows the FAST declarative HTML format. See the [`@microsoft/fast-html` README](../fast-html/README.md) for full documentation on bindings, conditionals, repeats, and directives.
|
package/bin/fast.js
CHANGED
|
@@ -6,6 +6,14 @@ const fs = require("fs");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
|
|
8
8
|
const WASM_MODULE = path.join(__dirname, "../wasm/microsoft_fast_build.js");
|
|
9
|
+
const DEFAULT_CONFIG_FILENAME = "fast-build.config.json";
|
|
10
|
+
const ALLOWED_CONFIG_KEYS = new Set([
|
|
11
|
+
"entry",
|
|
12
|
+
"state",
|
|
13
|
+
"output",
|
|
14
|
+
"templates",
|
|
15
|
+
"attribute-name-strategy",
|
|
16
|
+
]);
|
|
9
17
|
|
|
10
18
|
/**
|
|
11
19
|
* Parse CLI arguments of the form --key="value" or --key=value.
|
|
@@ -23,6 +31,106 @@ function parseArgs(argv) {
|
|
|
23
31
|
return args;
|
|
24
32
|
}
|
|
25
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Load and validate a fast-build config file.
|
|
36
|
+
*
|
|
37
|
+
* - If `configPath` is provided, the file must exist (error if missing).
|
|
38
|
+
* - If `configPath` is not provided, looks for `fast-build.config.json` in CWD.
|
|
39
|
+
* Returns an empty object if the default file does not exist.
|
|
40
|
+
*
|
|
41
|
+
* File paths in the returned config are resolved relative to the config
|
|
42
|
+
* file's directory so that the caller can use them directly.
|
|
43
|
+
*
|
|
44
|
+
* @param {string | undefined} configPath - Explicit path from --config, if any.
|
|
45
|
+
* @returns {{ config: Record<string, string>, configDir: string | null }}
|
|
46
|
+
*/
|
|
47
|
+
function loadConfig(configPath) {
|
|
48
|
+
/** @type {string} */
|
|
49
|
+
let resolvedPath;
|
|
50
|
+
/** @type {boolean} */
|
|
51
|
+
let isExplicit;
|
|
52
|
+
|
|
53
|
+
if (configPath !== undefined) {
|
|
54
|
+
resolvedPath = path.resolve(configPath);
|
|
55
|
+
isExplicit = true;
|
|
56
|
+
} else {
|
|
57
|
+
resolvedPath = path.resolve(DEFAULT_CONFIG_FILENAME);
|
|
58
|
+
isExplicit = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
62
|
+
if (isExplicit) {
|
|
63
|
+
process.stderr.write(
|
|
64
|
+
`Error: Config file "${configPath}" not found.\n`
|
|
65
|
+
);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return { config: {}, configDir: null };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let raw;
|
|
72
|
+
try {
|
|
73
|
+
raw = JSON.parse(fs.readFileSync(resolvedPath, "utf8"));
|
|
74
|
+
} catch (e) {
|
|
75
|
+
process.stderr.write(
|
|
76
|
+
`Error: Failed to parse config file "${resolvedPath}": ${e.message}\n`
|
|
77
|
+
);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
82
|
+
process.stderr.write(
|
|
83
|
+
`Error: Config file "${resolvedPath}" must contain a JSON object.\n`
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const key of Object.keys(raw)) {
|
|
89
|
+
if (!ALLOWED_CONFIG_KEYS.has(key)) {
|
|
90
|
+
process.stderr.write(
|
|
91
|
+
`Error: Unknown key "${key}" in config file "${resolvedPath}". Allowed keys: ${[...ALLOWED_CONFIG_KEYS].join(", ")}.\n`
|
|
92
|
+
);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
if (typeof raw[key] !== "string") {
|
|
96
|
+
process.stderr.write(
|
|
97
|
+
`Error: Value for "${key}" in config file "${resolvedPath}" must be a string.\n`
|
|
98
|
+
);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const configDir = path.dirname(resolvedPath);
|
|
104
|
+
return { config: raw, configDir };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a file path value, preferring the CLI arg over the config value.
|
|
109
|
+
* Config-derived paths are resolved relative to the config file's directory.
|
|
110
|
+
* CLI-derived paths are resolved relative to CWD (the default behaviour).
|
|
111
|
+
*
|
|
112
|
+
* @param {Record<string, string>} args - Parsed CLI arguments.
|
|
113
|
+
* @param {Record<string, string>} config - Parsed config file values.
|
|
114
|
+
* @param {string | null} configDir - Directory of the config file, or null.
|
|
115
|
+
* @param {string} key - The option key.
|
|
116
|
+
* @param {string} [defaultValue] - Fallback when neither source provides a value.
|
|
117
|
+
* @returns {string | undefined}
|
|
118
|
+
*/
|
|
119
|
+
function resolveOption(args, config, configDir, key, defaultValue) {
|
|
120
|
+
if (Object.prototype.hasOwnProperty.call(args, key)) {
|
|
121
|
+
return args[key];
|
|
122
|
+
}
|
|
123
|
+
if (Object.prototype.hasOwnProperty.call(config, key)) {
|
|
124
|
+
const value = config[key];
|
|
125
|
+
const isFilePath = key === "entry" || key === "state" || key === "output" || key === "templates";
|
|
126
|
+
if (isFilePath && configDir !== null) {
|
|
127
|
+
return path.resolve(configDir, value);
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
return defaultValue;
|
|
132
|
+
}
|
|
133
|
+
|
|
26
134
|
/**
|
|
27
135
|
* Walk a directory recursively and collect all .html file paths.
|
|
28
136
|
* @param {string} dir
|
|
@@ -110,24 +218,28 @@ function staticPrefixDir(pattern) {
|
|
|
110
218
|
|
|
111
219
|
/**
|
|
112
220
|
* Parse all `<f-template>` elements from an HTML string using the WASM module.
|
|
113
|
-
* Returns
|
|
221
|
+
* Returns template metadata for templates that have a `name` attribute.
|
|
114
222
|
* Emits a warning to stderr for any `<f-template>` without a `name`.
|
|
115
223
|
* @param {string} html
|
|
116
224
|
* @param {string} filePath - used in warning messages
|
|
117
225
|
* @param {object} wasm - the loaded WASM module
|
|
118
|
-
* @returns {{ name: string, content: string }[]}
|
|
226
|
+
* @returns {{ name: string, content: string, shadowrootAttributes: { name: string, value: string | null }[] }[]}
|
|
119
227
|
*/
|
|
120
228
|
function parseFTemplates(html, filePath, wasm) {
|
|
121
|
-
/** @type {{ name: string | null, content: string }[]} */
|
|
229
|
+
/** @type {{ name: string | null, content: string, shadowrootAttributes?: { name: string, value: string | null }[] }[]} */
|
|
122
230
|
const parsed = JSON.parse(wasm.parse_f_templates(html));
|
|
123
231
|
const results = [];
|
|
124
|
-
for (const { name, content } of parsed) {
|
|
232
|
+
for (const { name, content, shadowrootAttributes } of parsed) {
|
|
125
233
|
if (name === null) {
|
|
126
234
|
process.stderr.write(
|
|
127
235
|
`Warning: <f-template> without a 'name' attribute in '${filePath}': ${content.trim()}\n`
|
|
128
236
|
);
|
|
129
237
|
} else {
|
|
130
|
-
results.push({
|
|
238
|
+
results.push({
|
|
239
|
+
name,
|
|
240
|
+
content,
|
|
241
|
+
shadowrootAttributes: shadowrootAttributes || [],
|
|
242
|
+
});
|
|
131
243
|
}
|
|
132
244
|
}
|
|
133
245
|
return results;
|
|
@@ -140,7 +252,7 @@ function parseFTemplates(html, filePath, wasm) {
|
|
|
140
252
|
* Warns (but does not error) if the base directory does not exist.
|
|
141
253
|
* @param {string} pattern
|
|
142
254
|
* @param {object} wasm - the loaded WASM module
|
|
143
|
-
* @returns {{ name: string, content: string }[]}
|
|
255
|
+
* @returns {{ name: string, content: string, shadowrootAttributes: { name: string, value: string | null }[] }[]}
|
|
144
256
|
*/
|
|
145
257
|
function resolvePattern(pattern, wasm) {
|
|
146
258
|
const baseDir = staticPrefixDir(pattern);
|
|
@@ -154,8 +266,8 @@ function resolvePattern(pattern, wasm) {
|
|
|
154
266
|
if (globMatch(pattern, file)) {
|
|
155
267
|
const html = fs.readFileSync(file, "utf8");
|
|
156
268
|
const templates = parseFTemplates(html, file, wasm);
|
|
157
|
-
for (const { name, content } of templates) {
|
|
158
|
-
results.push({ name, content });
|
|
269
|
+
for (const { name, content, shadowrootAttributes } of templates) {
|
|
270
|
+
results.push({ name, content, shadowrootAttributes });
|
|
159
271
|
}
|
|
160
272
|
}
|
|
161
273
|
}
|
|
@@ -163,10 +275,25 @@ function resolvePattern(pattern, wasm) {
|
|
|
163
275
|
}
|
|
164
276
|
|
|
165
277
|
async function runBuild(args) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
278
|
+
const { config, configDir } = loadConfig(
|
|
279
|
+
Object.prototype.hasOwnProperty.call(args, "config") ? args["config"] : undefined
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const templatesArg = resolveOption(args, config, configDir, "templates");
|
|
283
|
+
const output = resolveOption(args, config, configDir, "output", "output.html");
|
|
284
|
+
const entry = resolveOption(args, config, configDir, "entry", "index.html");
|
|
285
|
+
const stateFile = resolveOption(args, config, configDir, "state");
|
|
286
|
+
const stateWasProvided =
|
|
287
|
+
Object.prototype.hasOwnProperty.call(args, "state") ||
|
|
288
|
+
Object.prototype.hasOwnProperty.call(config, "state");
|
|
289
|
+
const attributeNameStrategy = resolveOption(args, config, configDir, "attribute-name-strategy");
|
|
290
|
+
|
|
291
|
+
if (attributeNameStrategy && attributeNameStrategy !== "none" && attributeNameStrategy !== "camelCase") {
|
|
292
|
+
process.stderr.write(
|
|
293
|
+
`Error: Invalid --attribute-name-strategy "${attributeNameStrategy}". Expected "none" or "camelCase".\n`
|
|
294
|
+
);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
170
297
|
|
|
171
298
|
// Load WASM module first — needed for both template parsing and rendering.
|
|
172
299
|
const wasm = require(WASM_MODULE);
|
|
@@ -186,13 +313,13 @@ async function runBuild(args) {
|
|
|
186
313
|
`Warning: No template files found for pattern "${pattern}".\n`
|
|
187
314
|
);
|
|
188
315
|
}
|
|
189
|
-
for (const { name, content } of matches) {
|
|
316
|
+
for (const { name, content, shadowrootAttributes } of matches) {
|
|
190
317
|
if (name in templatesMap) {
|
|
191
318
|
process.stderr.write(
|
|
192
319
|
`Warning: Duplicate template name "${name}" — later file overwrites earlier.\n`
|
|
193
320
|
);
|
|
194
321
|
}
|
|
195
|
-
templatesMap[name] = content;
|
|
322
|
+
templatesMap[name] = { content, shadowrootAttributes };
|
|
196
323
|
}
|
|
197
324
|
}
|
|
198
325
|
}
|
|
@@ -204,17 +331,29 @@ async function runBuild(args) {
|
|
|
204
331
|
}
|
|
205
332
|
const entryContent = fs.readFileSync(entry, "utf8");
|
|
206
333
|
|
|
207
|
-
// Read state
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
334
|
+
// Read state only when explicitly provided; omitted state is handled by WASM as `{}`.
|
|
335
|
+
let stateContent;
|
|
336
|
+
if (stateWasProvided) {
|
|
337
|
+
if (stateFile === undefined || stateFile === "" || !fs.existsSync(stateFile)) {
|
|
338
|
+
const stateFileLabel =
|
|
339
|
+
stateFile === undefined || stateFile === ""
|
|
340
|
+
? "(no path provided)"
|
|
341
|
+
: `"${stateFile}"`;
|
|
342
|
+
process.stderr.write(`Error: State file ${stateFileLabel} not found.\n`);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
stateContent = fs.readFileSync(stateFile, "utf8");
|
|
211
346
|
}
|
|
212
|
-
const stateContent = fs.readFileSync(stateFile, "utf8");
|
|
213
347
|
|
|
214
348
|
// Render
|
|
215
349
|
let rendered;
|
|
216
350
|
if (Object.keys(templatesMap).length > 0) {
|
|
217
|
-
rendered = wasm.render_entry_with_templates(
|
|
351
|
+
rendered = wasm.render_entry_with_templates(
|
|
352
|
+
entryContent,
|
|
353
|
+
JSON.stringify(templatesMap),
|
|
354
|
+
stateContent,
|
|
355
|
+
attributeNameStrategy || "",
|
|
356
|
+
);
|
|
218
357
|
} else {
|
|
219
358
|
rendered = wasm.render(entryContent, stateContent);
|
|
220
359
|
}
|
|
@@ -234,7 +373,17 @@ async function main() {
|
|
|
234
373
|
' Separate multiple patterns with commas.\n' +
|
|
235
374
|
' --output="output.html" Output file path (default: output.html)\n' +
|
|
236
375
|
' --entry="index.html" Entry HTML template file (default: index.html)\n' +
|
|
237
|
-
' --state="state.json" State JSON file
|
|
376
|
+
' --state="state.json" State JSON file. If omitted, rendering uses\n' +
|
|
377
|
+
' an empty state object.\n' +
|
|
378
|
+
' --attribute-name-strategy="camelCase"\n' +
|
|
379
|
+
' Strategy for mapping attribute names to property names.\n' +
|
|
380
|
+
' "camelCase" (default) or "none".\n' +
|
|
381
|
+
' --config="<path>" Path to a fast-build config JSON file.\n' +
|
|
382
|
+
' Defaults to "fast-build.config.json" in the\n' +
|
|
383
|
+
' current directory if it exists. File paths in\n' +
|
|
384
|
+
' the config are resolved relative to the config\n' +
|
|
385
|
+
' file\'s directory. CLI arguments take precedence\n' +
|
|
386
|
+
' over config file values.\n'
|
|
238
387
|
);
|
|
239
388
|
return;
|
|
240
389
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/fast-build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "CLI and Node.js API for server-side rendering of FAST declarative HTML templates.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Microsoft",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"wasm/microsoft_fast_build_bg.wasm.d.ts"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "cargo build --manifest-path ../../crates/microsoft-fast-build/Cargo.toml && wasm-pack build --target nodejs ../../crates/microsoft-fast-build --out-dir ../../packages/fast-build/wasm"
|
|
31
|
+
"build": "cargo build --manifest-path ../../crates/microsoft-fast-build/Cargo.toml && wasm-pack build --target nodejs ../../crates/microsoft-fast-build --out-dir ../../packages/fast-build/wasm",
|
|
32
|
+
"test:node": "node --test test/config.test.js"
|
|
32
33
|
},
|
|
33
34
|
"engines": {
|
|
34
35
|
"node": ">=22.18.0"
|
|
@@ -3,34 +3,46 @@
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Parse all `<f-template>` elements from an HTML string.
|
|
6
|
-
* Returns a JSON array of
|
|
6
|
+
* Returns a JSON array of template metadata objects,
|
|
7
7
|
* one per `<f-template>` element found. `name` is `null` when the element has
|
|
8
8
|
* no `name` attribute.
|
|
9
9
|
*/
|
|
10
10
|
export function parse_f_templates(html: string): string;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Render a FAST HTML template with
|
|
13
|
+
* Render a FAST HTML template with an optional JSON state string.
|
|
14
|
+
* Omitted state is treated as an empty object.
|
|
14
15
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
15
16
|
*/
|
|
16
|
-
export function render(entry: string, state
|
|
17
|
+
export function render(entry: string, state?: string | null): string;
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Render the top-level **entry HTML** with custom element templates and
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Render the top-level **entry HTML** with custom element templates and an optional JSON state string.
|
|
21
|
+
* Omitted state is treated as an empty object.
|
|
22
|
+
* Custom elements found at the root level of `entry` build child state from the full root
|
|
23
|
+
* state with HTML attributes overlaid on top. For `{{binding}}` attributes on root custom
|
|
22
24
|
* elements, primitive results (`string`, `number`, and `bool`) are preserved in the rendered
|
|
23
|
-
* output, while non-primitive values (`array`, `object`, `null`) are stripped.
|
|
25
|
+
* output, while missing and non-primitive values (`array`, `object`, `null`) are stripped.
|
|
24
26
|
*
|
|
25
|
-
* `templates_json` is a JSON object mapping element names to their HTML template strings
|
|
27
|
+
* `templates_json` is a JSON object mapping element names to their HTML template strings,
|
|
28
|
+
* e.g. `{"my-button": "<template>...</template>"}`, or template metadata objects.
|
|
29
|
+
* `attribute_name_strategy` controls attribute-to-property mapping: `"camelCase"` (default)
|
|
30
|
+
* or `"none"`. Pass an empty string for the default.
|
|
26
31
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
27
32
|
*/
|
|
28
|
-
export function render_entry_with_templates(entry: string, templates_json: string, state
|
|
33
|
+
export function render_entry_with_templates(entry: string, templates_json: string, state?: string | null, attribute_name_strategy?: string | null): string;
|
|
29
34
|
|
|
30
35
|
/**
|
|
31
|
-
* Render a FAST HTML template with custom element templates and
|
|
36
|
+
* Render a FAST HTML template with custom element templates and an optional JSON state string.
|
|
37
|
+
* Omitted state is treated as an empty object.
|
|
38
|
+
*
|
|
39
|
+
* This preserves the original non-entry rendering semantics for this export.
|
|
40
|
+
* Use `render_entry_with_templates` for top-level entry HTML rendering.
|
|
41
|
+
*
|
|
32
42
|
* `templates_json` is a JSON object mapping element names to their HTML template strings,
|
|
33
|
-
* e.g. `{"my-button": "<template>...</template>"}
|
|
43
|
+
* e.g. `{"my-button": "<template>...</template>"}`, or template metadata objects.
|
|
44
|
+
* `attribute_name_strategy` controls attribute-to-property mapping: `"camelCase"` (default)
|
|
45
|
+
* or `"none"`. Pass an empty string for the default.
|
|
34
46
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
35
47
|
*/
|
|
36
|
-
export function render_with_templates(entry: string, templates_json: string, state
|
|
48
|
+
export function render_with_templates(entry: string, templates_json: string, state?: string | null, attribute_name_strategy?: string | null): string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Parse all `<f-template>` elements from an HTML string.
|
|
5
|
-
* Returns a JSON array of
|
|
5
|
+
* Returns a JSON array of template metadata objects,
|
|
6
6
|
* one per `<f-template>` element found. `name` is `null` when the element has
|
|
7
7
|
* no `name` attribute.
|
|
8
8
|
* @param {string} html
|
|
@@ -25,10 +25,11 @@ function parse_f_templates(html) {
|
|
|
25
25
|
exports.parse_f_templates = parse_f_templates;
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Render a FAST HTML template with
|
|
28
|
+
* Render a FAST HTML template with an optional JSON state string.
|
|
29
|
+
* Omitted state is treated as an empty object.
|
|
29
30
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
30
31
|
* @param {string} entry
|
|
31
|
-
* @param {string} state
|
|
32
|
+
* @param {string | null} [state]
|
|
32
33
|
* @returns {string}
|
|
33
34
|
*/
|
|
34
35
|
function render(entry, state) {
|
|
@@ -37,8 +38,8 @@ function render(entry, state) {
|
|
|
37
38
|
try {
|
|
38
39
|
const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
39
40
|
const len0 = WASM_VECTOR_LEN;
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
var ptr1 = isLikeNone(state) ? 0 : passStringToWasm0(state, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
42
|
+
var len1 = WASM_VECTOR_LEN;
|
|
42
43
|
const ret = wasm.render(ptr0, len0, ptr1, len1);
|
|
43
44
|
var ptr3 = ret[0];
|
|
44
45
|
var len3 = ret[1];
|
|
@@ -56,77 +57,94 @@ function render(entry, state) {
|
|
|
56
57
|
exports.render = render;
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
|
-
* Render the top-level **entry HTML** with custom element templates and
|
|
60
|
-
*
|
|
61
|
-
*
|
|
60
|
+
* Render the top-level **entry HTML** with custom element templates and an optional JSON state string.
|
|
61
|
+
* Omitted state is treated as an empty object.
|
|
62
|
+
* Custom elements found at the root level of `entry` build child state from the full root
|
|
63
|
+
* state with HTML attributes overlaid on top. For `{{binding}}` attributes on root custom
|
|
62
64
|
* elements, primitive results (`string`, `number`, and `bool`) are preserved in the rendered
|
|
63
|
-
* output, while non-primitive values (`array`, `object`, `null`) are stripped.
|
|
65
|
+
* output, while missing and non-primitive values (`array`, `object`, `null`) are stripped.
|
|
64
66
|
*
|
|
65
|
-
* `templates_json` is a JSON object mapping element names to their HTML template strings
|
|
67
|
+
* `templates_json` is a JSON object mapping element names to their HTML template strings,
|
|
68
|
+
* e.g. `{"my-button": "<template>...</template>"}`, or template metadata objects.
|
|
69
|
+
* `attribute_name_strategy` controls attribute-to-property mapping: `"camelCase"` (default)
|
|
70
|
+
* or `"none"`. Pass an empty string for the default.
|
|
66
71
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
67
72
|
* @param {string} entry
|
|
68
73
|
* @param {string} templates_json
|
|
69
|
-
* @param {string} state
|
|
74
|
+
* @param {string | null} [state]
|
|
75
|
+
* @param {string | null} [attribute_name_strategy]
|
|
70
76
|
* @returns {string}
|
|
71
77
|
*/
|
|
72
|
-
function render_entry_with_templates(entry, templates_json, state) {
|
|
73
|
-
let
|
|
74
|
-
let
|
|
78
|
+
function render_entry_with_templates(entry, templates_json, state, attribute_name_strategy) {
|
|
79
|
+
let deferred6_0;
|
|
80
|
+
let deferred6_1;
|
|
75
81
|
try {
|
|
76
82
|
const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
77
83
|
const len0 = WASM_VECTOR_LEN;
|
|
78
84
|
const ptr1 = passStringToWasm0(templates_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
79
85
|
const len1 = WASM_VECTOR_LEN;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
var
|
|
84
|
-
|
|
86
|
+
var ptr2 = isLikeNone(state) ? 0 : passStringToWasm0(state, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
87
|
+
var len2 = WASM_VECTOR_LEN;
|
|
88
|
+
var ptr3 = isLikeNone(attribute_name_strategy) ? 0 : passStringToWasm0(attribute_name_strategy, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
89
|
+
var len3 = WASM_VECTOR_LEN;
|
|
90
|
+
const ret = wasm.render_entry_with_templates(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
91
|
+
var ptr5 = ret[0];
|
|
92
|
+
var len5 = ret[1];
|
|
85
93
|
if (ret[3]) {
|
|
86
|
-
|
|
94
|
+
ptr5 = 0; len5 = 0;
|
|
87
95
|
throw takeFromExternrefTable0(ret[2]);
|
|
88
96
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return getStringFromWasm0(
|
|
97
|
+
deferred6_0 = ptr5;
|
|
98
|
+
deferred6_1 = len5;
|
|
99
|
+
return getStringFromWasm0(ptr5, len5);
|
|
92
100
|
} finally {
|
|
93
|
-
wasm.__wbindgen_free(
|
|
101
|
+
wasm.__wbindgen_free(deferred6_0, deferred6_1, 1);
|
|
94
102
|
}
|
|
95
103
|
}
|
|
96
104
|
exports.render_entry_with_templates = render_entry_with_templates;
|
|
97
105
|
|
|
98
106
|
/**
|
|
99
|
-
* Render a FAST HTML template with custom element templates and
|
|
107
|
+
* Render a FAST HTML template with custom element templates and an optional JSON state string.
|
|
108
|
+
* Omitted state is treated as an empty object.
|
|
109
|
+
*
|
|
110
|
+
* This preserves the original non-entry rendering semantics for this export.
|
|
111
|
+
* Use `render_entry_with_templates` for top-level entry HTML rendering.
|
|
112
|
+
*
|
|
100
113
|
* `templates_json` is a JSON object mapping element names to their HTML template strings,
|
|
101
|
-
* e.g. `{"my-button": "<template>...</template>"}
|
|
114
|
+
* e.g. `{"my-button": "<template>...</template>"}`, or template metadata objects.
|
|
115
|
+
* `attribute_name_strategy` controls attribute-to-property mapping: `"camelCase"` (default)
|
|
116
|
+
* or `"none"`. Pass an empty string for the default.
|
|
102
117
|
* Returns the rendered HTML or throws a JavaScript error.
|
|
103
118
|
* @param {string} entry
|
|
104
119
|
* @param {string} templates_json
|
|
105
|
-
* @param {string} state
|
|
120
|
+
* @param {string | null} [state]
|
|
121
|
+
* @param {string | null} [attribute_name_strategy]
|
|
106
122
|
* @returns {string}
|
|
107
123
|
*/
|
|
108
|
-
function render_with_templates(entry, templates_json, state) {
|
|
109
|
-
let
|
|
110
|
-
let
|
|
124
|
+
function render_with_templates(entry, templates_json, state, attribute_name_strategy) {
|
|
125
|
+
let deferred6_0;
|
|
126
|
+
let deferred6_1;
|
|
111
127
|
try {
|
|
112
128
|
const ptr0 = passStringToWasm0(entry, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
113
129
|
const len0 = WASM_VECTOR_LEN;
|
|
114
130
|
const ptr1 = passStringToWasm0(templates_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
115
131
|
const len1 = WASM_VECTOR_LEN;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
var
|
|
120
|
-
|
|
132
|
+
var ptr2 = isLikeNone(state) ? 0 : passStringToWasm0(state, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
133
|
+
var len2 = WASM_VECTOR_LEN;
|
|
134
|
+
var ptr3 = isLikeNone(attribute_name_strategy) ? 0 : passStringToWasm0(attribute_name_strategy, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
135
|
+
var len3 = WASM_VECTOR_LEN;
|
|
136
|
+
const ret = wasm.render_with_templates(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
137
|
+
var ptr5 = ret[0];
|
|
138
|
+
var len5 = ret[1];
|
|
121
139
|
if (ret[3]) {
|
|
122
|
-
|
|
140
|
+
ptr5 = 0; len5 = 0;
|
|
123
141
|
throw takeFromExternrefTable0(ret[2]);
|
|
124
142
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return getStringFromWasm0(
|
|
143
|
+
deferred6_0 = ptr5;
|
|
144
|
+
deferred6_1 = len5;
|
|
145
|
+
return getStringFromWasm0(ptr5, len5);
|
|
128
146
|
} finally {
|
|
129
|
-
wasm.__wbindgen_free(
|
|
147
|
+
wasm.__wbindgen_free(deferred6_0, deferred6_1, 1);
|
|
130
148
|
}
|
|
131
149
|
}
|
|
132
150
|
exports.render_with_templates = render_with_templates;
|
|
@@ -168,6 +186,10 @@ function getUint8ArrayMemory0() {
|
|
|
168
186
|
return cachedUint8ArrayMemory0;
|
|
169
187
|
}
|
|
170
188
|
|
|
189
|
+
function isLikeNone(x) {
|
|
190
|
+
return x === undefined || x === null;
|
|
191
|
+
}
|
|
192
|
+
|
|
171
193
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
172
194
|
if (realloc === undefined) {
|
|
173
195
|
const buf = cachedTextEncoder.encode(arg);
|
|
Binary file
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
export const memory: WebAssembly.Memory;
|
|
4
4
|
export const parse_f_templates: (a: number, b: number) => [number, number];
|
|
5
5
|
export const render: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
6
|
-
export const render_entry_with_templates: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
|
|
7
|
-
export const render_with_templates: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
|
|
6
|
+
export const render_entry_with_templates: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number, number, number];
|
|
7
|
+
export const render_with_templates: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number, number, number];
|
|
8
8
|
export const __wbindgen_externrefs: WebAssembly.Table;
|
|
9
9
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
10
10
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|