@kurot/cli 1.2.0 → 2.0.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 (154) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +115 -303
  3. package/dist/core/components/discover-components.d.ts.map +1 -1
  4. package/dist/core/components/discover-components.js +12 -11
  5. package/dist/core/components/discover-components.js.map +1 -1
  6. package/dist/core/config.d.ts +8 -16
  7. package/dist/core/config.d.ts.map +1 -1
  8. package/dist/core/config.js +12 -4
  9. package/dist/core/config.js.map +1 -1
  10. package/dist/core/dev-server.d.ts +1 -1
  11. package/dist/core/dev-server.js +13 -13
  12. package/dist/core/dev-server.js.map +1 -1
  13. package/dist/core/diagnostics/codes.d.ts +3 -6
  14. package/dist/core/diagnostics/codes.d.ts.map +1 -1
  15. package/dist/core/diagnostics/codes.js +4 -10
  16. package/dist/core/diagnostics/codes.js.map +1 -1
  17. package/dist/core/diagnostics/output.d.ts +1 -1
  18. package/dist/core/diagnostics/output.d.ts.map +1 -1
  19. package/dist/core/{exml → kui}/ast.d.ts +17 -107
  20. package/dist/core/kui/ast.d.ts.map +1 -0
  21. package/dist/core/kui/ast.js +8 -0
  22. package/dist/core/{exml → kui}/ast.js.map +1 -1
  23. package/dist/core/kui/codegen.d.ts.map +1 -0
  24. package/dist/core/{exml → kui}/codegen.js +17 -176
  25. package/dist/core/kui/codegen.js.map +1 -0
  26. package/dist/core/kui/index.d.ts +29 -0
  27. package/dist/core/kui/index.d.ts.map +1 -0
  28. package/dist/core/kui/index.js +23 -0
  29. package/dist/core/kui/index.js.map +1 -0
  30. package/dist/core/{exml/exml-diagnostics.d.ts → kui/kui-diagnostics.d.ts} +2 -2
  31. package/dist/core/kui/kui-diagnostics.d.ts.map +1 -0
  32. package/dist/core/{exml/exml-diagnostics.js → kui/kui-diagnostics.js} +4 -4
  33. package/dist/core/kui/kui-diagnostics.js.map +1 -0
  34. package/dist/core/kui/kui-parser.d.ts +7 -0
  35. package/dist/core/kui/kui-parser.d.ts.map +1 -0
  36. package/dist/core/kui/kui-parser.js +157 -0
  37. package/dist/core/kui/kui-parser.js.map +1 -0
  38. package/dist/core/{exml → kui}/registry.d.ts +3 -26
  39. package/dist/core/kui/registry.d.ts.map +1 -0
  40. package/dist/core/{exml → kui}/registry.js +12 -43
  41. package/dist/core/kui/registry.js.map +1 -0
  42. package/dist/core/{exml → kui}/skin-module-builder.d.ts +6 -6
  43. package/dist/core/kui/skin-module-builder.d.ts.map +1 -0
  44. package/dist/core/{exml → kui}/skin-module-builder.js +7 -6
  45. package/dist/core/kui/skin-module-builder.js.map +1 -0
  46. package/dist/core/{exml → kui}/skin-parts-declaration.d.ts +1 -1
  47. package/dist/core/kui/skin-parts-declaration.d.ts.map +1 -0
  48. package/dist/core/kui/skin-parts-declaration.js +182 -0
  49. package/dist/core/kui/skin-parts-declaration.js.map +1 -0
  50. package/dist/core/kui/source-location.d.ts.map +1 -0
  51. package/dist/core/kui/source-location.js.map +1 -0
  52. package/dist/core/namespace-external-plugin.d.ts +1 -1
  53. package/dist/core/namespace-external-plugin.js +1 -1
  54. package/dist/core/plugins/compile-custom-namespaces.d.ts +3 -3
  55. package/dist/core/plugins/compile-custom-namespaces.js +3 -3
  56. package/dist/core/plugins/compile-kui.d.ts +6 -0
  57. package/dist/core/plugins/compile-kui.d.ts.map +1 -0
  58. package/dist/core/plugins/compile-kui.js +143 -0
  59. package/dist/core/plugins/compile-kui.js.map +1 -0
  60. package/dist/core/plugins/copy-assets.d.ts +3 -3
  61. package/dist/core/plugins/copy-assets.d.ts.map +1 -1
  62. package/dist/core/plugins/copy-assets.js +7 -6
  63. package/dist/core/plugins/copy-assets.js.map +1 -1
  64. package/dist/core/plugins/index.d.ts +1 -1
  65. package/dist/core/plugins/index.d.ts.map +1 -1
  66. package/dist/core/plugins/index.js +3 -3
  67. package/dist/core/plugins/index.js.map +1 -1
  68. package/dist/core/project.d.ts +14 -10
  69. package/dist/core/project.d.ts.map +1 -1
  70. package/dist/core/project.js +11 -7
  71. package/dist/core/project.js.map +1 -1
  72. package/dist/core/template.js +1 -1
  73. package/dist/core/template.js.map +1 -1
  74. package/dist/define.d.ts +1 -1
  75. package/dist/define.d.ts.map +1 -1
  76. package/package.json +5 -3
  77. package/templates/game/kurot.config.ts +3 -3
  78. package/templates/game/resource/ui/skins/ButtonSkin.kui.xml +24 -0
  79. package/templates/game/resource/ui/skins/CheckBoxSkin.kui.xml +45 -0
  80. package/templates/game/resource/ui/skins/ComboBoxSkin.kui.xml +32 -0
  81. package/templates/game/resource/ui/skins/GroupSkin.kui.xml +11 -0
  82. package/templates/game/resource/ui/skins/HScrollBarSkin.kui.xml +11 -0
  83. package/templates/game/resource/ui/skins/HSliderSkin.kui.xml +13 -0
  84. package/templates/game/resource/ui/skins/ImageSkin.kui.xml +11 -0
  85. package/templates/game/resource/ui/skins/ItemRendererSkin.kui.xml +22 -0
  86. package/templates/game/resource/ui/skins/LabelSkin.kui.xml +11 -0
  87. package/templates/game/resource/ui/skins/ListSkin.kui.xml +15 -0
  88. package/templates/game/resource/ui/skins/PanelSkin.kui.xml +18 -0
  89. package/templates/game/resource/ui/skins/ProgressBarSkin.kui.xml +14 -0
  90. package/templates/game/resource/ui/skins/RadioButtonSkin.kui.xml +45 -0
  91. package/templates/game/resource/ui/skins/ScrollerSkin.kui.xml +13 -0
  92. package/templates/game/resource/ui/skins/TabBarSkin.kui.xml +12 -0
  93. package/templates/game/resource/ui/skins/TextInputSkin.kui.xml +28 -0
  94. package/templates/game/resource/ui/skins/ToggleButtonSkin.kui.xml +34 -0
  95. package/templates/game/resource/ui/skins/ToggleSwitchSkin.kui.xml +29 -0
  96. package/templates/game/resource/ui/skins/VScrollBarSkin.kui.xml +11 -0
  97. package/templates/game/resource/ui/skins/VSliderSkin.kui.xml +13 -0
  98. package/templates/game/resource/ui/skins/ViewStackSkin.kui.xml +11 -0
  99. package/dist/core/exml/ast.d.ts.map +0 -1
  100. package/dist/core/exml/ast.js +0 -8
  101. package/dist/core/exml/codegen.d.ts.map +0 -1
  102. package/dist/core/exml/codegen.js.map +0 -1
  103. package/dist/core/exml/exml-diagnostics.d.ts.map +0 -1
  104. package/dist/core/exml/exml-diagnostics.js.map +0 -1
  105. package/dist/core/exml/exml-parser.d.ts +0 -27
  106. package/dist/core/exml/exml-parser.d.ts.map +0 -1
  107. package/dist/core/exml/exml-parser.js +0 -380
  108. package/dist/core/exml/exml-parser.js.map +0 -1
  109. package/dist/core/exml/index.d.ts +0 -54
  110. package/dist/core/exml/index.d.ts.map +0 -1
  111. package/dist/core/exml/index.js +0 -51
  112. package/dist/core/exml/index.js.map +0 -1
  113. package/dist/core/exml/registry.d.ts.map +0 -1
  114. package/dist/core/exml/registry.js.map +0 -1
  115. package/dist/core/exml/skin-module-builder.d.ts.map +0 -1
  116. package/dist/core/exml/skin-module-builder.js.map +0 -1
  117. package/dist/core/exml/skin-parts-declaration.d.ts.map +0 -1
  118. package/dist/core/exml/skin-parts-declaration.js +0 -82
  119. package/dist/core/exml/skin-parts-declaration.js.map +0 -1
  120. package/dist/core/exml/source-location.d.ts.map +0 -1
  121. package/dist/core/exml/source-location.js.map +0 -1
  122. package/dist/core/exml/xml-parser.d.ts +0 -86
  123. package/dist/core/exml/xml-parser.d.ts.map +0 -1
  124. package/dist/core/exml/xml-parser.js +0 -196
  125. package/dist/core/exml/xml-parser.js.map +0 -1
  126. package/dist/core/plugins/compile-exml.d.ts +0 -14
  127. package/dist/core/plugins/compile-exml.d.ts.map +0 -1
  128. package/dist/core/plugins/compile-exml.js +0 -256
  129. package/dist/core/plugins/compile-exml.js.map +0 -1
  130. package/templates/game/resource/default.thm.json +0 -19
  131. package/templates/game/resource/skins/eui/ButtonSkin.exml +0 -16
  132. package/templates/game/resource/skins/eui/CheckBoxSkin.exml +0 -18
  133. package/templates/game/resource/skins/eui/ComboBoxSkin.exml +0 -31
  134. package/templates/game/resource/skins/eui/GroupSkin.exml +0 -4
  135. package/templates/game/resource/skins/eui/HScrollBarSkin.exml +0 -6
  136. package/templates/game/resource/skins/eui/HSliderSkin.exml +0 -7
  137. package/templates/game/resource/skins/eui/ImageSkin.exml +0 -4
  138. package/templates/game/resource/skins/eui/ItemRendererSkin.exml +0 -15
  139. package/templates/game/resource/skins/eui/LabelSkin.exml +0 -7
  140. package/templates/game/resource/skins/eui/ListSkin.exml +0 -11
  141. package/templates/game/resource/skins/eui/PanelSkin.exml +0 -15
  142. package/templates/game/resource/skins/eui/ProgressBarSkin.exml +0 -11
  143. package/templates/game/resource/skins/eui/RadioButtonSkin.exml +0 -18
  144. package/templates/game/resource/skins/eui/ScrollerSkin.exml +0 -8
  145. package/templates/game/resource/skins/eui/TabBarSkin.exml +0 -6
  146. package/templates/game/resource/skins/eui/TextInputSkin.exml +0 -22
  147. package/templates/game/resource/skins/eui/ToggleButtonSkin.exml +0 -21
  148. package/templates/game/resource/skins/eui/ToggleSwitchSkin.exml +0 -15
  149. package/templates/game/resource/skins/eui/VScrollBarSkin.exml +0 -6
  150. package/templates/game/resource/skins/eui/VSliderSkin.exml +0 -7
  151. package/templates/game/resource/skins/eui/ViewStackSkin.exml +0 -4
  152. /package/dist/core/{exml → kui}/codegen.d.ts +0 -0
  153. /package/dist/core/{exml → kui}/source-location.d.ts +0 -0
  154. /package/dist/core/{exml → kui}/source-location.js +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,53 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 2.0.0 — 2026-09-21
11
+
12
+ This release is scoped to the Kurot Editor toolchain. Existing EXML game
13
+ projects remain supported by the 1.3.x line and are not expected to upgrade or
14
+ change their project configuration.
15
+
16
+ ### Added
17
+
18
+ - Canonical KUI XML Skin compilation through `@kurot/ui-document`.
19
+ - Generated default theme mappings derived from Skin `target` and `default`
20
+ metadata, including duplicate-default diagnostics.
21
+ - KUI XML project templates for all 21 built-in UI skins.
22
+
23
+ ### Changed
24
+
25
+ - Replaced the `exml` project configuration with `ui.sourceDir`,
26
+ `ui.namespaces`, and `ui.components`.
27
+ - Simplified SkinIR to the runtime operations produced by semantic KUI
28
+ documents and removed syntax-specific compatibility branches.
29
+ - The theme JSON is generated at the fixed `resource/default.thm.json` output
30
+ path rather than configured or supplied as an authored input.
31
+
32
+ ### Removed
33
+
34
+ - EXML parsing, theme-input discovery, `.exml` templates, and compatibility
35
+ diagnostics.
36
+
37
+ ## 1.3.0 — 2026-09-20
38
+
39
+ ### Added
40
+
41
+ - Generated declarations now narrow each exported project class's public
42
+ `skinParts` accessor from an explicit string-literal `skinName`, a configured
43
+ reusable-component pair, or a unique `<ClassName>Skin` naming match.
44
+ - Added ambiguity protection: convention inference is omitted when multiple
45
+ compiled skins share the same short `<ClassName>Skin` name.
46
+
47
+ ### Changed
48
+
49
+ - Project classes no longer need to repeat their skin as a base-class generic
50
+ when the CLI can establish an unambiguous host-to-skin relationship.
51
+
52
+ ### Tests
53
+
54
+ - Added regression coverage for explicit assignment, reusable-component,
55
+ unique naming-convention, and ambiguous naming-convention host inference.
56
+
10
57
  ## 1.2.0 — 2026-09-20
11
58
 
12
59
  ### Added
package/README.md CHANGED
@@ -1,371 +1,183 @@
1
1
  # @kurot/cli
2
2
 
3
- CLI tool for the Kurot game engine — a modern replacement for the legacy Egret CLI. Powered by esbuild for fast compilation, with a built-in EXML skin parser and code generator.
3
+ Build tooling for the Kurot UI Editor workflow. It uses esbuild, emits ES2022
4
+ ESM, and compiles canonical KUI XML skins into runtime theme modules.
4
5
 
5
- > **Current release: 1.2.0.** Requires Node.js 20 or later and emits ES2022 ESM projects.
6
+ > **Current release: 2.0.0.** Node.js 20 or later is required.
6
7
 
7
- > Migrating from Egret? See [egret-migration.md](../../docs/egret-migration.md)
8
- >
9
- > Release history: [CHANGELOG.md](CHANGELOG.md)
8
+ > **Release scope:** 2.0.0 is currently dedicated to Kurot Editor integration.
9
+ > Existing game projects that use EXML, including CrashMaster, should remain on
10
+ > `@kurot/cli@1.3.x`. Version 2.0.0 is not an in-place project upgrade and does
11
+ > not require those projects to change their configuration or UI assets.
10
12
 
11
- ## Usage
12
-
13
- `@kurot/cli` does **not** require a global install.
13
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
14
14
 
15
- ### Creating a project
15
+ ## Usage
16
16
 
17
- Use `npx` to scaffold a new project — no installation needed:
17
+ The CLI does not require a global install.
18
18
 
19
19
  ```bash
20
20
  npx @kurot/cli create my-game
21
- npx @kurot/cli create my-lib --template empty
22
- ```
23
-
24
- ### In-project commands
25
-
26
- Scaffolded projects include `@kurot/cli` as a devDependency and expose commands via npm scripts:
27
-
28
- ```bash
29
21
  cd my-game
30
22
  pnpm install
31
- pnpm build # build
32
- pnpm dev # dev server
33
- pnpm clean # clean output
23
+ pnpm dev
34
24
  ```
35
25
 
36
- You can also add it to an existing project manually:
37
-
38
- ```bash
39
- pnpm add -D @kurot/cli
40
- ```
26
+ Scaffolded projects expose `build`, `dev`, and `clean` scripts. For an
27
+ Editor-managed KUI XML project, install the package with
28
+ `pnpm add -D @kurot/cli@2`. Existing EXML projects should keep their current
29
+ 1.3.x dependency.
41
30
 
42
31
  ## Commands
43
32
 
44
33
  ### `kurot create`
45
34
 
46
- Scaffold a new project from a template.
47
-
48
35
  ```bash
49
- kurot create <name> [options]
36
+ kurot create <name> [--template game|empty]
50
37
  ```
51
38
 
52
- | Option | Description | Default |
53
- | ----------------------- | --------------------------- | ------- |
54
- | `--template <template>` | Template: `game` \| `empty` | `game` |
55
-
56
- **Templates:**
57
-
58
- | Template | Extends | Dependencies | Description |
59
- | -------- | --------- | ------------------------------------------------- | -------------------------------------------------------------------------------- |
60
- | `game` | `UILayer` | `@kurot/core` + `@kurot/game` + `@kurot/ui` | Full-featured project with resource loading, scene building, and Tween animation |
61
- | `empty` | `Sprite` | `@kurot/core` | Minimal project — pure Canvas rendering, no extra dependencies |
62
-
63
- **Lifecycle:**
64
-
65
- | Template | Entry class | Lifecycle |
66
- | -------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
67
- | `game` | `Main extends UILayer` | `createChildren` → `runGame` → `loadResource` → `loadTheme` → `createGameScene` → `startAnimation` |
68
- | `empty` | `Main extends Sprite` | constructor → `ADDED_TO_STAGE` → `onAddToStage` |
39
+ The `game` template includes `@kurot/core`, `@kurot/game`, `@kurot/ui`, KUI
40
+ skins, resource loading, and an editable HTML template. The `empty` template
41
+ contains a minimal `Sprite` application.
69
42
 
70
43
  ### `kurot build`
71
44
 
72
- Compile the project into ESM application, engine, namespace, and theme bundles.
73
-
74
- ```bash
75
- kurot build [options]
76
- ```
77
-
78
- | Option | Description | Default |
79
- | ------------------------ | ------------------------------------------------------ | ------- |
80
- | `-r, --release` | Minified, content-hashed release build (→ bin-release) | `false` |
81
- | `--sourcemap` | Generate sourcemaps | `false` |
82
- | `--watch` | Rebuild source on file changes | `false` |
83
- | `--analyze` | Print bundle size analysis (esbuild metafile) | `false` |
84
- | `--strict` | Promote supported warnings to build errors | `false` |
85
- | `--diagnostics <format>` | Diagnostic output: `human` or `json` | `human` |
86
-
87
- `--diagnostics json` writes exactly one JSON result to stdout. It includes
88
- `success`, `mode`, `durationMs`, the output directory on success, and all
89
- structured diagnostics. Release builds use strict diagnostic policy by default.
90
-
91
45
  ```bash
92
- kurot build --strict --diagnostics json
46
+ kurot build [--release] [--sourcemap] [--watch] [--analyze]
47
+ [--strict] [--diagnostics human|json]
93
48
  ```
94
49
 
95
- **Output (Egret-aligned shape, ESM under the hood):**
96
-
97
- | Mode | Layout |
98
- | -------------- | --------------------------------------------------------------------------------------------------------- |
99
- | development | `bin-debug/` — per-file `.js` mirroring `src/` (`Main.js`, `com/.../X.js`) + engine chunks in `js/` |
100
- | release (`-r`) | `bin-release/web/<timestamp>/` — `js/main.min_<hash>.js` + `js/kurot.*.min_<hash>.js` + `manifest.json` |
50
+ Development output is written to `bin-debug/`. Release output is written to
51
+ `bin-release/web/<timestamp>/` with minified, content-hashed files. Engine,
52
+ project namespace, theme, and application code are separate ESM chunks joined
53
+ by the generated HTML import map.
101
54
 
102
- Engine packages (`@kurot/*`) are bundled into separate `js/kurot.<name>.js`
103
- chunks and wired up through an HTML **import map**, so the app bundle and engine
104
- resolve bare specifiers (`import { Sprite } from '@kurot/core'`) in the browser
105
- without duplicating engine code. `resource/` (including the compiled
106
- `default.thm.json`) is copied with fixed names, since user code references those
107
- paths directly. The entry script bootstraps via your own `createPlayer()` call.
55
+ `--diagnostics json` reserves stdout for one machine-readable build result.
56
+ Release builds apply strict diagnostic policy by default.
108
57
 
109
58
  ### `kurot dev`
110
59
 
111
- Start a development server with auto-recompilation on file changes (manual browser refresh required).
112
-
113
- ```bash
114
- kurot dev [options]
115
- ```
116
-
117
- | Option | Description | Default |
118
- | ------------------------ | ------------------------------------------ | ------- |
119
- | `-p, --port <port>` | Port to listen | `3000` |
120
- | `--sourcemap` | Generate sourcemaps | `false` |
121
- | `--strict` | Promote supported warnings to build errors | `false` |
122
- | `--diagnostics <format>` | Diagnostic output: `human` or `jsonl` | `human` |
123
-
124
- Unlike build's single JSON result, `kurot dev --diagnostics jsonl` writes one
125
- JSON event per line so an agent can follow initial builds, diagnostics,
126
- rebuilds, and server readiness incrementally.
127
-
128
60
  ```bash
129
- kurot dev --strict --diagnostics jsonl
61
+ kurot dev [--port 3000] [--sourcemap] [--strict]
62
+ [--diagnostics human|jsonl]
130
63
  ```
131
64
 
132
- Machine-readable modes reserve stdout for their JSON protocol and never include
133
- ANSI color sequences. Failures set a non-zero process exit code.
65
+ The development server rebuilds TypeScript, KUI XML, custom namespaces, and
66
+ the component catalog as their sources change. Browser refresh is currently
67
+ manual. JSONL mode reserves stdout for incremental build and server events.
134
68
 
135
69
  ### `kurot clean`
136
70
 
137
- Remove the build output directories (`bin-debug` and `bin-release`).
138
-
139
- ```bash
140
- kurot clean
141
- ```
71
+ Removes `bin-debug` and `bin-release`.
142
72
 
143
73
  ## Configuration
144
74
 
145
- Create a `kurot.config.ts` in your project root:
75
+ Create `kurot.config.ts` in the project root:
146
76
 
147
77
  ```ts
148
78
  export default {
149
- target: 'html5',
150
- entry: 'src/Main.ts',
151
- output: { dir: 'bin-debug' },
152
- html: { template: 'template/web/index.html' },
153
- stage: {
154
- width: 640,
155
- height: 1136,
156
- scaleMode: 'showAll',
157
- orientation: 'auto',
158
- frameRate: 60,
159
- },
160
- // Optional: enable EXML skin compilation
161
- exml: {
162
- themeFile: 'resource/default.thm.json',
163
- // Optional: discover reusable component source/Skin pairs
164
- components: {
165
- namespace: 'game',
166
- sourceDir: 'src/components',
167
- skinDir: 'resource/skins/components',
168
- },
169
- },
79
+ target: 'html5',
80
+ entry: 'src/Main.ts',
81
+ output: { dir: 'bin-debug' },
82
+ html: { template: 'template/web/index.html' },
83
+ stage: {
84
+ width: 640,
85
+ height: 1136,
86
+ scaleMode: 'showAll',
87
+ orientation: 'auto',
88
+ frameRate: 60,
89
+ },
90
+ ui: {
91
+ sourceDir: 'resource/ui',
92
+ components: {
93
+ namespace: 'game',
94
+ sourceDir: 'src/components',
95
+ skinDir: 'resource/ui/components',
96
+ },
97
+ },
170
98
  };
171
99
  ```
172
100
 
173
- **Options:**
174
-
175
- | Field | Type | Description |
176
- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
177
- | `target` | `string` | Build target — currently only `'html5'` |
178
- | `entry` | `string` | Entry file path, default `'src/Main.ts'` |
179
- | `output.dir` | `string` | Output directory, default `'bin-debug'` |
180
- | `html.template` | `string` | Optional project-owned HTML template; the CLI default page is used when omitted |
181
- | `stage.width` | `number` | Stage width |
182
- | `stage.height` | `number` | Stage height |
183
- | `stage.scaleMode` | `string` | Scale mode: `showAll` / `noScale` / `exactFit` / `noBorder` / `fixedHeight` / `fixedWidth` / `fixedNarrow` / `fixedWide` |
184
- | `stage.orientation` | `string` | Orientation: `auto` / `portrait` / `landscape` |
185
- | `stage.frameRate` | `number` | Frame rate — must be a positive integer |
186
- | `exml.themeFile` | `string` | Theme JSON file path |
187
- | `exml.components` | `ComponentsConfig` | Optional reusable-component convention: namespace, TypeScript source directory, and Skin directory |
188
- | `exml.namespaces` | `Record<string, string>` | Optional EXML prefix → source barrel-file mapping |
189
-
190
- ## HTML Template
191
-
192
- New projects include an editable `template/web/index.html`. The build reads
193
- this file and writes the rendered page to the active output directory. Existing
194
- projects that do not configure `html.template` continue to use the CLI's
195
- built-in default page.
196
-
197
- The following placeholders are required in a configured project template:
198
-
199
- | Placeholder | Generated value |
200
- | --- | --- |
201
- | `{{KUROT_IMPORT_MAP}}` | Engine and custom namespace import map |
202
- | `{{KUROT_STAGE_WIDTH}}` | Configured stage width |
203
- | `{{KUROT_STAGE_HEIGHT}}` | Configured stage height |
204
- | `{{KUROT_SCALE_MODE}}` | Configured scale mode |
205
- | `{{KUROT_ORIENTATION}}` | Configured orientation |
206
- | `{{KUROT_FRAME_RATE}}` | Configured frame rate |
207
- | `{{KUROT_ENTRY_SCRIPT}}` | Compiled application entry script |
208
-
209
- The template may otherwise contain any project-specific HTML, styles, loading
210
- screen, platform SDK, analytics, fonts, or additional containers. A build fails
211
- with a clear error when a configured template is missing a required placeholder.
212
-
213
- ## EXML Skin Compiler
214
-
215
- The CLI includes a complete EXML skin parsing and code generation pipeline (XML → SkinIR → ESM JavaScript). `.exml` files placed in the `resource/` directory are compiled automatically during `kurot build`.
216
-
217
- ### Features
218
-
219
- - **XML Parsing** — lightweight parser with namespace, CDATA, and comment support
220
- - **AST / IR Generation** — converts to an intermediate representation (SkinIR)
221
- - **Code Generation** — outputs ESM factory functions
222
- - **Component Registry** — built-in `eui:*` / `egret:*` namespace mapping to `@kurot/ui` / `@kurot/core`
223
- - **Reusable Components** — pairs `src/components/<Name>.ts` with `resource/skins/components/<Name>Skin.exml`, then exposes `<game:Name>` without a hand-written barrel
224
- - **Custom Namespaces** — retains `exml.namespaces` for advanced manually maintained source barrels
225
- - **View States** — supports `<eui:states>`, shorthand `states="up,down"`, state properties, `includeIn`, and `excludeFrom`
226
- - **Skin Properties** — preserves root properties such as `minWidth`, `minHeight`, and state-specific values
227
- - **Percent Layout** — auto-detects `width="100%"` and converts to `percentWidth`
228
- - **Data Binding** — parses `{expression}` binding syntax and generates `Binding.bindProperty` calls
229
- - **Structured Diagnostics** — stable codes, source locations, suggestions, and strict warning promotion
230
-
231
- Unknown tags remain warnings in normal development builds and are omitted from
232
- the generated visual tree. Under `--strict` (and in release builds), those
233
- warnings become errors. Syntax errors, invalid theme JSON, and other genuine
234
- Skin compilation failures always stop the build; the compiler never substitutes
235
- an empty Skin factory.
236
-
237
- The standard declarations `xmlns:eui="http://ns.egret.com/eui"` and
238
- `xmlns:egret="http://ns.egret.com/egret"` are namespace identifiers. The CLI
239
- resolves their prefixes internally and does not access those URLs over the
240
- network, so the original Egret namespace pages do not need to be hosted.
241
-
242
- ### Reusable components
243
-
244
- The game template keeps reusable component logic and white-Egret-compatible
245
- skins in parallel directories:
246
-
247
- ```text
248
- src/components/<path>/<Name>.ts
249
- resource/skins/components/<path>/<Name>Skin.exml
250
- ```
101
+ `ui.sourceDir` contains `.kui.xml` documents. The build always generates the
102
+ runtime theme manifest at `resource/default.thm.json`; it is not an authored
103
+ input. `ui.namespaces` can map additional XML prefixes to project barrel files.
251
104
 
252
- The TypeScript file must export a class named `<Name>`, and the paired EXML
253
- must use a standard `eui:Skin` root with a `class` attribute. The CLI validates
254
- the pair, generates the shared `#ns/game` entry, adds the default Theme mapping,
255
- and accepts `<game:Name />` in other skins. Components are globally unique by
256
- class name within the configured namespace.
105
+ The HTML template must contain these placeholders:
257
106
 
258
- Development builds also emit `.kurot/component-catalog.json` for editor and
259
- agent tooling. The catalog is intentionally omitted from release output.
107
+ - `{{KUROT_IMPORT_MAP}}`
108
+ - `{{KUROT_STAGE_WIDTH}}`
109
+ - `{{KUROT_STAGE_HEIGHT}}`
110
+ - `{{KUROT_SCALE_MODE}}`
111
+ - `{{KUROT_ORIENTATION}}`
112
+ - `{{KUROT_FRAME_RATE}}`
113
+ - `{{KUROT_ENTRY_SCRIPT}}`
260
114
 
261
- Every successful EXML compilation also writes `.kurot/skin-parts.d.ts`. It
262
- augments `@kurot/ui`'s `SkinPartsMap` with the exact named parts and runtime
263
- types found in each compiled skin. The generated file is included by the
264
- template `tsconfig.json`, ignored by git, and never enters browser or release
265
- bundles.
115
+ ## KUI XML compilation
266
116
 
267
- `exml.namespaces` remains available for advanced manual namespace barrels, but
268
- its prefix must not conflict with `exml.components.namespace`.
117
+ KUI XML is the only authored UI format. A skin declares its runtime target and
118
+ whether it is the default skin directly on the document root:
269
119
 
270
- ### Custom component lifecycle
271
-
272
- Select the generated part shape through the skin class name and initialize
273
- skin-dependent behavior in `onSkinReady()`:
274
-
275
- ```ts
276
- import { Component } from '@kurot/ui';
277
-
278
- export class BattlePanel extends Component<'game.ui.BattlePanelSkin'> {
279
- protected override onSkinReady(): void {
280
- super.onSkinReady();
281
- this.skinParts.groupField.visible = true;
282
- }
283
- }
120
+ ```xml
121
+ <?xml version="1.0" encoding="utf-8"?>
122
+ <Skin xmlns="https://kurot.dev/ui/1"
123
+ id="skins.ButtonSkin"
124
+ version="2"
125
+ target="kui.Button"
126
+ default="true">
127
+ <Group id="root" minWidth="100" minHeight="50">
128
+ <Label id="labelDisplay" horizontalCenter="0" verticalCenter="0" />
129
+ </Group>
130
+ </Skin>
284
131
  ```
285
132
 
286
- The event-based equivalent is useful when initialization is composed externally:
133
+ The pipeline is:
287
134
 
288
- ```ts
289
- import { UIEvent } from '@kurot/ui';
290
-
291
- this.once(UIEvent.CREATION_COMPLETE, this.onCreationComplete);
135
+ ```text
136
+ .kui.xml → UIDocument → SkinIR → ESM skin factory → theme bundle
292
137
  ```
293
138
 
294
- Use `onSkinReady()` to initialize logic that depends on skin parts and
295
- `onSkinRemoved()` to release listeners or other bindings before replacement.
296
- `childrenCreated()` and `UIEvent.CREATION_COMPLETE` run once for the component's
297
- initial creation and are not skin-replacement hooks.
298
-
299
- UI 2.0 no longer copies part names onto component instances and no longer
300
- supports `setSkinPart()`, `partAdded()`, or `partRemoved()`. Read parts through
301
- `this.skinParts` only while the skin-ready lifecycle is active.
139
+ Default skin mappings are derived from `target` and `default`; the build writes
140
+ `default.thm.json` with the mappings and the generated `skinsJs` module path.
141
+ Duplicate defaults are errors. Unknown tags are warnings in normal development
142
+ and errors under strict or release builds.
302
143
 
303
- EXML skins compiled by the CLI are registered under their complete `class`
304
- attribute, so an Egret-style value such as
305
- `skinName = "game.ui.BattlePanelSkin"` works when that EXML is included in the
306
- loaded theme bundle. A component covered by the theme's `skins` mapping normally
307
- does not need to assign `skinName` itself. Hand-written `Skin` subclasses may be
308
- imported and assigned directly instead of using a string.
144
+ Successful compilation also writes `.kurot/skin-parts.d.ts`. It augments the UI
145
+ runtime with the exact named parts and types declared by each skin. This file is
146
+ editor-only, ignored by git, and never enters browser bundles.
309
147
 
310
- ### Compilation Pipeline
148
+ ## Reusable components
311
149
 
312
- All `.exml` skins compile into a single ESM module — `js/default.thm.js` (dev)
313
- or `js/default.thm.min_<hash>.js` (release) — that registers each skin factory.
314
- `default.thm.json` keeps only the component→skin mapping plus a `skinsJs`
315
- pointer to that module, which the runtime `Theme` imports. No `.exml` is shipped.
150
+ Convention-based reusable components pair:
316
151
 
317
- ```
318
- resource/skins/**/*.exml
319
- ↓ parseXML()
320
- XML Element Tree
321
- ↓ parseEXML()
322
- SkinIR
323
- ↓ generateCode({ format: 'esm' })
324
- per-skin ESM factories
325
- ↓ esbuild bundle (+ minify in release)
326
- js/default.thm[.min_<hash>].js (skins register on globalThis)
152
+ ```text
153
+ src/components/<path>/<Name>.ts
154
+ resource/ui/components/<path>/<Name>Skin.kui.xml
327
155
  ```
328
156
 
329
- ## Project Structure
157
+ The TypeScript module must export `<Name>`. The paired skin must target
158
+ `<namespace>.<Name>`. The CLI exposes the component as `<namespace>:<Name>` in
159
+ KUI XML, refreshes the namespace bundle, and emits development catalog data at
160
+ `.kurot/component-catalog.json`.
330
161
 
331
- A project created with the default template (`game`) has the following structure:
162
+ Use `onSkinReady()` for logic that needs skin parts and `onSkinRemoved()` for
163
+ cleanup before a skin replacement. Access generated parts through
164
+ `this.skinParts`.
332
165
 
333
- ```
166
+ ## Generated project shape
167
+
168
+ ```text
334
169
  my-game/
335
- ├── .kurot/
336
- │ └── skin-parts.d.ts # Generated typed EXML part declarations
337
- ├── .gitignore # Excludes .kurot and build/dependency output
338
- ├── kurot.config.ts # Project config (includes exml options)
339
- ├── package.json # Dependencies & scripts
340
- ├── tsconfig.json # TypeScript config
341
- ├── template/
342
- │ └── web/
343
- │ └── index.html # Editable output page template
170
+ ├── .kurot/skin-parts.d.ts
171
+ ├── kurot.config.ts
344
172
  ├── resource/
345
- │ ├── default.res.json # Resource config
346
- │ ├── default.thm.json # Theme file: component → skin mapping
347
- │ └── skins/ # EXML skin directory
348
- │ ├── components/ # Reusable component skins (standard eui:Skin)
349
- │ └── eui/ # 21 built-in EUI component skins
350
- │ ├── ButtonSkin.exml
351
- │ ├── ...
352
- │ └── ViewStackSkin.exml
353
- └── src/
354
- ├── components/ # Reusable component TypeScript classes
355
- ├── Main.ts # Entry: class Main extends Sprite
356
- └── LoadingUI.ts # Loading progress display
357
- ```
358
-
359
- ## Quick Start
360
-
361
- ```bash
362
- # Full-featured game project (default)
363
- npx @kurot/cli create my-game
364
- cd my-game && pnpm install
365
- pnpm dev
366
-
367
- # Minimal project
368
- npx @kurot/cli create my-lib --template empty
369
- cd my-lib && pnpm install
370
- pnpm dev
173
+ │ ├── default.res.json
174
+ │ ├── assets/
175
+ │ └── ui/
176
+ │ ├── components/
177
+ │ └── skins/*.kui.xml
178
+ ├── src/
179
+ │ ├── components/
180
+ │ ├── LoadingUI.ts
181
+ │ └── Main.ts
182
+ └── template/web/index.html
371
183
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"discover-components.d.ts","sourceRoot":"","sources":["../../../src/core/components/discover-components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqF7B;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB"}
1
+ {"version":3,"file":"discover-components.d.ts","sourceRoot":"","sources":["../../../src/core/components/discover-components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3E;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAsF7B;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC;CACxC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB"}
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
+ import { parseUIDocument } from '@kurot/ui-document';
3
4
  import { ConfigError } from '../errors.js';
4
- import { localName, parseXML } from '../exml/index.js';
5
5
  /**
6
6
  * Discovers reusable component source/skin pairs using the configured
7
7
  * directory convention.
@@ -11,7 +11,7 @@ export async function discoverComponents(root, convention) {
11
11
  return [];
12
12
  const [sourceFiles, skinFiles] = await Promise.all([
13
13
  collectFiles(convention.sourceDir, file => file.endsWith('.ts') && !file.endsWith('.d.ts')),
14
- collectFiles(convention.skinDir, file => file.endsWith('Skin.exml')),
14
+ collectFiles(convention.skinDir, file => file.endsWith('Skin.kui.xml')),
15
15
  ]);
16
16
  const sourceByPair = new Map(sourceFiles.map(file => [sourcePairKey(convention.sourceDir, file), file]));
17
17
  const skinByPair = new Map(skinFiles.map(file => [skinPairKey(convention.skinDir, file), file]));
@@ -29,7 +29,7 @@ export async function discoverComponents(root, convention) {
29
29
  continue;
30
30
  }
31
31
  if (!skin) {
32
- errors.push(`Component source '${relative(root, source)}' has no matching skin '${pairKey}Skin.exml'.`);
32
+ errors.push(`Component source '${relative(root, source)}' has no matching skin '${pairKey}Skin.kui.xml'.`);
33
33
  continue;
34
34
  }
35
35
  const name = path.basename(source, '.ts');
@@ -54,20 +54,21 @@ export async function discoverComponents(root, convention) {
54
54
  }
55
55
  let skinClass;
56
56
  try {
57
- const skinRoot = parseXML(await fs.readFile(skin, 'utf-8'));
58
- if (localName(skinRoot.name) !== 'Skin') {
59
- errors.push(`Component skin '${relative(root, skin)}' must use an eui:Skin root.`);
57
+ const skinDocument = parseUIDocument(await fs.readFile(skin, 'utf-8'));
58
+ if (skinDocument.assetKind !== 'appearance') {
59
+ errors.push(`Component skin '${relative(root, skin)}' must be a KUI Skin document.`);
60
60
  continue;
61
61
  }
62
- skinClass = skinRoot.attributes.find(attribute => attribute.name === 'class')?.value ?? '';
63
- if (!skinClass) {
64
- errors.push(`Component skin '${relative(root, skin)}' must declare a class attribute.`);
62
+ const targetType = `${convention.prefix}.${name}`;
63
+ if (skinDocument.contract.targetType !== targetType) {
64
+ errors.push(`Component skin '${relative(root, skin)}' must target '${targetType}'.`);
65
65
  continue;
66
66
  }
67
+ skinClass = skinDocument.id;
67
68
  }
68
69
  catch (error) {
69
70
  const message = error instanceof Error ? error.message : String(error);
70
- errors.push(`Component skin '${relative(root, skin)}' is invalid EXML: ${message}`);
71
+ errors.push(`Component skin '${relative(root, skin)}' is invalid KUI XML: ${message}`);
71
72
  continue;
72
73
  }
73
74
  components.push({
@@ -124,7 +125,7 @@ function sourcePairKey(sourceDir, file) {
124
125
  return toPosix(path.relative(sourceDir, file).slice(0, -'.ts'.length));
125
126
  }
126
127
  function skinPairKey(skinDir, file) {
127
- return toPosix(path.relative(skinDir, file).slice(0, -'Skin.exml'.length));
128
+ return toPosix(path.relative(skinDir, file).slice(0, -'Skin.kui.xml'.length));
128
129
  }
129
130
  function hasNamedClassExport(source, name) {
130
131
  source = stripComments(source);