metaowl 0.1.2 → 0.2.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.
@@ -91,10 +91,10 @@ module.exports = createPostcssConfig()
91
91
  write('jsconfig.json', JSON.stringify({
92
92
  extends: './node_modules/metaowl/config/jsconfig.base.json',
93
93
  compilerOptions: {
94
- baseUrl: 'src',
94
+ baseUrl: '.',
95
95
  paths: {
96
- '@pages/*': ['pages/*'],
97
- '@components/*': ['components/*']
96
+ '@pages/*': ['src/pages/*'],
97
+ '@components/*': ['src/components/*']
98
98
  }
99
99
  },
100
100
  include: ['src']
@@ -159,8 +159,7 @@ export default class Index extends Component {
159
159
 
160
160
  // --- src/pages/index/Index.xml ---
161
161
  write('src/pages/index/Index.xml',
162
- `<?xml version="1.0" encoding="UTF-8"?>
163
- <templates>
162
+ `<templates>
164
163
  <t t-name="Index">
165
164
  <div class="layout">
166
165
  <AppHeader />
@@ -198,8 +197,7 @@ export default class AppHeader extends Component {
198
197
 
199
198
  // --- src/components/AppHeader/AppHeader.xml ---
200
199
  write('src/components/AppHeader/AppHeader.xml',
201
- `<?xml version="1.0" encoding="UTF-8"?>
202
- <templates>
200
+ `<templates>
203
201
  <t t-name="AppHeader">
204
202
  <header class="app-header">
205
203
  <span class="app-header__logo">${name}</span>
@@ -235,8 +233,7 @@ export default class AppFooter extends Component {
235
233
 
236
234
  // --- src/components/AppFooter/AppFooter.xml ---
237
235
  write('src/components/AppFooter/AppFooter.xml',
238
- `<?xml version="1.0" encoding="UTF-8"?>
239
- <templates>
236
+ `<templates>
240
237
  <t t-name="AppFooter">
241
238
  <footer class="app-footer">
242
239
  <span>Built with metaowl</span>
@@ -258,6 +255,431 @@ write('src/components/AppFooter/AppFooter.css',
258
255
  }
259
256
  `)
260
257
 
258
+ // --- .cursorrules ---
259
+ write('.cursorrules',
260
+ `# MetaOwl Project Rules
261
+
262
+ ## Framework Overview
263
+ This is a MetaOwl application - a lightweight meta-framework for Odoo OWL built on top of Vite.
264
+
265
+ ## Key Concepts
266
+
267
+ ### File-Based Routing
268
+ - Pages in src/pages/ automatically become routes
269
+ - Directory structure mirrors URL structure
270
+ - Dynamic routes use [param] syntax
271
+ - Index directories map to /
272
+
273
+ ### Components
274
+ - Use OWL (Odoo Web Library) component model
275
+ - Templates defined in .xml files with same name
276
+ - CSS scoped to component in .css files
277
+ - Import components from @components/* alias
278
+
279
+ ### State Management
280
+ - Store: Use Store.define() for Pinia-like stores
281
+ - Cache: localStorage wrapper via Cache class
282
+ - Meta: Document head management via Meta.* helpers
283
+
284
+ ### Routing & Navigation
285
+ - File-based routing via pages/ directory
286
+ - Navigation guards via router.beforeEach()
287
+ - Dynamic routes with [id], [slug?], [...path]
288
+
289
+ ## Code Conventions
290
+ - Use static template property: static template = 'TemplateName'
291
+ - Use static components property for child components
292
+ - Use setup() lifecycle hook for initialization
293
+ - Import from 'metaowl' for framework utilities
294
+
295
+ ## File Structure
296
+ - pages/ - Route components
297
+ - components/ - Reusable components
298
+ - layouts/ - Page layouts (optional)
299
+ - metaowl.js - App entry point
300
+
301
+ ## Common Patterns
302
+ - API calls: Fetch.url('/api/endpoint')
303
+ - State: store.commit('mutation', payload)
304
+ - Meta tags: Meta.title('Page Title')
305
+ - Routing: router.push('/path')
306
+
307
+ ## Documentation
308
+ - See README.md for full API reference
309
+ - See metaowl module docs for detailed API
310
+ `)
311
+
312
+ // --- CLAUDE.md ---
313
+ write('CLAUDE.md',
314
+ `# ${name} - MetaOwl Project
315
+
316
+ ## Quick Start for Claude Code
317
+
318
+ This project uses MetaOwl, a meta-framework for Odoo OWL on Vite.
319
+
320
+ ### Architecture
321
+ - **Frontend**: OWL (Odoo Web Library) - reactive components
322
+ - **Build Tool**: Vite with MetaOwl plugin
323
+ - **Routing**: File-based (pages/ directory)
324
+ - **State**: Store pattern with mutations/actions
325
+
326
+ ### Key Files
327
+ \`\`\`
328
+ src/
329
+ ├── metaowl.js # App bootstrap
330
+ ├── pages/ # Route components (file-based routing)
331
+ │ └── index/
332
+ │ ├── Index.js # Page component
333
+ │ ├── Index.xml # OWL template
334
+ │ └── index.css # Scoped styles
335
+ ├── components/ # Reusable components
336
+ │ ├── AppHeader/
337
+ │ └── AppFooter/
338
+ └── css.js # Global styles
339
+ \`\`\`
340
+
341
+ ### Development Commands
342
+ - \`npm run dev\` - Start dev server
343
+ - \`npm run build\` - Production build
344
+ - \`npm run generate\` - Static site generation
345
+ - \`npm run lint\` - Lint with Prettier + ESLint
346
+
347
+ ### OWL Component Pattern
348
+ \`\`\`javascript
349
+ import { Component } from '@odoo/owl'
350
+ import { Meta, Store } from 'metaowl'
351
+
352
+ export default class MyPage extends Component {
353
+ static template = 'MyPage'
354
+ static components = { ChildComponent }
355
+
356
+ setup() {
357
+ // Lifecycle hook
358
+ Meta.title('Page Title')
359
+ }
360
+ }
361
+ \`\`\`
362
+
363
+ ### Store Pattern
364
+ \`\`\`javascript
365
+ const useCounterStore = Store.define('counter', {
366
+ state: () => ({ count: 0 }),
367
+ mutations: {
368
+ increment: (state) => state.count++
369
+ },
370
+ actions: {
371
+ async fetchData({ commit }) { }
372
+ }
373
+ })
374
+
375
+ // In component:
376
+ const store = useCounterStore()
377
+ store.commit('increment')
378
+ \`\`\`
379
+
380
+ ### Navigation Guard Pattern
381
+ \`\`\`javascript
382
+ import { router } from 'metaowl'
383
+
384
+ router.beforeEach((to, from, next) => {
385
+ if (to.meta.requiresAuth && !isLoggedIn) {
386
+ next('/login')
387
+ } else {
388
+ next()
389
+ }
390
+ })
391
+ \`\`\`
392
+
393
+ ### Dynamic Routes
394
+ - File: pages/user/[id]/User.js → URL: /user/:id
395
+ - File: pages/blog/[slug]/Blog.js → URL: /blog/:slug
396
+ - File: pages/[...path]/NotFound.js → URL: /:path(.*)
397
+
398
+ ### API Requests
399
+ \`\`\`javascript
400
+ import { Fetch } from 'metaowl'
401
+
402
+ const data = await Fetch.url('/api/users', 'GET')
403
+ const result = await Fetch.url('/api/users', 'POST', { name: 'John' })
404
+ \`\`\`
405
+
406
+ ### Meta Tags
407
+ \`\`\`javascript
408
+ import { Meta } from 'metaowl'
409
+
410
+ Meta.title('Page Title')
411
+ Meta.description('Page description')
412
+ Meta.ogTitle('Social Title')
413
+ Meta.canonical('https://example.com/page')
414
+ \`\`\`
415
+
416
+ ### Best Practices
417
+ 1. Keep components small and focused
418
+ 2. Use stores for shared state
419
+ 3. Use Meta helpers for SEO
420
+ 4. Leverage file-based routing
421
+ 5. Scope CSS to components
422
+ 6. Use navigation guards for auth
423
+
424
+ ### Common Gotchas
425
+ - Templates must match t-name attribute exactly
426
+ - File-based routing uses directory name, not file name
427
+ - OWL uses xml templates, not JSX
428
+ - Static properties are required (template, components)
429
+ `)
430
+
431
+ // --- llms.txt ---
432
+ write('llms.txt',
433
+ `# MetaOwl LLM Context
434
+
435
+ ## Framework Identity
436
+ 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.
437
+
438
+ ## Core Concepts
439
+
440
+ ### OWL (Odoo Web Library)
441
+ - Reactive component framework
442
+ - XML-based templates (not JSX)
443
+ - Class-based components with static properties
444
+ - Lifecycle: setup(), willStart(), mounted(), etc.
445
+
446
+ ### File-Based Routing
447
+ Convention:
448
+ - pages/index/Index.js → /
449
+ - pages/about/About.js → /about
450
+ - pages/user/[id]/User.js → /user/:id
451
+ - pages/[...404]/NotFound.js → catch-all
452
+
453
+ ### Project Structure
454
+ ${name}/
455
+ ├── src/
456
+ │ ├── metaowl.js # App entry
457
+ │ ├── index.html # HTML shell
458
+ │ ├── css.js # Global styles
459
+ │ ├── pages/ # Route components
460
+ │ │ └── index/
461
+ │ │ ├── Index.js
462
+ │ │ ├── Index.xml
463
+ │ │ └── index.css
464
+ │ ├── components/ # Shared components
465
+ │ │ ├── AppHeader/
466
+ │ │ └── AppFooter/
467
+ │ └── layouts/ # Page layouts (optional)
468
+ ├── vite.config.js
469
+ └── package.json
470
+
471
+ ## API Reference
472
+
473
+ ### boot(routes)
474
+ Mounts app to #metaowl element.
475
+ \`\`\`javascript
476
+ import { boot } from 'metaowl'
477
+ boot() // auto file-based routing
478
+ \`\`\`
479
+
480
+ ### Fetch
481
+ HTTP client with base URL.
482
+ \`\`\`javascript
483
+ Fetch.configure({ baseUrl: '/api' })
484
+ await Fetch.url('/users', 'GET')
485
+ await Fetch.url('/users', 'POST', data)
486
+ \`\`\`
487
+
488
+ ### Cache
489
+ localStorage wrapper.
490
+ \`\`\`javascript
491
+ await Cache.set('key', value)
492
+ const value = await Cache.get('key')
493
+ \`\`\`
494
+
495
+ ### Store
496
+ State management.
497
+ \`\`\`javascript
498
+ const useStore = Store.define('store', {
499
+ state: () => ({ count: 0 }),
500
+ getters: { double: (s) => s.count * 2 },
501
+ mutations: { inc: (s) => s.count++ },
502
+ actions: { async fetch({ commit }) {} }
503
+ })
504
+ \`\`\`
505
+
506
+ ### Meta
507
+ Document head management.
508
+ \`\`\`javascript
509
+ Meta.title('Title')
510
+ Meta.description('Desc')
511
+ Meta.ogTitle('Social Title')
512
+ \`\`\`
513
+
514
+ ### router
515
+ Navigation guards and helpers.
516
+ \`\`\`javascript
517
+ router.beforeEach((to, from, next) => next())
518
+ router.push('/path')
519
+ router.back()
520
+ \`\`\`
521
+
522
+ ## Component Template
523
+ \`\`\`javascript
524
+ import { Component } from '@odoo/owl'
525
+ import { Meta } from 'metaowl'
526
+
527
+ export default class ComponentName extends Component {
528
+ static template = 'ComponentName'
529
+ static components = { /* child components */ }
530
+
531
+ setup() {
532
+ // Initialize
533
+ Meta.title('Page')
534
+ }
535
+ }
536
+ \`\`\`
537
+
538
+ ## Template Format (XML)
539
+ \`\`\`xml
540
+ <templates>
541
+ <t t-name="ComponentName">
542
+ <div class="component">
543
+ <ChildComponent />
544
+ <span t-esc="someValue" />
545
+ </div>
546
+ </t>
547
+ </templates>
548
+ \`\`\`
549
+
550
+ ## CLI Commands
551
+ - metaowl-create - Scaffold project
552
+ - metaowl-dev - Dev server
553
+ - metaowl-build - Production build
554
+ - metaowl-generate - SSG
555
+ - metaowl-lint - Linting
556
+
557
+ ## See Also
558
+ - README.md - Full documentation
559
+ - https://github.com/odoo/owl - OWL framework
560
+ - https://vitejs.dev - Vite documentation
561
+ `)
562
+
563
+ // --- .github/copilot-instructions.md ---
564
+ write('.github/copilot-instructions.md',
565
+ `# GitHub Copilot Instructions for MetaOwl
566
+
567
+ ## About This Project
568
+ This is a MetaOwl application - a lightweight meta-framework for Odoo OWL (Odoo Web Library) built on Vite.
569
+
570
+ ## Framework Overview
571
+
572
+ ### OWL Components
573
+ - Class-based components extending Component from '@odoo/owl'
574
+ - Templates defined in separate .xml files
575
+ - CSS scoped to component in .css files
576
+ - Static properties: template, components
577
+ - Lifecycle: setup(), willStart(), mounted(), willUnmount()
578
+
579
+ ### File-Based Routing
580
+ - src/pages/ directory structure maps to URLs
581
+ - pages/index/Index.js → /
582
+ - pages/about/About.js → /about
583
+ - pages/user/[id]/User.js → /user/:id (dynamic)
584
+ - pages/[...path]/NotFound.js → catch-all
585
+
586
+ ### State Management
587
+ Use Store for application state:
588
+ \`\`\`javascript
589
+ const useStore = Store.define('storeName', {
590
+ state: () => ({ count: 0 }),
591
+ getters: { double: (state) => state.count * 2 },
592
+ mutations: { increment: (state) => state.count++ },
593
+ actions: { async fetch({ commit }) {} }
594
+ })
595
+ \`\`\`
596
+
597
+ ### Common Tasks
598
+
599
+ When generating components:
600
+ 1. Create .js file with Component class
601
+ 2. Create matching .xml file with template
602
+ 3. Create .css file for scoped styles
603
+ 4. Update parent component's static components
604
+
605
+ When adding pages:
606
+ 1. Create directory in src/pages/
607
+ 2. Add Page.js, Page.xml, page.css
608
+ 3. Route is auto-generated from directory name
609
+
610
+ When using stores:
611
+ 1. Define store with Store.define()
612
+ 2. Use in component: const store = useStore()
613
+ 3. Read state: store.state.property
614
+ 4. Call mutations: store.commit('mutationName', payload)
615
+ 5. Call actions: await store.dispatch('actionName', payload)
616
+
617
+ When setting meta tags:
618
+ 1. Import { Meta } from 'metaowl'
619
+ 2. Call in setup(): Meta.title('Title')
620
+
621
+ ### Code Patterns
622
+
623
+ **New Page Component:**
624
+ \`\`\`javascript
625
+ // pages/Feature/Feature.js
626
+ import { Component } from '@odoo/owl'
627
+ import { Meta } from 'metaowl'
628
+
629
+ export default class Feature extends Component {
630
+ static template = 'Feature'
631
+
632
+ setup() {
633
+ Meta.title('Feature Page')
634
+ }
635
+ }
636
+ \`\`\`
637
+
638
+ **New Child Component:**
639
+ \`\`\`javascript
640
+ // components/MyComponent/MyComponent.js
641
+ import { Component } from '@odoo/owl'
642
+
643
+ export default class MyComponent extends Component {
644
+ static template = 'MyComponent'
645
+ }
646
+ \`\`\`
647
+
648
+ **Navigation Guard:**
649
+ \`\`\`javascript
650
+ // In metaowl.js
651
+ import { router } from 'metaowl'
652
+
653
+ router.beforeEach((to, from, next) => {
654
+ if (to.meta.requiresAuth && !isLoggedIn) {
655
+ next('/login')
656
+ } else {
657
+ next()
658
+ }
659
+ })
660
+ \`\`\`
661
+
662
+ ## File Conventions
663
+ - Component class names: PascalCase
664
+ - Template names: match class name
665
+ - CSS classes: kebab-case
666
+ - Store names: camelCase
667
+ - Page components: default export
668
+
669
+ ## Do Not
670
+ - Use JSX (OWL uses XML templates)
671
+ - Import React or Vue
672
+ - Modify prototype-based inheritance
673
+ - Use JSX-style event handlers (onclick -> t-on-click)
674
+
675
+ ## Do
676
+ - Use OWL's reactive system
677
+ - Follow file-based routing conventions
678
+ - Scope CSS to components
679
+ - Use Meta helpers for SEO
680
+ - Leverage Store for shared state
681
+ `)
682
+
261
683
  // --- Done ---
262
684
  console.log()
263
685
  success(`Project "${name}" ready`)
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { processRoutes } from './modules/router.js'
2
1
  import { mountApp } from './modules/app-mounter.js'
3
2
  import { buildRoutes } from './modules/file-router.js'
4
3
 
@@ -7,6 +6,161 @@ export { default as Cache } from './modules/cache.js'
7
6
  export { configureOwl } from './modules/app-mounter.js'
8
7
  export * as Meta from './modules/meta.js'
9
8
  export { buildRoutes }
9
+ export { Store, createPersistencePlugin, createStore } from './modules/store.js'
10
+ export {
11
+ registerLayout,
12
+ unregisterLayout,
13
+ getLayout,
14
+ hasLayout,
15
+ getLayoutNames,
16
+ setDefaultLayout,
17
+ getDefaultLayout,
18
+ resolveLayout,
19
+ setRouteLayout,
20
+ getRouteLayout,
21
+ createLayoutWrapper,
22
+ mountWithLayout,
23
+ getCurrentLayout,
24
+ subscribeToLayouts,
25
+ clearLayouts,
26
+ layout,
27
+ defineLayout,
28
+ buildLayouts,
29
+ discoverLayouts
30
+ } from './modules/layouts.js'
31
+ export {
32
+ processRoutes,
33
+ beforeEach,
34
+ afterEach,
35
+ getCurrentRoute,
36
+ getPreviousRoute,
37
+ isNavigating,
38
+ cancelNavigation,
39
+ navigate,
40
+ push,
41
+ replace,
42
+ back,
43
+ forward,
44
+ go,
45
+ router
46
+ } from './modules/router.js'
47
+ export {
48
+ matchRoute,
49
+ isDynamicRoute,
50
+ findRoute,
51
+ generateUrl,
52
+ validateRouteParams,
53
+ createCatchAllRoute,
54
+ createRedirectRoute,
55
+ defineRoute,
56
+ route
57
+ } from './modules/file-router.js'
58
+ export {
59
+ onError,
60
+ setErrorContext,
61
+ getErrorContext,
62
+ clearErrorContext,
63
+ captureError,
64
+ initGlobalErrorHandling,
65
+ errorBoundary
66
+ } from './modules/error-boundary.js'
67
+ export {
68
+ configureI18n,
69
+ t,
70
+ getLocale,
71
+ setLocale,
72
+ i18n,
73
+ loadLocaleMessages,
74
+ formatDate,
75
+ formatNumber,
76
+ formatCurrency,
77
+ formatRelativeTime,
78
+ createNamespacedT
79
+ } from './modules/i18n.js'
80
+ export {
81
+ useForm,
82
+ validators,
83
+ createSchema,
84
+ fieldProps
85
+ } from './modules/forms.js'
86
+ export {
87
+ generateComponentMap,
88
+ generateImports,
89
+ generateComponentsObject,
90
+ createAutoImportPlugin,
91
+ scanComponents,
92
+ generateComponentDts
93
+ } from './modules/auto-import.js'
94
+ export {
95
+ OdooService,
96
+ configure,
97
+ authenticate,
98
+ logout,
99
+ searchRead,
100
+ call,
101
+ read,
102
+ create,
103
+ write,
104
+ unlink,
105
+ searchCount,
106
+ listDatabases,
107
+ versionInfo,
108
+ isAuthenticated,
109
+ getSession,
110
+ onAuthChange
111
+ } from './modules/odoo-rpc.js'
112
+ export {
113
+ useAuth,
114
+ useLocalStorage,
115
+ useFetch,
116
+ useDebounce,
117
+ useThrottle,
118
+ useWindowSize,
119
+ useOnlineStatus,
120
+ useAsyncState,
121
+ useCache,
122
+ Composables
123
+ } from './modules/composables.js'
124
+ export {
125
+ createMockStore,
126
+ mockRouter,
127
+ mountComponent,
128
+ wait,
129
+ nextTick,
130
+ flushPromises,
131
+ userEvent,
132
+ dom,
133
+ TestUtils
134
+ } from './modules/test-utils.js'
135
+ export {
136
+ generateSitemap,
137
+ generateRobotsTxt,
138
+ jsonLd,
139
+ createCanonicalUrl,
140
+ generateOpenGraph,
141
+ generateTwitterCard,
142
+ validateSitemap,
143
+ getPriorityByDepth,
144
+ generateSitemapIndex,
145
+ SEO
146
+ } from './modules/seo.js'
147
+ export {
148
+ generateManifest,
149
+ registerServiceWorker,
150
+ unregisterServiceWorker,
151
+ isStandalone,
152
+ isOnline,
153
+ subscribeToConnectivity,
154
+ requestPersistentStorage,
155
+ getStorageInfo,
156
+ sync,
157
+ subscribeToPush,
158
+ unsubscribeFromPush,
159
+ showNotification,
160
+ cache,
161
+ checkCapabilities,
162
+ PWA
163
+ } from './modules/pwa.js'
10
164
 
11
165
  /**
12
166
  * Boots the metaowl application.
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @module AppMounter
3
+ *
4
+ * OWL application mounting with template merging.
5
+ * Handles the low-level mounting of components into the DOM with
6
+ * merged XML templates from the build process.
7
+ */
1
8
  import { mount } from '@odoo/owl'
2
9
  import { mergeTemplates } from './templates-manager.js'
3
10