@aurelia/storybook 2.1.0 → 2.2.1

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 (108) hide show
  1. package/.github/workflows/ci.yml +61 -0
  2. package/.github/workflows/publish.yml +82 -0
  3. package/.github/workflows/storybook-preview.yml +62 -0
  4. package/CHANGELOG.md +5 -0
  5. package/README.md +180 -1
  6. package/__tests__/create-aurelia-app.test.ts +94 -0
  7. package/__tests__/preset.test.ts +32 -3
  8. package/__tests__/preview.test.ts +9 -131
  9. package/__tests__/render.test.ts +15 -26
  10. package/apps/hello-world/package-lock.json +404 -287
  11. package/apps/hello-world/package.json +10 -10
  12. package/apps/hello-world/src/components/notification-center.ts +2 -2
  13. package/apps/hello-world/src/components/stat-card.ts +12 -4
  14. package/apps/hello-world/src/components/weather-widget.ts +2 -1
  15. package/apps/hello-world/src/stories/feedback-form.stories.ts +15 -9
  16. package/apps/hello-world/src/stories/hello-world.stories.ts +20 -9
  17. package/apps/hello-world/src/stories/notification-center.stories.ts +17 -10
  18. package/apps/hello-world/src/stories/stat-card.stories.ts +23 -13
  19. package/apps/hello-world/src/stories/weather-widget.stories.ts +18 -13
  20. package/apps/hello-world-rsbuild/.storybook/main.ts +16 -0
  21. package/apps/hello-world-rsbuild/.storybook/preview.ts +1 -0
  22. package/apps/hello-world-rsbuild/.stylelintrc.json +5 -0
  23. package/apps/hello-world-rsbuild/README.md +28 -0
  24. package/apps/hello-world-rsbuild/eslint.config.mjs +25 -0
  25. package/apps/hello-world-rsbuild/favicon.ico +0 -0
  26. package/apps/hello-world-rsbuild/index.html +17 -0
  27. package/apps/hello-world-rsbuild/package-lock.json +11131 -0
  28. package/apps/hello-world-rsbuild/package.json +56 -0
  29. package/apps/hello-world-rsbuild/src/components/feedback-form.html +111 -0
  30. package/apps/hello-world-rsbuild/src/components/feedback-form.ts +45 -0
  31. package/apps/hello-world-rsbuild/src/components/notification-center.html +119 -0
  32. package/apps/hello-world-rsbuild/src/components/notification-center.ts +27 -0
  33. package/apps/hello-world-rsbuild/src/components/stat-card.html +107 -0
  34. package/apps/hello-world-rsbuild/src/components/stat-card.ts +41 -0
  35. package/apps/hello-world-rsbuild/src/components/weather-widget.html +89 -0
  36. package/apps/hello-world-rsbuild/src/components/weather-widget.ts +31 -0
  37. package/apps/hello-world-rsbuild/src/hello-world.html +48 -0
  38. package/apps/hello-world-rsbuild/src/hello-world.ts +17 -0
  39. package/apps/hello-world-rsbuild/src/main.ts +6 -0
  40. package/apps/hello-world-rsbuild/src/my-app.html +1 -0
  41. package/apps/hello-world-rsbuild/src/my-app.ts +3 -0
  42. package/apps/hello-world-rsbuild/src/resource.d.ts +15 -0
  43. package/apps/hello-world-rsbuild/src/services/weather-service.ts +15 -0
  44. package/apps/hello-world-rsbuild/src/stories/feedback-form.stories.ts +58 -0
  45. package/apps/hello-world-rsbuild/src/stories/hello-world.stories.ts +64 -0
  46. package/apps/hello-world-rsbuild/src/stories/notification-center.stories.ts +88 -0
  47. package/apps/hello-world-rsbuild/src/stories/stat-card.stories.ts +75 -0
  48. package/apps/hello-world-rsbuild/src/stories/weather-widget.stories.ts +62 -0
  49. package/apps/hello-world-rsbuild/test/my-app.spec.ts +15 -0
  50. package/apps/hello-world-rsbuild/test/setup.ts +29 -0
  51. package/apps/hello-world-rsbuild/tsconfig.json +19 -0
  52. package/apps/hello-world-rsbuild/tsconfig.vitest.json +11 -0
  53. package/apps/hello-world-rsbuild/vite.config.ts +17 -0
  54. package/apps/hello-world-rsbuild/vitest.config.ts +15 -0
  55. package/apps/hello-world-webpack/package-lock.json +239 -264
  56. package/apps/hello-world-webpack/package.json +8 -7
  57. package/apps/hello-world-webpack/src/components/notification-center.ts +2 -2
  58. package/apps/hello-world-webpack/src/components/stat-card.ts +12 -4
  59. package/apps/hello-world-webpack/src/components/weather-widget.ts +2 -1
  60. package/apps/hello-world-webpack/src/my-app.stories.ts +6 -4
  61. package/apps/hello-world-webpack/src/stories/feedback-form.stories.ts +15 -9
  62. package/apps/hello-world-webpack/src/stories/hello-world.stories.ts +20 -9
  63. package/apps/hello-world-webpack/src/stories/notification-center.stories.ts +17 -10
  64. package/apps/hello-world-webpack/src/stories/stat-card.stories.ts +23 -13
  65. package/apps/hello-world-webpack/src/stories/weather-widget.stories.ts +18 -13
  66. package/dist/index.d.ts +25 -0
  67. package/dist/index.js +68 -14
  68. package/dist/index.js.map +1 -1
  69. package/dist/preset.d.ts +21 -0
  70. package/dist/preset.js +46 -2
  71. package/dist/preset.js.map +1 -1
  72. package/dist/preview/helpers.d.ts +2 -0
  73. package/dist/preview/helpers.js +6 -0
  74. package/dist/preview/helpers.js.map +1 -0
  75. package/dist/preview/render.d.ts +7 -0
  76. package/dist/preview/render.js +66 -15
  77. package/dist/preview/render.js.map +1 -1
  78. package/dist/preview/storybook-types-runtime.d.ts +1 -0
  79. package/dist/preview/storybook-types-runtime.js +5 -0
  80. package/dist/preview/storybook-types-runtime.js.map +1 -0
  81. package/dist/preview/storybook-types.d.ts +27 -0
  82. package/dist/preview/types-runtime.d.ts +1 -0
  83. package/dist/preview/types-runtime.js +5 -0
  84. package/dist/preview/types-runtime.js.map +1 -0
  85. package/dist/preview/types.d.ts +44 -0
  86. package/dist/preview.d.ts +3 -0
  87. package/dist/preview.js +71 -16
  88. package/dist/preview.js.map +1 -1
  89. package/dist/webpack.d.ts +10 -0
  90. package/dist/webpack.js +19 -1
  91. package/dist/webpack.js.map +1 -1
  92. package/package.json +54 -9
  93. package/rollup.config.mjs +5 -3
  94. package/scripts/sync-versions.cjs +55 -0
  95. package/src/index.ts +11 -1
  96. package/src/preset.ts +32 -2
  97. package/src/preview/helpers.ts +7 -0
  98. package/src/preview/render.ts +98 -30
  99. package/src/preview/storybook-types-runtime.ts +2 -0
  100. package/src/preview/storybook-types.ts +34 -0
  101. package/src/preview/types-runtime.ts +2 -0
  102. package/src/preview/types.ts +57 -2
  103. package/src/preview.ts +11 -1
  104. package/src/webpack.ts +19 -0
  105. package/CONTINUITY.md +0 -22
  106. package/dist/preview/types.js +0 -2
  107. package/dist/preview/types.js.map +0 -1
  108. /package/{jest.config.js → jest.config.cjs} +0 -0
package/dist/webpack.js CHANGED
@@ -16,6 +16,24 @@ function getRules() {
16
16
  },
17
17
  ];
18
18
  }
19
+ /**
20
+ * Rsbuild/Rspack rules (avoid ts-loader; Rsbuild handles TS transpilation).
21
+ */
22
+ function getRsbuildRules() {
23
+ return [
24
+ {
25
+ test: /\.ts$/i,
26
+ enforce: 'pre',
27
+ use: ['@aurelia/webpack-loader'],
28
+ exclude: /node_modules/,
29
+ },
30
+ {
31
+ test: /\.html$/i,
32
+ use: '@aurelia/webpack-loader',
33
+ exclude: /node_modules/,
34
+ },
35
+ ];
36
+ }
19
37
 
20
- export { getRules };
38
+ export { getRsbuildRules, getRules };
21
39
  //# sourceMappingURL=webpack.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webpack.js","sources":["../src/webpack.ts"],"sourcesContent":["// src/webpack.ts\nimport type { RuleSetRule } from 'webpack';\n\n/**\n * A set of rules to be added to the webpack configuration.\n * @returns\n */\nexport function getRules(): RuleSetRule[] {\n return [\n {\n test: /\\.ts$/i,\n use: ['ts-loader', '@aurelia/webpack-loader'],\n exclude: /node_modules/,\n },\n {\n test: /\\.html$/i,\n use: '@aurelia/webpack-loader',\n exclude: /node_modules/,\n },\n ];\n}\n"],"names":[],"mappings":"AAGA;;;AAGG;SACa,QAAQ,GAAA;IACtB,OAAO;AACL,QAAA;AACE,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,GAAG,EAAE,CAAC,WAAW,EAAE,yBAAyB,CAAC;AAC7C,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,UAAU;AAChB,YAAA,GAAG,EAAE,yBAAyB;AAC9B,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;KACF;AACH;;;;"}
1
+ {"version":3,"file":"webpack.js","sources":["../src/webpack.ts"],"sourcesContent":["// src/webpack.ts\nimport type { RuleSetRule } from 'webpack';\n\n/**\n * A set of rules to be added to the webpack configuration.\n * @returns\n */\nexport function getRules(): RuleSetRule[] {\n return [\n {\n test: /\\.ts$/i,\n use: ['ts-loader', '@aurelia/webpack-loader'],\n exclude: /node_modules/,\n },\n {\n test: /\\.html$/i,\n use: '@aurelia/webpack-loader',\n exclude: /node_modules/,\n },\n ];\n}\n\n/**\n * Rsbuild/Rspack rules (avoid ts-loader; Rsbuild handles TS transpilation).\n */\nexport function getRsbuildRules(): RuleSetRule[] {\n return [\n {\n test: /\\.ts$/i,\n enforce: 'pre',\n use: ['@aurelia/webpack-loader'],\n exclude: /node_modules/,\n },\n {\n test: /\\.html$/i,\n use: '@aurelia/webpack-loader',\n exclude: /node_modules/,\n },\n ];\n}\n"],"names":[],"mappings":"AAGA;;;AAGG;SACa,QAAQ,GAAA;IACtB,OAAO;AACL,QAAA;AACE,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,GAAG,EAAE,CAAC,WAAW,EAAE,yBAAyB,CAAC;AAC7C,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,UAAU;AAChB,YAAA,GAAG,EAAE,yBAAyB;AAC9B,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;KACF;AACH;AAEA;;AAEG;SACa,eAAe,GAAA;IAC7B,OAAO;AACL,QAAA;AACE,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,OAAO,EAAE,KAAK;YACd,GAAG,EAAE,CAAC,yBAAyB,CAAC;AAChC,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,UAAU;AAChB,YAAA,GAAG,EAAE,yBAAyB;AAC9B,YAAA,OAAO,EAAE,cAAc;AACxB,SAAA;KACF;AACH;;;;"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@aurelia/storybook",
3
- "version": "2.1.0",
4
- "description": "A Storybook plugin to render Aurelia 2 components using Vite or Webpack",
3
+ "version": "2.2.1",
4
+ "description": "A Storybook plugin to render Aurelia 2 components using Vite, Webpack, or Rsbuild/Rspack",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "sideEffects": false,
8
9
  "publishConfig": {
9
10
  "access": "public"
10
11
  },
@@ -14,35 +15,79 @@
14
15
  "scripts": {
15
16
  "build": "rollup -c",
16
17
  "build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly",
18
+ "changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s -r 0",
19
+ "sync:versions": "node scripts/sync-versions.cjs",
20
+ "sync:versions:check": "node scripts/sync-versions.cjs --check",
17
21
  "watch": "rollup -c -w",
18
22
  "test": "NODE_OPTIONS=--no-deprecation jest"
19
23
  },
20
24
  "peerDependencies": {
21
25
  "@aurelia/runtime-html": "^2.0.0-rc.0",
22
26
  "@aurelia/vite-plugin": "^2.0.0-rc.0",
23
- "@storybook/builder-vite": "^10.0.0",
24
- "@storybook/builder-webpack5": "^10.0.0",
27
+ "@rsbuild/core": "^1.7.2",
28
+ "@storybook/builder-vite": "^10.2.0",
29
+ "@storybook/builder-webpack5": "^10.2.0",
25
30
  "aurelia": "^2.0.0-rc.0",
26
- "storybook": "^10.0.0"
31
+ "storybook": "^10.2.0",
32
+ "storybook-builder-rsbuild": "^3.2.2"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "@rsbuild/core": {
36
+ "optional": true
37
+ },
38
+ "@storybook/builder-vite": {
39
+ "optional": true
40
+ },
41
+ "@storybook/builder-webpack5": {
42
+ "optional": true
43
+ },
44
+ "storybook-builder-rsbuild": {
45
+ "optional": true
46
+ }
27
47
  },
28
48
  "devDependencies": {
29
49
  "@aurelia/webpack-loader": "^2.0.0-rc.0",
30
50
  "@rollup/plugin-commonjs": "^28.0.6",
31
51
  "@rollup/plugin-node-resolve": "^16.0.1",
32
52
  "@rollup/plugin-typescript": "^12.1.3",
33
- "@storybook/builder-vite": "^10.0.5",
53
+ "@rsbuild/core": "^1.7.2",
54
+ "@storybook/builder-vite": "^10.2.0",
34
55
  "@types/jest": "^30.0.0",
56
+ "conventional-changelog-cli": "^5.0.0",
57
+ "conventional-changelog-conventionalcommits": "^8.0.0",
35
58
  "glob": "^11.0.3",
36
59
  "jest": "^30.0.2",
37
60
  "jest-environment-jsdom": "^30.0.2",
38
61
  "rollup": "^4.44.0",
39
- "storybook": "^10.0.0",
62
+ "storybook": "^10.2.0",
40
63
  "ts-jest": "^29.4.0",
41
64
  "ts-loader": "^9.5.2",
42
65
  "typescript": "^5.8.3"
43
66
  },
44
67
  "exports": {
45
- ".": "./dist/index.js",
46
- "./preset": "./dist/preset.js"
68
+ ".": {
69
+ "types": "./dist/index.d.ts",
70
+ "default": "./dist/index.js"
71
+ },
72
+ "./preset": {
73
+ "types": "./dist/preset.d.ts",
74
+ "default": "./dist/preset.js"
75
+ },
76
+ "./preview": {
77
+ "types": "./dist/preview.d.ts",
78
+ "default": "./dist/preview.js"
79
+ },
80
+ "./preview/types": {
81
+ "types": "./dist/preview/types.d.ts",
82
+ "default": "./dist/preview/types-runtime.js"
83
+ },
84
+ "./preview/storybook-types": {
85
+ "types": "./dist/preview/storybook-types.d.ts",
86
+ "default": "./dist/preview/storybook-types-runtime.js"
87
+ },
88
+ "./preview/helpers": {
89
+ "types": "./dist/preview/helpers.d.ts",
90
+ "default": "./dist/preview/helpers.js"
91
+ }
47
92
  }
48
93
  }
package/rollup.config.mjs CHANGED
@@ -7,16 +7,18 @@ const external = [
7
7
  '@aurelia/runtime-html',
8
8
  '@aurelia/vite-plugin',
9
9
  '@storybook/builder-vite',
10
+ '@rsbuild/core',
10
11
  'aurelia',
11
12
  'react',
12
13
  'react-dom',
13
- 'storybook/internal/core-events',
14
14
  'storybook/internal/types',
15
15
  'storybook/theming'
16
16
  ];
17
17
 
18
18
  // Get all TypeScript files from src directory
19
- const srcFiles = glob.sync('src/**/*.ts').reduce((acc, file) => {
19
+ const srcFiles = glob
20
+ .sync('src/**/*.ts', { ignore: ['src/preview/types.ts', 'src/preview/storybook-types.ts'] })
21
+ .reduce((acc, file) => {
20
22
  const key = file.replace(/^src\//, '').replace(/\.ts$/, '');
21
23
  acc[key] = file;
22
24
  return acc;
@@ -47,4 +49,4 @@ const configs = Object.entries(srcFiles).map(([name, input]) =>
47
49
  createConfig(input, `dist/${name}.js`)
48
50
  );
49
51
 
50
- export default configs;
52
+ export default configs;
@@ -0,0 +1,55 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const checkOnly = process.argv.includes('--check');
5
+
6
+ const rootPath = path.resolve(__dirname, '..');
7
+ const rootPkgPath = path.join(rootPath, 'package.json');
8
+ const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
9
+ const version = rootPkg.version;
10
+
11
+ const exampleDirs = [
12
+ 'apps/hello-world',
13
+ 'apps/hello-world-webpack',
14
+ 'apps/hello-world-rsbuild',
15
+ ];
16
+
17
+ let failed = false;
18
+
19
+ for (const dir of exampleDirs) {
20
+ const pkgPath = path.join(rootPath, dir, 'package.json');
21
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
22
+ let changed = false;
23
+
24
+ if (pkg.version !== version) {
25
+ if (checkOnly) {
26
+ failed = true;
27
+ } else {
28
+ pkg.version = version;
29
+ changed = true;
30
+ }
31
+ }
32
+
33
+ for (const depKey of ['dependencies', 'devDependencies', 'peerDependencies']) {
34
+ if (pkg[depKey] && pkg[depKey]['@aurelia/storybook']) {
35
+ const desired = `^${version}`;
36
+ if (pkg[depKey]['@aurelia/storybook'] !== desired) {
37
+ if (checkOnly) {
38
+ failed = true;
39
+ } else {
40
+ pkg[depKey]['@aurelia/storybook'] = desired;
41
+ changed = true;
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ if (!checkOnly && changed) {
48
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
49
+ }
50
+ }
51
+
52
+ if (checkOnly && failed) {
53
+ console.error('Example app versions are out of sync with root package.json.');
54
+ process.exit(1);
55
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,18 @@
1
- import type { StorybookConfig } from 'storybook/internal/types';
2
1
  import { renderToCanvas } from './preview/render';
2
+ import { defineAureliaStory } from './preview/helpers';
3
3
 
4
4
  export { renderToCanvas };
5
5
  export const render = renderToCanvas;
6
+ export { defineAureliaStory };
7
+ export type {
8
+ AureliaRenderer,
9
+ AureliaStoryResult,
10
+ AureliaArgsStoryFn,
11
+ AureliaRenderContext,
12
+ AureliaStoryContext,
13
+ AureliaParameters,
14
+ AureliaStoryParameters
15
+ } from './preview/types';
6
16
 
7
17
  // Define the framework
8
18
  export const framework = {
package/src/preset.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/preset.ts
2
2
  // Minimal preset for Storybook-Aurelia2
3
3
 
4
- import { getRules } from './webpack';
4
+ import { getRules, getRsbuildRules } from './webpack';
5
5
 
6
6
  /**
7
7
  * Optionally adjust the Vite configuration.
@@ -29,6 +29,36 @@ export async function viteFinal(config: any): Promise<any> {
29
29
  return config;
30
30
  }
31
31
 
32
+ async function loadMergeRsbuildConfig() {
33
+ try {
34
+ const { mergeRsbuildConfig } = await import('@rsbuild/core');
35
+ return mergeRsbuildConfig;
36
+ } catch (error: any) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ throw new Error(
39
+ `@aurelia/storybook: rsbuild support requires @rsbuild/core to be installed. Original error: ${message}`
40
+ );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Optionally adjust the Rsbuild configuration (Rspack-based).
46
+ */
47
+ export async function rsbuildFinal(config: any): Promise<any> {
48
+ const mergeRsbuildConfig = await loadMergeRsbuildConfig();
49
+
50
+ return mergeRsbuildConfig(config, {
51
+ tools: {
52
+ rspack: (rspackConfig: any) => {
53
+ const moduleConfig = rspackConfig.module ?? (rspackConfig.module = {});
54
+ const rules = moduleConfig.rules ?? (moduleConfig.rules = []);
55
+ rules.push(...getRsbuildRules());
56
+ return rspackConfig;
57
+ }
58
+ }
59
+ });
60
+ }
61
+
32
62
  /**
33
63
  * A function to configure webpack.
34
64
  * @param config
@@ -44,6 +74,6 @@ export async function webpackFinal(config: any): Promise<any> {
44
74
  }
45
75
 
46
76
  // Export a default for compatibility.
47
- export default { viteFinal, webpackFinal };
77
+ export default { viteFinal, rsbuildFinal, webpackFinal };
48
78
 
49
79
  export const previewAnnotations = ['./preview.js'];
@@ -0,0 +1,7 @@
1
+ import type { AureliaStoryResult } from './types';
2
+
3
+ export function defineAureliaStory<TArgs = Record<string, unknown>>(
4
+ story: AureliaStoryResult<TArgs>
5
+ ): AureliaStoryResult<TArgs> {
6
+ return story;
7
+ }
@@ -1,21 +1,47 @@
1
- import { STORY_CHANGED } from 'storybook/internal/core-events';
2
- import type { RenderContext, ArgsStoryFn } from 'storybook/internal/types';
3
- import type { AureliaRenderer } from './types';
1
+ import type { RenderContext, ArgsStoryFn } from './storybook-types';
2
+ import type {
3
+ AureliaRenderer,
4
+ AureliaStoryResult,
5
+ AureliaParameters,
6
+ AureliaStoryContext,
7
+ } from './types';
4
8
  import Aurelia, { Constructable, CustomElement } from 'aurelia';
5
9
 
6
- interface AureliaStoryResult {
7
- template: string;
8
- components?: unknown[];
9
- Component?: unknown;
10
- container?: any;
11
- items?: unknown[];
12
- innerHtml?: string;
13
- props?: Record<string, any>;
14
- }
15
-
16
10
  // Track Aurelia apps for cleanup
17
11
  const appMap = new Map<HTMLElement, any>();
18
12
 
13
+ function mergeStoryProps(
14
+ parameters: { args?: Record<string, any> } | undefined,
15
+ storyArgs: Record<string, any> | undefined,
16
+ storyProps: Record<string, any> | undefined
17
+ ) {
18
+ return { ...parameters?.args, ...storyArgs, ...storyProps };
19
+ }
20
+
21
+ function getAureliaParameters(
22
+ storyContext?: AureliaStoryContext
23
+ ): AureliaParameters | undefined {
24
+ const parameters = storyContext?.parameters?.aurelia;
25
+ if (!parameters || typeof parameters !== 'object') {
26
+ return undefined;
27
+ }
28
+ return parameters as AureliaParameters;
29
+ }
30
+
31
+ function normalizeRegistrations(
32
+ parameters: AureliaParameters | undefined
33
+ ): unknown[] {
34
+ if (!parameters) {
35
+ return [];
36
+ }
37
+
38
+ const register = Array.isArray(parameters.register) ? parameters.register : [];
39
+ const components = Array.isArray(parameters.components) ? parameters.components : [];
40
+ const items = Array.isArray(parameters.items) ? parameters.items : [];
41
+
42
+ return [...register, ...components, ...items].filter(Boolean);
43
+ }
44
+
19
45
  async function teardown(element: HTMLElement) {
20
46
  if (appMap.has(element)) {
21
47
  const app = appMap.get(element);
@@ -30,11 +56,12 @@ export const render: ArgsStoryFn<AureliaRenderer> = (args, context) => {
30
56
  const { id, component: Component } = context;
31
57
 
32
58
  if (!Component) {
59
+ const label = context.title && context.name ? `${context.title} / ${context.name}` : id;
33
60
  throw new Error(
34
- `Unable to render story ${id} as the component annotation is missing from the default export`
61
+ `Unable to render story ${label} as the component annotation is missing from the default export`
35
62
  );
36
63
  }
37
- return { Component, props: args, template: '' };
64
+ return { Component, props: args };
38
65
  };
39
66
 
40
67
  export async function renderToCanvas(
@@ -74,9 +101,6 @@ export async function renderToCanvas(
74
101
  let app = appMap.get(rootElement);
75
102
  const story = storyFn() as AureliaStoryResult;
76
103
 
77
- // Temporary debug logging
78
- console.log(`[DEBUG] Story: ${name}, forceRemount: ${forceRemount}, hasExistingApp: ${!!app}, canvasId: ${canvasElement.className}`);
79
-
80
104
  if (!story) {
81
105
  showError({
82
106
  title: `Expecting an Aurelia component from the story: "${name}" of "${title}".`,
@@ -98,20 +122,21 @@ export async function renderToCanvas(
98
122
  // Clear container before mounting new app
99
123
  hostElement.innerHTML = '';
100
124
 
101
- const mergedProps = { ...parameters?.args, ...args, ...story.props };
125
+ const mergedProps = mergeStoryProps(parameters, args, story.props);
102
126
 
103
127
  const aureliaApp = appBootstrapFn(
104
128
  story,
105
129
  mergedProps,
106
130
  hostElement,
107
- component as Constructable
131
+ component as Constructable,
132
+ storyContext
108
133
  );
109
134
  await aureliaApp.start();
110
135
  appMap.set(rootElement, aureliaApp);
111
136
  app = aureliaApp;
112
137
  } else {
113
138
  // update existing app props
114
- const mergedProps = { ...parameters?.args, ...args, ...story.props };
139
+ const mergedProps = mergeStoryProps(parameters, args, story.props);
115
140
  if (app?.root?.controller?.viewModel) {
116
141
  Object.assign(app.root.controller.viewModel, mergedProps);
117
142
  }
@@ -127,23 +152,59 @@ export function createAureliaApp(
127
152
  story: AureliaStoryResult,
128
153
  args: Record<string, any>,
129
154
  domElement: HTMLElement,
130
- component?: Constructable
155
+ component?: Constructable,
156
+ storyContext?: AureliaStoryContext
131
157
  ) {
132
158
  const aurelia = new Aurelia(story.container);
159
+ const { container } = aurelia;
160
+ const aureliaParameters = getAureliaParameters(storyContext);
161
+
162
+ const registerIfNeeded = (resource: unknown) => {
163
+ if (!resource) {
164
+ return;
165
+ }
133
166
 
134
- if (story.items?.length) {
135
- aurelia.register(...story.items);
167
+ if (CustomElement.isType(resource)) {
168
+ const definition = CustomElement.getDefinition(resource);
169
+ if (container.has(definition.key, false)) {
170
+ return;
171
+ }
172
+ }
173
+
174
+ aurelia.register(resource);
175
+ };
176
+
177
+ const registerAll = (resources?: unknown[]) => {
178
+ if (!resources?.length) {
179
+ return;
180
+ }
181
+
182
+ for (const resource of resources) {
183
+ registerIfNeeded(resource);
184
+ }
185
+ };
186
+
187
+ if (aureliaParameters?.configureContainer && storyContext) {
188
+ aureliaParameters.configureContainer(container, storyContext);
136
189
  }
137
190
 
138
- if (story.components?.length) {
139
- aurelia.register(...story.components);
191
+ registerAll(normalizeRegistrations(aureliaParameters));
192
+ registerAll(story.items);
193
+
194
+ const storyComponents = (story.components ?? []).filter(Boolean);
195
+ const dedupedComponents = component
196
+ ? storyComponents.filter((entry) => entry !== component)
197
+ : storyComponents;
198
+
199
+ for (const entry of dedupedComponents) {
200
+ registerIfNeeded(entry);
140
201
  }
141
202
 
142
203
  let { template } = story;
143
204
 
144
205
  if (component) {
145
206
  template = template ?? createComponentTemplate(component, story.innerHtml);
146
- aurelia.register(component);
207
+ registerIfNeeded(component);
147
208
  }
148
209
 
149
210
  const App = CustomElement.define(
@@ -157,6 +218,10 @@ export function createAureliaApp(
157
218
 
158
219
  const app = Object.assign(new App(), args);
159
220
 
221
+ if (aureliaParameters?.configure && storyContext) {
222
+ aureliaParameters.configure(aurelia, storyContext);
223
+ }
224
+
160
225
  return aurelia.app({
161
226
  host: domElement,
162
227
  component: app,
@@ -169,7 +234,10 @@ export function createComponentTemplate(
169
234
  ): string {
170
235
  const def = CustomElement.getDefinition(component);
171
236
 
172
- return `<${def.name} ${Object.values(def.bindables)
237
+ const bindings = Object.values(def.bindables)
173
238
  .map((bindable) => `${bindable.attribute}.bind="${bindable.name}"`)
174
- .join(' ')}>${innerHtml ?? ''}</${def.name}>`;
175
- }
239
+ .join(' ');
240
+ const bindingAttributes = bindings ? ` ${bindings}` : '';
241
+
242
+ return `<${def.name}${bindingAttributes}>${innerHtml ?? ''}</${def.name}>`;
243
+ }
@@ -0,0 +1,2 @@
1
+ // Runtime stub so the export map has a JS target for type-only entries.
2
+ export const __AURELIA_STORYBOOK_STORYBOOK_TYPES__ = true;
@@ -0,0 +1,34 @@
1
+ export interface Renderer {
2
+ component?: unknown;
3
+ storyResult?: unknown;
4
+ canvasElement?: HTMLElement;
5
+ }
6
+
7
+ export interface StoryContext<
8
+ TRenderer = Renderer,
9
+ TArgs = Record<string, unknown>
10
+ > {
11
+ id?: string;
12
+ component?: unknown;
13
+ args: TArgs;
14
+ parameters?: { args?: Record<string, unknown> } & Record<string, unknown>;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ export type ArgsStoryFn<
19
+ TRenderer = Renderer,
20
+ TArgs = Record<string, unknown>
21
+ > = (args: TArgs, context: StoryContext<TRenderer, TArgs>) => unknown;
22
+
23
+ export interface RenderContext<
24
+ TRenderer = Renderer,
25
+ TArgs = Record<string, unknown>
26
+ > {
27
+ storyFn: () => unknown;
28
+ title: string;
29
+ name: string;
30
+ showMain: () => void;
31
+ showError: (error: { title: string; description?: string }) => void;
32
+ storyContext: StoryContext<TRenderer, TArgs>;
33
+ forceRemount?: boolean;
34
+ }
@@ -0,0 +1,2 @@
1
+ // Runtime stub so the export map has a JS target for type-only entries.
2
+ export const __AURELIA_STORYBOOK_TYPES__ = true;
@@ -1,7 +1,62 @@
1
- import type { Renderer } from 'storybook/internal/types';
1
+ import type { IContainer } from 'aurelia';
2
+ import type Aurelia from 'aurelia';
3
+ import type { Renderer, StoryContext, RenderContext, ArgsStoryFn } from './storybook-types';
2
4
 
3
5
  export interface AureliaRenderer extends Renderer {
4
6
  /** The DOM element in which the story is rendered */
5
7
  canvasElement: HTMLElement;
6
8
  }
7
-
9
+
10
+ export interface AureliaStoryResult<TArgs = Record<string, unknown>> {
11
+ template?: string;
12
+ components?: unknown[];
13
+ Component?: unknown;
14
+ container?: IContainer;
15
+ items?: unknown[];
16
+ innerHtml?: string;
17
+ props?: Partial<TArgs>;
18
+ }
19
+
20
+ export type AureliaStoryContext<TArgs = Record<string, unknown>> = StoryContext<
21
+ AureliaRenderer,
22
+ TArgs
23
+ >;
24
+ export type AureliaRenderContext<TArgs = Record<string, unknown>> = RenderContext<
25
+ AureliaRenderer,
26
+ TArgs
27
+ >;
28
+ export type AureliaArgsStoryFn<TArgs = Record<string, unknown>> = ArgsStoryFn<
29
+ AureliaRenderer,
30
+ TArgs
31
+ >;
32
+
33
+ export interface AureliaParameters<TArgs = Record<string, unknown>> {
34
+ /**
35
+ * Global resources/plugins to register with Aurelia (merged across preview + story parameters).
36
+ */
37
+ register?: unknown[];
38
+ /**
39
+ * Alias for register: match the story result `components` contract.
40
+ */
41
+ components?: unknown[];
42
+ /**
43
+ * Alias for register: match the story result `items` contract.
44
+ */
45
+ items?: unknown[];
46
+ /**
47
+ * Configure the container before the Aurelia app starts.
48
+ */
49
+ configureContainer?: (
50
+ container: IContainer,
51
+ context: AureliaStoryContext<TArgs>
52
+ ) => void;
53
+ /**
54
+ * Configure the Aurelia instance before the app starts.
55
+ */
56
+ configure?: (aurelia: Aurelia, context: AureliaStoryContext<TArgs>) => void;
57
+ }
58
+
59
+ export interface AureliaStoryParameters<TArgs = Record<string, unknown>> {
60
+ aurelia?: AureliaParameters<TArgs>;
61
+ }
62
+
package/src/preview.ts CHANGED
@@ -1 +1,11 @@
1
- export { renderToCanvas, render } from './preview/render';
1
+ export { renderToCanvas, render } from './preview/render';
2
+ export { defineAureliaStory } from './preview/helpers';
3
+ export type {
4
+ AureliaArgsStoryFn,
5
+ AureliaParameters,
6
+ AureliaRenderContext,
7
+ AureliaRenderer,
8
+ AureliaStoryParameters,
9
+ AureliaStoryContext,
10
+ AureliaStoryResult
11
+ } from './preview/types';
package/src/webpack.ts CHANGED
@@ -19,3 +19,22 @@ export function getRules(): RuleSetRule[] {
19
19
  },
20
20
  ];
21
21
  }
22
+
23
+ /**
24
+ * Rsbuild/Rspack rules (avoid ts-loader; Rsbuild handles TS transpilation).
25
+ */
26
+ export function getRsbuildRules(): RuleSetRule[] {
27
+ return [
28
+ {
29
+ test: /\.ts$/i,
30
+ enforce: 'pre',
31
+ use: ['@aurelia/webpack-loader'],
32
+ exclude: /node_modules/,
33
+ },
34
+ {
35
+ test: /\.html$/i,
36
+ use: '@aurelia/webpack-loader',
37
+ exclude: /node_modules/,
38
+ },
39
+ ];
40
+ }
package/CONTINUITY.md DELETED
@@ -1,22 +0,0 @@
1
- Goal (incl. success criteria):
2
- - Address question about empty chunk generation during build; explain whether outputs are unnecessary and options.
3
- Constraints/Assumptions:
4
- - Use repo instructions in AGENTS.md; maintain this ledger.
5
- - Build uses Rollup per-file entries from globbed src/**/*.ts.
6
- Key decisions:
7
- - None yet for empty chunk handling.
8
- State:
9
- - Identified empty chunk comes from type-only file src/preview/types.ts.
10
- Done:
11
- - Inspected rollup config and types file to confirm empty output cause.
12
- Now:
13
- - Provide explanation and possible ways to avoid generating empty JS file.
14
- Next:
15
- - If requested, change rollup config or file layout to suppress empty chunk.
16
- Open questions (UNCONFIRMED if needed):
17
- - Should we change build config to avoid empty chunk? UNCONFIRMED.
18
- - Remove the untracked .DS_Store file? UNCONFIRMED.
19
- Working set (files/ids/commands):
20
- - CONTINUITY.md
21
- - /Users/dwayne/Code/aurelia2-storybook/rollup.config.mjs
22
- - /Users/dwayne/Code/aurelia2-storybook/src/preview/types.ts
@@ -1,2 +0,0 @@
1
-
2
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}