metaowl 0.4.0 → 0.5.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 (79) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +13 -15
  3. package/build/runtime/bin/metaowl-build.js +10 -0
  4. package/{bin → build/runtime/bin}/metaowl-create.js +96 -177
  5. package/build/runtime/bin/metaowl-dev.js +10 -0
  6. package/build/runtime/bin/metaowl-generate.js +231 -0
  7. package/build/runtime/bin/metaowl-lint.js +58 -0
  8. package/build/runtime/bin/utils.js +68 -0
  9. package/build/runtime/index.js +141 -0
  10. package/build/runtime/modules/app-mounter.js +65 -0
  11. package/build/runtime/modules/auto-import.js +140 -0
  12. package/build/runtime/modules/cache.js +49 -0
  13. package/build/runtime/modules/composables.js +353 -0
  14. package/build/runtime/modules/error-boundary.js +116 -0
  15. package/build/runtime/modules/fetch.js +31 -0
  16. package/build/runtime/modules/file-router.js +205 -0
  17. package/build/runtime/modules/forms.js +193 -0
  18. package/build/runtime/modules/i18n.js +167 -0
  19. package/build/runtime/modules/layouts.js +163 -0
  20. package/build/runtime/modules/link.js +141 -0
  21. package/build/runtime/modules/meta.js +117 -0
  22. package/build/runtime/modules/odoo-rpc.js +264 -0
  23. package/build/runtime/modules/pwa.js +262 -0
  24. package/build/runtime/modules/router.js +389 -0
  25. package/build/runtime/modules/seo.js +186 -0
  26. package/build/runtime/modules/store.js +196 -0
  27. package/build/runtime/modules/templates-manager.js +52 -0
  28. package/build/runtime/modules/test-utils.js +238 -0
  29. package/build/runtime/vite/plugin.js +183 -0
  30. package/eslint.js +29 -0
  31. package/package.json +29 -11
  32. package/CONTRIBUTING.md +0 -49
  33. package/bin/metaowl-build.js +0 -12
  34. package/bin/metaowl-dev.js +0 -12
  35. package/bin/metaowl-generate.js +0 -339
  36. package/bin/metaowl-lint.js +0 -71
  37. package/bin/utils.js +0 -82
  38. package/index.js +0 -328
  39. package/modules/app-mounter.js +0 -104
  40. package/modules/auto-import.js +0 -225
  41. package/modules/cache.js +0 -59
  42. package/modules/composables.js +0 -600
  43. package/modules/error-boundary.js +0 -228
  44. package/modules/fetch.js +0 -51
  45. package/modules/file-router.js +0 -478
  46. package/modules/forms.js +0 -353
  47. package/modules/i18n.js +0 -333
  48. package/modules/layouts.js +0 -431
  49. package/modules/link.js +0 -255
  50. package/modules/meta.js +0 -119
  51. package/modules/odoo-rpc.js +0 -511
  52. package/modules/pwa.js +0 -515
  53. package/modules/router.js +0 -769
  54. package/modules/seo.js +0 -501
  55. package/modules/store.js +0 -409
  56. package/modules/templates-manager.js +0 -89
  57. package/modules/test-utils.js +0 -532
  58. package/test/auto-import.test.js +0 -110
  59. package/test/cache.test.js +0 -55
  60. package/test/composables.test.js +0 -103
  61. package/test/dynamic-routes.test.js +0 -469
  62. package/test/error-boundary.test.js +0 -126
  63. package/test/fetch.test.js +0 -100
  64. package/test/file-router.test.js +0 -55
  65. package/test/forms.test.js +0 -203
  66. package/test/i18n.test.js +0 -188
  67. package/test/layouts.test.js +0 -395
  68. package/test/link.test.js +0 -189
  69. package/test/meta.test.js +0 -146
  70. package/test/odoo-rpc.test.js +0 -547
  71. package/test/pwa.test.js +0 -154
  72. package/test/router-guards.test.js +0 -229
  73. package/test/router.test.js +0 -77
  74. package/test/seo.test.js +0 -353
  75. package/test/store.test.js +0 -476
  76. package/test/templates-manager.test.js +0 -83
  77. package/test/test-utils.test.js +0 -314
  78. package/vite/plugin.js +0 -277
  79. package/vitest.config.js +0 -8
package/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be 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 adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.5.0] - 2026-04-24
9
+
10
+ ### Changed
11
+
12
+ - **TypeScript migration completed** — the framework source, Vite integration, CLI entrypoints,
13
+ and test suite now use TypeScript as the primary source of truth while preserving the existing
14
+ public API and runtime behavior.
15
+ - **Runtime build output separated from source** — publishable JavaScript is now emitted to
16
+ `build/runtime`, and package `main`, `exports`, and `bin` entries resolve from that generated
17
+ runtime output.
18
+
19
+ ### Removed
20
+
21
+ - **Redundant source JavaScript files** — legacy hand-maintained `.js` source files in `modules/`,
22
+ `bin/`, `vite/`, and the root entrypoint were removed in favor of the TypeScript sources and the
23
+ generated runtime build.
24
+
25
+ ### Added
26
+
27
+ - **Release build workflow** — added dedicated runtime build and release helper scripts for
28
+ clean runtime generation, release checks, and package dry-run validation.
29
+
30
+ ## [0.4.1] - 2026-03-25
31
+
32
+ ### Added
33
+
34
+ - **Content hash for `templates.xml`** — the merged OWL template file is now written
35
+ as `templates.<hash>.xml` (8-character SHA-256 content hash) during `build` and
36
+ `generate`. All references in the built HTML and JS assets are rewritten accordingly,
37
+ ensuring browsers always fetch the latest templates after a deployment and never serve
38
+ stale cached versions.
39
+
40
+ ## [0.4.0] - 2026-03-24
41
+
42
+ ### Added
43
+
44
+ - **Link component** added.
45
+
46
+ ## [0.3.7] - 2026-03-24
47
+
48
+ ### Fixed
49
+
50
+ - **bin/metaowl-lint.js** — Fixed inconsistent default lint paths. Changed from `src/owl/pages/**` and `src/owl/components/**` to `src/pages/**` and `src/components/**` to match the documented project structure.
51
+ - **eslint.js** — Fixed `ignores` configuration placement. Moved `ignores` to a separate configuration object as required by ESLint Flat Config format. Also added `.metaowl/**` to the ignore list for the auto-generated component declarations.
52
+ - **modules/auto-import.js** — Fixed missing `node:` prefix for Node.js built-in module import.
package/README.md CHANGED
@@ -117,6 +117,18 @@ npm install metaowl
117
117
 
118
118
  `@odoo/owl` is bundled with metaowl and resolved automatically — no separate installation required.
119
119
 
120
+ ### Release Workflow
121
+
122
+ For the package itself, the TypeScript sources remain the source of truth and the publishable runtime files are generated into `build/runtime`.
123
+
124
+ ```bash
125
+ npm run release:check # typecheck + tests
126
+ npm run build:runtime # clean build/runtime and emit JS
127
+ npm run release:pack # full check + build + npm pack --dry-run
128
+ ```
129
+
130
+ `npm pack` and `npm publish` also trigger `prepack`, which rebuilds `build/runtime` automatically.
131
+
120
132
  ---
121
133
 
122
134
  ## Create a New Project
@@ -1654,21 +1666,7 @@ npx serve -s dist
1654
1666
 
1655
1667
  ## Changelog
1656
1668
 
1657
- ### v0.4.0 (2026-03-24)
1658
-
1659
- **Added:**
1660
-
1661
- - **Link component** added.
1662
-
1663
- ### v0.3.7 (2026-03-24)
1664
-
1665
- **Fixed:**
1666
-
1667
- - **bin/metaowl-lint.js**: Fixed inconsistent default lint paths. Changed from `src/owl/pages/**` and `src/owl/components/**` to `src/pages/**` and `src/components/**` to match the documented project structure.
1668
-
1669
- - **eslint.js**: Fixed `ignores` configuration placement. Moved `ignores` to a separate configuration object as required by ESLint Flat Config format. Also added `.metaowl/**` to the ignore list for the auto-generated component declarations.
1670
-
1671
- - **modules/auto-import.js**: Fixed missing `node:` prefix for Node.js built-in module import.
1669
+ See [CHANGELOG.md](CHANGELOG.md) for the full release history.
1672
1670
 
1673
1671
  ---
1674
1672
 
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * metaowl build — lint then production build.
4
+ */
5
+ import { banner, resolveBin, resolveOwnRuntimeBin, run, success } from './utils.js';
6
+ banner('build');
7
+ run('Linting', `node "${resolveOwnRuntimeBin('metaowl-lint')}"`);
8
+ run('Building', `"${resolveBin('vite')}" build`);
9
+ success('Build complete');
10
+ console.log();
@@ -1,71 +1,51 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * metaowl create — scaffold a new metaowl project.
4
- *
5
- * Usage:
6
- * metaowl-create [project-name]
7
- *
8
- * If no name is given, it will be prompted interactively.
9
4
  */
10
- import { createInterface } from 'node:readline/promises'
11
- import { mkdirSync, writeFileSync, existsSync } from 'node:fs'
12
- import { resolve, join, dirname } from 'node:path'
13
- import { banner, step, success, failure, version } from './utils.js'
14
-
15
- banner('create')
16
-
17
- // --- Project name ---
18
- let name = process.argv[2]?.trim()
19
-
5
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
6
+ import { dirname, join, resolve } from 'node:path';
7
+ import { createInterface } from 'node:readline/promises';
8
+ import { banner, failure, step, success, version } from './utils.js';
9
+ banner('create');
10
+ let name = process.argv[2]?.trim() ?? '';
20
11
  if (!name) {
21
- const rl = createInterface({ input: process.stdin, output: process.stdout })
22
- name = (await rl.question(' Project name: ')).trim()
23
- rl.close()
24
- console.log()
12
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
13
+ name = (await rl.question(' Project name: ')).trim();
14
+ rl.close();
15
+ console.log();
25
16
  }
26
-
27
17
  if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
28
- failure('Invalid project name. Use only letters, numbers, hyphens, or underscores.')
29
- process.exit(1)
18
+ failure('Invalid project name. Use only letters, numbers, hyphens, or underscores.');
19
+ process.exit(1);
30
20
  }
31
-
32
- const dest = resolve(process.cwd(), name)
33
-
21
+ const dest = resolve(process.cwd(), name);
34
22
  if (existsSync(dest)) {
35
- failure(`Directory "${name}" already exists.`)
36
- process.exit(1)
23
+ failure(`Directory "${name}" already exists.`);
24
+ process.exit(1);
37
25
  }
38
-
39
- step(`Scaffolding project "${name}"...`)
40
- console.log()
41
-
42
- // --- File writer helper ---
26
+ step(`Scaffolding project "${name}"...`);
27
+ console.log();
43
28
  function write(filePath, content) {
44
- const abs = join(dest, filePath)
45
- mkdirSync(dirname(abs), { recursive: true })
46
- writeFileSync(abs, content, 'utf-8')
47
- console.log(` ${filePath}`)
29
+ const abs = join(dest, filePath);
30
+ mkdirSync(dirname(abs), { recursive: true });
31
+ writeFileSync(abs, content, 'utf-8');
32
+ console.log(` ${filePath}`);
48
33
  }
49
-
50
- // --- package.json ---
51
34
  write('package.json', JSON.stringify({
52
- name,
53
- version: '0.1.0',
54
- type: 'module',
55
- scripts: {
56
- dev: 'metaowl-dev',
57
- build: 'metaowl-build',
58
- generate: 'metaowl-generate',
59
- lint: 'metaowl-lint'
60
- },
61
- dependencies: {
62
- metaowl: `^${version}`
63
- }
64
- }, null, 2) + '\n')
65
-
66
- // --- vite.config.js ---
67
- write('vite.config.js',
68
- `import { metaowlConfig } from 'metaowl/vite'
35
+ name,
36
+ version: '0.1.0',
37
+ type: 'module',
38
+ scripts: {
39
+ dev: 'metaowl-dev',
40
+ build: 'metaowl-build',
41
+ generate: 'metaowl-generate',
42
+ lint: 'metaowl-lint'
43
+ },
44
+ dependencies: {
45
+ metaowl: `^${version}`
46
+ }
47
+ }, null, 2) + '\n');
48
+ write('vite.config.js', `import { metaowlConfig } from 'metaowl/vite'
69
49
 
70
50
  export default async () => {
71
51
  return metaowlConfig({
@@ -73,45 +53,31 @@ export default async () => {
73
53
  pagesDir: 'src/pages'
74
54
  })
75
55
  }
76
- `)
77
-
78
- // --- eslint.config.js ---
79
- write('eslint.config.js',
80
- `import { eslintConfig } from 'metaowl/eslint'
56
+ `);
57
+ write('eslint.config.js', `import { eslintConfig } from 'metaowl/eslint'
81
58
 
82
59
  export default eslintConfig
83
- `)
84
-
85
- // --- postcss.config.cjs ---
86
- write('postcss.config.cjs',
87
- `const { createPostcssConfig } = require('metaowl/postcss')
60
+ `);
61
+ write('postcss.config.cjs', `const { createPostcssConfig } = require('metaowl/postcss')
88
62
 
89
63
  module.exports = createPostcssConfig()
90
- `)
91
-
92
- // --- jsconfig.json ---
64
+ `);
93
65
  write('jsconfig.json', JSON.stringify({
94
- extends: './node_modules/metaowl/config/jsconfig.base.json',
95
- compilerOptions: {
96
- baseUrl: '.',
97
- paths: {
98
- '@pages/*': ['src/pages/*'],
99
- '@components/*': ['src/components/*']
100
- }
101
- },
102
- include: ['src']
103
- }, null, 2) + '\n')
104
-
105
- // --- .gitignore ---
106
- write('.gitignore',
107
- `node_modules/
66
+ extends: './node_modules/metaowl/config/jsconfig.base.json',
67
+ compilerOptions: {
68
+ baseUrl: '.',
69
+ paths: {
70
+ '@pages/*': ['src/pages/*'],
71
+ '@components/*': ['src/components/*']
72
+ }
73
+ },
74
+ include: ['src']
75
+ }, null, 2) + '\n');
76
+ write('.gitignore', `node_modules/
108
77
  dist/
109
78
  .env
110
- `)
111
-
112
- // --- src/index.html ---
113
- write('src/index.html',
114
- `<!doctype html>
79
+ `);
80
+ write('src/index.html', `<!doctype html>
115
81
  <html lang="en">
116
82
  <head>
117
83
  <meta charset="UTF-8" />
@@ -123,28 +89,19 @@ write('src/index.html',
123
89
  <script type="module" src="/metaowl.js"></script>
124
90
  </body>
125
91
  </html>
126
- `)
127
-
128
- // --- src/metaowl.js ---
129
- write('src/metaowl.js',
130
- `import { boot, Fetch } from 'metaowl'
92
+ `);
93
+ write('src/metaowl.js', `import { boot, Fetch } from 'metaowl'
131
94
 
132
95
  Fetch.configure({
133
96
  baseUrl: import.meta.env.VITE_API_URL ?? ''
134
97
  })
135
98
 
136
99
  boot()
137
- `)
138
-
139
- // --- src/css.js ---
140
- write('src/css.js',
141
- `// Global styles — import shared CSS here.
100
+ `);
101
+ write('src/css.js', `// Global styles — import shared CSS here.
142
102
  // Component and page CSS files are auto-imported by the metaowl Vite plugin.
143
- `)
144
-
145
- // --- src/pages/index/Index.js ---
146
- write('src/pages/index/Index.js',
147
- `import { Component } from '@odoo/owl'
103
+ `);
104
+ write('src/pages/index/Index.js', `import { Component } from '@odoo/owl'
148
105
  import { Meta } from 'metaowl'
149
106
  import AppHeader from '@components/AppHeader/AppHeader'
150
107
  import AppFooter from '@components/AppFooter/AppFooter'
@@ -157,11 +114,8 @@ export default class Index extends Component {
157
114
  Meta.title('Home — ${name}')
158
115
  }
159
116
  }
160
- `)
161
-
162
- // --- src/pages/index/Index.xml ---
163
- write('src/pages/index/Index.xml',
164
- `<templates>
117
+ `);
118
+ write('src/pages/index/Index.xml', `<templates>
165
119
  <t t-name="Index">
166
120
  <div class="layout">
167
121
  <AppHeader />
@@ -172,11 +126,8 @@ write('src/pages/index/Index.xml',
172
126
  </div>
173
127
  </t>
174
128
  </templates>
175
- `)
176
-
177
- // --- src/pages/index/index.css ---
178
- write('src/pages/index/index.css',
179
- `.layout {
129
+ `);
130
+ write('src/pages/index/index.css', `.layout {
180
131
  display: flex;
181
132
  flex-direction: column;
182
133
  min-height: 100vh;
@@ -186,31 +137,22 @@ write('src/pages/index/index.css',
186
137
  flex: 1;
187
138
  padding: 2rem;
188
139
  }
189
- `)
190
-
191
- // --- src/components/AppHeader/AppHeader.js ---
192
- write('src/components/AppHeader/AppHeader.js',
193
- `import { Component } from '@odoo/owl'
140
+ `);
141
+ write('src/components/AppHeader/AppHeader.js', `import { Component } from '@odoo/owl'
194
142
 
195
143
  export default class AppHeader extends Component {
196
144
  static template = 'AppHeader'
197
145
  }
198
- `)
199
-
200
- // --- src/components/AppHeader/AppHeader.xml ---
201
- write('src/components/AppHeader/AppHeader.xml',
202
- `<templates>
146
+ `);
147
+ write('src/components/AppHeader/AppHeader.xml', `<templates>
203
148
  <t t-name="AppHeader">
204
149
  <header class="app-header">
205
150
  <span class="app-header__logo">${name}</span>
206
151
  </header>
207
152
  </t>
208
153
  </templates>
209
- `)
210
-
211
- // --- src/components/AppHeader/AppHeader.css ---
212
- write('src/components/AppHeader/AppHeader.css',
213
- `.app-header {
154
+ `);
155
+ write('src/components/AppHeader/AppHeader.css', `.app-header {
214
156
  display: flex;
215
157
  align-items: center;
216
158
  padding: 0 1.5rem;
@@ -222,31 +164,22 @@ write('src/components/AppHeader/AppHeader.css',
222
164
  font-weight: 600;
223
165
  font-size: 1.1rem;
224
166
  }
225
- `)
226
-
227
- // --- src/components/AppFooter/AppFooter.js ---
228
- write('src/components/AppFooter/AppFooter.js',
229
- `import { Component } from '@odoo/owl'
167
+ `);
168
+ write('src/components/AppFooter/AppFooter.js', `import { Component } from '@odoo/owl'
230
169
 
231
170
  export default class AppFooter extends Component {
232
171
  static template = 'AppFooter'
233
172
  }
234
- `)
235
-
236
- // --- src/components/AppFooter/AppFooter.xml ---
237
- write('src/components/AppFooter/AppFooter.xml',
238
- `<templates>
173
+ `);
174
+ write('src/components/AppFooter/AppFooter.xml', `<templates>
239
175
  <t t-name="AppFooter">
240
176
  <footer class="app-footer">
241
177
  <span>Built with metaowl</span>
242
178
  </footer>
243
179
  </t>
244
180
  </templates>
245
- `)
246
-
247
- // --- src/components/AppFooter/AppFooter.css ---
248
- write('src/components/AppFooter/AppFooter.css',
249
- `.app-footer {
181
+ `);
182
+ write('src/components/AppFooter/AppFooter.css', `.app-footer {
250
183
  display: flex;
251
184
  align-items: center;
252
185
  justify-content: center;
@@ -255,11 +188,8 @@ write('src/components/AppFooter/AppFooter.css',
255
188
  color: #6b7280;
256
189
  border-top: 1px solid #e5e7eb;
257
190
  }
258
- `)
259
-
260
- // --- .cursorrules ---
261
- write('.cursorrules',
262
- `# MetaOwl Project Rules
191
+ `);
192
+ write('.cursorrules', `# MetaOwl Project Rules
263
193
 
264
194
  ## Framework Overview
265
195
  This is a MetaOwl application - a lightweight meta-framework for Odoo OWL built on top of Vite.
@@ -296,7 +226,7 @@ This is a MetaOwl application - a lightweight meta-framework for Odoo OWL built
296
226
 
297
227
  ## File Structure
298
228
  - pages/ - Route components
299
- - components/ - Reusable components
229
+ - components/ - Reusable components
300
230
  - layouts/ - Page layouts (optional)
301
231
  - metaowl.js - App entry point
302
232
 
@@ -309,11 +239,8 @@ This is a MetaOwl application - a lightweight meta-framework for Odoo OWL built
309
239
  ## Documentation
310
240
  - See README.md for full API reference
311
241
  - See metaowl module docs for detailed API
312
- `)
313
-
314
- // --- CLAUDE.md ---
315
- write('CLAUDE.md',
316
- `# ${name} - MetaOwl Project
242
+ `);
243
+ write('CLAUDE.md', `# ${name} - MetaOwl Project
317
244
 
318
245
  ## Quick Start for Claude Code
319
246
 
@@ -329,7 +256,7 @@ This project uses MetaOwl, a meta-framework for Odoo OWL on Vite.
329
256
  \`\`\`
330
257
  src/
331
258
  ├── metaowl.js # App bootstrap
332
- ├── pages/ # Route components (file-based routing)
259
+ ├── pages/ # Route components
333
260
  │ └── index/
334
261
  │ ├── Index.js # Page component
335
262
  │ ├── Index.xml # OWL template
@@ -420,7 +347,7 @@ src/
420
347
  │ └── AdminLayout.xml
421
348
  \`\`\`
422
349
 
423
- Layout Template uses \`<t t-slot=\"default\"/>\` to render page content:
350
+ Layout Template uses \`<t t-slot="default"/>\` to render page content:
424
351
  \`\`\`xml
425
352
  <t t-name="AdminLayout">
426
353
  <div class="layout-admin">
@@ -461,11 +388,8 @@ Meta.canonical('https://example.com/page')
461
388
  - File-based routing uses directory name, not file name
462
389
  - OWL uses xml templates, not JSX
463
390
  - Static properties are required (template, components)
464
- `)
465
-
466
- // --- llms.txt ---
467
- write('llms.txt',
468
- `# MetaOwl LLM Context
391
+ `);
392
+ write('llms.txt', `# MetaOwl LLM Context
469
393
 
470
394
  ## Framework Identity
471
395
  MetaOwl is a lightweight meta-framework for Odoo OWL (Odoo Web Library) applications, built on Vite. It provides file-based routing, state management, and app mounting.
@@ -611,11 +535,8 @@ export default class ComponentName extends Component {
611
535
  - README.md - Full documentation
612
536
  - https://github.com/odoo/owl - OWL framework
613
537
  - https://vitejs.dev - Vite documentation
614
- `)
615
-
616
- // --- .github/copilot-instructions.md ---
617
- write('.github/copilot-instructions.md',
618
- `# GitHub Copilot Instructions for MetaOwl
538
+ `);
539
+ write('.github/copilot-instructions.md', `# GitHub Copilot Instructions for MetaOwl
619
540
 
620
541
  ## About This Project
621
542
  This is a MetaOwl application - a lightweight meta-framework for Odoo OWL (Odoo Web Library) built on Vite.
@@ -731,15 +652,13 @@ router.beforeEach((to, from, next) => {
731
652
  - Scope CSS to components
732
653
  - Use Meta helpers for SEO
733
654
  - Leverage Store for shared state
734
- `)
735
-
736
- // --- Done ---
737
- console.log()
738
- success(`Project "${name}" ready`)
739
- console.log()
740
- console.log(' Next steps:')
741
- console.log()
742
- console.log(` cd ${name}`)
743
- console.log(` npm install`)
744
- console.log(` npm run dev`)
745
- console.log()
655
+ `);
656
+ console.log();
657
+ success(`Project "${name}" ready`);
658
+ console.log();
659
+ console.log(' Next steps:');
660
+ console.log();
661
+ console.log(` cd ${name}`);
662
+ console.log(' npm install');
663
+ console.log(' npm run dev');
664
+ console.log();
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * metaowl dev — start the Vite development server.
4
+ */
5
+ import { execSync } from 'node:child_process';
6
+ import { banner, cwd, resolveBin, step } from './utils.js';
7
+ banner('dev');
8
+ step('Starting development server...');
9
+ console.log();
10
+ execSync(`"${resolveBin('vite')}"`, { stdio: 'inherit', cwd });