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/index.js DELETED
@@ -1,328 +0,0 @@
1
- import { mountApp } from './modules/app-mounter.js'
2
- import { buildRoutes } from './modules/file-router.js'
3
- import { processRoutes, setSpaMode, _setSpaNavigationCallback } from './modules/router.js'
4
- import { discoverLayouts, buildLayouts, setDefaultLayout } from './modules/layouts.js'
5
-
6
- export { default as Fetch } from './modules/fetch.js'
7
- export { default as Cache } from './modules/cache.js'
8
- export { configureOwl } from './modules/app-mounter.js'
9
- export * as Meta from './modules/meta.js'
10
- export { buildRoutes }
11
- export { Store, createPersistencePlugin, createStore } from './modules/store.js'
12
- export {
13
- registerLayout,
14
- unregisterLayout,
15
- getLayout,
16
- hasLayout,
17
- getLayoutNames,
18
- setDefaultLayout,
19
- getDefaultLayout,
20
- resolveLayout,
21
- setRouteLayout,
22
- getRouteLayout,
23
- createLayoutWrapper,
24
- mountWithLayout,
25
- getCurrentLayout,
26
- subscribeToLayouts,
27
- clearLayouts,
28
- layout,
29
- defineLayout,
30
- buildLayouts,
31
- discoverLayouts
32
- } from './modules/layouts.js'
33
- export {
34
- processRoutes,
35
- beforeEach,
36
- afterEach,
37
- getCurrentRoute,
38
- getPreviousRoute,
39
- isNavigating,
40
- cancelNavigation,
41
- navigate,
42
- navigateTo,
43
- push,
44
- replace,
45
- back,
46
- forward,
47
- go,
48
- router,
49
- setSpaMode,
50
- isSpaMode
51
- } from './modules/router.js'
52
- export { Link, registerLinkTemplate } from './modules/link.js'
53
- export {
54
- matchRoute,
55
- isDynamicRoute,
56
- findRoute,
57
- generateUrl,
58
- validateRouteParams,
59
- createCatchAllRoute,
60
- createRedirectRoute,
61
- defineRoute,
62
- route
63
- } from './modules/file-router.js'
64
- export {
65
- onError,
66
- setErrorContext,
67
- getErrorContext,
68
- clearErrorContext,
69
- captureError,
70
- initGlobalErrorHandling,
71
- errorBoundary
72
- } from './modules/error-boundary.js'
73
- export {
74
- configureI18n,
75
- t,
76
- getLocale,
77
- setLocale,
78
- i18n,
79
- loadLocaleMessages,
80
- formatDate,
81
- formatNumber,
82
- formatCurrency,
83
- formatRelativeTime,
84
- createNamespacedT
85
- } from './modules/i18n.js'
86
- export {
87
- useForm,
88
- validators,
89
- createSchema,
90
- fieldProps
91
- } from './modules/forms.js'
92
- export {
93
- OdooService,
94
- configure,
95
- authenticate,
96
- logout,
97
- searchRead,
98
- call,
99
- read,
100
- create,
101
- write,
102
- unlink,
103
- searchCount,
104
- listDatabases,
105
- versionInfo,
106
- isAuthenticated,
107
- getSession,
108
- onAuthChange
109
- } from './modules/odoo-rpc.js'
110
- export {
111
- useAuth,
112
- useLocalStorage,
113
- useFetch,
114
- useDebounce,
115
- useThrottle,
116
- useWindowSize,
117
- useOnlineStatus,
118
- useAsyncState,
119
- useCache,
120
- Composables
121
- } from './modules/composables.js'
122
- export {
123
- createMockStore,
124
- mockRouter,
125
- mountComponent,
126
- wait,
127
- nextTick,
128
- flushPromises,
129
- userEvent,
130
- dom,
131
- TestUtils
132
- } from './modules/test-utils.js'
133
- export {
134
- generateSitemap,
135
- generateRobotsTxt,
136
- jsonLd,
137
- createCanonicalUrl,
138
- generateOpenGraph,
139
- generateTwitterCard,
140
- validateSitemap,
141
- getPriorityByDepth,
142
- generateSitemapIndex,
143
- SEO
144
- } from './modules/seo.js'
145
- export {
146
- generateManifest,
147
- registerServiceWorker,
148
- unregisterServiceWorker,
149
- isStandalone,
150
- isOnline,
151
- subscribeToConnectivity,
152
- requestPersistentStorage,
153
- getStorageInfo,
154
- sync,
155
- subscribeToPush,
156
- unsubscribeFromPush,
157
- showNotification,
158
- cache,
159
- checkCapabilities,
160
- PWA
161
- } from './modules/pwa.js'
162
-
163
- /**
164
- * Global routes reference for SPA navigation.
165
- * @type {object[]|null}
166
- */
167
- let _appRoutes = null
168
-
169
- /**
170
- * Monotonically-increasing navigation counter.
171
- * Incremented on every navigation attempt; lets us discard stale navigations
172
- * that complete AFTER a newer one was already triggered.
173
- * @type {number}
174
- */
175
- let _navSeq = 0
176
-
177
- /**
178
- * Promise of the currently-running mountApp call.
179
- * Used to serialize mounts: a new navigation waits for the in-progress mount
180
- * to finish, then checks if it is still the latest before mounting itself.
181
- * This prevents concurrent OWL App instances on the same element.
182
- * @type {Promise<void>|null}
183
- */
184
- let _mountingPromise = null
185
-
186
- function _handle404() {
187
- const el = document.getElementById('metaowl')
188
- if (el) {
189
- el.innerHTML = [
190
- '<div style="font-family:sans-serif;padding:3rem;text-align:center">',
191
- '<h1 style="font-size:4rem;font-weight:700;margin:0;color:#6b7280">404</h1>',
192
- '<p style="font-size:1.25rem;color:#9ca3af;margin-top:0.5rem">Page not found</p>',
193
- '<p style="margin-top:2rem"><a href="/" style="color:#3b82f6;text-decoration:none">← Go home</a></p>',
194
- '</div>'
195
- ].join('')
196
- }
197
- }
198
-
199
- /**
200
- * SPA navigation callback.
201
- * Called when navigateTo() is used.
202
- *
203
- * @param {string} path - The target path
204
- * @returns {Promise<void>}
205
- */
206
- async function _spaNavigate(path) {
207
- if (!_appRoutes) {
208
- console.error('[metaowl] Routes not available for SPA navigation')
209
- return
210
- }
211
-
212
- const seq = ++_navSeq
213
-
214
- let route
215
- try {
216
- route = await processRoutes(_appRoutes, path)
217
- } catch (error) {
218
- if (seq !== _navSeq) return
219
- if (error.message && error.message.startsWith('No route found')) {
220
- console.warn('[metaowl]', error.message)
221
- _handle404()
222
- } else {
223
- throw error
224
- }
225
- return
226
- }
227
-
228
- // Bail early if a newer navigation overtook us while processRoutes was running
229
- if (seq !== _navSeq || !route) return
230
-
231
- // Wait for any in-progress mount to finish before starting our own.
232
- // This is the key serialization: it ensures only one OWL App mounts at a time.
233
- if (_mountingPromise) {
234
- await _mountingPromise.catch(() => {})
235
- // After waiting, check again — a newer navigation may have started
236
- if (seq !== _navSeq) return
237
- }
238
-
239
- // Claim the mount slot
240
- _mountingPromise = mountApp(route)
241
- try {
242
- await _mountingPromise
243
- } finally {
244
- _mountingPromise = null
245
- }
246
- }
247
-
248
- /**
249
- * Boots the metaowl application.
250
- *
251
- * When called without arguments inside a Vite project, the `metaowl:app`
252
- * plugin transform automatically rewrites `boot()` to
253
- * `boot(import.meta.glob('./pages/**\/*.js', { eager: true }))` at build time.
254
- *
255
- * Can also be called explicitly with:
256
- * - An import.meta.glob result (file-based routing):
257
- * boot(import.meta.glob('./pages/**\/*.js', { eager: true }))
258
- * - A manual route array:
259
- * boot([{ name: 'index', path: ['/'], component: IndexPage }])
260
- *
261
- * @param {Record<string, object>|object[]} [routesOrModules]
262
- * @param {object} [options] - Boot options
263
- * @param {boolean} [options.spa=true] - Enable SPA navigation mode
264
- */
265
- export async function boot(routesOrModules = {}, layoutsOrModules = null, options = {}) {
266
- const { spa = true } = options
267
-
268
- // Auto-discover layouts
269
- try {
270
- if (layoutsOrModules && typeof layoutsOrModules === 'object' && !Array.isArray(layoutsOrModules)) {
271
- // Use layouts provided by Vite plugin transformation
272
- buildLayouts(layoutsOrModules)
273
- setDefaultLayout('default')
274
- } else if (typeof layoutsOrModules === 'object' && layoutsOrModules?.spa !== undefined) {
275
- // Options object passed as second argument
276
- Object.assign(options, layoutsOrModules)
277
- } else {
278
- await discoverLayouts()
279
- }
280
- } catch (e) {
281
- console.warn('[metaowl] Could not auto-discover layouts:', e.message)
282
- }
283
-
284
- const routes = Array.isArray(routesOrModules)
285
- ? routesOrModules
286
- : buildRoutes(routesOrModules)
287
-
288
- // Store routes for SPA navigation
289
- _appRoutes = routes
290
-
291
- // Enable SPA mode
292
- if (spa) {
293
- setSpaMode(true)
294
- _setSpaNavigationCallback(_spaNavigate)
295
-
296
- // Register global navigateTo handler for Link component
297
- window.__metaowlNavigate = _spaNavigate
298
-
299
- // Listen to PopState events (Browser Back/Forward)
300
- window.addEventListener('popstate', (event) => {
301
- const path = document.location.pathname
302
- _spaNavigate(path)
303
- })
304
- }
305
-
306
- let route
307
- try {
308
- route = await processRoutes(routes)
309
- } catch (error) {
310
- if (error.message && error.message.startsWith('No route found')) {
311
- console.warn('[metaowl]', error.message)
312
- const el = document.getElementById('metaowl')
313
- if (el) {
314
- el.innerHTML = [
315
- '<div style="font-family:sans-serif;padding:3rem;text-align:center">',
316
- '<h1 style="font-size:4rem;font-weight:700;margin:0;color:#6b7280">404</h1>',
317
- '<p style="font-size:1.25rem;color:#9ca3af;margin-top:0.5rem">Page not found</p>',
318
- '<p style="margin-top:2rem"><a href="/" style="color:#3b82f6;text-decoration:none">← Go home</a></p>',
319
- '</div>'
320
- ].join('')
321
- }
322
- return
323
- }
324
- throw error
325
- }
326
-
327
- await mountApp(route)
328
- }
@@ -1,104 +0,0 @@
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
- */
8
- import { mount } from '@odoo/owl'
9
- import { mergeTemplates } from './templates-manager.js'
10
- import { resolveLayout, getLayout, mountWithLayout } from './layouts.js'
11
- import { Link } from './link.js'
12
-
13
- const _defaults = {
14
- warnIfNoStaticProps: true,
15
- willStartTimeout: 10000,
16
- translatableAttributes: ['title', 'placeholder', 'label', 'alt']
17
- }
18
-
19
- let _config = { ..._defaults }
20
-
21
- /**
22
- * Reference to the currently mounted OWL App instance.
23
- * Destroyed before each new mount to prevent zombie app accumulation.
24
- * @type {import('@odoo/owl').App|null}
25
- */
26
- let _currentApp = null
27
-
28
- /**
29
- * Override or extend the default OWL mount configuration.
30
- * Call before boot() in your project's metaowl.js.
31
- *
32
- * @param {object} config - Partial OWL config merged over the defaults.
33
- */
34
- export function configureOwl(config) {
35
- _config = { ..._defaults, ...config }
36
- }
37
-
38
- /**
39
- * Mount the resolved route's OWL component into `#app`.
40
- *
41
- * Loads and merges all XML templates (collected at build time by the
42
- * metaowl Vite plugin via the `COMPONENTS` define), then mounts the component
43
- * using the active OWL config.
44
- *
45
- * @param {object[]} route - Single-element array returned by `processRoutes()`.
46
- * @returns {Promise<void>}
47
- */
48
- /**
49
- * Cached merged templates string. Computed once on first navigation;
50
- * COMPONENTS (the list of XML files) never changes at runtime so the
51
- * result is the same for every mount.
52
- * @type {string|null}
53
- */
54
- let _cachedTemplates = null
55
-
56
- export async function mountApp(route) {
57
- // Load and cache templates on first call; reuse on every subsequent navigation.
58
- // Without caching, every navigation re-fetches all XML template files.
59
- const components = typeof COMPONENTS !== 'undefined' ? COMPONENTS : []
60
- if (!_cachedTemplates) {
61
- _cachedTemplates = await mergeTemplates(components)
62
- }
63
- const templates = _cachedTemplates
64
- const mountElement = document.getElementById('metaowl')
65
-
66
- // Destroy the previous OWL App before mounting a new one.
67
- // Without this, every navigation leaves a zombie app running in the background
68
- // (scheduler, reactive effects, event listeners) that accumulates and causes freezes.
69
- if (_currentApp) {
70
- try { _currentApp.destroy() } catch (_) {}
71
- _currentApp = null
72
- }
73
- mountElement.innerHTML = ''
74
-
75
- const pageComponent = route[0].component
76
- const pagePath = route[0].path
77
-
78
- // Check for layout
79
- const layoutName = resolveLayout(pageComponent, pagePath)
80
- const LayoutClass = getLayout(layoutName)
81
-
82
- // Base mount configuration with built-in components
83
- const baseConfig = {
84
- ..._config,
85
- templates,
86
- components: {
87
- Link,
88
- 't-link': Link
89
- }
90
- }
91
-
92
- let instance
93
- if (LayoutClass) {
94
- // Mount with layout
95
- instance = await mountWithLayout(pageComponent, mountElement, { routePath: pagePath, ...baseConfig })
96
- } else {
97
- // Mount without layout
98
- instance = await mount(pageComponent, mountElement, baseConfig)
99
- }
100
-
101
- // Store OWL App reference so we can destroy it before the next navigation.
102
- // instance.__owl__.app is the underlying App object that owns the scheduler.
103
- _currentApp = instance?.__owl__?.app ?? null
104
- }
@@ -1,225 +0,0 @@
1
- /**
2
- * @module AutoImport
3
- *
4
- * Automatic component importing for MetaOwl applications.
5
- *
6
- * Features:
7
- * - Auto-discovers components from src/components/
8
- * - Generates import statements at build time
9
- * - Optional - disabled by default
10
- *
11
- * Configuration in vite.config.js:
12
- * metaowlConfig({
13
- * autoImport: {
14
- * enabled: true,
15
- * componentsDir: 'src/components',
16
- * pattern: '*.js'
17
- * }
18
- * })
19
- *
20
- * Usage:
21
- * - Components are automatically available in templates
22
- * - No manual import needed
23
- */
24
-
25
- import { globSync } from 'glob'
26
- import { resolve, relative, basename, extname, dirname } from 'node:path'
27
-
28
- /**
29
- * Registry of auto-discovered components.
30
- */
31
- let _importMap = null
32
-
33
- /**
34
- * Generate component import map from directory.
35
- *
36
- * @param {string} componentsDir - e.g. 'src/components'
37
- * @returns {Map<string, string>} Map of PascalCase names to import paths
38
- */
39
- export function generateComponentMap(componentsDir, pattern = '*.js') {
40
- const map = new Map()
41
- const globPattern = pattern.includes('/') ? pattern : `${componentsDir}/${pattern}`
42
- const files = globSync(globPattern)
43
-
44
- for (const file of files) {
45
- if (file.includes('.test.') || file.includes('.spec.')) continue
46
-
47
- const name = getComponentName(file)
48
- if (!name) continue
49
-
50
- const importPath = `/@components/${relative(componentsDir, file).replace(/\\/g, '/')}`
51
- map.set(name, importPath)
52
- }
53
-
54
- return map
55
- }
56
-
57
- function getComponentName(filePath) {
58
- const ext = extname(filePath)
59
- const base = basename(filePath, ext)
60
-
61
- if (basename(filePath) === base + ext) {
62
- return base
63
- }
64
-
65
- const dir = relative(process.cwd(), filePath).split('/')
66
- if (base === 'index' && dir.length > 1) {
67
- return toPascalCase(dir[dir.length - 2])
68
- }
69
-
70
- return toPascalCase(base)
71
- }
72
-
73
- function toPascalCase(str) {
74
- return str
75
- .replace(/[-_](.)/g, (_, char) => char.toUpperCase())
76
- .replace(/^[a-z]/, char => char.toUpperCase())
77
- }
78
-
79
- /**
80
- * Scan components directory for component files.
81
- *
82
- * @param {string} componentsDir - Directory to scan
83
- * @param {object} options - Options
84
- * @param {string} options.pattern - Glob pattern
85
- * @returns {Promise<string[]>} Array of component names
86
- */
87
- export async function scanComponents(componentsDir, options = {}) {
88
- const { pattern = '*.js' } = options
89
- const absoluteDir = resolve(componentsDir)
90
- const fs = await import('fs/promises')
91
- const { join } = await import('path')
92
-
93
- const components = []
94
-
95
- async function scanDir(dir) {
96
- try {
97
- const entries = await fs.readdir(dir, { withFileTypes: true })
98
-
99
- for (const entry of entries) {
100
- const fullPath = join(dir, entry.name)
101
-
102
- if (entry.isDirectory()) {
103
- // Recursively scan subdirectories
104
- await scanDir(fullPath)
105
- } else if (entry.isFile() && entry.name.endsWith('.js')) {
106
- // Skip test files
107
- if (entry.name.includes('.test.') || entry.name.includes('.spec.')) continue
108
-
109
- const name = getComponentName(fullPath)
110
- if (name && !components.includes(name)) {
111
- components.push(name)
112
- }
113
- }
114
- }
115
- } catch {
116
- // Directory doesn't exist or can't be read
117
- }
118
- }
119
-
120
- await scanDir(absoluteDir)
121
- return components
122
- }
123
-
124
- /**
125
- * Generate TypeScript declarations file for components.
126
- *
127
- * @param {string[]} components - Array of component names
128
- * @param {string} outputPath - Path to write .d.ts file
129
- * @returns {Promise<void>}
130
- */
131
- export async function generateComponentDts(components, outputPath) {
132
- const { writeFileSync, mkdirSync } = await import('fs')
133
- const { join } = await import('path')
134
-
135
- // Ensure directory exists
136
- const dir = dirname(outputPath)
137
- mkdirSync(dir, { recursive: true })
138
-
139
- const declarations = components.map(name =>
140
- ` ${name}: typeof import('./components/${name}/${name}.js').default`
141
- ).join('\n')
142
-
143
- const content = `// Auto-generated by metaowl - do not edit\ndeclare module '@metaowl/components' {\n${declarations}\n}\n`
144
-
145
- writeFileSync(outputPath, content, 'utf-8')
146
- }
147
-
148
- export function generateImports(componentMap) {
149
- const lines = []
150
- for (const [name, path] of componentMap) {
151
- lines.push(`import ${name} from '${path}'`)
152
- }
153
- return lines.join('\n')
154
- }
155
-
156
- export function generateComponentsObject(componentMap) {
157
- const entries = Array.from(componentMap.keys())
158
- .map(name => ` ${name}`)
159
- .join(',\n')
160
- return `{\n${entries}\n}`
161
- }
162
-
163
- export function createAutoImportPlugin(options = {}) {
164
- const {
165
- enabled = false,
166
- componentsDir = 'src/components',
167
- pattern = '**/*.js'
168
- } = options
169
-
170
- if (!enabled) {
171
- return null
172
- }
173
-
174
- _importMap = generateComponentMap(componentsDir, `${componentsDir}/${pattern}`)
175
-
176
- return {
177
- name: 'metaowl:auto-import',
178
- enforce: 'pre',
179
-
180
- config(config) {
181
- config.resolve ||= {}
182
- config.resolve.alias ||= {}
183
- config.resolve.alias['/@components'] = resolve(process.cwd(), componentsDir)
184
- },
185
-
186
- transform(code, id) {
187
- if (!id.includes('/pages/') || !/\.[jt]s$/.test(id)) {
188
- return null
189
- }
190
-
191
- if (!code.includes('/* auto-import */') && !code.includes('// auto-import')) {
192
- return null
193
- }
194
-
195
- const imports = generateImports(_importMap)
196
- const componentsObj = generateComponentsObject(_importMap)
197
-
198
- let transformed = imports + '\n\n' + code
199
-
200
- if (transformed.includes('extends Component')) {
201
- transformed = transformed.replace(
202
- /(class\s+\w+\s+extends\s+Component\s*\{)/,
203
- `$1\n static components = ${componentsObj}\n`
204
- )
205
- }
206
-
207
- return { code: transformed, map: null }
208
- }
209
- }
210
- }
211
-
212
- export function registerAutoImport(name, path) {
213
- if (!_importMap) {
214
- _importMap = new Map()
215
- }
216
- _importMap.set(name, path)
217
- }
218
-
219
- export function getAutoImportMap() {
220
- return _importMap ? new Map(_importMap) : null
221
- }
222
-
223
- export function clearAutoImports() {
224
- _importMap = null
225
- }
package/modules/cache.js DELETED
@@ -1,59 +0,0 @@
1
- /**
2
- * @module Cache
3
- *
4
- * Async-style localStorage wrapper.
5
- *
6
- * Values are automatically JSON-serialised on write and deserialised on read.
7
- * All methods return Promises so they are interchangeable with IndexedDB-based
8
- * alternatives without changing call-sites.
9
- */
10
- export default class Cache {
11
- /**
12
- * Retrieve a value by key.
13
- *
14
- * @param {string} key
15
- * @returns {Promise<any>} Parsed value, or `null` if the key does not exist.
16
- */
17
- static async get(key) {
18
- return JSON.parse(localStorage.getItem(key))
19
- }
20
-
21
- /**
22
- * Store a value under the given key.
23
- *
24
- * @param {string} key
25
- * @param {any} value - Must be JSON-serialisable.
26
- * @returns {Promise<void>}
27
- */
28
- static async set(key, value) {
29
- localStorage.setItem(key, JSON.stringify(value))
30
- }
31
-
32
- /**
33
- * Remove a single entry.
34
- *
35
- * @param {string} key
36
- * @returns {Promise<void>}
37
- */
38
- static async remove(key) {
39
- localStorage.removeItem(key)
40
- }
41
-
42
- /**
43
- * Remove **all** entries from localStorage.
44
- *
45
- * @returns {Promise<void>}
46
- */
47
- static async clear() {
48
- localStorage.clear()
49
- }
50
-
51
- /**
52
- * Return all keys currently stored in localStorage.
53
- *
54
- * @returns {Promise<string[]>}
55
- */
56
- static async keys() {
57
- return Array.from({ length: localStorage.length }, (_, i) => localStorage.key(i))
58
- }
59
- }