@microsoft/fast-test-harness 0.1.0 → 0.3.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 (37) hide show
  1. package/README.md +25 -19
  2. package/dist/dts/build/generate-templates.d.ts +35 -0
  3. package/dist/dts/build/generate-templates.d.ts.map +1 -1
  4. package/dist/dts/build/generate-webui-templates.d.ts +14 -1
  5. package/dist/dts/build/generate-webui-templates.d.ts.map +1 -1
  6. package/dist/dts/fixtures/csr-fixture.d.ts +28 -0
  7. package/dist/dts/fixtures/csr-fixture.d.ts.map +1 -1
  8. package/dist/dts/fixtures/ssr-fixture.d.ts +19 -0
  9. package/dist/dts/fixtures/ssr-fixture.d.ts.map +1 -1
  10. package/dist/dts/ssr/render.d.ts +11 -16
  11. package/dist/dts/ssr/render.d.ts.map +1 -1
  12. package/dist/esm/build/generate-templates.js +62 -2
  13. package/dist/esm/build/generate-webui-templates.js +9 -26
  14. package/dist/esm/fixtures/ssr-fixture.js +19 -1
  15. package/dist/esm/ssr/render.js +48 -104
  16. package/package.json +26 -15
  17. package/dist/dts/build/dom-shim.test.d.ts +0 -2
  18. package/dist/dts/build/dom-shim.test.d.ts.map +0 -1
  19. package/dist/dts/build/generate-stylesheets.test.d.ts +0 -2
  20. package/dist/dts/build/generate-stylesheets.test.d.ts.map +0 -1
  21. package/dist/dts/build/generate-templates.test.d.ts +0 -2
  22. package/dist/dts/build/generate-templates.test.d.ts.map +0 -1
  23. package/dist/dts/build/generate-webui-templates.test.d.ts +0 -2
  24. package/dist/dts/build/generate-webui-templates.test.d.ts.map +0 -1
  25. package/dist/dts/fixtures/csr-fixture.pw.spec.d.ts +0 -2
  26. package/dist/dts/fixtures/csr-fixture.pw.spec.d.ts.map +0 -1
  27. package/dist/dts/fixtures/ssr-fixture.pw.spec.d.ts +0 -2
  28. package/dist/dts/fixtures/ssr-fixture.pw.spec.d.ts.map +0 -1
  29. package/dist/dts/ssr/render.test.d.ts +0 -2
  30. package/dist/dts/ssr/render.test.d.ts.map +0 -1
  31. package/dist/esm/build/dom-shim.test.js +0 -202
  32. package/dist/esm/build/generate-stylesheets.test.js +0 -74
  33. package/dist/esm/build/generate-templates.test.js +0 -231
  34. package/dist/esm/build/generate-webui-templates.test.js +0 -179
  35. package/dist/esm/fixtures/csr-fixture.pw.spec.js +0 -137
  36. package/dist/esm/fixtures/ssr-fixture.pw.spec.js +0 -189
  37. package/dist/esm/ssr/render.test.js +0 -236
package/README.md CHANGED
@@ -4,6 +4,11 @@
4
4
 
5
5
  The `fast-test-harness` package is a Playwright testing harness for FAST Element web components with CSR and SSR support.
6
6
 
7
+ ## Requirements
8
+
9
+ - Node.js 22.18 or later
10
+ - Playwright 1.56 or later
11
+
7
12
  ## Installation
8
13
 
9
14
  To install `fast-test-harness` using `npm`:
@@ -12,6 +17,21 @@ To install `fast-test-harness` using `npm`:
12
17
  npm install --save-dev @microsoft/fast-test-harness
13
18
  ```
14
19
 
20
+ ## Test directory setup
21
+
22
+ The harness serves a Vite dev server from a `test/` directory in your project. CSR and SSR modes use different entry points from the same directory.
23
+
24
+ ```
25
+ test/
26
+ ├── index.html # CSR: loads main.ts
27
+ ├── ssr.html # SSR: template with comment placeholders
28
+ ├── vite.config.ts # Vite config (shared by both modes)
29
+ └── src/
30
+ ├── main.ts # CSR: registers components, applies theme
31
+ ├── entry-client.ts # SSR: registers components for hydration
32
+ └── entry-server.ts # SSR: exports render() for fixture generation
33
+ ```
34
+
15
35
  ## Writing tests
16
36
 
17
37
  Import `test` and `expect` from the harness. Configure the component tag name with `test.use()`, then call `fastPage.setTemplate()` in each test to render it.
@@ -67,21 +87,6 @@ await expect(element).toHaveCustomState("checked");
67
87
  | `waitFor` | `string[]` | `[]` | Additional elements to wait for before testing |
68
88
  | `ssr` | `boolean` | `false` | Use SSR mode (or set `PLAYWRIGHT_TEST_SSR=true`) |
69
89
 
70
- ## Test directory setup
71
-
72
- The harness serves a Vite dev server from a `test/` directory in your project. CSR and SSR modes use different entry points from the same directory.
73
-
74
- ```
75
- test/
76
- ├── index.html # CSR: loads main.ts
77
- ├── ssr.html # SSR: template with comment placeholders
78
- ├── vite.config.ts # Vite config (shared by both modes)
79
- └── src/
80
- ├── main.ts # CSR: registers components, applies theme
81
- ├── entry-client.ts # SSR: registers components for hydration
82
- └── entry-server.ts # SSR: exports render() for fixture generation
83
- ```
84
-
85
90
  ### CSR files
86
91
 
87
92
  **`index.html`** loads a script that registers your components:
@@ -248,7 +253,7 @@ CLI flags take precedence over environment variables.
248
253
 
249
254
  | Option | Type | Description |
250
255
  |--------|------|-------------|
251
- | `tagPrefix` | `string` | Tag name prefix for custom elements (e.g., `"fluent"`, `"mai"`) |
256
+ | `tagPrefix` | `string` | Tag name prefix for custom elements (e.g., `"fluent"`, `"contoso"`) |
252
257
  | `packageName` | `string?` | Monolithic package name — scans subdirectories for component artifacts. Mutually exclusive with `components`. |
253
258
  | `components` | `ComponentRegistration[]?` | Explicit list of per-component packages. Mutually exclusive with `packageName`. |
254
259
  | `distDir` | `string?` | Artifact directory relative to the package root (default: `"dist/esm"`). Only used with `packageName`. |
@@ -258,10 +263,11 @@ CLI flags take precedence over environment variables.
258
263
 
259
264
  | Specifier | Contents |
260
265
  |-----------|----------|
261
- | `@microsoft/fast-test-harness` | `test`, `expect`, `CSRFixture`, `SSRFixture`, `createSSRRenderer`, build utilities |
266
+ | `@microsoft/fast-test-harness` | `test`, `expect`, `CSRFixture`, `SSRFixture`, `toHaveCustomState`, `installDomShim`, `createSSRRenderer` |
267
+ | `@microsoft/fast-test-harness/build/*.js` | `installDomShim`, `generateStylesheets`, `generateFTemplates`, `generateWebuiTemplates`, `definitionAsyncResolver`, `shadowOptionsToAttributes`, `ShadowOptionsResolver` |
268
+ | `@microsoft/fast-test-harness/fixtures/*.js` | `CSRFixture`, `SSRFixture`, `toHaveCustomState`, extended `test` and `expect` |
269
+ | `@microsoft/fast-test-harness/ssr/render.js` | `createSSRRenderer`, `renderTemplate`, `buildEntryHtml`, `buildState` |
262
270
  | `@microsoft/fast-test-harness/server.mjs` | `startServer` |
263
- | `@microsoft/fast-test-harness/ssr/render.js` | `createSSRRenderer`, `ComponentRegistration`, `RenderResult`, `SSRRendererOptions` |
264
- | `@microsoft/fast-test-harness/build/*.js` | `installDomShim`, `generateStylesheets`, `generateFTemplates`, `generateWebuiTemplates` |
265
271
  | `@microsoft/fast-test-harness/playwright.config.mjs` | Shared Playwright configuration |
266
272
  | `@microsoft/fast-test-harness/vite.config.mjs` | Shared Vite configuration |
267
273
  | `@microsoft/fast-test-harness/public/*` | Static assets (base CSS) |
@@ -45,7 +45,37 @@ export interface GenerateFTemplatesOptions {
45
45
  * Optional formatter function applied to generated HTML before writing.
46
46
  */
47
47
  format?: (html: string, filePath: string) => string | Promise<string>;
48
+ /**
49
+ * Resolves shadow DOM options for a given template module path.
50
+ * Returns a `shadowOptions` object (e.g. `{ delegatesFocus: true }`) or
51
+ * `undefined` if the component has no special shadow options.
52
+ *
53
+ * Defaults to {@link definitionAsyncResolver}, which loads a companion
54
+ * `*.definition-async.js` module next to each template. Set to `null`
55
+ * to disable shadow options resolution.
56
+ *
57
+ * @default definitionAsyncResolver
58
+ */
59
+ resolveShadowOptions?: ShadowOptionsResolver | null;
48
60
  }
61
+ /**
62
+ * A function that resolves shadow DOM options for a template module.
63
+ * Receives the absolute path to the compiled `*.template.js` file and
64
+ * returns shadow options or `undefined`.
65
+ */
66
+ export type ShadowOptionsResolver = (templateJsPath: string) => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
67
+ /**
68
+ * Convention-based resolver that loads a companion `*.definition-async.js`
69
+ * module next to the template module and returns its `shadowOptions`.
70
+ *
71
+ * For a template at `dist/textarea/textarea.template.js`, this looks for
72
+ * `dist/textarea/textarea.definition-async.js`.
73
+ *
74
+ * This is the default resolver used by {@link generateFTemplates} and
75
+ * {@link generateWebuiTemplates} when `resolveShadowOptions` is not
76
+ * specified.
77
+ */
78
+ export declare function definitionAsyncResolver(templateJsPath: string): Promise<Record<string, unknown> | undefined>;
49
79
  export interface ViewTemplate {
50
80
  html: string | HTMLTemplateElement;
51
81
  factories: Record<string, Factory>;
@@ -64,6 +94,11 @@ interface Factory {
64
94
  * f-template HTML string.
65
95
  */
66
96
  export declare function convertTemplate(viewTemplate: ViewTemplate, componentName: string): string | null;
97
+ /**
98
+ * Convert a `shadowOptions` object (e.g. `{ delegatesFocus: true }`) into
99
+ * DSD template attribute entries (e.g. `{ shadowrootdelegatesfocus: "" }`).
100
+ */
101
+ export declare function shadowOptionsToAttributes(shadowOptions: Record<string, unknown> | undefined): Record<string, string>;
67
102
  export declare function generateFTemplates(options?: GenerateFTemplatesOptions): Promise<void>;
68
103
  export {};
69
104
  //# sourceMappingURL=generate-templates.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-templates.d.ts","sourceRoot":"","sources":["../../../src/build/generate-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA8BH,MAAM,WAAW,yBAAyB;IACtC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,OAAO;IACb,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE;QACV,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;KACrC,CAAC;CACL;AAmFD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CA4Hf;AAED,wBAAsB,kBAAkB,CACpC,OAAO,GAAE,yBAA8B,GACxC,OAAO,CAAC,IAAI,CAAC,CAkEf"}
1
+ {"version":3,"file":"generate-templates.d.ts","sourceRoot":"","sources":["../../../src/build/generate-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA8BH,MAAM,WAAW,yBAAyB;IACtC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtE;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACvD;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAChC,cAAc,EAAE,MAAM,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;AAExF;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CACzC,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAY9C;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,OAAO;IACb,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE;QACV,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;KACrC,CAAC;CACL;AAmFD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CA4Hf;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACrC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACnD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBxB;AAED,wBAAsB,kBAAkB,CACpC,OAAO,GAAE,yBAA8B,GACxC,OAAO,CAAC,IAAI,CAAC,CAgFf"}
@@ -14,9 +14,10 @@
14
14
  * ```ts
15
15
  * import { generateWebuiTemplates } from "@microsoft/fast-test-harness/build/generate-webui-templates.js";
16
16
  *
17
- * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "mai" });
17
+ * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "contoso" });
18
18
  * ```
19
19
  */
20
+ import { type ShadowOptionsResolver } from "./generate-templates.js";
20
21
  export interface GenerateWebuiTemplatesOptions {
21
22
  /**
22
23
  * Root directory of the package. Defaults to `process.cwd()`.
@@ -49,6 +50,18 @@ export interface GenerateWebuiTemplatesOptions {
49
50
  * Optional formatter function applied to generated HTML before writing.
50
51
  */
51
52
  format?: (html: string, filePath: string) => string | Promise<string>;
53
+ /**
54
+ * Resolves shadow DOM options for a given template module path.
55
+ * Returns a `shadowOptions` object (e.g. `{ delegatesFocus: true }`) or
56
+ * `undefined` if the component has no special shadow options.
57
+ *
58
+ * Defaults to {@link definitionAsyncResolver}, which loads a companion
59
+ * `*.definition-async.js` module next to each template. Set to `null`
60
+ * to disable shadow options resolution.
61
+ *
62
+ * @default definitionAsyncResolver
63
+ */
64
+ resolveShadowOptions?: ShadowOptionsResolver | null;
52
65
  }
53
66
  export declare function generateWebuiTemplates(options?: GenerateWebuiTemplatesOptions): Promise<void>;
54
67
  //# sourceMappingURL=generate-webui-templates.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-webui-templates.d.ts","sourceRoot":"","sources":["../../../src/build/generate-webui-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgBH,MAAM,WAAW,6BAA6B;IAC1C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACzE;AAuED,wBAAsB,sBAAsB,CACxC,OAAO,GAAE,6BAAkC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAmEf"}
1
+ {"version":3,"file":"generate-webui-templates.d.ts","sourceRoot":"","sources":["../../../src/build/generate-webui-templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,EAGH,KAAK,qBAAqB,EAG7B,MAAM,yBAAyB,CAAC;AAQjC,MAAM,WAAW,6BAA6B;IAC1C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtE;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACvD;AA4CD,wBAAsB,sBAAsB,CACxC,OAAO,GAAE,6BAAkC,GAC5C,OAAO,CAAC,IAAI,CAAC,CA6Ef"}
@@ -1,6 +1,17 @@
1
1
  import type { Locator, Page } from "@playwright/test";
2
2
  export type ThemeTokens = Record<string, string | number | boolean>;
3
+ /**
4
+ * The initial attributes for the fixture's template, where boolean attributes are
5
+ * represented as `true` and omitted when `false`. This allows for a more intuitive
6
+ * configuration of boolean attributes in the template options.
7
+ */
3
8
  export type InitialTemplateAttributes = Record<string, string | true>;
9
+ /**
10
+ * The attributes for the fixture's template, where boolean attributes can be represented as `true`
11
+ * or `false`. When `true`, the attribute will be included without a value (e.g., `disabled`), and
12
+ * when `false`, the attribute will be omitted entirely. This type is used for updating the
13
+ * template, allowing for both adding and removing boolean attributes.
14
+ */
4
15
  export type TemplateAttributes = Record<string, string | boolean>;
5
16
  /**
6
17
  * The options for configuring the fixture's template.
@@ -9,6 +20,10 @@ export type InitialTemplateOptions = {
9
20
  attributes?: InitialTemplateAttributes;
10
21
  innerHTML?: string;
11
22
  };
23
+ /**
24
+ * The options for updating the fixture's template, where `attributes` can include boolean values to
25
+ * add or remove attributes from the element.
26
+ */
12
27
  export type FixtureOptions = Omit<InitialTemplateOptions, "attributes"> & {
13
28
  attributes?: TemplateAttributes;
14
29
  };
@@ -36,6 +51,19 @@ export declare class CSRFixture {
36
51
  protected readonly innerHTML: string;
37
52
  /**
38
53
  * Additional custom elements to wait for before running the test.
54
+ *
55
+ * @remarks
56
+ * This is useful for fixtures that depend on multiple custom elements being defined
57
+ * and stable before the test can run. By specifying additional tag names here, the
58
+ * fixture will wait for these elements to be defined before proceeding. Ensure that
59
+ * any elements specified here are included on the page and properly defined to
60
+ * prevent test timeouts.
61
+ *
62
+ * @example
63
+ * test.use({
64
+ * tagName: "fast-dropdown",
65
+ * waitFor: ["fast-listbox", "fast-option"],
66
+ * });
39
67
  */
40
68
  protected readonly waitFor: string[];
41
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"csr-fixture.d.ts","sourceRoot":"","sources":["../../../src/fixtures/csr-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEpE,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,sBAAsB,EAAE,YAAY,CAAC,GAAG;IACtE,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,MAAM,CAAC;AAEhE;;GAEG;AACH,qBAAa,UAAU;aA8BC,IAAI,EAAE,IAAI;IA7B9B;;OAEG;IACH,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEnC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAErC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAErC;;;;;;;OAOG;IACH,YACoB,IAAI,EAAE,IAAI,EAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAM,EAAO,EAMzB;IAED;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED;;;;OAIG;IACG,IAAI,CAAC,GAAG,GAAE,MAAY,iBAE3B;IAED;;;;;OAKG;IACG,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CASpD;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;IAkBvB;;;;;;;;;;;;;;OAcG;IACG,WAAW,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBtE;IAED;;;;;OAKG;IACH,UAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBhD;IAED;;;;;;OAMG;IACG,cAAc,CAChB,OAAO,EAAE,MAAM,GAAG,OAAO,EACzB,OAAO,EAAE,cAAc,GACxB,OAAO,CAAC,IAAI,CAAC,CAuBf;IAED;;;;;;OAMG;IACG,oBAAoB,CACtB,OAAO,GAAE,MAAqB,EAC9B,GAAG,QAAQ,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,IAAI,CAAC,CAUf;CACJ"}
1
+ {"version":3,"file":"csr-fixture.d.ts","sourceRoot":"","sources":["../../../src/fixtures/csr-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,sBAAsB,EAAE,YAAY,CAAC,GAAG;IACtE,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,MAAM,CAAC;AAEhE;;GAEG;AACH,qBAAa,UAAU;aA2CC,IAAI,EAAE,IAAI;IA1C9B;;OAEG;IACH,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEnC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAErC;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAErC;;;;;;;OAOG;IACH,YACoB,IAAI,EAAE,IAAI,EAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAM,EAAO,EAMzB;IAED;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;IAED;;;;OAIG;IACG,IAAI,CAAC,GAAG,GAAE,MAAY,iBAE3B;IAED;;;;;OAKG;IACG,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CASpD;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;IAkBvB;;;;;;;;;;;;;;OAcG;IACG,WAAW,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBtE;IAED;;;;;OAKG;IACH,UAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBhD;IAED;;;;;;OAMG;IACG,cAAc,CAChB,OAAO,EAAE,MAAM,GAAG,OAAO,EACzB,OAAO,EAAE,cAAc,GACxB,OAAO,CAAC,IAAI,CAAC,CAuBf;IAED;;;;;;OAMG;IACG,oBAAoB,CACtB,OAAO,GAAE,MAAqB,EAC9B,GAAG,QAAQ,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,IAAI,CAAC,CAUf;CACJ"}
@@ -11,6 +11,25 @@ export declare class SSRFixture extends CSRFixture {
11
11
  * Whether the template has been rendered.
12
12
  */
13
13
  private templateRendered;
14
+ /**
15
+ * The internal reference to the generated fixture URL.
16
+ */
17
+ private _url?;
18
+ /**
19
+ * The URL of the generated SSR fixture page. This is set after {@link setTemplate}
20
+ * is called and the page navigates to the generated fixture.
21
+ */
22
+ get url(): string | undefined;
23
+ /**
24
+ * Creates an instance of the SSRFixture.
25
+ *
26
+ * @param page - The Playwright page object.
27
+ * @param tagName - The tag name of the custom element.
28
+ * @param innerHTML - The inner HTML of the custom element.
29
+ * @param waitFor - Additional custom elements to wait for.
30
+ * @param testId - The test ID for the SSR fixture.
31
+ * @param testTitle - The test title for the SSR fixture.
32
+ */
14
33
  constructor(page: Page, tagName: string, innerHTML?: string, waitFor?: string[], testId?: string, testTitle?: string);
15
34
  /**
16
35
  * Buffers style tags added before {@link setTemplate} so they can be
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-fixture.d.ts","sourceRoot":"","sources":["../../../src/fixtures/ssr-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,qBAAa,UAAW,SAAQ,UAAU;IAgBlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAhB/B;;OAEG;IACH,OAAO,CAAC,aAAa,CAA4C;IAEjE;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAS;IAEjC,YACI,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAW,EACtB,OAAO,GAAE,MAAM,EAAO,EACL,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAGtC;IAED;;;;;;;OAOG;IACY,WAAW,CACtB,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAMf;IAED;;;;;;;;;;;OAWG;IACY,WAAW,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoE/E;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;CAqB1B"}
1
+ {"version":3,"file":"ssr-fixture.d.ts","sourceRoot":"","sources":["../../../src/fixtures/ssr-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,qBAAa,UAAW,SAAQ,UAAU;IAuClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAvC/B;;OAEG;IACH,OAAO,CAAC,aAAa,CAA4C;IAEjE;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAS;IAEjC;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,CAAS;IAEtB;;;OAGG;IACH,IAAW,GAAG,IAAI,MAAM,GAAG,SAAS,CAEnC;IAED;;;;;;;;;OASG;IACH,YACI,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAW,EACtB,OAAO,GAAE,MAAM,EAAO,EACL,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAGtC;IAED;;;;;;;OAOG;IACY,WAAW,CACtB,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAMf;IAED;;;;;;;;;;;OAWG;IACY,WAAW,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE/E;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;CAqB1B"}
@@ -21,10 +21,10 @@
21
21
  * flat exports.
22
22
  * ```ts
23
23
  * const { render } = createSSRRenderer({
24
- * tagPrefix: "mai",
24
+ * tagPrefix: "contoso",
25
25
  * components: [
26
- * { name: "button", packageName: "@mai-ui/button" },
27
- * { name: "checkbox", packageName: "@mai-ui/checkbox" },
26
+ * { name: "button", packageName: "@contoso/button" },
27
+ * { name: "checkbox", packageName: "@contoso/checkbox" },
28
28
  * ],
29
29
  * });
30
30
  * ```
@@ -37,25 +37,25 @@ export interface ComponentRegistration {
37
37
  name: string;
38
38
  /**
39
39
  * The npm package name for this component
40
- * (e.g., "@mai-ui/button").
40
+ * (e.g., "@contoso/button").
41
41
  */
42
42
  packageName: string;
43
43
  }
44
44
  export interface SSRRendererOptions {
45
45
  /**
46
- * Tag name prefix for custom elements (e.g., "fluent", "mai").
46
+ * Tag name prefix for custom elements (e.g., "fluent", "contoso").
47
47
  */
48
48
  tagPrefix: string;
49
49
  /**
50
50
  * The npm package name used to resolve component build artifacts
51
51
  * when all components live in subdirectories of a single package
52
- * (Fluent-style). Mutually exclusive with `components`.
52
+ * (monolithic layout). Mutually exclusive with `components`.
53
53
  */
54
54
  packageName?: string;
55
55
  /**
56
- * Explicit list of per-component packages (MAI-style). Each entry
57
- * maps a component name to its npm package. Mutually exclusive
58
- * with `packageName`.
56
+ * Explicit list of per-component packages. Each entry maps a
57
+ * component name to its npm package. Mutually exclusive with
58
+ * `packageName`.
59
59
  */
60
60
  components?: ComponentRegistration[];
61
61
  /**
@@ -82,11 +82,6 @@ export interface RenderResult {
82
82
  /** Preload link tags (empty string when using fast-build). */
83
83
  preloadLinks: string;
84
84
  }
85
- /**
86
- * Parse a JavaScript default value string from CEM into a JSON-safe value.
87
- * @internal
88
- */
89
- export declare function parseDefaultValue(raw: string): unknown;
90
85
  /**
91
86
  * Replace the `{{styles}}` placeholder in an f-template with a
92
87
  * stylesheet `<link>` tag. Falls back to injecting after the opening
@@ -117,9 +112,9 @@ export declare function buildState(queryObj: Record<string, string>): Record<str
117
112
  *
118
113
  * Supports two modes:
119
114
  * - **`packageName`**: Scans a monolithic package's dist directory for
120
- * components in subdirectories (Fluent-style).
115
+ * components in subdirectories (monolithic layout).
121
116
  * - **`components`**: Uses an explicit list of per-component packages
122
- * with flat exports (MAI-style).
117
+ * with flat exports.
123
118
  */
124
119
  export declare function createSSRRenderer(options: SSRRendererOptions): {
125
120
  render: (queryObj: Record<string, string>) => RenderResult;
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/ssr/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAOH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAErC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;CACxB;AA4CD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA6BtD;AA0FD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAwCvE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAC5D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,YAAY,CAAC;CAC9D,CA4IA"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/ssr/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAOH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAErC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;CACxB;AAqFD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAwCvE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAC5D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,YAAY,CAAC;CAC9D,CA6KA"}
@@ -29,6 +29,30 @@ function wrapDefaultExpression(expression) {
29
29
  function attributeDirective(name, value) {
30
30
  return `${attributeDirectivePrefix}${name}="${wrapClientExpression(value)}"`;
31
31
  }
32
+ /**
33
+ * Convention-based resolver that loads a companion `*.definition-async.js`
34
+ * module next to the template module and returns its `shadowOptions`.
35
+ *
36
+ * For a template at `dist/textarea/textarea.template.js`, this looks for
37
+ * `dist/textarea/textarea.definition-async.js`.
38
+ *
39
+ * This is the default resolver used by {@link generateFTemplates} and
40
+ * {@link generateWebuiTemplates} when `resolveShadowOptions` is not
41
+ * specified.
42
+ */
43
+ export async function definitionAsyncResolver(templateJsPath) {
44
+ const dir = path.dirname(templateJsPath);
45
+ const base = path.basename(templateJsPath, ".template.js");
46
+ const defAsyncPath = path.resolve(dir, `${base}.definition-async.js`);
47
+ try {
48
+ const mod = await import(pathToFileURL(defAsyncPath).href);
49
+ const definition = mod.definition ?? mod.default;
50
+ return definition?.shadowOptions;
51
+ }
52
+ catch {
53
+ return undefined;
54
+ }
55
+ }
32
56
  /**
33
57
  * Extract a readable binding expression from a factory's evaluate function.
34
58
  */
@@ -195,10 +219,33 @@ export function convertTemplate(viewTemplate, componentName) {
195
219
  // so the test harness can substitute it with a <link rel="stylesheet"> at
196
220
  // render time. Harness fallback auto-injects if the marker is missing, but
197
221
  // emitting it explicitly keeps generated output consistent with hand-authored
198
- // f-templates (see MAI core components).
222
+ // f-templates.
199
223
  fInner = fInner.replace(/(<template[^>]*>)/, `$1${stylesMarker}`);
200
224
  return `<f-template name="${componentName}" shadowrootmode="open">\n${fInner}\n</f-template>\n`;
201
225
  }
226
+ /**
227
+ * Convert a `shadowOptions` object (e.g. `{ delegatesFocus: true }`) into
228
+ * DSD template attribute entries (e.g. `{ shadowrootdelegatesfocus: "" }`).
229
+ */
230
+ export function shadowOptionsToAttributes(shadowOptions) {
231
+ const attrs = {};
232
+ if (!shadowOptions) {
233
+ return attrs;
234
+ }
235
+ for (const [key, value] of Object.entries(shadowOptions)) {
236
+ if (key === "mode") {
237
+ continue;
238
+ }
239
+ const attrName = `shadowroot${key.toLowerCase()}`;
240
+ if (value === true) {
241
+ attrs[attrName] = "";
242
+ }
243
+ else if (typeof value === "string" && value !== "") {
244
+ attrs[attrName] = value;
245
+ }
246
+ }
247
+ return attrs;
248
+ }
202
249
  export async function generateFTemplates(options = {}) {
203
250
  installDomShim();
204
251
  const cwd = options.cwd ?? process.cwd();
@@ -220,7 +267,19 @@ export async function generateFTemplates(options = {}) {
220
267
  if (!fTemplateHtml) {
221
268
  continue;
222
269
  }
270
+ // Resolve shadow options and inject them into the <f-template> tag.
271
+ const resolver = options.resolveShadowOptions === null
272
+ ? undefined
273
+ : (options.resolveShadowOptions ?? definitionAsyncResolver);
274
+ const shadowOpts = resolver ? await resolver(jsFilePath) : undefined;
275
+ const shadowAttrs = shadowOptionsToAttributes(shadowOpts);
223
276
  let html = fTemplateHtml;
277
+ if (Object.keys(shadowAttrs).length > 0) {
278
+ const extraAttrs = Object.entries(shadowAttrs)
279
+ .map(([k, v]) => (v ? ` ${k}="${v}"` : ` ${k}`))
280
+ .join("");
281
+ html = html.replace(/(<f-template[^>]*)(>)/, `$1${extraAttrs}$2`);
282
+ }
224
283
  if (options.format) {
225
284
  try {
226
285
  html = await options.format(html, jsFilePath);
@@ -229,8 +288,9 @@ export async function generateFTemplates(options = {}) {
229
288
  console.warn(styleText(["yellow", "bold"], "⚠"), `Format failed for ${componentName}:`, formatError.message);
230
289
  }
231
290
  }
291
+ const relativeDir = path.relative(distDir, path.dirname(jsFilePath));
232
292
  const fTemplatePath = outDir
233
- ? path.resolve(outDir, `${componentBaseName}.template.html`)
293
+ ? path.resolve(outDir, relativeDir, `${componentBaseName}.template.html`)
234
294
  : path.resolve(path.dirname(jsFilePath), `${componentBaseName}.template.html`);
235
295
  await mkdir(path.dirname(fTemplatePath), { recursive: true });
236
296
  await writeFile(fTemplatePath, html, "utf8");
@@ -14,7 +14,7 @@
14
14
  * ```ts
15
15
  * import { generateWebuiTemplates } from "@microsoft/fast-test-harness/build/generate-webui-templates.js";
16
16
  *
17
- * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "mai" });
17
+ * await generateWebuiTemplates({ cwd: process.cwd(), tagPrefix: "contoso" });
18
18
  * ```
19
19
  */
20
20
  import { glob, mkdir, writeFile } from "node:fs/promises";
@@ -23,31 +23,9 @@ import { pathToFileURL } from "node:url";
23
23
  import { styleText } from "node:util";
24
24
  import { closeExpression, openExpression } from "@microsoft/fast-html/syntax.js";
25
25
  import { installDomShim } from "./dom-shim.js";
26
- import { convertTemplate } from "./generate-templates.js";
26
+ import { convertTemplate, definitionAsyncResolver, shadowOptionsToAttributes, } from "./generate-templates.js";
27
27
  const stylesMarker = `${openExpression}styles${closeExpression}`;
28
28
  const escapedStylesMarker = new RegExp(stylesMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
29
- /**
30
- * Try to load shadow options from a companion `*.definition-async.js`
31
- * module next to the template module. Returns an object with template
32
- * attribute strings to add (e.g. `shadowrootdelegatesfocus`).
33
- */
34
- async function loadShadowAttributes(templateJsPath) {
35
- const dir = path.dirname(templateJsPath);
36
- const base = path.basename(templateJsPath, ".template.js");
37
- const defAsyncPath = path.resolve(dir, `${base}.definition-async.js`);
38
- const attrs = {};
39
- try {
40
- const mod = await import(pathToFileURL(defAsyncPath).href);
41
- const definition = mod.definition ?? mod.default;
42
- if (definition?.shadowOptions?.delegatesFocus) {
43
- attrs.shadowrootdelegatesfocus = "";
44
- }
45
- }
46
- catch {
47
- // No definition-async module or it failed to load — skip.
48
- }
49
- return attrs;
50
- }
51
29
  /**
52
30
  * Transform an f-template string into a webui template by replacing the
53
31
  * `<f-template>` wrapper with `<template shadowrootmode="open">` and
@@ -97,7 +75,11 @@ export async function generateWebuiTemplates(options = {}) {
97
75
  if (!fTemplateHtml) {
98
76
  continue;
99
77
  }
100
- const shadowAttrs = await loadShadowAttributes(jsFilePath);
78
+ const resolver = options.resolveShadowOptions === null
79
+ ? undefined
80
+ : (options.resolveShadowOptions ?? definitionAsyncResolver);
81
+ const shadowOpts = resolver ? await resolver(jsFilePath) : undefined;
82
+ const shadowAttrs = shadowOptionsToAttributes(shadowOpts);
101
83
  let html = fTemplateToWebui(fTemplateHtml, shadowAttrs);
102
84
  if (options.format) {
103
85
  try {
@@ -107,8 +89,9 @@ export async function generateWebuiTemplates(options = {}) {
107
89
  console.warn(styleText(["yellow", "bold"], "⚠"), `Format failed for ${componentName}:`, formatError.message);
108
90
  }
109
91
  }
92
+ const relativeDir = path.relative(distDir, path.dirname(jsFilePath));
110
93
  const webuiPath = outDir
111
- ? path.resolve(outDir, `${componentBaseName}.template-webui.html`)
94
+ ? path.resolve(outDir, relativeDir, `${componentBaseName}.template-webui.html`)
112
95
  : path.resolve(path.dirname(jsFilePath), `${componentBaseName}.template-webui.html`);
113
96
  await mkdir(path.dirname(webuiPath), { recursive: true });
114
97
  await writeFile(webuiPath, html, "utf8");
@@ -1,5 +1,22 @@
1
1
  import { CSRFixture } from "./csr-fixture.js";
2
2
  export class SSRFixture extends CSRFixture {
3
+ /**
4
+ * The URL of the generated SSR fixture page. This is set after {@link setTemplate}
5
+ * is called and the page navigates to the generated fixture.
6
+ */
7
+ get url() {
8
+ return this._url;
9
+ }
10
+ /**
11
+ * Creates an instance of the SSRFixture.
12
+ *
13
+ * @param page - The Playwright page object.
14
+ * @param tagName - The tag name of the custom element.
15
+ * @param innerHTML - The inner HTML of the custom element.
16
+ * @param waitFor - Additional custom elements to wait for.
17
+ * @param testId - The test ID for the SSR fixture.
18
+ * @param testTitle - The test title for the SSR fixture.
19
+ */
3
20
  constructor(page, tagName, innerHTML = "", waitFor = [], testId, testTitle) {
4
21
  super(page, tagName, innerHTML, waitFor);
5
22
  this.testId = testId;
@@ -86,7 +103,8 @@ export class SSRFixture extends CSRFixture {
86
103
  if (!result.url) {
87
104
  throw new Error(`Invalid response from server: ${JSON.stringify(result)}`);
88
105
  }
89
- await this.page.goto(result.url);
106
+ this._url = result.url;
107
+ await this.page.goto(this._url);
90
108
  await this.waitForStability();
91
109
  this.templateRendered = true;
92
110
  this.pendingStyles.length = 0;