Cross-Module Imports
Modules can share components, composables, and utilities with each other. Because each module is compiled into an isolated bundle, sharing works through the same globalThis.imports / globalThis.importAsync API the host uses for framework packages.
There are two sides to the contract:
| Role | Responsibility |
|---|---|
| Source module (provides) | Registers its exports on globalThis.imports during onRegister() |
| Target module (consumes) | Declares the source module's alias in module.json build.imports |
Source module — registering exports
In client/module.client.ts, register whatever you want to expose during the onRegister() lifecycle hook. Pick a short, unique alias for your module (e.g. #zpayments) and use it as the key prefix.
Exposing Vue components
// modules/zenith-payments/client/module.client.ts
export default class ZenithPayments extends Module {
public async onRegister(): Promise<void> {
for (const [path, importFn] of Object.entries(import.meta.glob<any>('./{components,layouts}/**/*.vue'))) {
const id = path.replace('./', '#zpayments/')
const ext = path.split('.').pop()
globalThis.imports[id] = importFn
globalThis.imports[id.replace(`.${ext}`, '')] = importFn
}
}
}This registers every .vue file under components/ and layouts/ under two ids:
#zpayments/components/UserOrdersCard.vue
#zpayments/components/UserOrdersCard ← without extensionExposing shared utilities / entities
for (const [path, mod] of Object.entries(import.meta.glob('./../shared/**'))) {
const id = path.replace('../shared/', '#zpayments/shared/')
globalThis.imports[id] = mod
globalThis.imports[id.replace('.ts', '')] = mod
}This registers every file under modules/zenith-payments/shared/ as #zpayments/shared/….
Target module — declaring the dependency
1. module.json
Tell the build system that imports from #zpayments are external and should be routed through globalThis.importAsync at runtime:
{
"dependencies": {
"zenith-payments": {}
},
"build": {
"imports": [
{ "from": "#zpayments", "type": "global_import" }
]
}
}dependenciesdocuments the runtime dependency on the source module.build.importstells the Vite build to externalize#zpayments(and any subpath like#zpayments/components/…) and rewrite those imports toawait globalThis.importAsync("…").
2. Use the alias in your code
Once declared, you can import from the source module's alias just like any other package:
import UserOrdersCard from '#zpayments/components/UserOrdersCard.vue'
import type { Order } from '#zpayments/shared/entities/order.entity'At build time the import rewriter turns these into:
const __m__ = await globalThis.importAsync("#zpayments/components/UserOrdersCard.vue");
const UserOrdersCard = __m__.default || __m__;At runtime this resolves against whatever the source module registered in onRegister().
Execution order guarantee
onRegister() runs for all enabled modules before onLoad() starts. This means a target module can safely use exported symbols from a source module inside its own onLoad() — the source's globalThis.imports entries are always ready in time.
all modules → onRegister() ← source registers its exports here
all modules → onLoad() ← target can safely import from source hereSummary checklist
Source module (zenith-payments)
- [ ] Choose a unique alias (e.g.
#zpayments) - [ ] Register components/utils on
globalThis.importsinonRegister()
Target module (artlyze)
- [ ] Add source to
dependenciesinmodule.json - [ ] Add
{ "from": "#zpayments", "type": "global_import" }tobuild.importsinmodule.json - [ ] Run
module:buildfor the target module after changingmodule.json - [ ] Import using the alias:
import Foo from '#zpayments/components/Foo.vue'