@kimesh/kit 0.2.20 → 0.2.21
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/dist/index.d.mts +99 -2
- package/dist/index.mjs +108 -8
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -116,6 +116,26 @@ interface KimeshRouteMiddleware {
|
|
|
116
116
|
//#endregion
|
|
117
117
|
//#region src/types/hooks.d.ts
|
|
118
118
|
type HookResult = void | Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Normalized component directory for shadcn extension hook.
|
|
121
|
+
* Defined inline to avoid circular dependency with @kimesh/shadcn.
|
|
122
|
+
*/
|
|
123
|
+
interface ShadcnComponentDir {
|
|
124
|
+
/** Absolute or relative path to component directory */
|
|
125
|
+
path: string;
|
|
126
|
+
/** Component name prefix (e.g., 'Ui' for UiButton) */
|
|
127
|
+
prefix: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Tailwind source entry for extension hook.
|
|
131
|
+
* Defined inline to avoid circular dependency with @kimesh/tailwindcss.
|
|
132
|
+
*/
|
|
133
|
+
interface TailwindSourceEntry {
|
|
134
|
+
/** Absolute path to source directory */
|
|
135
|
+
path: string;
|
|
136
|
+
/** Optional comment in generated CSS */
|
|
137
|
+
comment?: string;
|
|
138
|
+
}
|
|
119
139
|
interface KimeshHooks {
|
|
120
140
|
/** Called after config is loaded, before layers */
|
|
121
141
|
'config:loaded': (config: KimeshConfig) => HookResult;
|
|
@@ -179,6 +199,44 @@ interface KimeshHooks {
|
|
|
179
199
|
'hmr:after': (file: string, kimesh: Kimesh) => HookResult;
|
|
180
200
|
/** Called when Kimesh is closing */
|
|
181
201
|
close: (kimesh: Kimesh) => HookResult;
|
|
202
|
+
/**
|
|
203
|
+
* Called by @kimesh/shadcn to allow extending modules to add component directories.
|
|
204
|
+
* Hook handlers can push entries to the componentDirs array.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* defineKimeshModule({
|
|
209
|
+
* hooks: {
|
|
210
|
+
* 'shadcn:componentDirs:extend': (componentDirs) => {
|
|
211
|
+
* componentDirs.push({
|
|
212
|
+
* path: '/path/to/uikit/components',
|
|
213
|
+
* prefix: 'Ui'
|
|
214
|
+
* })
|
|
215
|
+
* }
|
|
216
|
+
* }
|
|
217
|
+
* })
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
'shadcn:componentDirs:extend': (componentDirs: ShadcnComponentDir[], kimesh: Kimesh) => HookResult;
|
|
221
|
+
/**
|
|
222
|
+
* Called by @kimesh/tailwindcss to allow extending modules to add source directories.
|
|
223
|
+
* Hook handlers can push entries to the sources array.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* defineKimeshModule({
|
|
228
|
+
* hooks: {
|
|
229
|
+
* 'tailwindcss:sources:extend': (sources) => {
|
|
230
|
+
* sources.push({
|
|
231
|
+
* path: '/path/to/uikit/src',
|
|
232
|
+
* comment: '@onesystem/uikit'
|
|
233
|
+
* })
|
|
234
|
+
* }
|
|
235
|
+
* }
|
|
236
|
+
* })
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
'tailwindcss:sources:extend': (sources: TailwindSourceEntry[], kimesh: Kimesh) => HookResult;
|
|
182
240
|
}
|
|
183
241
|
//#endregion
|
|
184
242
|
//#region src/types/runtime-plugin.d.ts
|
|
@@ -399,6 +457,23 @@ interface KimeshModuleMeta {
|
|
|
399
457
|
/** Minimum Kimesh version required */kimesh?: string; /** Minimum Vite version required */
|
|
400
458
|
vite?: string;
|
|
401
459
|
};
|
|
460
|
+
/**
|
|
461
|
+
* Modules to auto-load before this module.
|
|
462
|
+
* Extended modules will be resolved and their hooks registered
|
|
463
|
+
* before this module's setup() is called.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```ts
|
|
467
|
+
* defineKimeshModule({
|
|
468
|
+
* meta: {
|
|
469
|
+
* name: '@onesystem/uikit',
|
|
470
|
+
* extends: ['@kimesh/shadcn', '@kimesh/tailwindcss'],
|
|
471
|
+
* },
|
|
472
|
+
* // ...
|
|
473
|
+
* })
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
extends?: string[];
|
|
402
477
|
}
|
|
403
478
|
/**
|
|
404
479
|
* Module defaults can be:
|
|
@@ -1473,11 +1548,33 @@ declare function normalizeModuleInput<TOptions>(input: KimeshModuleInput<TOption
|
|
|
1473
1548
|
options: Partial<TOptions>;
|
|
1474
1549
|
}>;
|
|
1475
1550
|
/**
|
|
1476
|
-
* Execute a single module
|
|
1551
|
+
* Execute a single module (legacy API - kept for backward compatibility)
|
|
1552
|
+
* @deprecated Use executeModules for proper extends support
|
|
1477
1553
|
*/
|
|
1478
1554
|
declare function executeModule<TOptions>(moduleInput: KimeshModuleInput<TOptions>, kimesh: Kimesh): Promise<void>;
|
|
1479
1555
|
/**
|
|
1480
|
-
* Execute all modules
|
|
1556
|
+
* Execute all modules using two-phase execution
|
|
1557
|
+
*
|
|
1558
|
+
* Two-phase execution ensures proper hook timing for module extends:
|
|
1559
|
+
*
|
|
1560
|
+
* **Phase 1: Resolve & Register Hooks**
|
|
1561
|
+
* - Resolves all modules (including those in `extends`)
|
|
1562
|
+
* - Registers hooks from all modules BEFORE any setup() runs
|
|
1563
|
+
* - This allows extending modules to inject config via hooks
|
|
1564
|
+
*
|
|
1565
|
+
* **Phase 2: Execute Setup**
|
|
1566
|
+
* - Calls setup() on all modules in dependency order
|
|
1567
|
+
* - Extended modules run first, then extending modules
|
|
1568
|
+
*
|
|
1569
|
+
* @example
|
|
1570
|
+
* ```ts
|
|
1571
|
+
* // @onesystem/uikit extends @kimesh/shadcn
|
|
1572
|
+
* // Execution order:
|
|
1573
|
+
* // 1. shadcn hooks registered
|
|
1574
|
+
* // 2. uikit hooks registered (can hook into shadcn)
|
|
1575
|
+
* // 3. shadcn.setup() runs (calls shadcn:componentDirs:extend hook)
|
|
1576
|
+
* // 4. uikit.setup() runs
|
|
1577
|
+
* ```
|
|
1481
1578
|
*/
|
|
1482
1579
|
declare function executeModules(modules: KimeshModuleInput[], kimesh: Kimesh): Promise<void>;
|
|
1483
1580
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -316,7 +316,8 @@ function defineKimeshModule(definition) {
|
|
|
316
316
|
name: definition.meta?.name ?? "anonymous-module",
|
|
317
317
|
version: definition.meta?.version ?? "0.0.0",
|
|
318
318
|
configKey: definition.meta?.configKey ?? "",
|
|
319
|
-
compatibility: definition.meta?.compatibility ?? {}
|
|
319
|
+
compatibility: definition.meta?.compatibility ?? {},
|
|
320
|
+
extends: definition.meta?.extends ?? []
|
|
320
321
|
},
|
|
321
322
|
async getDefaults(kimesh) {
|
|
322
323
|
if (!definition.defaults) return {};
|
|
@@ -324,9 +325,6 @@ function defineKimeshModule(definition) {
|
|
|
324
325
|
return definition.defaults;
|
|
325
326
|
},
|
|
326
327
|
async setup(options, kimesh) {
|
|
327
|
-
if (definition.hooks) {
|
|
328
|
-
for (const [hookName, handler] of Object.entries(definition.hooks)) if (handler) kimesh.hook(hookName, handler);
|
|
329
|
-
}
|
|
330
328
|
if (definition.setup) await definition.setup(options, kimesh);
|
|
331
329
|
}
|
|
332
330
|
};
|
|
@@ -335,6 +333,7 @@ function defineKimeshModule(definition) {
|
|
|
335
333
|
* Resolve a string module name to an actual module
|
|
336
334
|
*/
|
|
337
335
|
async function resolveStringModule(moduleName, kimesh) {
|
|
336
|
+
if (moduleName.includes("..") || moduleName.startsWith("/") || moduleName.includes("\\")) throw new Error(`Invalid module name: "${moduleName}". Module names cannot contain path traversal characters.`);
|
|
338
337
|
try {
|
|
339
338
|
const requirePath = kimesh.root + "/package.json";
|
|
340
339
|
const require = createRequire(pathToFileURL(requirePath).href);
|
|
@@ -381,7 +380,82 @@ async function normalizeModuleInput(input, kimesh) {
|
|
|
381
380
|
};
|
|
382
381
|
}
|
|
383
382
|
/**
|
|
384
|
-
*
|
|
383
|
+
* Create a fresh execution context
|
|
384
|
+
*/
|
|
385
|
+
function createExecutionContext() {
|
|
386
|
+
return {
|
|
387
|
+
loadedModules: /* @__PURE__ */ new Set(),
|
|
388
|
+
loadingStack: [],
|
|
389
|
+
resolvedModules: /* @__PURE__ */ new Map()
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* PHASE 1: Resolve module and its dependencies, register hooks
|
|
394
|
+
*
|
|
395
|
+
* This function:
|
|
396
|
+
* 1. Resolves the module (from string or object)
|
|
397
|
+
* 2. Recursively resolves all modules in `extends` first
|
|
398
|
+
* 3. Registers the module's hooks
|
|
399
|
+
* 4. Tracks loaded modules to prevent duplicates
|
|
400
|
+
* 5. Detects circular dependencies
|
|
401
|
+
*/
|
|
402
|
+
async function resolveAndRegisterHooks(moduleInput, kimesh, ctx, userOptions = {}) {
|
|
403
|
+
const { module, options: inputOptions } = await normalizeModuleInput(moduleInput, kimesh);
|
|
404
|
+
const moduleName = module.meta.name;
|
|
405
|
+
if (ctx.loadedModules.has(moduleName)) return;
|
|
406
|
+
if (ctx.loadingStack.includes(moduleName)) {
|
|
407
|
+
const cycle = [...ctx.loadingStack, moduleName].join(" -> ");
|
|
408
|
+
throw new Error(`Circular dependency detected: ${cycle}`);
|
|
409
|
+
}
|
|
410
|
+
ctx.loadingStack.push(moduleName);
|
|
411
|
+
try {
|
|
412
|
+
const extendsModules = module.meta.extends || [];
|
|
413
|
+
for (const extendedName of extendsModules) await resolveAndRegisterHooks(extendedName, kimesh, ctx);
|
|
414
|
+
ctx.loadedModules.add(moduleName);
|
|
415
|
+
const mergedOptions = {
|
|
416
|
+
...inputOptions,
|
|
417
|
+
...userOptions
|
|
418
|
+
};
|
|
419
|
+
ctx.resolvedModules.set(moduleName, {
|
|
420
|
+
module,
|
|
421
|
+
options: mergedOptions
|
|
422
|
+
});
|
|
423
|
+
if (module._def.hooks) {
|
|
424
|
+
for (const [hookName, handler] of Object.entries(module._def.hooks)) if (handler) kimesh.hook(hookName, handler);
|
|
425
|
+
}
|
|
426
|
+
} finally {
|
|
427
|
+
ctx.loadingStack.pop();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* PHASE 2: Execute all setup() functions in dependency order
|
|
432
|
+
*
|
|
433
|
+
* Called after all modules are resolved and hooks registered.
|
|
434
|
+
* Modules execute in the order they were resolved (dependencies first).
|
|
435
|
+
*/
|
|
436
|
+
async function executeAllSetups(kimesh, ctx) {
|
|
437
|
+
for (const [moduleName, { module, options: userOptions }] of ctx.resolvedModules) {
|
|
438
|
+
_setKimeshContext(kimesh);
|
|
439
|
+
try {
|
|
440
|
+
const defaults = await module.getDefaults(kimesh);
|
|
441
|
+
const configKey = module.meta.configKey;
|
|
442
|
+
const configOptions = configKey ? kimesh.options.config[configKey] ?? {} : {};
|
|
443
|
+
const mergedOptions = {
|
|
444
|
+
...defaults,
|
|
445
|
+
...configOptions,
|
|
446
|
+
...userOptions
|
|
447
|
+
};
|
|
448
|
+
await module.setup(mergedOptions, kimesh);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
consola.error(`[Kimesh] Failed to execute module "${moduleName}":`, error);
|
|
451
|
+
} finally {
|
|
452
|
+
_setKimeshContext(void 0);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Execute a single module (legacy API - kept for backward compatibility)
|
|
458
|
+
* @deprecated Use executeModules for proper extends support
|
|
385
459
|
*/
|
|
386
460
|
async function executeModule(moduleInput, kimesh) {
|
|
387
461
|
const { module, options: userOptions } = await normalizeModuleInput(moduleInput, kimesh);
|
|
@@ -393,6 +467,9 @@ async function executeModule(moduleInput, kimesh) {
|
|
|
393
467
|
...configOptions,
|
|
394
468
|
...userOptions
|
|
395
469
|
};
|
|
470
|
+
if (module._def.hooks) {
|
|
471
|
+
for (const [hookName, handler] of Object.entries(module._def.hooks)) if (handler) kimesh.hook(hookName, handler);
|
|
472
|
+
}
|
|
396
473
|
_setKimeshContext(kimesh);
|
|
397
474
|
try {
|
|
398
475
|
await module.setup(mergedOptions, kimesh);
|
|
@@ -413,16 +490,39 @@ function getModuleName(input) {
|
|
|
413
490
|
return input.meta?.name ?? "unknown";
|
|
414
491
|
}
|
|
415
492
|
/**
|
|
416
|
-
* Execute all modules
|
|
493
|
+
* Execute all modules using two-phase execution
|
|
494
|
+
*
|
|
495
|
+
* Two-phase execution ensures proper hook timing for module extends:
|
|
496
|
+
*
|
|
497
|
+
* **Phase 1: Resolve & Register Hooks**
|
|
498
|
+
* - Resolves all modules (including those in `extends`)
|
|
499
|
+
* - Registers hooks from all modules BEFORE any setup() runs
|
|
500
|
+
* - This allows extending modules to inject config via hooks
|
|
501
|
+
*
|
|
502
|
+
* **Phase 2: Execute Setup**
|
|
503
|
+
* - Calls setup() on all modules in dependency order
|
|
504
|
+
* - Extended modules run first, then extending modules
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```ts
|
|
508
|
+
* // @onesystem/uikit extends @kimesh/shadcn
|
|
509
|
+
* // Execution order:
|
|
510
|
+
* // 1. shadcn hooks registered
|
|
511
|
+
* // 2. uikit hooks registered (can hook into shadcn)
|
|
512
|
+
* // 3. shadcn.setup() runs (calls shadcn:componentDirs:extend hook)
|
|
513
|
+
* // 4. uikit.setup() runs
|
|
514
|
+
* ```
|
|
417
515
|
*/
|
|
418
516
|
async function executeModules(modules, kimesh) {
|
|
419
517
|
await kimesh.callHook("modules:before", kimesh);
|
|
518
|
+
const ctx = createExecutionContext();
|
|
420
519
|
for (const moduleInput of modules) try {
|
|
421
|
-
await
|
|
520
|
+
await resolveAndRegisterHooks(moduleInput, kimesh, ctx, Array.isArray(moduleInput) ? moduleInput[1] : {});
|
|
422
521
|
} catch (error) {
|
|
423
522
|
const moduleName = getModuleName(moduleInput);
|
|
424
|
-
consola.error(`[Kimesh] Failed to
|
|
523
|
+
consola.error(`[Kimesh] Failed to resolve module "${moduleName}":`, error);
|
|
425
524
|
}
|
|
525
|
+
await executeAllSetups(kimesh, ctx);
|
|
426
526
|
await kimesh.callHook("modules:done", kimesh);
|
|
427
527
|
}
|
|
428
528
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kimesh/kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
4
4
|
"description": "Build-time engine for Kimesh framework",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@kimesh/auto-import": "0.2.
|
|
31
|
-
"@kimesh/layers": "0.2.
|
|
32
|
-
"@kimesh/router-generator": "0.2.
|
|
30
|
+
"@kimesh/auto-import": "0.2.21",
|
|
31
|
+
"@kimesh/layers": "0.2.21",
|
|
32
|
+
"@kimesh/router-generator": "0.2.21",
|
|
33
33
|
"@vitejs/plugin-vue": "^6.0.3",
|
|
34
34
|
"c12": "^3.3.3",
|
|
35
35
|
"consola": "^3.4.2",
|