@kurot/cli 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +296 -0
  3. package/dist/commands/build.d.ts +3 -0
  4. package/dist/commands/build.d.ts.map +1 -0
  5. package/dist/commands/build.js +59 -0
  6. package/dist/commands/build.js.map +1 -0
  7. package/dist/commands/clean.d.ts +3 -0
  8. package/dist/commands/clean.d.ts.map +1 -0
  9. package/dist/commands/clean.js +15 -0
  10. package/dist/commands/clean.js.map +1 -0
  11. package/dist/commands/create.d.ts +3 -0
  12. package/dist/commands/create.d.ts.map +1 -0
  13. package/dist/commands/create.js +24 -0
  14. package/dist/commands/create.js.map +1 -0
  15. package/dist/commands/dev.d.ts +3 -0
  16. package/dist/commands/dev.d.ts.map +1 -0
  17. package/dist/commands/dev.js +25 -0
  18. package/dist/commands/dev.js.map +1 -0
  19. package/dist/core/config.d.ts +94 -0
  20. package/dist/core/config.d.ts.map +1 -0
  21. package/dist/core/config.js +70 -0
  22. package/dist/core/config.js.map +1 -0
  23. package/dist/core/dev-server.d.ts +21 -0
  24. package/dist/core/dev-server.d.ts.map +1 -0
  25. package/dist/core/dev-server.js +113 -0
  26. package/dist/core/dev-server.js.map +1 -0
  27. package/dist/core/errors.d.ts +16 -0
  28. package/dist/core/errors.d.ts.map +1 -0
  29. package/dist/core/errors.js +23 -0
  30. package/dist/core/errors.js.map +1 -0
  31. package/dist/core/exml/ast.d.ts +243 -0
  32. package/dist/core/exml/ast.d.ts.map +1 -0
  33. package/dist/core/exml/ast.js +8 -0
  34. package/dist/core/exml/ast.js.map +1 -0
  35. package/dist/core/exml/codegen.d.ts +23 -0
  36. package/dist/core/exml/codegen.d.ts.map +1 -0
  37. package/dist/core/exml/codegen.js +422 -0
  38. package/dist/core/exml/codegen.js.map +1 -0
  39. package/dist/core/exml/exml-parser.d.ts +27 -0
  40. package/dist/core/exml/exml-parser.d.ts.map +1 -0
  41. package/dist/core/exml/exml-parser.js +466 -0
  42. package/dist/core/exml/exml-parser.js.map +1 -0
  43. package/dist/core/exml/index.d.ts +52 -0
  44. package/dist/core/exml/index.d.ts.map +1 -0
  45. package/dist/core/exml/index.js +51 -0
  46. package/dist/core/exml/index.js.map +1 -0
  47. package/dist/core/exml/registry.d.ts +92 -0
  48. package/dist/core/exml/registry.d.ts.map +1 -0
  49. package/dist/core/exml/registry.js +177 -0
  50. package/dist/core/exml/registry.js.map +1 -0
  51. package/dist/core/exml/xml-parser.d.ts +61 -0
  52. package/dist/core/exml/xml-parser.d.ts.map +1 -0
  53. package/dist/core/exml/xml-parser.js +201 -0
  54. package/dist/core/exml/xml-parser.js.map +1 -0
  55. package/dist/core/namespace-external-plugin.d.ts +45 -0
  56. package/dist/core/namespace-external-plugin.d.ts.map +1 -0
  57. package/dist/core/namespace-external-plugin.js +66 -0
  58. package/dist/core/namespace-external-plugin.js.map +1 -0
  59. package/dist/core/pipeline.d.ts +75 -0
  60. package/dist/core/pipeline.d.ts.map +1 -0
  61. package/dist/core/pipeline.js +30 -0
  62. package/dist/core/pipeline.js.map +1 -0
  63. package/dist/core/plugins/clean-output.d.ts +3 -0
  64. package/dist/core/plugins/clean-output.d.ts.map +1 -0
  65. package/dist/core/plugins/clean-output.js +11 -0
  66. package/dist/core/plugins/clean-output.js.map +1 -0
  67. package/dist/core/plugins/compile-custom-namespaces.d.ts +19 -0
  68. package/dist/core/plugins/compile-custom-namespaces.d.ts.map +1 -0
  69. package/dist/core/plugins/compile-custom-namespaces.js +104 -0
  70. package/dist/core/plugins/compile-custom-namespaces.js.map +1 -0
  71. package/dist/core/plugins/compile-engine.d.ts +19 -0
  72. package/dist/core/plugins/compile-engine.d.ts.map +1 -0
  73. package/dist/core/plugins/compile-engine.js +75 -0
  74. package/dist/core/plugins/compile-engine.js.map +1 -0
  75. package/dist/core/plugins/compile-exml.d.ts +14 -0
  76. package/dist/core/plugins/compile-exml.d.ts.map +1 -0
  77. package/dist/core/plugins/compile-exml.js +223 -0
  78. package/dist/core/plugins/compile-exml.js.map +1 -0
  79. package/dist/core/plugins/compile-source.d.ts +14 -0
  80. package/dist/core/plugins/compile-source.d.ts.map +1 -0
  81. package/dist/core/plugins/compile-source.js +132 -0
  82. package/dist/core/plugins/compile-source.js.map +1 -0
  83. package/dist/core/plugins/copy-assets.d.ts +12 -0
  84. package/dist/core/plugins/copy-assets.d.ts.map +1 -0
  85. package/dist/core/plugins/copy-assets.js +33 -0
  86. package/dist/core/plugins/copy-assets.js.map +1 -0
  87. package/dist/core/plugins/generate-html.d.ts +16 -0
  88. package/dist/core/plugins/generate-html.d.ts.map +1 -0
  89. package/dist/core/plugins/generate-html.js +137 -0
  90. package/dist/core/plugins/generate-html.js.map +1 -0
  91. package/dist/core/plugins/index.d.ts +21 -0
  92. package/dist/core/plugins/index.d.ts.map +1 -0
  93. package/dist/core/plugins/index.js +39 -0
  94. package/dist/core/plugins/index.js.map +1 -0
  95. package/dist/core/plugins/manifest.d.ts +14 -0
  96. package/dist/core/plugins/manifest.d.ts.map +1 -0
  97. package/dist/core/plugins/manifest.js +28 -0
  98. package/dist/core/plugins/manifest.js.map +1 -0
  99. package/dist/core/project.d.ts +95 -0
  100. package/dist/core/project.d.ts.map +1 -0
  101. package/dist/core/project.js +79 -0
  102. package/dist/core/project.js.map +1 -0
  103. package/dist/core/template.d.ts +13 -0
  104. package/dist/core/template.d.ts.map +1 -0
  105. package/dist/core/template.js +79 -0
  106. package/dist/core/template.js.map +1 -0
  107. package/dist/define.d.ts +2 -0
  108. package/dist/define.d.ts.map +1 -0
  109. package/dist/define.js +2 -0
  110. package/dist/define.js.map +1 -0
  111. package/dist/index.d.ts +3 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +19 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/utils/fs.d.ts +5 -0
  116. package/dist/utils/fs.d.ts.map +1 -0
  117. package/dist/utils/fs.js +30 -0
  118. package/dist/utils/fs.js.map +1 -0
  119. package/dist/utils/logger.d.ts +11 -0
  120. package/dist/utils/logger.d.ts.map +1 -0
  121. package/dist/utils/logger.js +18 -0
  122. package/dist/utils/logger.js.map +1 -0
  123. package/package.json +45 -0
  124. package/templates/empty/kurot.config.ts +13 -0
  125. package/templates/empty/package.json +17 -0
  126. package/templates/empty/pnpm-workspace.yaml +3 -0
  127. package/templates/empty/src/Main.ts +32 -0
  128. package/templates/empty/template/web/index.html +38 -0
  129. package/templates/empty/tsconfig.json +10 -0
  130. package/templates/game/kurot.config.ts +16 -0
  131. package/templates/game/package.json +19 -0
  132. package/templates/game/pnpm-workspace.yaml +3 -0
  133. package/templates/game/resource/assets/eui.json +24 -0
  134. package/templates/game/resource/assets/eui.png +0 -0
  135. package/templates/game/resource/default.res.json +15 -0
  136. package/templates/game/resource/default.thm.json +19 -0
  137. package/templates/game/resource/skins/ButtonSkin.exml +16 -0
  138. package/templates/game/resource/skins/CheckBoxSkin.exml +18 -0
  139. package/templates/game/resource/skins/ComboBoxSkin.exml +31 -0
  140. package/templates/game/resource/skins/GroupSkin.exml +4 -0
  141. package/templates/game/resource/skins/HScrollBarSkin.exml +6 -0
  142. package/templates/game/resource/skins/HSliderSkin.exml +7 -0
  143. package/templates/game/resource/skins/ImageSkin.exml +4 -0
  144. package/templates/game/resource/skins/ItemRendererSkin.exml +15 -0
  145. package/templates/game/resource/skins/LabelSkin.exml +7 -0
  146. package/templates/game/resource/skins/ListSkin.exml +11 -0
  147. package/templates/game/resource/skins/PanelSkin.exml +15 -0
  148. package/templates/game/resource/skins/ProgressBarSkin.exml +11 -0
  149. package/templates/game/resource/skins/RadioButtonSkin.exml +18 -0
  150. package/templates/game/resource/skins/ScrollerSkin.exml +8 -0
  151. package/templates/game/resource/skins/TabBarSkin.exml +6 -0
  152. package/templates/game/resource/skins/TextInputSkin.exml +22 -0
  153. package/templates/game/resource/skins/ToggleButtonSkin.exml +21 -0
  154. package/templates/game/resource/skins/ToggleSwitchSkin.exml +15 -0
  155. package/templates/game/resource/skins/VScrollBarSkin.exml +6 -0
  156. package/templates/game/resource/skins/VSliderSkin.exml +7 -0
  157. package/templates/game/resource/skins/ViewStackSkin.exml +4 -0
  158. package/templates/game/src/LoadingUI.ts +27 -0
  159. package/templates/game/src/Main.ts +165 -0
  160. package/templates/game/template/web/index.html +109 -0
  161. package/templates/game/template/web/logo.png +0 -0
  162. package/templates/game/tsconfig.json +10 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@kurot/cli` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## Unreleased
9
+
10
+ ### Added
11
+
12
+ - Project-owned HTML templates through `html.template`, with explicit placeholders for the import map, entry script, and stage settings.
13
+ - Editable `template/web/index.html` files in both the `game` and `empty` project templates.
14
+
15
+ ### Changed
16
+
17
+ - The default page centers the game canvas horizontally and vertically.
18
+ - Development builds clear the previous output before compiling and place shared application chunks under `js/chunks/`.
19
+
20
+ ## 1.0.0 — 2026-08-06
21
+
22
+ ### Added
23
+
24
+ - **`scale9Grid` attribute compiled to `new Rectangle()`** — EXML attributes like `scale9Grid="1,3,8,8"` are now compiled to `new Rectangle(1, 3, 8, 8)` instead of being passed as a raw string. Core `Rectangle` is automatically imported when a skin contains any `scale9Grid` property.
25
+ - **Lowercase property-node shorthand** — `<eui:layout><eui:HorizontalLayout/></eui:layout>` is now accepted as a shorthand for the Egret-qualified `<eui:Group.layout>` tag, bringing EXML parsing closer to Egret's original behaviour.
26
+ - **Game template: resource-aware asset adapter** — `Main.ts` now installs a custom `AssetAdapter` that resolves EXML `source` strings through `resource.get<Texture>()` first (supports preloaded atlases and sprite sheets), falling back to the default URL-based `ImageLoader`.
27
+
28
+ ### Changed
29
+
30
+ - **Updated all game-template skins** — Button, CheckBox, HScrollBar, HSlider, ItemRenderer, Panel, ProgressBar, RadioButton, TextInput, ToggleSwitch, VScrollBar, and VSlider skins modernised with consistent constraints, state names, and sizing conventions matching Egret EUI defaults.
31
+ - Release builds now fail on invalid EXML instead of silently publishing an empty skin stub; development builds continue with a warning for faster iteration.
32
+
33
+ ### Tests
34
+
35
+ - `test/exml-parser.test.ts`: 2 new cases (scale9Grid Rectangle compilation, lowercase property-node shorthand).
36
+
37
+ ---
38
+
39
+ ## 0.7.2 — 2026-08-06
40
+
41
+ ### Changed
42
+
43
+ - **TextInputSkin**: `textDisplay` and `promptDisplay` now use `left="10"` / `right="10"` constraints so their layout bounds match the input background and native StageText overlay.
44
+ - Updated the game-template documentation to describe its current `UILayer` / `createChildren()` lifecycle and the recommended custom-component initialization hooks.
45
+ - Release builds now fail on invalid EXML instead of silently publishing an empty skin stub; development builds continue with a warning for faster iteration.
46
+
47
+ ## 0.7.1 — 2026-08-06
48
+
49
+ ### Changed
50
+
51
+ - **HSliderSkin / VSliderSkin**: tracks now use `width="100%"` / `height="100%"` instead of inset `left`/`right` / `top`/`bottom` constraints, aligning with the `@kurot/ui` 1.1.0 fix that positions the thumb relative to the track's layout bounds.
52
+ - **TextInputSkin**: `textDisplay` and `promptDisplay` use a fixed height with `verticalCenter`; `promptDisplay` sets `multiline="false"` / `wordWrap="false"` to prevent accidental wrapping of placeholder text.
53
+
54
+ ## 0.7.0 - 2026-08-06
55
+
56
+ ### Added
57
+
58
+ - Support for root `Skin` properties such as `minWidth` and `minHeight` during EXML compilation.
59
+ - Support for shorthand `states="up,down,disabled"` declarations and root state-specific properties.
60
+ - Support for `excludeFrom` as well as `includeIn` when generating state overrides.
61
+ - Parser and code-generation coverage for every EXML skin included in the game template.
62
+
63
+ ### Changed
64
+
65
+ - Updated the default game-template skins to follow Egret EUI state and sizing conventions more closely.
66
+ - Made control, container, slider, and scrollbar skins responsive through constraints and minimum dimensions.
67
+
68
+ ## 0.6.1 - 2026-07-16
69
+
70
+ ### Added
71
+
72
+ - Support for project-defined EXML namespace prefixes through `exml.namespaces`.
73
+ - Shared namespace chunks so classes referenced by both game code and EXML retain the same module identity.
74
+
75
+ ## 0.6.0 - 2026-06-08
76
+
77
+ ### Changed
78
+
79
+ - Compiled all EXML skins into a bundled ESM theme module loaded dynamically by the runtime.
80
+ - Stopped shipping source `.exml` files in release output.
81
+
82
+ ## 0.5.1 - 2026-06-07
83
+
84
+ ### Changed
85
+
86
+ - Added npm publishing configuration and package metadata updates.
87
+
88
+ ## 0.5.0 - 2026-06-07
89
+
90
+ ### Added
91
+
92
+ - Split Kurot engine packages into independent browser chunks.
93
+ - Generated import maps and manifests to connect application, engine, and theme modules without duplication.
94
+
95
+ ## 0.4.0 - 2026-06-06
96
+
97
+ ### Changed
98
+
99
+ - Reworked the build system around an extensible plugin pipeline.
100
+
101
+ ## 0.3.11 - 2026-05-08
102
+
103
+ ### Changed
104
+
105
+ - Switched release builds to two-pass bundling with content-hashed filenames.
106
+
107
+ ## 0.3.0 - 2026-05-07
108
+
109
+ ### Added
110
+
111
+ - Bundled development builds and EXML code generation.
112
+ - Game and empty project templates with local CLI scripts.
113
+
114
+ ### Changed
115
+
116
+ - Moved default skins and resource configuration into the game template.
117
+
118
+ ## 0.2.0 - 2026-05-05
119
+
120
+ ### Added
121
+
122
+ - Built-in EXML parsing, code generation, view states, watch mode, and bundle analysis.
123
+
124
+ ## 0.1.0 - 2026-05-01
125
+
126
+ ### Added
127
+
128
+ - Initial `@kurot/cli` release with project creation, HTML5 builds, development server, and cleaning commands.
package/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # @kurot/cli
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.
4
+
5
+ > Migrating from Egret? See [migration.md](docs/migration.md)
6
+ >
7
+ > Release history: [CHANGELOG.md](CHANGELOG.md)
8
+
9
+ ## Usage
10
+
11
+ `@kurot/cli` does **not** require a global install.
12
+
13
+ ### Creating a project
14
+
15
+ Use `npx` to scaffold a new project — no installation needed:
16
+
17
+ ```bash
18
+ npx @kurot/cli create my-game
19
+ npx @kurot/cli create my-lib --template empty
20
+ ```
21
+
22
+ ### In-project commands
23
+
24
+ Scaffolded projects include `@kurot/cli` as a devDependency and expose commands via npm scripts:
25
+
26
+ ```bash
27
+ cd my-game
28
+ pnpm install
29
+ pnpm build # build
30
+ pnpm dev # dev server
31
+ pnpm clean # clean output
32
+ ```
33
+
34
+ You can also add it to an existing project manually:
35
+
36
+ ```bash
37
+ pnpm add -D @kurot/cli
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ ### `kurot create`
43
+
44
+ Scaffold a new project from a template.
45
+
46
+ ```bash
47
+ kurot create <name> [options]
48
+ ```
49
+
50
+ | Option | Description | Default |
51
+ | ----------------------- | --------------------------- | ------- |
52
+ | `--template <template>` | Template: `game` \| `empty` | `game` |
53
+
54
+ **Templates:**
55
+
56
+ | Template | Extends | Dependencies | Description |
57
+ | -------- | --------- | ------------------------------------------------- | -------------------------------------------------------------------------------- |
58
+ | `game` | `UILayer` | `@kurot/core` + `@kurot/game` + `@kurot/ui` | Full-featured project with resource loading, scene building, and Tween animation |
59
+ | `empty` | `Sprite` | `@kurot/core` | Minimal project — pure Canvas rendering, no extra dependencies |
60
+
61
+ **Lifecycle:**
62
+
63
+ | Template | Entry class | Lifecycle |
64
+ | -------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
65
+ | `game` | `Main extends UILayer` | `createChildren` → `runGame` → `loadResource` → `loadTheme` → `createGameScene` → `startAnimation` |
66
+ | `empty` | `Main extends Sprite` | constructor → `ADDED_TO_STAGE` → `onAddToStage` |
67
+
68
+ ### `kurot build`
69
+
70
+ Compile the project into ESM application, engine, namespace, and theme bundles.
71
+
72
+ ```bash
73
+ kurot build [options]
74
+ ```
75
+
76
+ | Option | Description | Default |
77
+ | --------------- | ------------------------------------------------------ | ------- |
78
+ | `-r, --release` | Minified, content-hashed release build (→ bin-release) | `false` |
79
+ | `--sourcemap` | Generate sourcemaps | `false` |
80
+ | `--watch` | Rebuild source on file changes | `false` |
81
+ | `--analyze` | Print bundle size analysis (esbuild metafile) | `false` |
82
+
83
+ **Output (Egret-aligned shape, ESM under the hood):**
84
+
85
+ | Mode | Layout |
86
+ | -------------- | --------------------------------------------------------------------------------------------------------- |
87
+ | development | `bin-debug/` — per-file `.js` mirroring `src/` (`Main.js`, `com/.../X.js`) + engine chunks in `js/` |
88
+ | release (`-r`) | `bin-release/web/<timestamp>/` — `js/main.min_<hash>.js` + `js/kurot.*.min_<hash>.js` + `manifest.json` |
89
+
90
+ Engine packages (`@kurot/*`) are bundled into separate `js/kurot.<name>.js`
91
+ chunks and wired up through an HTML **import map**, so the app bundle and engine
92
+ resolve bare specifiers (`import { Sprite } from '@kurot/core'`) in the browser
93
+ without duplicating engine code. `resource/` (including the compiled
94
+ `default.thm.json`) is copied with fixed names, since user code references those
95
+ paths directly. The entry script bootstraps via your own `createPlayer()` call.
96
+
97
+ ### `kurot dev`
98
+
99
+ Start a development server with auto-recompilation on file changes (manual browser refresh required).
100
+
101
+ ```bash
102
+ kurot dev [options]
103
+ ```
104
+
105
+ | Option | Description | Default |
106
+ | ------------------- | ------------------- | ------- |
107
+ | `-p, --port <port>` | Port to listen | `3000` |
108
+ | `--sourcemap` | Generate sourcemaps | `false` |
109
+
110
+ ### `kurot clean`
111
+
112
+ Remove the build output directories (`bin-debug` and `bin-release`).
113
+
114
+ ```bash
115
+ kurot clean
116
+ ```
117
+
118
+ ## Configuration
119
+
120
+ Create a `kurot.config.ts` in your project root:
121
+
122
+ ```ts
123
+ export default {
124
+ target: 'html5',
125
+ entry: 'src/Main.ts',
126
+ output: { dir: 'bin-debug' },
127
+ html: { template: 'template/web/index.html' },
128
+ stage: {
129
+ width: 640,
130
+ height: 1136,
131
+ scaleMode: 'showAll',
132
+ orientation: 'auto',
133
+ frameRate: 60,
134
+ },
135
+ // Optional: enable EXML skin compilation
136
+ exml: {
137
+ themeFile: 'resource/default.thm.json',
138
+ // Optional: map a custom EXML prefix to a source barrel file
139
+ namespaces: {
140
+ game: 'src/ui/index.ts',
141
+ },
142
+ },
143
+ };
144
+ ```
145
+
146
+ **Options:**
147
+
148
+ | Field | Type | Description |
149
+ | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
150
+ | `target` | `string` | Build target — currently only `'html5'` |
151
+ | `entry` | `string` | Entry file path, default `'src/Main.ts'` |
152
+ | `output.dir` | `string` | Output directory, default `'bin-debug'` |
153
+ | `html.template` | `string` | Optional project-owned HTML template; the CLI default page is used when omitted |
154
+ | `stage.width` | `number` | Stage width |
155
+ | `stage.height` | `number` | Stage height |
156
+ | `stage.scaleMode` | `string` | Scale mode: `showAll` / `noScale` / `exactFit` / `noBorder` / `fixedHeight` / `fixedWidth` / `fixedNarrow` / `fixedWide` |
157
+ | `stage.orientation` | `string` | Orientation: `auto` / `portrait` / `landscape` |
158
+ | `stage.frameRate` | `number` | Frame rate — must be a positive integer |
159
+ | `exml.themeFile` | `string` | Theme JSON file path |
160
+ | `exml.namespaces` | `Record<string, string>` | Optional EXML prefix → source barrel-file mapping |
161
+
162
+ ## HTML Template
163
+
164
+ New projects include an editable `template/web/index.html`. The build reads
165
+ this file and writes the rendered page to the active output directory. Existing
166
+ projects that do not configure `html.template` continue to use the CLI's
167
+ built-in default page.
168
+
169
+ The following placeholders are required in a configured project template:
170
+
171
+ | Placeholder | Generated value |
172
+ | --- | --- |
173
+ | `{{KUROT_IMPORT_MAP}}` | Engine and custom namespace import map |
174
+ | `{{KUROT_STAGE_WIDTH}}` | Configured stage width |
175
+ | `{{KUROT_STAGE_HEIGHT}}` | Configured stage height |
176
+ | `{{KUROT_SCALE_MODE}}` | Configured scale mode |
177
+ | `{{KUROT_ORIENTATION}}` | Configured orientation |
178
+ | `{{KUROT_FRAME_RATE}}` | Configured frame rate |
179
+ | `{{KUROT_ENTRY_SCRIPT}}` | Compiled application entry script |
180
+
181
+ The template may otherwise contain any project-specific HTML, styles, loading
182
+ screen, platform SDK, analytics, fonts, or additional containers. A build fails
183
+ with a clear error when a configured template is missing a required placeholder.
184
+
185
+ ## EXML Skin Compiler
186
+
187
+ 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`.
188
+
189
+ ### Features
190
+
191
+ - **XML Parsing** — lightweight parser with namespace, CDATA, and comment support
192
+ - **AST / IR Generation** — converts to an intermediate representation (SkinIR)
193
+ - **Code Generation** — outputs ESM factory functions
194
+ - **Component Registry** — built-in `eui:*` / `egret:*` namespace mapping to `@kurot/ui` / `@kurot/core`
195
+ - **Custom Namespaces** — maps project prefixes such as `game:*` to source barrel files through `exml.namespaces`
196
+ - **View States** — supports `<eui:states>`, shorthand `states="up,down"`, state properties, `includeIn`, and `excludeFrom`
197
+ - **Skin Properties** — preserves root properties such as `minWidth`, `minHeight`, and state-specific values
198
+ - **Percent Layout** — auto-detects `width="100%"` and converts to `percentWidth`
199
+ - **Data Binding** — parses `{expression}` binding syntax and generates `Binding.bindProperty` calls
200
+
201
+ The standard declarations `xmlns:eui="http://ns.egret.com/eui"` and
202
+ `xmlns:egret="http://ns.egret.com/egret"` are namespace identifiers. The CLI
203
+ resolves their prefixes internally and does not access those URLs over the
204
+ network, so the original Egret namespace pages do not need to be hosted.
205
+
206
+ ### Custom component lifecycle
207
+
208
+ For initialization that requires every EXML skin part to be available, override
209
+ `childrenCreated()`:
210
+
211
+ ```ts
212
+ import { Component } from '@kurot/ui';
213
+
214
+ export class BattlePanel extends Component {
215
+ protected override childrenCreated(): void {
216
+ super.childrenCreated();
217
+ // imgBg, imgFrame, groupField, and other skin parts are now bound.
218
+ }
219
+ }
220
+ ```
221
+
222
+ The event-based equivalent is useful when initialization is composed externally:
223
+
224
+ ```ts
225
+ import { UIEvent } from '@kurot/ui';
226
+
227
+ this.once(UIEvent.CREATION_COMPLETE, this.onCreationComplete);
228
+ ```
229
+
230
+ Use `partAdded()` and `partRemoved()` only when logic must follow an individual
231
+ skin part across skin replacements. `childrenCreated()` and
232
+ `UIEvent.CREATION_COMPLETE` run once for the component's initial creation.
233
+
234
+ EXML skins compiled by the CLI are registered under their complete `class`
235
+ attribute, so an Egret-style value such as
236
+ `skinName = "game.ui.BattlePanelSkin"` works when that EXML is included in the
237
+ loaded theme bundle. A component covered by the theme's `skins` mapping normally
238
+ does not need to assign `skinName` itself. Hand-written `Skin` subclasses may be
239
+ imported and assigned directly instead of using a string.
240
+
241
+ ### Compilation Pipeline
242
+
243
+ All `.exml` skins compile into a single ESM module — `js/default.thm.js` (dev)
244
+ or `js/default.thm.min_<hash>.js` (release) — that registers each skin factory.
245
+ `default.thm.json` keeps only the component→skin mapping plus a `skinsJs`
246
+ pointer to that module, which the runtime `Theme` imports. No `.exml` is shipped.
247
+
248
+ ```
249
+ resource/skins/*.exml
250
+ ↓ parseXML()
251
+ XML Element Tree
252
+ ↓ parseEXML()
253
+ SkinIR
254
+ ↓ generateCode({ format: 'esm' })
255
+ per-skin ESM factories
256
+ ↓ esbuild bundle (+ minify in release)
257
+ js/default.thm[.min_<hash>].js (skins register on globalThis)
258
+ ```
259
+
260
+ ## Project Structure
261
+
262
+ A project created with the default template (`game`) has the following structure:
263
+
264
+ ```
265
+ my-game/
266
+ ├── kurot.config.ts # Project config (includes exml options)
267
+ ├── package.json # Dependencies & scripts
268
+ ├── tsconfig.json # TypeScript config
269
+ ├── template/
270
+ │ └── web/
271
+ │ └── index.html # Editable output page template
272
+ ├── resource/
273
+ │ ├── default.res.json # Resource config
274
+ │ ├── default.thm.json # Theme file: component → skin mapping
275
+ │ └── skins/ # EXML skin directory (21 default skins)
276
+ │ ├── ButtonSkin.exml
277
+ │ ├── ...
278
+ │ └── ViewStackSkin.exml
279
+ └── src/
280
+ ├── Main.ts # Entry: class Main extends Sprite
281
+ └── LoadingUI.ts # Loading progress display
282
+ ```
283
+
284
+ ## Quick Start
285
+
286
+ ```bash
287
+ # Full-featured game project (default)
288
+ npx @kurot/cli create my-game
289
+ cd my-game && pnpm install
290
+ pnpm dev
291
+
292
+ # Minimal project
293
+ npx @kurot/cli create my-lib --template empty
294
+ cd my-lib && pnpm install
295
+ pnpm dev
296
+ ```
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const buildCommand: Command;
3
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,eAAO,MAAM,YAAY,SAoCtB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { Command } from 'commander';
2
+ import { loadProject } from '../core/project.js';
3
+ import { createContext, runPipeline, disposeContext } from '../core/pipeline.js';
4
+ import { defaultPlugins } from '../core/plugins/index.js';
5
+ import { BuildError } from '../core/errors.js';
6
+ import { logger } from '../utils/logger.js';
7
+ export const buildCommand = new Command('build')
8
+ .description('Build the project')
9
+ .option('-r, --release', 'Minified, content-hashed release build (→ bin-release)', false)
10
+ .option('--sourcemap', 'Generate sourcemaps', false)
11
+ .option('--watch', 'Rebuild source on file changes', false)
12
+ .option('--analyze', 'Print esbuild bundle size analysis', false)
13
+ .action(async (options) => {
14
+ const start = Date.now();
15
+ try {
16
+ const mode = options.release ? 'release' : 'development';
17
+ if (options.watch && options.release) {
18
+ logger.warn('--watch is a development workflow; ignoring --release.');
19
+ }
20
+ const project = await loadProject(options.watch ? 'development' : mode);
21
+ logger.info(`Building (${project.mode})...`);
22
+ const ctx = createContext(project, {
23
+ sourcemap: options.sourcemap,
24
+ analyze: options.analyze,
25
+ watch: options.watch,
26
+ });
27
+ await runPipeline(ctx, defaultPlugins());
28
+ if (options.watch) {
29
+ logger.success('Watching for changes. Press Ctrl+C to stop.');
30
+ await waitForShutdown(() => disposeContext(ctx));
31
+ return;
32
+ }
33
+ const elapsed = ((Date.now() - start) / 1000).toFixed(2);
34
+ logger.success(`Build completed in ${elapsed}s → ${relativeOut(project.outputDir)}/`);
35
+ }
36
+ catch (err) {
37
+ const message = err instanceof Error ? err.message : String(err);
38
+ throw new BuildError(`Build failed: ${message}`, err instanceof Error ? err : undefined);
39
+ }
40
+ });
41
+ /**
42
+ * Formats an absolute output directory as a path relative to the cwd, for
43
+ * display in the success message.
44
+ */
45
+ function relativeOut(outputDir) {
46
+ return outputDir.replace(process.cwd() + '/', '');
47
+ }
48
+ /**
49
+ * Resolves when the process receives SIGINT, after running cleanup.
50
+ */
51
+ function waitForShutdown(cleanup) {
52
+ return new Promise(resolve => {
53
+ process.on('SIGINT', async () => {
54
+ await cleanup();
55
+ resolve();
56
+ });
57
+ });
58
+ }
59
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const cleanCommand: Command;
3
+ //# sourceMappingURL=clean.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["../../src/commands/clean.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,YAAY,SAQvB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import { loadConfig } from '../core/config.js';
5
+ import { OUTPUT_DIRS } from '../core/project.js';
6
+ import { logger } from '../utils/logger.js';
7
+ export const cleanCommand = new Command('clean').description('Remove build output directories').action(async () => {
8
+ const config = await loadConfig();
9
+ const names = [...new Set([config.output.dir, OUTPUT_DIRS.release])];
10
+ for (const name of names) {
11
+ await fs.rm(path.resolve(name), { recursive: true, force: true });
12
+ }
13
+ logger.success(`Cleaned ${names.map(n => n + '/').join(', ')}`);
14
+ });
15
+ //# sourceMappingURL=clean.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean.js","sourceRoot":"","sources":["../../src/commands/clean.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,iCAAiC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const createCommand: Command;
3
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,aAAa,SAoBvB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Command } from 'commander';
2
+ import { scaffoldProject, TEMPLATES } from '../core/template.js';
3
+ import { logger } from '../utils/logger.js';
4
+ export const createCommand = new Command('create')
5
+ .description('Create a new Kurot project')
6
+ .argument('<name>', 'Project name')
7
+ .option('--template <template>', `Project template: ${TEMPLATES.join(' | ')}`, 'game')
8
+ .action(async (name, options) => {
9
+ if (!TEMPLATES.includes(options.template)) {
10
+ logger.error(`Unknown template "${options.template}". Available: ${TEMPLATES.join(', ')}`);
11
+ process.exit(1);
12
+ }
13
+ logger.info(`Creating project "${name}" from template "${options.template}"...`);
14
+ try {
15
+ await scaffoldProject(name, options.template);
16
+ }
17
+ catch (err) {
18
+ logger.error(err instanceof Error ? err.message : String(err));
19
+ process.exit(1);
20
+ }
21
+ logger.success(`Project created at ./${name}`);
22
+ logger.info(`Next steps:\n cd ${name}\n pnpm install\n pnpm dev`);
23
+ });
24
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAqB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAChD,WAAW,CAAC,4BAA4B,CAAC;KACzC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;KAClC,MAAM,CAAC,uBAAuB,EAAE,qBAAqB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC;KACrF,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAA6B,EAAE,EAAE;IAC7D,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAwB,CAAC,EAAE,CAAC;QAC3D,MAAM,CAAC,KAAK,CAAC,qBAAqB,OAAO,CAAC,QAAQ,iBAAiB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,oBAAoB,OAAO,CAAC,QAAQ,MAAM,CAAC,CAAC;IACjF,IAAI,CAAC;QACJ,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,QAAwB,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,8BAA8B,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const devCommand: Command;
3
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,UAAU,SAmBpB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Command } from 'commander';
2
+ import { loadProject } from '../core/project.js';
3
+ import { startDevServer } from '../core/dev-server.js';
4
+ import { logger } from '../utils/logger.js';
5
+ export const devCommand = new Command('dev')
6
+ .description('Start a development server with rebuild on change')
7
+ .option('-p, --port <port>', 'Port to listen on', '3000')
8
+ .option('--sourcemap', 'Generate sourcemaps', false)
9
+ .action(async (options) => {
10
+ const port = Number.parseInt(options.port, 10);
11
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
12
+ logger.error(`Invalid port: ${options.port}`);
13
+ process.exit(1);
14
+ }
15
+ try {
16
+ const project = await loadProject('development');
17
+ logger.info(`Starting dev server on port ${port}...`);
18
+ await startDevServer(project, { port, sourcemap: options.sourcemap });
19
+ }
20
+ catch (err) {
21
+ logger.error(`Dev server failed: ${err instanceof Error ? err.message : String(err)}`);
22
+ process.exit(1);
23
+ }
24
+ });
25
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KAC1C,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,CAAC;KACxD,MAAM,CAAC,aAAa,EAAE,qBAAqB,EAAE,KAAK,CAAC;KACnD,MAAM,CAAC,KAAK,EAAE,OAA6C,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,iBAAiB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,+BAA+B,IAAI,KAAK,CAAC,CAAC;QACtD,MAAM,cAAc,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AACF,CAAC,CAAC,CAAC"}