metaowl 0.2.9 → 0.2.11
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.
- package/bin/metaowl-create.js +51 -0
- package/modules/app-mounter.js +15 -1
- package/modules/layouts.js +3 -3
- package/package.json +1 -1
- package/vite/plugin.js +14 -4
package/bin/metaowl-create.js
CHANGED
|
@@ -397,6 +397,39 @@ router.beforeEach((to, from, next) => {
|
|
|
397
397
|
- File: pages/blog/[slug]/Blog.js → URL: /blog/:slug
|
|
398
398
|
- File: pages/[...path]/NotFound.js → URL: /:path(.*)
|
|
399
399
|
|
|
400
|
+
### Layouts
|
|
401
|
+
Share page structures across routes with automatic layout resolution:
|
|
402
|
+
|
|
403
|
+
\`\`\`javascript
|
|
404
|
+
// pages/admin/Dashboard.js
|
|
405
|
+
export class DashboardPage extends Component {
|
|
406
|
+
static template = 'DashboardPage'
|
|
407
|
+
static layout = 'admin' // Use admin layout
|
|
408
|
+
}
|
|
409
|
+
\`\`\`
|
|
410
|
+
|
|
411
|
+
Layout Directory:
|
|
412
|
+
\`\`\`
|
|
413
|
+
src/
|
|
414
|
+
├── layouts/
|
|
415
|
+
│ ├── default/
|
|
416
|
+
│ │ ├── DefaultLayout.js
|
|
417
|
+
│ │ └── DefaultLayout.xml
|
|
418
|
+
│ └── admin/
|
|
419
|
+
│ ├── AdminLayout.js
|
|
420
|
+
│ └── AdminLayout.xml
|
|
421
|
+
\`\`\`
|
|
422
|
+
|
|
423
|
+
Layout Template uses \`<t t-slot=\"default\"/>\` to render page content:
|
|
424
|
+
\`\`\`xml
|
|
425
|
+
<t t-name="AdminLayout">
|
|
426
|
+
<div class="layout-admin">
|
|
427
|
+
<aside>Sidebar</aside>
|
|
428
|
+
<main><t t-slot="default"/></main>
|
|
429
|
+
</div>
|
|
430
|
+
</t>
|
|
431
|
+
\`\`\`
|
|
432
|
+
|
|
400
433
|
### API Requests
|
|
401
434
|
\`\`\`javascript
|
|
402
435
|
import { Fetch } from 'metaowl'
|
|
@@ -521,6 +554,24 @@ router.push('/path')
|
|
|
521
554
|
router.back()
|
|
522
555
|
\`\`\`
|
|
523
556
|
|
|
557
|
+
### Layouts
|
|
558
|
+
Shared page structures with automatic resolution.
|
|
559
|
+
\`\`\`javascript
|
|
560
|
+
// In page component
|
|
561
|
+
export class DashboardPage extends Component {
|
|
562
|
+
static template = 'DashboardPage'
|
|
563
|
+
static layout = 'admin' // Use admin layout
|
|
564
|
+
}
|
|
565
|
+
\`\`\`
|
|
566
|
+
|
|
567
|
+
// Layout component uses t-slot for page content:
|
|
568
|
+
// <t t-slot="default"/>
|
|
569
|
+
|
|
570
|
+
Layout functions:
|
|
571
|
+
- registerLayout(name, Component) - Register a layout
|
|
572
|
+
- resolveLayout(Component, path?) - Get layout for component
|
|
573
|
+
- setRouteLayout(path, name) - Assign layout to route
|
|
574
|
+
|
|
524
575
|
## Component Template
|
|
525
576
|
\`\`\`javascript
|
|
526
577
|
import { Component } from '@odoo/owl'
|
package/modules/app-mounter.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { mount } from '@odoo/owl'
|
|
9
9
|
import { mergeTemplates } from './templates-manager.js'
|
|
10
|
+
import { resolveLayout, getLayout, mountWithLayout } from './layouts.js'
|
|
10
11
|
|
|
11
12
|
const _defaults = {
|
|
12
13
|
warnIfNoStaticProps: true,
|
|
@@ -43,5 +44,18 @@ export async function mountApp(route) {
|
|
|
43
44
|
const mountElement = document.getElementById('metaowl')
|
|
44
45
|
mountElement.innerHTML = ''
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
const pageComponent = route[0].component
|
|
48
|
+
const pagePath = route[0].path
|
|
49
|
+
|
|
50
|
+
// Check for layout
|
|
51
|
+
const layoutName = resolveLayout(pageComponent, pagePath)
|
|
52
|
+
const LayoutClass = getLayout(layoutName)
|
|
53
|
+
|
|
54
|
+
if (LayoutClass) {
|
|
55
|
+
// Mount with layout
|
|
56
|
+
await mountWithLayout(pageComponent, mountElement, { routePath: pagePath, templates })
|
|
57
|
+
} else {
|
|
58
|
+
// Mount without layout
|
|
59
|
+
await mount(pageComponent, mountElement, { ..._config, templates })
|
|
60
|
+
}
|
|
47
61
|
}
|
package/modules/layouts.js
CHANGED
|
@@ -272,7 +272,7 @@ export function createLayoutWrapper(layoutComponent, pageComponent, props = {})
|
|
|
272
272
|
* @returns {Promise<Component>} Mounted component instance
|
|
273
273
|
*/
|
|
274
274
|
export async function mountWithLayout(pageComponent, target, options = {}, config = {}) {
|
|
275
|
-
const { routePath, props = {} } = options
|
|
275
|
+
const { routePath, props = {}, templates } = options
|
|
276
276
|
|
|
277
277
|
const layoutName = resolveLayout(pageComponent, routePath)
|
|
278
278
|
const LayoutClass = getLayout(layoutName)
|
|
@@ -280,14 +280,14 @@ export async function mountWithLayout(pageComponent, target, options = {}, confi
|
|
|
280
280
|
if (!LayoutClass) {
|
|
281
281
|
console.warn(`[metaowl] Layout "${layoutName}" not found, mounting page without layout`)
|
|
282
282
|
const { mount } = await import('@odoo/owl')
|
|
283
|
-
return mount(pageComponent, target, { ...config, props })
|
|
283
|
+
return mount(pageComponent, target, { ...config, props, templates })
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// Create wrapper that combines layout and page
|
|
287
287
|
const WrapperClass = createLayoutWrapper(LayoutClass, pageComponent, props)
|
|
288
288
|
|
|
289
289
|
const { mount } = await import('@odoo/owl')
|
|
290
|
-
const instance = await mount(WrapperClass, target, config)
|
|
290
|
+
const instance = await mount(WrapperClass, target, { ...config, templates })
|
|
291
291
|
|
|
292
292
|
_currentLayout = instance
|
|
293
293
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metaowl",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Lightweight meta-framework for Odoo OWL — file-based routing, app mounting, Fetch helper, Cache, Meta tags, SSG generator, and a Vite plugin.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/vite/plugin.js
CHANGED
|
@@ -35,6 +35,7 @@ function collectXml(globPattern) {
|
|
|
35
35
|
* @param {string} [options.publicDir='../public'] - Public assets directory.
|
|
36
36
|
* @param {string} [options.componentsDir='src/components'] - OWL components directory.
|
|
37
37
|
* @param {string} [options.pagesDir='src/pages'] - OWL pages directory.
|
|
38
|
+
* @param {string} [options.layoutsDir='src/layouts'] - OWL layouts directory.
|
|
38
39
|
* @param {string[]} [options.restartGlobs] - Additional globs that trigger dev-server restart.
|
|
39
40
|
* @param {string} [options.frameworkEntry] - Framework entry for manual chunk.
|
|
40
41
|
* @param {string[]} [options.vendorPackages] - npm packages bundled into the vendor chunk.
|
|
@@ -51,6 +52,7 @@ export async function metaowlPlugin(options = {}) {
|
|
|
51
52
|
publicDir = '../public',
|
|
52
53
|
componentsDir = 'src/components',
|
|
53
54
|
pagesDir = 'src/pages',
|
|
55
|
+
layoutsDir = 'src/layouts',
|
|
54
56
|
restartGlobs = [],
|
|
55
57
|
frameworkEntry = './node_modules/metaowl/index.js',
|
|
56
58
|
vendorPackages = ['@odoo/owl'],
|
|
@@ -60,7 +62,8 @@ export async function metaowlPlugin(options = {}) {
|
|
|
60
62
|
|
|
61
63
|
const componentXml = collectXml(`${componentsDir}/**/*.xml`)
|
|
62
64
|
const pageXml = collectXml(`${pagesDir}/**/*.xml`)
|
|
63
|
-
const
|
|
65
|
+
const layoutXml = collectXml(`${layoutsDir}/**/*.xml`)
|
|
66
|
+
const allComponents = [...pageXml, ...componentXml, ...layoutXml]
|
|
64
67
|
|
|
65
68
|
const defaultRestartGlobs = [
|
|
66
69
|
`${root}/**/*.[jt]s`,
|
|
@@ -169,7 +172,8 @@ export async function metaowlPlugin(options = {}) {
|
|
|
169
172
|
include: ['@odoo/owl'],
|
|
170
173
|
entries: [
|
|
171
174
|
`${componentsDir}/**/*.[jt]s`,
|
|
172
|
-
`${pagesDir}/**/*.[jt]s
|
|
175
|
+
`${pagesDir}/**/*.[jt]s`,
|
|
176
|
+
`${layoutsDir}/**/*.[jt]s`
|
|
173
177
|
],
|
|
174
178
|
...(cfg.optimizeDeps ?? {})
|
|
175
179
|
}
|
|
@@ -183,10 +187,14 @@ export async function metaowlPlugin(options = {}) {
|
|
|
183
187
|
transform(code, id) {
|
|
184
188
|
if (!id.endsWith('/metaowl.js')) return
|
|
185
189
|
const pagesRel = pagesDir.replace(new RegExp(`^${root}[\\/]`), '')
|
|
190
|
+
const layoutsRel = layoutsDir.replace(new RegExp(`^${root}[\\/]`), '')
|
|
186
191
|
return {
|
|
187
192
|
code: code.replace(
|
|
188
193
|
/boot\(\s*\)/,
|
|
189
194
|
`boot(import.meta.glob('./${pagesRel}/**/*.js', { eager: true }))`
|
|
195
|
+
).replace(
|
|
196
|
+
/discoverLayouts\(\s*\)/,
|
|
197
|
+
`discoverLayouts(import.meta.glob('./${layoutsRel}/**/*.js', { eager: true }))`
|
|
190
198
|
),
|
|
191
199
|
map: null
|
|
192
200
|
}
|
|
@@ -198,10 +206,12 @@ export async function metaowlPlugin(options = {}) {
|
|
|
198
206
|
if (!id.endsWith('/css.js')) return
|
|
199
207
|
const compRel = componentsDir.replace(new RegExp(`^${root}[\\/]`), '')
|
|
200
208
|
const pagesRel = pagesDir.replace(new RegExp(`^${root}[\\/]`), '')
|
|
209
|
+
const layoutsRel = layoutsDir.replace(new RegExp(`^${root}[\\/]`), '')
|
|
201
210
|
return {
|
|
202
211
|
code: code + '\n' +
|
|
203
212
|
`import.meta.glob('/${compRel}/**/*.{css,scss}', { eager: true })\n` +
|
|
204
|
-
`import.meta.glob('/${pagesRel}/**/*.{css,scss}', { eager: true })\n
|
|
213
|
+
`import.meta.glob('/${pagesRel}/**/*.{css,scss}', { eager: true })\n` +
|
|
214
|
+
`import.meta.glob('/${layoutsRel}/**/*.{css,scss}', { eager: true })\n`,
|
|
205
215
|
map: null
|
|
206
216
|
}
|
|
207
217
|
}
|
|
@@ -213,7 +223,7 @@ export async function metaowlPlugin(options = {}) {
|
|
|
213
223
|
const projectRoot = process.cwd()
|
|
214
224
|
|
|
215
225
|
// Copy OWL XML templates (loaded at runtime via fetch — not processed by Vite)
|
|
216
|
-
const xmlFiles = globSync([`${componentsDir}/**/*.xml`, `${pagesDir}/**/*.xml`])
|
|
226
|
+
const xmlFiles = globSync([`${componentsDir}/**/*.xml`, `${pagesDir}/**/*.xml`, `${layoutsDir}/**/*.xml`])
|
|
217
227
|
for (const xmlFile of xmlFiles) {
|
|
218
228
|
const relPath = xmlFile.replace(new RegExp(`^${root}[\\/]`), '')
|
|
219
229
|
const dest = resolve(_outDirResolved, relPath)
|