metaowl 0.4.1 → 0.6.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 (83) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +267 -2
  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 +144 -0
  10. package/build/runtime/modules/app-mounter.js +73 -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/constants.js +38 -0
  15. package/build/runtime/modules/error-boundary.js +116 -0
  16. package/build/runtime/modules/fetch.js +31 -0
  17. package/build/runtime/modules/file-router.js +207 -0
  18. package/build/runtime/modules/fonts.js +172 -0
  19. package/build/runtime/modules/forms.js +193 -0
  20. package/build/runtime/modules/i18n.js +180 -0
  21. package/build/runtime/modules/image.js +175 -0
  22. package/build/runtime/modules/layouts.js +214 -0
  23. package/build/runtime/modules/link.js +141 -0
  24. package/build/runtime/modules/meta.js +117 -0
  25. package/build/runtime/modules/odoo-rpc.js +265 -0
  26. package/build/runtime/modules/pwa.js +272 -0
  27. package/build/runtime/modules/router.js +384 -0
  28. package/build/runtime/modules/seo.js +186 -0
  29. package/build/runtime/modules/store.js +198 -0
  30. package/build/runtime/modules/templates-manager.js +52 -0
  31. package/build/runtime/modules/test-utils.js +238 -0
  32. package/build/runtime/vite/plugin.js +197 -0
  33. package/eslint.js +29 -0
  34. package/package.json +45 -27
  35. package/CONTRIBUTING.md +0 -49
  36. package/bin/metaowl-build.js +0 -12
  37. package/bin/metaowl-dev.js +0 -12
  38. package/bin/metaowl-generate.js +0 -339
  39. package/bin/metaowl-lint.js +0 -71
  40. package/bin/utils.js +0 -82
  41. package/eslint.config.js +0 -3
  42. package/index.js +0 -328
  43. package/modules/app-mounter.js +0 -104
  44. package/modules/auto-import.js +0 -225
  45. package/modules/cache.js +0 -59
  46. package/modules/composables.js +0 -600
  47. package/modules/error-boundary.js +0 -228
  48. package/modules/fetch.js +0 -51
  49. package/modules/file-router.js +0 -478
  50. package/modules/forms.js +0 -353
  51. package/modules/i18n.js +0 -333
  52. package/modules/layouts.js +0 -431
  53. package/modules/link.js +0 -255
  54. package/modules/meta.js +0 -119
  55. package/modules/odoo-rpc.js +0 -511
  56. package/modules/pwa.js +0 -515
  57. package/modules/router.js +0 -769
  58. package/modules/seo.js +0 -501
  59. package/modules/store.js +0 -409
  60. package/modules/templates-manager.js +0 -89
  61. package/modules/test-utils.js +0 -532
  62. package/test/auto-import.test.js +0 -110
  63. package/test/cache.test.js +0 -55
  64. package/test/composables.test.js +0 -103
  65. package/test/dynamic-routes.test.js +0 -469
  66. package/test/error-boundary.test.js +0 -126
  67. package/test/fetch.test.js +0 -100
  68. package/test/file-router.test.js +0 -55
  69. package/test/forms.test.js +0 -203
  70. package/test/i18n.test.js +0 -188
  71. package/test/layouts.test.js +0 -395
  72. package/test/link.test.js +0 -189
  73. package/test/meta.test.js +0 -146
  74. package/test/odoo-rpc.test.js +0 -547
  75. package/test/pwa.test.js +0 -154
  76. package/test/router-guards.test.js +0 -229
  77. package/test/router.test.js +0 -77
  78. package/test/seo.test.js +0 -353
  79. package/test/store.test.js +0 -476
  80. package/test/templates-manager.test.js +0 -83
  81. package/test/test-utils.test.js +0 -314
  82. package/vite/plugin.js +0 -290
  83. package/vitest.config.js +0 -8
@@ -1,314 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest'
2
- import {
3
- createMockStore,
4
- mockRouter,
5
- mountComponent,
6
- wait,
7
- nextTick,
8
- flushPromises,
9
- userEvent,
10
- dom,
11
- TestUtils
12
- } from '../modules/test-utils.js'
13
-
14
- describe('TestUtils', () => {
15
- describe('Exports', () => {
16
- it('should export all functions', () => {
17
- expect(typeof createMockStore).toBe('function')
18
- expect(typeof mockRouter).toBe('function')
19
- expect(typeof mountComponent).toBe('function')
20
- expect(typeof wait).toBe('function')
21
- expect(typeof nextTick).toBe('function')
22
- expect(typeof flushPromises).toBe('function')
23
- expect(typeof userEvent.click).toBe('function')
24
- expect(typeof dom.query).toBe('function')
25
- })
26
-
27
- it('should export TestUtils namespace', () => {
28
- expect(TestUtils.createMockStore).toBe(createMockStore)
29
- expect(TestUtils.mockRouter).toBe(mockRouter)
30
- expect(TestUtils.mountComponent).toBe(mountComponent)
31
- })
32
- })
33
-
34
- describe('createMockStore', () => {
35
- it('should create store with initial state', () => {
36
- const store = createMockStore({
37
- state: { count: 0, user: null }
38
- })
39
-
40
- expect(store.state.count).toBe(0)
41
- expect(store.state.user).toBeNull()
42
- })
43
-
44
- it('should handle mutations', () => {
45
- const store = createMockStore({
46
- state: { count: 0 },
47
- mutations: {
48
- increment: (state) => { state.count++ },
49
- add: (state, n) => { state.count += n }
50
- }
51
- })
52
-
53
- store.commit('increment')
54
- expect(store.state.count).toBe(1)
55
-
56
- store.commit('add', 5)
57
- expect(store.state.count).toBe(6)
58
- })
59
-
60
- it('should handle actions', async () => {
61
- const store = createMockStore({
62
- state: { user: null },
63
- mutations: {
64
- setUser: (state, user) => { state.user = user }
65
- },
66
- actions: {
67
- login: async ({ commit }, credentials) => {
68
- const user = { name: 'Test User', email: credentials.email }
69
- commit('setUser', user)
70
- return user
71
- }
72
- }
73
- })
74
-
75
- const result = await store.dispatch('login', { email: 'test@test.com', password: '123' })
76
-
77
- expect(store.state.user.name).toBe('Test User')
78
- expect(result.name).toBe('Test User')
79
- })
80
-
81
- it('should handle getters', () => {
82
- const store = createMockStore({
83
- state: { count: 5 },
84
- getters: {
85
- doubled: (state) => state.count * 2,
86
- isPositive: (state) => state.count > 0
87
- }
88
- })
89
-
90
- expect(store.getters.doubled).toBe(10)
91
- expect(store.getters.isPositive).toBe(true)
92
- })
93
-
94
- it('should reset state', () => {
95
- const store = createMockStore({
96
- state: { count: 0, name: 'Initial' }
97
- })
98
-
99
- store.state.count = 10
100
- store.state.name = 'Changed'
101
-
102
- store.reset()
103
-
104
- expect(store.state.count).toBe(0)
105
- expect(store.state.name).toBe('Initial')
106
- })
107
-
108
- it('should set state directly', () => {
109
- const store = createMockStore({
110
- state: { count: 0 }
111
- })
112
-
113
- store.setState({ count: 42, extra: 'value' })
114
-
115
- expect(store.state.count).toBe(42)
116
- expect(store.state.extra).toBe('value')
117
- })
118
- })
119
-
120
- describe('mockRouter', () => {
121
- it('should create router with initial route', () => {
122
- const router = mockRouter({
123
- initialRoute: '/dashboard'
124
- })
125
-
126
- expect(router.currentRoute.path).toBe('/dashboard')
127
- })
128
-
129
- it('should navigate with push', async () => {
130
- const router = mockRouter({
131
- routes: [{ path: '/', name: 'home' }, { path: '/about', name: 'about' }]
132
- })
133
-
134
- await router.push('/about')
135
-
136
- expect(router.currentRoute.path).toBe('/about')
137
- expect(router.currentRoute.name).toBe('about')
138
- })
139
-
140
- it('should parse route params', async () => {
141
- const router = mockRouter({
142
- routes: [{ path: '/user/:id', name: 'user' }]
143
- })
144
-
145
- await router.push('/user/123')
146
-
147
- expect(router.currentRoute.params.id).toBe('123')
148
- })
149
-
150
- it('should parse query params', async () => {
151
- const router = mockRouter()
152
-
153
- await router.push('/search?q=test&page=2')
154
-
155
- expect(router.currentRoute.query.q).toBe('test')
156
- expect(router.currentRoute.query.page).toBe('2')
157
- })
158
-
159
- it('should run beforeEach guards', async () => {
160
- const guard = vi.fn()
161
- const router = mockRouter()
162
-
163
- router.beforeEach(guard)
164
- await router.push('/protected')
165
-
166
- expect(guard).toHaveBeenCalled()
167
- })
168
-
169
- it('should run afterEach hooks', async () => {
170
- const hook = vi.fn()
171
- const router = mockRouter()
172
-
173
- router.afterEach(hook)
174
- await router.push('/page')
175
-
176
- expect(hook).toHaveBeenCalled()
177
- })
178
-
179
- it('should unsubscribe from guards', async () => {
180
- const guard = vi.fn()
181
- const router = mockRouter()
182
-
183
- const unsubscribe = router.beforeEach(guard)
184
- unsubscribe()
185
-
186
- await router.push('/page')
187
- expect(guard).not.toHaveBeenCalled()
188
- })
189
-
190
- it('should resolve named routes', () => {
191
- const router = mockRouter({
192
- routes: [
193
- { path: '/', name: 'home' },
194
- { path: '/user/:id', name: 'user' }
195
- ]
196
- })
197
-
198
- expect(router.resolve('home')).toBe('/')
199
- expect(router.resolve('user', { id: 123 })).toBe('/user/123')
200
- })
201
-
202
- it('should handle hash', async () => {
203
- const router = mockRouter()
204
-
205
- await router.push('/page#section1')
206
-
207
- expect(router.currentRoute.hash).toBe('section1')
208
- })
209
- })
210
-
211
- describe('Async utilities', () => {
212
- it('wait should delay execution', async () => {
213
- const start = Date.now()
214
- await wait(50)
215
- const elapsed = Date.now() - start
216
-
217
- expect(elapsed).toBeGreaterThanOrEqual(45)
218
- })
219
-
220
- it('flushPromises should resolve pending promises', async () => {
221
- let resolved = false
222
- Promise.resolve().then(() => { resolved = true })
223
-
224
- expect(resolved).toBe(false)
225
- await flushPromises()
226
- expect(resolved).toBe(true)
227
- })
228
- })
229
-
230
- describe('DOM utilities', () => {
231
- beforeEach(() => {
232
- document.body.innerHTML = `
233
- <div id="app">
234
- <button class="btn primary">Click me</button>
235
- <span class="text">Hello World</span>
236
- </div>
237
- `
238
- })
239
-
240
- it('should query element', () => {
241
- const button = dom.query('.btn')
242
- expect(button).not.toBeNull()
243
- expect(button.tagName).toBe('BUTTON')
244
- })
245
-
246
- it('should query all elements', () => {
247
- const spans = dom.queryAll('.text')
248
- expect(spans.length).toBe(1)
249
- })
250
-
251
- it('should check for class', () => {
252
- const button = dom.query('.btn')
253
- expect(dom.hasClass(button, 'btn')).toBe(true)
254
- expect(dom.hasClass(button, 'primary')).toBe(true)
255
- expect(dom.hasClass(button, 'nonexistent')).toBe(false)
256
- })
257
-
258
- it('should get text content', () => {
259
- const span = dom.query('.text')
260
- expect(dom.text(span)).toBe('Hello World')
261
- })
262
- })
263
-
264
- describe('userEvent utilities', () => {
265
- beforeEach(() => {
266
- document.body.innerHTML = `
267
- <input id="input" type="text" />
268
- <form id="form">
269
- <button type="submit">Submit</button>
270
- </form>
271
- <select id="select">
272
- <option value="a">A</option>
273
- <option value="b">B</option>
274
- </select>
275
- `
276
- })
277
-
278
- it('should simulate click', async () => {
279
- const button = document.querySelector('button')
280
- const handler = vi.fn()
281
- button.addEventListener('click', handler)
282
-
283
- await userEvent.click(button)
284
-
285
- expect(handler).toHaveBeenCalled()
286
- })
287
-
288
- it('should simulate typing', async () => {
289
- const input = document.querySelector('#input')
290
-
291
- await userEvent.type(input, 'hello')
292
-
293
- expect(input.value).toBe('hello')
294
- })
295
-
296
- it('should simulate form submit', async () => {
297
- const form = document.querySelector('#form')
298
- const handler = vi.fn(e => e.preventDefault())
299
- form.addEventListener('submit', handler)
300
-
301
- await userEvent.submit(form)
302
-
303
- expect(handler).toHaveBeenCalled()
304
- })
305
-
306
- it('should simulate select change', async () => {
307
- const select = document.querySelector('#select')
308
-
309
- await userEvent.select(select, 'b')
310
-
311
- expect(select.value).toBe('b')
312
- })
313
- })
314
- })
package/vite/plugin.js DELETED
@@ -1,290 +0,0 @@
1
- import { createHash } from 'node:crypto'
2
- import { fileURLToPath } from 'node:url'
3
- import { resolve, dirname } from 'node:path'
4
- import { mkdirSync, cpSync, existsSync, readFileSync, writeFileSync } from 'node:fs'
5
- import { createRequire } from 'node:module'
6
- import { globSync } from 'glob'
7
- import { config as dotenvConfig } from 'dotenv'
8
- import tsconfigPaths from 'vite-tsconfig-paths'
9
-
10
- const require = createRequire(import.meta.url)
11
-
12
- function resolveOwlPath() {
13
- return require.resolve('@odoo/owl/dist/owl.es.js', {
14
- paths: [process.cwd(), dirname(fileURLToPath(import.meta.url))]
15
- })
16
- }
17
-
18
- /**
19
- * Collect all .xml files from a directory glob and return them as
20
- * URL-style paths (e.g. /components/Header/Header.xml).
21
- *
22
- * @param {string} globPattern - e.g. 'src/components/**\/*.xml'
23
- * @returns {string[]}
24
- */
25
- function collectXml(globPattern) {
26
- return globSync(globPattern).map(p => p.replace(/^src[\\/]/, '/'))
27
- }
28
-
29
- /**
30
- * Merge all XML template files into a single XML string.
31
- * Removes <templates> wrappers from individual files and wraps everything in a single <templates>.
32
- * Templates are concatenated without minification to preserve intentional line breaks.
33
- *
34
- * @param {string[]} xmlPaths - Array of absolute file paths to XML files
35
- * @returns {string} Merged XML string
36
- */
37
- function mergeXmlFiles(xmlPaths) {
38
- const templates = xmlPaths.map(filePath => {
39
- try {
40
- let content = readFileSync(filePath, 'utf-8')
41
- // Remove <templates> wrapper if present in individual file
42
- content = content.replace(/<templates>/g, '').replace(/<\/templates>/g, '')
43
- return content
44
- } catch (e) {
45
- console.error(`[metaowl] Failed to read XML file: ${filePath}`, e)
46
- return ''
47
- }
48
- }).join('')
49
-
50
- return '<templates>' + templates + '</templates>'
51
- }
52
-
53
- /**
54
- * metaowl Vite plugin.
55
- *
56
- * @param {object} [options]
57
- * @param {string} [options.root='src'] - Vite root directory.
58
- * @param {string} [options.outDir='../dist'] - Build output directory.
59
- * @param {string} [options.publicDir='../public'] - Public assets directory.
60
- * @param {string} [options.componentsDir='src/components'] - OWL components directory.
61
- * @param {string} [options.pagesDir='src/pages'] - OWL pages directory.
62
- * @param {string} [options.layoutsDir='src/layouts'] - OWL layouts directory.
63
- * @param {string} [options.frameworkEntry] - Framework entry for manual chunk.
64
- * @param {string[]} [options.vendorPackages] - npm packages bundled into the vendor chunk.
65
- * @param {string} [options.envPrefix] - Only expose env vars with this prefix (plus NODE_ENV) via process.env.
66
- * @param {object} [options.autoImport] - Enable component auto-import
67
- * @param {boolean} [options.autoImport.enabled=false] - Enable auto-import
68
- * @param {string} [options.autoImport.pattern='*.js'] - Glob pattern for components
69
- * @returns {import('vite').Plugin[]}
70
- */
71
- export async function metaowlPlugin(options = {}) {
72
- const {
73
- root = 'src',
74
- outDir = '../dist',
75
- publicDir = '../public',
76
- componentsDir = 'src/components',
77
- pagesDir = 'src/pages',
78
- layoutsDir = 'src/layouts',
79
- frameworkEntry = './node_modules/metaowl/index.js',
80
- vendorPackages = ['@odoo/owl'],
81
- autoImport = {},
82
- envPrefix
83
- } = options
84
-
85
- const componentXml = collectXml(`${componentsDir}/**/*.xml`)
86
- const pageXml = collectXml(`${pagesDir}/**/*.xml`)
87
- const layoutXml = collectXml(`${layoutsDir}/**/*.xml`)
88
- const allComponents = [...layoutXml, ...pageXml, ...componentXml]
89
-
90
- let _outDirResolved = null
91
-
92
- // Generate auto-import d.ts for components
93
- const autoImportDtsPath = resolve(process.cwd(), '.metaowl', 'components.d.ts')
94
- let autoImportPlugin = null
95
-
96
- if (autoImport.enabled) {
97
- const { generateComponentDts, scanComponents } = await import('../modules/auto-import.js')
98
- const components = await scanComponents(componentsDir, { pattern: autoImport.pattern || '*.js' })
99
-
100
- // Ensure .metaowl directory exists
101
- const metaowlDir = dirname(autoImportDtsPath)
102
- if (!existsSync(metaowlDir)) {
103
- mkdirSync(metaowlDir, { recursive: true })
104
- }
105
-
106
- await generateComponentDts(components, autoImportDtsPath)
107
-
108
- autoImportPlugin = {
109
- name: 'metaowl:auto-import',
110
- enforce: 'pre',
111
- configResolved() {
112
- // Components are scanned at startup
113
- },
114
- handleHotUpdate({ file }) {
115
- // Rescan when component files change
116
- if (file.startsWith(resolve(componentsDir)) && file.endsWith('.js')) {
117
- scanComponents(componentsDir, { pattern: autoImport.pattern || '*.js' }).then(comps => {
118
- generateComponentDts(comps, autoImportDtsPath)
119
- })
120
- }
121
- }
122
- }
123
- }
124
-
125
- const plugins = [
126
- ...(autoImportPlugin ? [autoImportPlugin] : []),
127
- tsconfigPaths({ root: process.cwd() }),
128
- {
129
- name: 'metaowl:define',
130
- config(cfg, { mode }) {
131
- // Load .env file from project root
132
- dotenvConfig()
133
-
134
- const isDev = mode === 'development'
135
-
136
- // Expose only NODE_ENV + vars matching the configured prefix.
137
- // Never expose the full system env to avoid leaking secrets.
138
- const safeEnv = Object.fromEntries(
139
- Object.entries(process.env).filter(([k]) =>
140
- k === 'NODE_ENV' || (envPrefix && k.startsWith(envPrefix))
141
- )
142
- )
143
-
144
- cfg.define = {
145
- ...(cfg.define ?? {}),
146
- DEV_MODE: isDev,
147
- COMPONENTS: JSON.stringify(isDev ? allComponents : ['/templates.xml']),
148
- 'process.env': safeEnv
149
- }
150
-
151
- cfg.root = cfg.root ?? root
152
- cfg.publicDir = cfg.publicDir ?? publicDir
153
- cfg.appType = cfg.appType ?? 'spa'
154
-
155
- const owlPath = resolveOwlPath()
156
- cfg.resolve = {
157
- ...(cfg.resolve ?? {}),
158
- alias: {
159
- ...(cfg.resolve?.alias ?? {}),
160
- '@odoo/owl': owlPath
161
- }
162
- }
163
-
164
- cfg.build = {
165
- outDir,
166
- emptyOutDir: true,
167
- sourcemap: isDev,
168
- chunkSizeWarningLimit: 1024,
169
- target: 'esnext',
170
- rollupOptions: {
171
- input: resolve(root, 'index.html'),
172
- output: {
173
- manualChunks: {
174
- vendor: vendorPackages,
175
- framework: [frameworkEntry]
176
- }
177
- }
178
- },
179
- ...(cfg.build ?? {})
180
- }
181
-
182
- cfg.optimizeDeps = {
183
- include: ['@odoo/owl'],
184
- ...(cfg.optimizeDeps ?? {})
185
- }
186
- },
187
- configResolved(resolvedConfig) {
188
- _outDirResolved = resolve(resolvedConfig.root, resolvedConfig.build.outDir)
189
- }
190
- },
191
- {
192
- name: 'metaowl:app',
193
- transform(code, id) {
194
- if (!id.endsWith('/metaowl.js')) return
195
- const pagesRel = pagesDir.replace(new RegExp(`^${root}[\\/]`), '')
196
- const layoutsRel = layoutsDir.replace(new RegExp(`^${root}[\\/]`), '')
197
- return {
198
- code: code.replace(
199
- /boot\(\s*\)/,
200
- `boot(import.meta.glob('./${pagesRel}/**/*.js', { eager: true }), import.meta.glob('./${layoutsRel}/**/*.js', { eager: true }))`
201
- ),
202
- map: null
203
- }
204
- }
205
- },
206
- {
207
- name: 'metaowl:styles',
208
- transform(code, id) {
209
- if (!id.endsWith('/css.js')) return
210
- const compRel = componentsDir.replace(new RegExp(`^${root}[\\/]`), '')
211
- const pagesRel = pagesDir.replace(new RegExp(`^${root}[\\/]`), '')
212
- const layoutsRel = layoutsDir.replace(new RegExp(`^${root}[\\/]`), '')
213
- return {
214
- code: code + '\n' +
215
- `import.meta.glob('/${compRel}/**/*.{css,scss}', { eager: true })\n` +
216
- `import.meta.glob('/${pagesRel}/**/*.{css,scss}', { eager: true })\n` +
217
- `import.meta.glob('/${layoutsRel}/**/*.{css,scss}', { eager: true })\n`,
218
- map: null
219
- }
220
- }
221
- },
222
- {
223
- name: 'metaowl:copy-assets',
224
- apply: 'build',
225
- closeBundle() {
226
- const projectRoot = process.cwd()
227
-
228
- // Merge all OWL XML templates into a single file
229
- const xmlFiles = globSync([`${componentsDir}/**/*.xml`, `${pagesDir}/**/*.xml`, `${layoutsDir}/**/*.xml`])
230
- const mergedXml = mergeXmlFiles(xmlFiles)
231
-
232
- // Compute content hash for cache-busting in production
233
- const hash = createHash('sha256').update(mergedXml).digest('hex').slice(0, 8)
234
- const hashedFilename = `templates.${hash}.xml`
235
- writeFileSync(resolve(_outDirResolved, hashedFilename), mergedXml, 'utf-8')
236
-
237
- // Rewrite /templates.xml references in all built HTML and JS files
238
- const outputFiles = globSync(['**/*.html', '**/*.js'], { cwd: _outDirResolved, absolute: true })
239
- for (const file of outputFiles) {
240
- const content = readFileSync(file, 'utf-8')
241
- if (content.includes('/templates.xml')) {
242
- writeFileSync(file, content.replace(/\/templates\.xml/g, `/${hashedFilename}`), 'utf-8')
243
- }
244
- }
245
-
246
- // Copy assets/images (referenced via absolute URLs in XML — not processed by Vite)
247
- const srcImages = resolve(projectRoot, root, 'assets', 'images')
248
- if (existsSync(srcImages)) {
249
- cpSync(srcImages, resolve(_outDirResolved, 'assets', 'images'), { recursive: true })
250
- }
251
- }
252
- }
253
- ]
254
-
255
- return plugins
256
- }
257
-
258
- /**
259
- * Convenience wrapper that returns a complete Vite config with metaowl defaults.
260
- * All options except `server`, `preview` and `build` are forwarded to metaowlPlugin().
261
- *
262
- * Usage in vite.config.js:
263
- *
264
- * import { metaowlConfig } from 'metaowl/vite'
265
- * export default async () => {
266
- * return metaowlConfig({
267
- * server: { port: 3333 },
268
- * preview: { port: 8095 },
269
- * envPrefix: 'MY_',
270
- * vendorPackages: ['@odoo/owl', 'apexcharts']
271
- * })
272
- * }
273
- *
274
- * @param {object} [options]
275
- * @param {object} [options.server] - Vite server config overrides (merged with defaults).
276
- * @param {object} [options.preview] - Vite preview config overrides (merged with defaults).
277
- * @param {object} [options.build] - Vite build config overrides.
278
- * @param {*} [options.*] - All other options forwarded to metaowlPlugin().
279
- * @returns {import('vite').UserConfig}
280
- */
281
- export async function metaowlConfig(options = {}) {
282
- const { server, preview, build, ...metaowlOptions } = options
283
- const plugins = await metaowlPlugin(metaowlOptions)
284
- return {
285
- server: { port: 3000, strictPort: true, host: true, ...server },
286
- preview: { port: 4173, strictPort: true, ...preview },
287
- ...(build ? { build } : {}),
288
- plugins
289
- }
290
- }
package/vitest.config.js DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: 'jsdom',
6
- include: ['test/**/*.test.js']
7
- }
8
- })