@kimesh/kit 0.0.1
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/README.md +3 -0
- package/dist/index.d.mts +2257 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2795 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2795 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { createHooks } from "hookable";
|
|
3
|
+
import { snakeCase } from "scule";
|
|
4
|
+
import { klona } from "klona/json";
|
|
5
|
+
import destr from "destr";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import consola from "consola";
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
|
+
import fg from "fast-glob";
|
|
11
|
+
import { basename, extname, isAbsolute as isAbsolute$1, join as join$1, relative, resolve as resolve$1 } from "pathe";
|
|
12
|
+
import ignore from "ignore";
|
|
13
|
+
import pc from "picocolors";
|
|
14
|
+
import { defu } from "defu";
|
|
15
|
+
import { loadEnv, mergeConfig } from "vite";
|
|
16
|
+
import vue from "@vitejs/plugin-vue";
|
|
17
|
+
import { kimeshRouterGenerator } from "@kimesh/router-generator";
|
|
18
|
+
import { generateLayerAliases, generateLayerAliases as generateLayerAliases$1, mergeLayerConfigs, prepareLayers, prepareLayers as prepareLayers$1, resolveLayers } from "@kimesh/layers";
|
|
19
|
+
import { buildImportRegistry, generateDts, kimeshAutoImport, kimeshAutoImport as kimeshAutoImport$1, scanExports } from "@kimesh/auto-import";
|
|
20
|
+
import { loadConfig as loadConfig$1 } from "c12";
|
|
21
|
+
|
|
22
|
+
//#region src/runtime-config.ts
|
|
23
|
+
/**
|
|
24
|
+
* @kimesh/kit - Runtime Configuration Processing
|
|
25
|
+
*
|
|
26
|
+
* Utilities for processing environment variables and merging with runtime config.
|
|
27
|
+
* Phase 1: Build-time only, all config is public.
|
|
28
|
+
*/
|
|
29
|
+
/** Default environment variable prefix */
|
|
30
|
+
const ENV_PREFIX = "KIMESH_";
|
|
31
|
+
/**
|
|
32
|
+
* Convert a config key path to environment variable name.
|
|
33
|
+
*
|
|
34
|
+
* @param keys - Array of keys representing the path (e.g., ['features', 'darkMode'])
|
|
35
|
+
* @param prefix - Environment variable prefix (default: 'KIMESH_')
|
|
36
|
+
* @returns Environment variable name (e.g., 'KIMESH_FEATURES_DARK_MODE')
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* keyToEnv(['apiBase']) // => 'KIMESH_API_BASE'
|
|
41
|
+
* keyToEnv(['features', 'darkMode']) // => 'KIMESH_FEATURES_DARK_MODE'
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function keyToEnv(keys, prefix = ENV_PREFIX) {
|
|
45
|
+
return prefix + keys.map((key) => snakeCase(key).toUpperCase()).join("_");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Convert environment variable name to config key path.
|
|
49
|
+
*
|
|
50
|
+
* Note: This function uses a best-effort heuristic for reverse conversion.
|
|
51
|
+
* Since SNAKE_CASE to camelCase is ambiguous (e.g., API_BASE could be 'apiBase' or 'apibase'),
|
|
52
|
+
* this function assumes each underscore-separated segment is a word boundary.
|
|
53
|
+
*
|
|
54
|
+
* @param envKey - Environment variable name
|
|
55
|
+
* @param prefix - Environment variable prefix (default: 'KIMESH_')
|
|
56
|
+
* @returns Array of keys or null if the env var doesn't match the prefix
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* envToKey('KIMESH_API_BASE') // => ['apiBase']
|
|
61
|
+
* envToKey('KIMESH_FEATURES_DARK_MODE') // => ['features', 'darkMode']
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function envToKey(envKey, prefix = ENV_PREFIX) {
|
|
65
|
+
if (!envKey.startsWith(prefix)) return null;
|
|
66
|
+
const rest = envKey.slice(prefix.length);
|
|
67
|
+
if (!rest) return null;
|
|
68
|
+
return rest.toLowerCase().split("_").reduce((acc, part, i) => {
|
|
69
|
+
if (i === 0) acc.push(part);
|
|
70
|
+
else if (part) {
|
|
71
|
+
const last = acc[acc.length - 1];
|
|
72
|
+
acc[acc.length - 1] = last + part.charAt(0).toUpperCase() + part.slice(1);
|
|
73
|
+
}
|
|
74
|
+
return acc;
|
|
75
|
+
}, []);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if a value at the given path exists in the config object.
|
|
79
|
+
* This helps determine if an env var corresponds to an existing config key.
|
|
80
|
+
*/
|
|
81
|
+
function hasConfigPath(config, keys) {
|
|
82
|
+
let current = config;
|
|
83
|
+
for (const key of keys) {
|
|
84
|
+
if (current === null || typeof current !== "object") return false;
|
|
85
|
+
if (!(key in current)) return false;
|
|
86
|
+
current = current[key];
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all possible key paths from environment variable name.
|
|
92
|
+
* Since SNAKE_CASE conversion is ambiguous, this generates multiple candidates.
|
|
93
|
+
*
|
|
94
|
+
* @param envKey - Environment variable name (without prefix)
|
|
95
|
+
* @returns Array of possible key path interpretations
|
|
96
|
+
*/
|
|
97
|
+
function getPossiblePaths(envKey) {
|
|
98
|
+
const parts = envKey.toLowerCase().split("_");
|
|
99
|
+
const paths = [];
|
|
100
|
+
function generateGroupings(parts$1, current, start) {
|
|
101
|
+
if (start >= parts$1.length) {
|
|
102
|
+
paths.push([...current]);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
for (let end = start + 1; end <= parts$1.length; end++) {
|
|
106
|
+
const segment = parts$1.slice(start, end).reduce((acc, part, i) => {
|
|
107
|
+
if (i === 0) return part;
|
|
108
|
+
return acc + part.charAt(0).toUpperCase() + part.slice(1);
|
|
109
|
+
}, "");
|
|
110
|
+
current.push(segment);
|
|
111
|
+
generateGroupings(parts$1, current, end);
|
|
112
|
+
current.pop();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
generateGroupings(parts, [], 0);
|
|
116
|
+
return paths;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Find the best matching config path for an environment variable.
|
|
120
|
+
* Prioritizes existing paths in the config, falls back to single camelCase key.
|
|
121
|
+
*/
|
|
122
|
+
function findBestPath(envKey, config, prefix) {
|
|
123
|
+
if (!envKey.startsWith(prefix)) return null;
|
|
124
|
+
const rest = envKey.slice(prefix.length);
|
|
125
|
+
if (!rest) return null;
|
|
126
|
+
const possiblePaths = getPossiblePaths(rest);
|
|
127
|
+
for (const path of possiblePaths) if (hasConfigPath(config, path)) return path;
|
|
128
|
+
return [rest.toLowerCase().split("_").reduce((acc, part, i) => {
|
|
129
|
+
if (i === 0) return part;
|
|
130
|
+
return acc + part.charAt(0).toUpperCase() + part.slice(1);
|
|
131
|
+
}, "")];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Set a value at a nested path in an object, creating intermediate objects as needed.
|
|
135
|
+
*/
|
|
136
|
+
function setNestedValue(obj, keys, value) {
|
|
137
|
+
let current = obj;
|
|
138
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
139
|
+
const key = keys[i];
|
|
140
|
+
if (!(key in current) || typeof current[key] !== "object" || current[key] === null) current[key] = {};
|
|
141
|
+
current = current[key];
|
|
142
|
+
}
|
|
143
|
+
current[keys[keys.length - 1]] = value;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Apply environment variables to runtime config.
|
|
147
|
+
*
|
|
148
|
+
* Scans the environment for `KIMESH_*` variables and overlays them onto
|
|
149
|
+
* the config object. Called at build time to bake env values into the bundle.
|
|
150
|
+
*
|
|
151
|
+
* @param config - Base runtime config from kimesh.config.ts
|
|
152
|
+
* @param opts - Options for environment processing
|
|
153
|
+
* @returns Merged config with env overrides
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* // Given:
|
|
158
|
+
* // KIMESH_API_BASE=https://api.example.com
|
|
159
|
+
* // KIMESH_DEBUG=true
|
|
160
|
+
*
|
|
161
|
+
* const config = applyEnv({
|
|
162
|
+
* apiBase: '/api',
|
|
163
|
+
* debug: false,
|
|
164
|
+
* });
|
|
165
|
+
*
|
|
166
|
+
* // Result:
|
|
167
|
+
* // { apiBase: 'https://api.example.com', debug: true }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
function applyEnv(config, opts = {}) {
|
|
171
|
+
const { prefix = ENV_PREFIX, env = process.env } = opts;
|
|
172
|
+
const result = klona(config);
|
|
173
|
+
for (const [key, value] of Object.entries(env)) {
|
|
174
|
+
if (value === void 0) continue;
|
|
175
|
+
if (!key.startsWith(prefix)) continue;
|
|
176
|
+
const configPath = findBestPath(key, result, prefix);
|
|
177
|
+
if (!configPath || configPath.length === 0) continue;
|
|
178
|
+
setNestedValue(result, configPath, destr(value));
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Create an empty runtime config object.
|
|
184
|
+
* Used as a default when no config is provided.
|
|
185
|
+
*/
|
|
186
|
+
function createDefaultRuntimeConfig() {
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/core/kimesh.ts
|
|
192
|
+
/**
|
|
193
|
+
* @kimesh/kit - Core Kimesh Context Implementation
|
|
194
|
+
*
|
|
195
|
+
* The Kimesh class is the central context for all module operations.
|
|
196
|
+
*/
|
|
197
|
+
/**
|
|
198
|
+
* Create empty registries
|
|
199
|
+
*/
|
|
200
|
+
function createEmptyRegistries() {
|
|
201
|
+
return {
|
|
202
|
+
vitePlugins: [],
|
|
203
|
+
aliases: [],
|
|
204
|
+
templates: [],
|
|
205
|
+
typeTemplates: [],
|
|
206
|
+
imports: [],
|
|
207
|
+
importsDirs: [],
|
|
208
|
+
importsPresets: [],
|
|
209
|
+
components: [],
|
|
210
|
+
componentsDirs: [],
|
|
211
|
+
componentResolvers: [],
|
|
212
|
+
routes: [],
|
|
213
|
+
routeMiddleware: [],
|
|
214
|
+
runtimePlugins: []
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Create a new Kimesh context
|
|
219
|
+
*/
|
|
220
|
+
function createKimesh(options) {
|
|
221
|
+
const hooks = createHooks();
|
|
222
|
+
const registries = createEmptyRegistries();
|
|
223
|
+
const resolvedRuntimeConfig = applyEnv(options.config.runtimeConfig || createDefaultRuntimeConfig());
|
|
224
|
+
const kimeshOptions = {
|
|
225
|
+
dev: options.dev ?? process.env.NODE_ENV !== "production",
|
|
226
|
+
root: options.root,
|
|
227
|
+
buildDir: options.buildDir,
|
|
228
|
+
config: options.config,
|
|
229
|
+
layers: options.layers,
|
|
230
|
+
runtimeConfig: {
|
|
231
|
+
public: resolvedRuntimeConfig,
|
|
232
|
+
private: {}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return {
|
|
236
|
+
options: kimeshOptions,
|
|
237
|
+
hooks,
|
|
238
|
+
_registries: registries,
|
|
239
|
+
get layers() {
|
|
240
|
+
return kimeshOptions.layers;
|
|
241
|
+
},
|
|
242
|
+
get root() {
|
|
243
|
+
return kimeshOptions.root;
|
|
244
|
+
},
|
|
245
|
+
get buildDir() {
|
|
246
|
+
return kimeshOptions.buildDir;
|
|
247
|
+
},
|
|
248
|
+
hook(name, handler, opts) {
|
|
249
|
+
return hooks.hook(name, handler, opts);
|
|
250
|
+
},
|
|
251
|
+
callHook(name, ...args) {
|
|
252
|
+
return hooks.callHook(name, ...args);
|
|
253
|
+
},
|
|
254
|
+
hookOnce(name, handler) {
|
|
255
|
+
return hooks.hookOnce(name, handler);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
let currentKimesh;
|
|
260
|
+
/**
|
|
261
|
+
* Set the current Kimesh context (internal use)
|
|
262
|
+
*/
|
|
263
|
+
function _setKimeshContext(kimesh) {
|
|
264
|
+
currentKimesh = kimesh;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get the current Kimesh context
|
|
268
|
+
* @throws Error if not in a Kimesh context
|
|
269
|
+
*/
|
|
270
|
+
function useKimesh() {
|
|
271
|
+
if (!currentKimesh) throw new Error("[kimesh] useKimesh() called outside of Kimesh context. Make sure you're calling this within a module setup function.");
|
|
272
|
+
return currentKimesh;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Try to get the current Kimesh context
|
|
276
|
+
* @returns Kimesh instance or undefined if not in context
|
|
277
|
+
*/
|
|
278
|
+
function tryUseKimesh() {
|
|
279
|
+
return currentKimesh;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/core/module.ts
|
|
284
|
+
/**
|
|
285
|
+
* @kimesh/kit - Module Definition & Execution
|
|
286
|
+
*/
|
|
287
|
+
/**
|
|
288
|
+
* Define a Kimesh module with full type inference
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* export default defineKimeshModule<{ apiKey: string }>({
|
|
293
|
+
* meta: {
|
|
294
|
+
* name: "@kimesh/analytics",
|
|
295
|
+
* configKey: "analytics",
|
|
296
|
+
* },
|
|
297
|
+
* defaults: {
|
|
298
|
+
* apiKey: "",
|
|
299
|
+
* },
|
|
300
|
+
* hooks: {
|
|
301
|
+
* "ready": (kimesh) => {
|
|
302
|
+
* console.log("Kimesh is ready!");
|
|
303
|
+
* },
|
|
304
|
+
* },
|
|
305
|
+
* async setup(options, kimesh) {
|
|
306
|
+
* // Module setup logic
|
|
307
|
+
* },
|
|
308
|
+
* });
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
function defineKimeshModule(definition) {
|
|
312
|
+
return {
|
|
313
|
+
_def: definition,
|
|
314
|
+
meta: {
|
|
315
|
+
name: definition.meta?.name ?? "anonymous-module",
|
|
316
|
+
version: definition.meta?.version ?? "0.0.0",
|
|
317
|
+
configKey: definition.meta?.configKey ?? "",
|
|
318
|
+
compatibility: definition.meta?.compatibility ?? {}
|
|
319
|
+
},
|
|
320
|
+
async getDefaults(kimesh) {
|
|
321
|
+
if (!definition.defaults) return {};
|
|
322
|
+
if (typeof definition.defaults === "function") return await definition.defaults(kimesh);
|
|
323
|
+
return definition.defaults;
|
|
324
|
+
},
|
|
325
|
+
async setup(options, kimesh) {
|
|
326
|
+
if (definition.hooks) {
|
|
327
|
+
for (const [hookName, handler] of Object.entries(definition.hooks)) if (handler) kimesh.hook(hookName, handler);
|
|
328
|
+
}
|
|
329
|
+
if (definition.setup) await definition.setup(options, kimesh);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Resolve a string module name to an actual module
|
|
335
|
+
*/
|
|
336
|
+
async function resolveStringModule(moduleName, kimesh) {
|
|
337
|
+
try {
|
|
338
|
+
const requirePath = kimesh.root + "/package.json";
|
|
339
|
+
const require = createRequire(pathToFileURL(requirePath).href);
|
|
340
|
+
let modulePath;
|
|
341
|
+
try {
|
|
342
|
+
modulePath = require.resolve(moduleName);
|
|
343
|
+
consola.debug(`[Kimesh] Resolved module "${moduleName}" to ${modulePath}`);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
consola.debug(`[Kimesh] Failed to resolve "${moduleName}" from ${requirePath}:`, err);
|
|
346
|
+
throw new Error(`Module "${moduleName}" not found. Make sure it's installed in your project.`);
|
|
347
|
+
}
|
|
348
|
+
const imported = await import(pathToFileURL(modulePath).href);
|
|
349
|
+
const module = imported.default || imported;
|
|
350
|
+
if (module && typeof module === "object" && "_def" in module) return module;
|
|
351
|
+
if (module && typeof module === "object" && "setup" in module) return defineKimeshModule(module);
|
|
352
|
+
throw new Error(`Module "${moduleName}" does not export a valid Kimesh module. Make sure the module uses defineKimeshModule() or exports a valid module definition.`);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (error instanceof Error) throw error;
|
|
355
|
+
throw new Error(`Failed to load module "${moduleName}": ${error}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Normalize module input (string, module, or [module, options] tuple)
|
|
360
|
+
*/
|
|
361
|
+
async function normalizeModuleInput(input, kimesh) {
|
|
362
|
+
if (typeof input === "string") return {
|
|
363
|
+
module: await resolveStringModule(input, kimesh),
|
|
364
|
+
options: {}
|
|
365
|
+
};
|
|
366
|
+
if (Array.isArray(input)) {
|
|
367
|
+
const [moduleOrString, options] = input;
|
|
368
|
+
if (typeof moduleOrString === "string") return {
|
|
369
|
+
module: await resolveStringModule(moduleOrString, kimesh),
|
|
370
|
+
options: options ?? {}
|
|
371
|
+
};
|
|
372
|
+
return {
|
|
373
|
+
module: moduleOrString,
|
|
374
|
+
options: options ?? {}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
module: input,
|
|
379
|
+
options: {}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Execute a single module
|
|
384
|
+
*/
|
|
385
|
+
async function executeModule(moduleInput, kimesh) {
|
|
386
|
+
const { module, options: userOptions } = await normalizeModuleInput(moduleInput, kimesh);
|
|
387
|
+
const defaults = await module.getDefaults(kimesh);
|
|
388
|
+
const configKey = module.meta.configKey;
|
|
389
|
+
const configOptions = configKey ? kimesh.options.config[configKey] ?? {} : {};
|
|
390
|
+
const mergedOptions = {
|
|
391
|
+
...defaults,
|
|
392
|
+
...configOptions,
|
|
393
|
+
...userOptions
|
|
394
|
+
};
|
|
395
|
+
_setKimeshContext(kimesh);
|
|
396
|
+
try {
|
|
397
|
+
await module.setup(mergedOptions, kimesh);
|
|
398
|
+
} finally {
|
|
399
|
+
_setKimeshContext(void 0);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Extract module name from various input formats
|
|
404
|
+
*/
|
|
405
|
+
function getModuleName(input) {
|
|
406
|
+
if (typeof input === "string") return input;
|
|
407
|
+
if (Array.isArray(input)) {
|
|
408
|
+
const first = input[0];
|
|
409
|
+
if (typeof first === "string") return first;
|
|
410
|
+
return first.meta?.name ?? "unknown";
|
|
411
|
+
}
|
|
412
|
+
return input.meta?.name ?? "unknown";
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Execute all modules in order
|
|
416
|
+
*/
|
|
417
|
+
async function executeModules(modules, kimesh) {
|
|
418
|
+
await kimesh.callHook("modules:before", kimesh);
|
|
419
|
+
for (const moduleInput of modules) try {
|
|
420
|
+
await executeModule(moduleInput, kimesh);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
const moduleName = getModuleName(moduleInput);
|
|
423
|
+
consola.error(`[Kimesh] Failed to execute module "${moduleName}":`, error);
|
|
424
|
+
}
|
|
425
|
+
await kimesh.callHook("modules:done", kimesh);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/core/plugin.ts
|
|
430
|
+
/**
|
|
431
|
+
* Define a Kimesh plugin (lightweight Vite plugin wrapper)
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```ts
|
|
435
|
+
* export default defineKimeshPlugin({
|
|
436
|
+
* name: "@kimesh/icons",
|
|
437
|
+
* setup(kimesh) {
|
|
438
|
+
* return Icons({
|
|
439
|
+
* compiler: "vue3",
|
|
440
|
+
* autoInstall: true,
|
|
441
|
+
* });
|
|
442
|
+
* },
|
|
443
|
+
* });
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
function defineKimeshPlugin(definition) {
|
|
447
|
+
return {
|
|
448
|
+
_def: definition,
|
|
449
|
+
name: definition.name,
|
|
450
|
+
getPlugins(kimesh) {
|
|
451
|
+
const result = definition.setup(kimesh);
|
|
452
|
+
return Array.isArray(result) ? result : [result];
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/kit/vite.ts
|
|
459
|
+
/**
|
|
460
|
+
* Add a Vite plugin to the build
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```ts
|
|
464
|
+
* addVitePlugin(myPlugin());
|
|
465
|
+
* addVitePlugin(myPlugin(), { enforce: "pre" });
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
function addVitePlugin(plugin, options) {
|
|
469
|
+
_addVitePlugin(useKimesh(), plugin, options);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Internal: Add Vite plugin with explicit Kimesh context
|
|
473
|
+
*/
|
|
474
|
+
function _addVitePlugin(kimesh, plugin, options) {
|
|
475
|
+
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
|
476
|
+
const registry = kimesh._registries.vitePlugins;
|
|
477
|
+
for (const p of plugins) {
|
|
478
|
+
if (!p) continue;
|
|
479
|
+
const entry = {
|
|
480
|
+
plugin: p,
|
|
481
|
+
enforce: options?.enforce,
|
|
482
|
+
order: options?.order,
|
|
483
|
+
meta: { name: p.name }
|
|
484
|
+
};
|
|
485
|
+
if (options?.prepend) registry.unshift(entry);
|
|
486
|
+
else registry.push(entry);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Add a build plugin (alias for addVitePlugin with default order)
|
|
491
|
+
*/
|
|
492
|
+
function addBuildPlugin(plugin, options) {
|
|
493
|
+
addVitePlugin(plugin, options);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/kit/alias.ts
|
|
498
|
+
/**
|
|
499
|
+
* Add an alias
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```ts
|
|
503
|
+
* addAlias("#my-module", "/path/to/module");
|
|
504
|
+
* addAlias("@/components", "./src/components");
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
function addAlias(find, replacement) {
|
|
508
|
+
_addAlias(useKimesh(), find, replacement);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Internal: Add alias with explicit Kimesh context
|
|
512
|
+
*/
|
|
513
|
+
function _addAlias(kimesh, find, replacement) {
|
|
514
|
+
const alias = {
|
|
515
|
+
find,
|
|
516
|
+
replacement
|
|
517
|
+
};
|
|
518
|
+
kimesh._registries.aliases.push(alias);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Resolve an alias to its actual path
|
|
522
|
+
*/
|
|
523
|
+
function resolveAlias(alias) {
|
|
524
|
+
return _resolveAlias(useKimesh(), alias);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Check if an alias matches a given find pattern
|
|
528
|
+
*/
|
|
529
|
+
function matchesAlias(alias, find) {
|
|
530
|
+
if (typeof find === "string") return alias === find || alias.startsWith(find + "/");
|
|
531
|
+
return find.test(alias);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Internal: Resolve alias with explicit Kimesh context
|
|
535
|
+
*/
|
|
536
|
+
function _resolveAlias(kimesh, alias) {
|
|
537
|
+
for (const entry of kimesh._registries.aliases) if (matchesAlias(alias, entry.find)) return alias.replace(entry.find, entry.replacement);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/kit/templates.ts
|
|
542
|
+
/**
|
|
543
|
+
* @kimesh/kit - Template Utilities
|
|
544
|
+
*/
|
|
545
|
+
/**
|
|
546
|
+
* Add a template to be generated
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```ts
|
|
550
|
+
* addTemplate({
|
|
551
|
+
* filename: "my-config.ts",
|
|
552
|
+
* getContents: ({ kimesh }) => `export default ${JSON.stringify(config)}`,
|
|
553
|
+
* });
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
function addTemplate(template) {
|
|
557
|
+
_addTemplate(useKimesh(), template);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Internal: Add template with explicit Kimesh context
|
|
561
|
+
*/
|
|
562
|
+
function _addTemplate(kimesh, template) {
|
|
563
|
+
kimesh._registries.templates.push(template);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Add a type template (.d.ts) to be generated
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```ts
|
|
570
|
+
* addTypeTemplate({
|
|
571
|
+
* filename: "components.d.ts",
|
|
572
|
+
* getContents: ({ kimesh }) => generateComponentTypes(kimesh),
|
|
573
|
+
* });
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
function addTypeTemplate(template) {
|
|
577
|
+
_addTypeTemplate(useKimesh(), template);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Internal: Add type template with explicit Kimesh context
|
|
581
|
+
*/
|
|
582
|
+
function _addTypeTemplate(kimesh, template) {
|
|
583
|
+
kimesh._registries.typeTemplates.push(template);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Ensure build directory exists
|
|
587
|
+
*/
|
|
588
|
+
function ensureBuildDir(kimesh) {
|
|
589
|
+
if (!existsSync(kimesh.buildDir)) mkdirSync(kimesh.buildDir, { recursive: true });
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Generate and write all templates
|
|
593
|
+
*/
|
|
594
|
+
async function writeTemplates(kimesh) {
|
|
595
|
+
ensureBuildDir(kimesh);
|
|
596
|
+
const results = [];
|
|
597
|
+
for (const template of kimesh._registries.templates) {
|
|
598
|
+
const resolved = await resolveTemplate(kimesh, template);
|
|
599
|
+
if (resolved) {
|
|
600
|
+
if (template.write !== false) writeTemplate(resolved);
|
|
601
|
+
results.push(resolved);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
for (const template of kimesh._registries.typeTemplates) {
|
|
605
|
+
const resolved = await resolveTemplate(kimesh, template);
|
|
606
|
+
if (resolved) {
|
|
607
|
+
writeTemplate(resolved);
|
|
608
|
+
results.push(resolved);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return results;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Resolve a single template
|
|
615
|
+
*/
|
|
616
|
+
async function resolveTemplate(kimesh, template) {
|
|
617
|
+
let contents;
|
|
618
|
+
if (template.getContents) contents = await template.getContents({
|
|
619
|
+
...template.data,
|
|
620
|
+
kimesh
|
|
621
|
+
});
|
|
622
|
+
else if (template.src) contents = readFileSync(template.src, "utf-8");
|
|
623
|
+
else return null;
|
|
624
|
+
const dst = join(kimesh.buildDir, template.filename);
|
|
625
|
+
return {
|
|
626
|
+
filename: template.filename,
|
|
627
|
+
dst,
|
|
628
|
+
contents
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Write a resolved template to disk
|
|
633
|
+
*/
|
|
634
|
+
function writeTemplate(template) {
|
|
635
|
+
const dir = dirname(template.dst);
|
|
636
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
637
|
+
writeFileSync(template.dst, template.contents, "utf-8");
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Update specific templates (for HMR)
|
|
641
|
+
*/
|
|
642
|
+
async function updateTemplates(kimesh, options) {
|
|
643
|
+
const templates = options?.filter ? kimesh._registries.templates.filter(options.filter) : kimesh._registries.templates;
|
|
644
|
+
const results = [];
|
|
645
|
+
for (const template of templates) {
|
|
646
|
+
const resolved = await resolveTemplate(kimesh, template);
|
|
647
|
+
if (resolved && template.write !== false) {
|
|
648
|
+
writeTemplate(resolved);
|
|
649
|
+
results.push(resolved);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return results;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/kit/components.ts
|
|
657
|
+
/**
|
|
658
|
+
* Add a single component
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```ts
|
|
662
|
+
* addComponent({
|
|
663
|
+
* name: "MyButton",
|
|
664
|
+
* filePath: "./components/MyButton.vue",
|
|
665
|
+
* });
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
function addComponent(component) {
|
|
669
|
+
_addComponent(useKimesh(), component);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Internal: Add component with explicit Kimesh context
|
|
673
|
+
*/
|
|
674
|
+
function _addComponent(kimesh, component) {
|
|
675
|
+
kimesh._registries.components.push(component);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Add a components directory
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```ts
|
|
682
|
+
* addComponentsDir({
|
|
683
|
+
* path: "./components/ui",
|
|
684
|
+
* prefix: "Ui",
|
|
685
|
+
* });
|
|
686
|
+
* ```
|
|
687
|
+
*/
|
|
688
|
+
function addComponentsDir(dir, options) {
|
|
689
|
+
_addComponentsDir(useKimesh(), dir, options);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Internal: Add components directory with explicit Kimesh context
|
|
693
|
+
*/
|
|
694
|
+
function _addComponentsDir(kimesh, dir, options) {
|
|
695
|
+
if (options?.prepend) kimesh._registries.componentsDirs.unshift(dir);
|
|
696
|
+
else kimesh._registries.componentsDirs.push(dir);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Add a component resolver
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```ts
|
|
703
|
+
* addComponentResolver({
|
|
704
|
+
* type: "component",
|
|
705
|
+
* resolve: (name) => {
|
|
706
|
+
* if (name.startsWith("Ui")) {
|
|
707
|
+
* return { name, from: `./components/ui/${name}.vue` };
|
|
708
|
+
* }
|
|
709
|
+
* },
|
|
710
|
+
* });
|
|
711
|
+
* ```
|
|
712
|
+
*/
|
|
713
|
+
function addComponentResolver(resolver) {
|
|
714
|
+
_addComponentResolver(useKimesh(), resolver);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Internal: Add component resolver with explicit Kimesh context
|
|
718
|
+
*/
|
|
719
|
+
function _addComponentResolver(kimesh, resolver) {
|
|
720
|
+
kimesh._registries.componentResolvers.push(resolver);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/kit/imports.ts
|
|
725
|
+
/**
|
|
726
|
+
* Add imports (auto-imports)
|
|
727
|
+
*
|
|
728
|
+
* @example
|
|
729
|
+
* ```ts
|
|
730
|
+
* addImports({ name: "useAuth", from: "./composables/auth" });
|
|
731
|
+
* addImports([
|
|
732
|
+
* { name: "ref", from: "vue" },
|
|
733
|
+
* { name: "computed", from: "vue" },
|
|
734
|
+
* ]);
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
function addImports(imports) {
|
|
738
|
+
_addImports(useKimesh(), imports);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Internal: Add imports with explicit Kimesh context
|
|
742
|
+
*/
|
|
743
|
+
function _addImports(kimesh, imports) {
|
|
744
|
+
const importsList = Array.isArray(imports) ? imports : [imports];
|
|
745
|
+
kimesh._registries.imports.push(...importsList);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Add an imports directory
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* addImportsDir("./composables");
|
|
753
|
+
* addImportsDir({ path: "./utils", pattern: "use*.ts" });
|
|
754
|
+
* ```
|
|
755
|
+
*/
|
|
756
|
+
function addImportsDir(dir, options) {
|
|
757
|
+
_addImportsDir(useKimesh(), dir, options);
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Internal: Add imports directory with explicit Kimesh context
|
|
761
|
+
*/
|
|
762
|
+
function _addImportsDir(kimesh, dir, options) {
|
|
763
|
+
const dirConfig = typeof dir === "string" ? { path: dir } : dir;
|
|
764
|
+
if (options?.prepend) kimesh._registries.importsDirs.unshift(dirConfig);
|
|
765
|
+
else kimesh._registries.importsDirs.push(dirConfig);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Add import presets
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* ```ts
|
|
772
|
+
* addImportsPreset({ from: "vue", imports: ["ref", "computed", "watch"] });
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
function addImportsPreset(preset) {
|
|
776
|
+
_addImportsPreset(useKimesh(), preset);
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Internal: Add import preset with explicit Kimesh context
|
|
780
|
+
*/
|
|
781
|
+
function _addImportsPreset(kimesh, preset) {
|
|
782
|
+
kimesh._registries.importsPresets.push(preset);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region src/kit/resolver.ts
|
|
787
|
+
/**
|
|
788
|
+
* @kimesh/kit - Resolver Utilities
|
|
789
|
+
*/
|
|
790
|
+
/**
|
|
791
|
+
* Create a resolver for the current module
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```ts
|
|
795
|
+
* const resolver = createResolver(import.meta.url);
|
|
796
|
+
* const runtimePath = resolver.resolve("./runtime");
|
|
797
|
+
* ```
|
|
798
|
+
*/
|
|
799
|
+
function createResolver(base) {
|
|
800
|
+
const baseDir = base.startsWith("file://") ? dirname(new URL(base).pathname) : dirname(base);
|
|
801
|
+
return {
|
|
802
|
+
resolve(...paths) {
|
|
803
|
+
return resolve(baseDir, ...paths);
|
|
804
|
+
},
|
|
805
|
+
async resolvePath(path) {
|
|
806
|
+
const resolved = resolve(baseDir, path);
|
|
807
|
+
if (!existsSync(resolved)) throw new Error(`Path not found: ${resolved}`);
|
|
808
|
+
return resolved;
|
|
809
|
+
},
|
|
810
|
+
resolveAlias(alias) {
|
|
811
|
+
const kimesh = tryUseKimesh();
|
|
812
|
+
if (!kimesh) return;
|
|
813
|
+
return _resolveAlias(kimesh, alias);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Resolve a path relative to project root
|
|
819
|
+
*/
|
|
820
|
+
function resolvePathFromRoot(...paths) {
|
|
821
|
+
return resolve(useKimesh().root, ...paths);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Resolve a path relative to build directory
|
|
825
|
+
*/
|
|
826
|
+
function resolvePathFromBuild(...paths) {
|
|
827
|
+
return resolve(useKimesh().buildDir, ...paths);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/kit/runtime-plugin.ts
|
|
832
|
+
/**
|
|
833
|
+
* @kimesh/kit - Runtime Plugin Kit Utilities
|
|
834
|
+
*
|
|
835
|
+
* Functions for registering runtime plugins during module setup.
|
|
836
|
+
*/
|
|
837
|
+
/**
|
|
838
|
+
* Extract plugin name from a file path
|
|
839
|
+
*/
|
|
840
|
+
function extractPluginName(path) {
|
|
841
|
+
const match = path.match(/([^/\\]+?)(?:\.[^.]+)?$/);
|
|
842
|
+
if (!match) return "anonymous";
|
|
843
|
+
const filename = match[1];
|
|
844
|
+
const orderMatch = filename.match(/^\d+\.(.+)$/);
|
|
845
|
+
return orderMatch ? orderMatch[1] : filename;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Normalize a plugin path or entry to a standard entry format
|
|
849
|
+
*/
|
|
850
|
+
function normalizeRuntimePlugin(plugin) {
|
|
851
|
+
if (typeof plugin === "string") return {
|
|
852
|
+
src: plugin,
|
|
853
|
+
name: extractPluginName(plugin)
|
|
854
|
+
};
|
|
855
|
+
return plugin;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Add a runtime plugin to the Kimesh app
|
|
859
|
+
*
|
|
860
|
+
* @example
|
|
861
|
+
* ```ts
|
|
862
|
+
* addRuntimePlugin('~/plugins/analytics.ts')
|
|
863
|
+
* addRuntimePlugin({ src: '~/plugins/analytics.ts', meta: { enforce: 'post' } })
|
|
864
|
+
* ```
|
|
865
|
+
*/
|
|
866
|
+
function addRuntimePlugin(plugin, options = {}) {
|
|
867
|
+
const kimesh = useKimesh();
|
|
868
|
+
const normalized = normalizeRuntimePlugin(plugin);
|
|
869
|
+
if (normalized.src) kimesh._registries.runtimePlugins = kimesh._registries.runtimePlugins.filter((p) => p.src !== normalized.src);
|
|
870
|
+
if (options.append) kimesh._registries.runtimePlugins.push(normalized);
|
|
871
|
+
else kimesh._registries.runtimePlugins.unshift(normalized);
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Remove a runtime plugin by source path
|
|
875
|
+
*/
|
|
876
|
+
function removeRuntimePlugin(src) {
|
|
877
|
+
const kimesh = useKimesh();
|
|
878
|
+
kimesh._registries.runtimePlugins = kimesh._registries.runtimePlugins.filter((p) => p.src !== src);
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Check if a runtime plugin is registered
|
|
882
|
+
*/
|
|
883
|
+
function hasRuntimePlugin(src) {
|
|
884
|
+
return useKimesh()._registries.runtimePlugins.some((p) => p.src === src);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Get all registered runtime plugins
|
|
888
|
+
*/
|
|
889
|
+
function getRuntimePlugins() {
|
|
890
|
+
return [...useKimesh()._registries.runtimePlugins];
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
//#endregion
|
|
894
|
+
//#region src/kit/plugin-scanner.ts
|
|
895
|
+
/**
|
|
896
|
+
* @kimesh/kit - Plugin Scanner
|
|
897
|
+
*
|
|
898
|
+
* Scans the plugins directory for auto-discovered runtime plugins.
|
|
899
|
+
*/
|
|
900
|
+
const DEFAULT_EXTENSIONS = [
|
|
901
|
+
".ts",
|
|
902
|
+
".js",
|
|
903
|
+
".mjs"
|
|
904
|
+
];
|
|
905
|
+
const DEFAULT_IGNORE = [
|
|
906
|
+
"**/node_modules/**",
|
|
907
|
+
"**/*.d.ts",
|
|
908
|
+
"**/*.test.ts",
|
|
909
|
+
"**/*.spec.ts"
|
|
910
|
+
];
|
|
911
|
+
/**
|
|
912
|
+
* Parse filename to extract order prefix and plugin name
|
|
913
|
+
*
|
|
914
|
+
* Filename conventions:
|
|
915
|
+
* - `auth.ts` -> name: 'auth', order: undefined
|
|
916
|
+
* - `01.auth.ts` -> name: 'auth', order: 1
|
|
917
|
+
* - `10.analytics.ts` -> name: 'analytics', order: 10
|
|
918
|
+
*/
|
|
919
|
+
function parsePluginFilename(filename) {
|
|
920
|
+
const withoutExt = basename(filename, extname(filename));
|
|
921
|
+
const orderMatch = withoutExt.match(/^(\d+)\.(.+)$/);
|
|
922
|
+
if (orderMatch) return {
|
|
923
|
+
name: orderMatch[2],
|
|
924
|
+
order: parseInt(orderMatch[1], 10)
|
|
925
|
+
};
|
|
926
|
+
return { name: withoutExt };
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Build glob pattern for plugin file scanning
|
|
930
|
+
*/
|
|
931
|
+
function buildPluginGlobPattern(extensions) {
|
|
932
|
+
const normalizedExts = extensions.map((e) => e.startsWith(".") ? e.slice(1) : e);
|
|
933
|
+
if (normalizedExts.length === 1) return `*.${normalizedExts[0]}`;
|
|
934
|
+
return `*.{${normalizedExts.join(",")}}`;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Scan plugins directory for auto-discovered plugins
|
|
938
|
+
*
|
|
939
|
+
* @example
|
|
940
|
+
* ```ts
|
|
941
|
+
* const plugins = await scanPluginsDir('/app/src/plugins')
|
|
942
|
+
* // Returns: [
|
|
943
|
+
* // { src: '/app/src/plugins/01.auth.ts', name: 'auth', order: 1 },
|
|
944
|
+
* // { src: '/app/src/plugins/02.analytics.ts', name: 'analytics', order: 2 },
|
|
945
|
+
* // { src: '/app/src/plugins/utils.ts', name: 'utils', order: undefined },
|
|
946
|
+
* // ]
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
async function scanPluginsDir(pluginsDir, options = {}) {
|
|
950
|
+
if (!existsSync(pluginsDir)) return [];
|
|
951
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
952
|
+
const ignore$1 = options.ignore ?? DEFAULT_IGNORE;
|
|
953
|
+
return (await fg(buildPluginGlobPattern(extensions), {
|
|
954
|
+
cwd: pluginsDir,
|
|
955
|
+
onlyFiles: true,
|
|
956
|
+
ignore: ignore$1,
|
|
957
|
+
absolute: false
|
|
958
|
+
})).map((file) => {
|
|
959
|
+
const { name, order } = parsePluginFilename(file);
|
|
960
|
+
return {
|
|
961
|
+
src: join$1(pluginsDir, file),
|
|
962
|
+
name,
|
|
963
|
+
order
|
|
964
|
+
};
|
|
965
|
+
}).sort((a, b) => {
|
|
966
|
+
if (a.order === void 0 && b.order === void 0) return a.name.localeCompare(b.name);
|
|
967
|
+
if (a.order === void 0) return 1;
|
|
968
|
+
if (b.order === void 0) return -1;
|
|
969
|
+
return a.order - b.order;
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Check if a directory has any plugin files
|
|
974
|
+
*/
|
|
975
|
+
async function hasPlugins(pluginsDir, options = {}) {
|
|
976
|
+
return (await scanPluginsDir(pluginsDir, options)).length > 0;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
//#endregion
|
|
980
|
+
//#region src/kit/plugin-template.ts
|
|
981
|
+
/**
|
|
982
|
+
* @kimesh/kit - Plugin Template Generator
|
|
983
|
+
*
|
|
984
|
+
* Generates the #build/plugins.mjs file that exports all runtime plugins.
|
|
985
|
+
*/
|
|
986
|
+
/**
|
|
987
|
+
* Create a valid JavaScript variable name from plugin name
|
|
988
|
+
*/
|
|
989
|
+
function toVariableName(name, index) {
|
|
990
|
+
return `plugin_${name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1").replace(/_+/g, "_")}_${index}`;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Generate the plugins.mjs template content
|
|
994
|
+
*
|
|
995
|
+
* @example Output:
|
|
996
|
+
* ```js
|
|
997
|
+
* // Auto-discovered plugins
|
|
998
|
+
* import plugin_auth_0 from '../src/plugins/01.auth.ts'
|
|
999
|
+
* import plugin_analytics_1 from '../src/plugins/02.analytics.ts'
|
|
1000
|
+
*
|
|
1001
|
+
* // Module-registered plugins
|
|
1002
|
+
* import plugin_icons_2 from '@kimesh/icons/runtime/plugin'
|
|
1003
|
+
*
|
|
1004
|
+
* export const plugins = [
|
|
1005
|
+
* plugin_auth_0,
|
|
1006
|
+
* plugin_analytics_1,
|
|
1007
|
+
* plugin_icons_2,
|
|
1008
|
+
* ]
|
|
1009
|
+
*
|
|
1010
|
+
* export default plugins
|
|
1011
|
+
* ```
|
|
1012
|
+
*/
|
|
1013
|
+
function generatePluginsTemplate(options) {
|
|
1014
|
+
const { discoveredPlugins, registeredPlugins, buildDir } = options;
|
|
1015
|
+
const imports = [];
|
|
1016
|
+
const pluginVars = [];
|
|
1017
|
+
let index = 0;
|
|
1018
|
+
if (discoveredPlugins.length > 0) {
|
|
1019
|
+
imports.push("// Auto-discovered plugins");
|
|
1020
|
+
for (const plugin of discoveredPlugins) {
|
|
1021
|
+
const varName = toVariableName(plugin.name, index);
|
|
1022
|
+
const importPath = relative(buildDir, plugin.src).replace(/\.ts$/, "");
|
|
1023
|
+
imports.push(`import ${varName} from '${importPath}'`);
|
|
1024
|
+
pluginVars.push(varName);
|
|
1025
|
+
index++;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
const pluginsWithSrc = registeredPlugins.filter((p) => p.src);
|
|
1029
|
+
if (pluginsWithSrc.length > 0) {
|
|
1030
|
+
if (imports.length > 0) imports.push("");
|
|
1031
|
+
imports.push("// Module-registered plugins");
|
|
1032
|
+
for (const plugin of pluginsWithSrc) {
|
|
1033
|
+
const varName = toVariableName(plugin.name || "module", index);
|
|
1034
|
+
const importPath = plugin.src.startsWith("@") || plugin.src.startsWith(".") ? plugin.src : relative(buildDir, plugin.src).replace(/\.ts$/, "");
|
|
1035
|
+
imports.push(`import ${varName} from '${importPath}'`);
|
|
1036
|
+
pluginVars.push(varName);
|
|
1037
|
+
index++;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (pluginVars.length === 0) return `/**
|
|
1041
|
+
* Kimesh Runtime Plugins
|
|
1042
|
+
* Auto-generated by @kimesh/kit - Do not edit manually
|
|
1043
|
+
*/
|
|
1044
|
+
|
|
1045
|
+
export const plugins = []
|
|
1046
|
+
|
|
1047
|
+
export default plugins
|
|
1048
|
+
`;
|
|
1049
|
+
const pluginsList = pluginVars.map((v) => ` ${v},`).join("\n");
|
|
1050
|
+
return `/**
|
|
1051
|
+
* Kimesh Runtime Plugins
|
|
1052
|
+
* Auto-generated by @kimesh/kit - Do not edit manually
|
|
1053
|
+
*/
|
|
1054
|
+
|
|
1055
|
+
${imports.join("\n")}
|
|
1056
|
+
|
|
1057
|
+
export const plugins = [
|
|
1058
|
+
${pluginsList}
|
|
1059
|
+
]
|
|
1060
|
+
|
|
1061
|
+
export default plugins
|
|
1062
|
+
`;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Add plugins template to Kimesh registries
|
|
1066
|
+
*/
|
|
1067
|
+
function addPluginsTemplate(kimesh, discoveredPlugins) {
|
|
1068
|
+
kimesh._registries.templates.push({
|
|
1069
|
+
filename: "plugins.mjs",
|
|
1070
|
+
getContents: ({ kimesh: km }) => {
|
|
1071
|
+
return generatePluginsTemplate({
|
|
1072
|
+
discoveredPlugins,
|
|
1073
|
+
registeredPlugins: km._registries.runtimePlugins,
|
|
1074
|
+
buildDir: km.buildDir
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
//#endregion
|
|
1081
|
+
//#region src/types/config.ts
|
|
1082
|
+
/**
|
|
1083
|
+
* Default aliases provided by Kimesh.
|
|
1084
|
+
* Templates use placeholders: <srcDir>, <rootDir>
|
|
1085
|
+
* These are replaced at build time with actual paths.
|
|
1086
|
+
*/
|
|
1087
|
+
const DEFAULT_ALIASES = {
|
|
1088
|
+
"~": "<srcDir>",
|
|
1089
|
+
"@": "<srcDir>",
|
|
1090
|
+
"~~": "<rootDir>",
|
|
1091
|
+
"@@": "<rootDir>",
|
|
1092
|
+
"#build": "<rootDir>/.kimesh",
|
|
1093
|
+
"#app": "<rootDir>/.kimesh/app"
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* Default file/folder ignore patterns
|
|
1097
|
+
*/
|
|
1098
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
1099
|
+
"**/*.stories.{js,cts,mts,ts,jsx,tsx}",
|
|
1100
|
+
"**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}",
|
|
1101
|
+
"**/*.d.{cts,mts,ts}",
|
|
1102
|
+
"**/.{git,cache,data,output}",
|
|
1103
|
+
"**/*.sock",
|
|
1104
|
+
".kimesh",
|
|
1105
|
+
"**/node_modules",
|
|
1106
|
+
"**/-*.*"
|
|
1107
|
+
];
|
|
1108
|
+
/**
|
|
1109
|
+
* Define Kimesh configuration with type inference.
|
|
1110
|
+
*/
|
|
1111
|
+
function defineKmConfig(config) {
|
|
1112
|
+
return config;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
//#endregion
|
|
1116
|
+
//#region src/kit/alias-utils.ts
|
|
1117
|
+
/**
|
|
1118
|
+
* @kimesh/kit - Alias Resolution Utilities
|
|
1119
|
+
*
|
|
1120
|
+
* Utilities for resolving and processing path aliases.
|
|
1121
|
+
*/
|
|
1122
|
+
/**
|
|
1123
|
+
* Resolve an alias path template to an actual path
|
|
1124
|
+
*
|
|
1125
|
+
* @param template - Alias path template (e.g., "/<srcDir>/components")
|
|
1126
|
+
* @param srcDir - Source directory path
|
|
1127
|
+
* @param rootDir - Root directory path
|
|
1128
|
+
* @returns Resolved absolute path
|
|
1129
|
+
*/
|
|
1130
|
+
function resolveAliasPath(template, srcDir, rootDir) {
|
|
1131
|
+
return template.replace(/<srcDir>/g, srcDir).replace(/<rootDir>/g, rootDir);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Build resolved aliases from config
|
|
1135
|
+
*
|
|
1136
|
+
* @param config - Kimesh configuration
|
|
1137
|
+
* @param srcDir - Source directory (default: rootDir/src)
|
|
1138
|
+
* @param rootDir - Root directory
|
|
1139
|
+
* @returns Resolved alias map
|
|
1140
|
+
*/
|
|
1141
|
+
function buildAliases(config, srcDir, rootDir) {
|
|
1142
|
+
const aliases = {};
|
|
1143
|
+
for (const [alias, template] of Object.entries(DEFAULT_ALIASES)) aliases[alias] = resolveAliasPath(template, srcDir, rootDir);
|
|
1144
|
+
if (config.alias) for (const [alias, path] of Object.entries(config.alias)) aliases[alias] = isAbsolute(path) ? path : resolve(rootDir, path);
|
|
1145
|
+
return aliases;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Convert aliases to Vite's resolve.alias format
|
|
1149
|
+
*
|
|
1150
|
+
* @param aliases - Alias map
|
|
1151
|
+
* @returns Array of Vite alias entries
|
|
1152
|
+
*/
|
|
1153
|
+
function toViteAliases(aliases) {
|
|
1154
|
+
return Object.entries(aliases).map(([find, replacement]) => ({
|
|
1155
|
+
find,
|
|
1156
|
+
replacement
|
|
1157
|
+
}));
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Generate TypeScript path mappings from aliases
|
|
1161
|
+
*
|
|
1162
|
+
* @param aliases - Alias map
|
|
1163
|
+
* @param rootDir - Root directory for relative paths
|
|
1164
|
+
* @returns TypeScript compilerOptions.paths object
|
|
1165
|
+
*/
|
|
1166
|
+
function toTsConfigPaths(aliases, rootDir) {
|
|
1167
|
+
const paths = {};
|
|
1168
|
+
for (const [alias, absolutePath] of Object.entries(aliases)) {
|
|
1169
|
+
const relativePath = absolutePath.startsWith(rootDir) ? "./" + absolutePath.slice(rootDir.length + 1) : absolutePath;
|
|
1170
|
+
paths[alias] = [relativePath];
|
|
1171
|
+
paths[`${alias}/*`] = [`${relativePath}/*`];
|
|
1172
|
+
}
|
|
1173
|
+
return paths;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Normalize debug configuration to DebugConfig object
|
|
1177
|
+
*
|
|
1178
|
+
* @param debug - Debug config from KimeshConfig (boolean or DebugConfig)
|
|
1179
|
+
* @returns Normalized DebugConfig
|
|
1180
|
+
*/
|
|
1181
|
+
function normalizeDebugConfig(debug$1) {
|
|
1182
|
+
if (!debug$1) return {};
|
|
1183
|
+
if (debug$1 === true) return {
|
|
1184
|
+
hooks: true,
|
|
1185
|
+
modules: true,
|
|
1186
|
+
layers: true,
|
|
1187
|
+
config: true,
|
|
1188
|
+
vite: true,
|
|
1189
|
+
routes: true,
|
|
1190
|
+
imports: true
|
|
1191
|
+
};
|
|
1192
|
+
return debug$1;
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Check if a specific debug option is enabled
|
|
1196
|
+
*
|
|
1197
|
+
* @param debug - Debug config
|
|
1198
|
+
* @param option - Debug option to check
|
|
1199
|
+
* @returns Whether the option is enabled
|
|
1200
|
+
*/
|
|
1201
|
+
function isDebugEnabled(debug$1, option) {
|
|
1202
|
+
if (!debug$1) return false;
|
|
1203
|
+
if (debug$1 === true) return true;
|
|
1204
|
+
return !!debug$1[option];
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
//#endregion
|
|
1208
|
+
//#region src/kit/ignore-utils.ts
|
|
1209
|
+
/**
|
|
1210
|
+
* @kimesh/kit - Ignore Patterns Utilities
|
|
1211
|
+
*
|
|
1212
|
+
* Utilities for handling file ignore patterns using node-ignore.
|
|
1213
|
+
*/
|
|
1214
|
+
/**
|
|
1215
|
+
* Create an ignore instance with default and custom patterns
|
|
1216
|
+
*
|
|
1217
|
+
* @param config - Kimesh configuration
|
|
1218
|
+
* @returns Configured ignore instance
|
|
1219
|
+
*/
|
|
1220
|
+
function createIgnoreFilter(config) {
|
|
1221
|
+
const ig = ignore(config.ignoreOptions);
|
|
1222
|
+
ig.add(DEFAULT_IGNORE_PATTERNS);
|
|
1223
|
+
if (config.ignore) ig.add(config.ignore);
|
|
1224
|
+
const prefix = config.ignorePrefix ?? "-";
|
|
1225
|
+
if (prefix) ig.add(`**/${prefix}*`);
|
|
1226
|
+
return ig;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Check if a file should be ignored
|
|
1230
|
+
*
|
|
1231
|
+
* @param ig - Ignore instance
|
|
1232
|
+
* @param filePath - Relative file path to check
|
|
1233
|
+
* @returns Whether the file should be ignored
|
|
1234
|
+
*/
|
|
1235
|
+
function shouldIgnore(ig, filePath) {
|
|
1236
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
1237
|
+
return ig.ignores(normalizedPath);
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Filter an array of paths, removing ignored ones
|
|
1241
|
+
*
|
|
1242
|
+
* @param ig - Ignore instance
|
|
1243
|
+
* @param paths - Array of relative paths
|
|
1244
|
+
* @returns Array of non-ignored paths
|
|
1245
|
+
*/
|
|
1246
|
+
function filterIgnored(ig, paths) {
|
|
1247
|
+
return paths.filter((p) => !shouldIgnore(ig, p));
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Create a simple matcher function for ignore patterns
|
|
1251
|
+
*
|
|
1252
|
+
* @param config - Kimesh configuration
|
|
1253
|
+
* @returns A function that returns true if a path should be ignored
|
|
1254
|
+
*/
|
|
1255
|
+
function createIgnoreMatcher(config) {
|
|
1256
|
+
const ig = createIgnoreFilter(config);
|
|
1257
|
+
return (filePath) => shouldIgnore(ig, filePath);
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Get all active ignore patterns from config
|
|
1261
|
+
*
|
|
1262
|
+
* @param config - Kimesh configuration
|
|
1263
|
+
* @returns Array of all ignore patterns
|
|
1264
|
+
*/
|
|
1265
|
+
function getIgnorePatterns(config) {
|
|
1266
|
+
const patterns = [...DEFAULT_IGNORE_PATTERNS];
|
|
1267
|
+
if (config.ignore) patterns.push(...config.ignore);
|
|
1268
|
+
const prefix = config.ignorePrefix ?? "-";
|
|
1269
|
+
if (prefix) patterns.push(`**/${prefix}*`);
|
|
1270
|
+
return patterns;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
//#endregion
|
|
1274
|
+
//#region src/kit/route-rules.ts
|
|
1275
|
+
/**
|
|
1276
|
+
* Match a route path against a pattern
|
|
1277
|
+
*
|
|
1278
|
+
* Supports:
|
|
1279
|
+
* - Exact matches: `/about`
|
|
1280
|
+
* - Wildcards: `/admin/**` (matches `/admin/users`, `/admin/users/123`)
|
|
1281
|
+
* - Single segment wildcards: `/users/*` (matches `/users/123` but not `/users/123/posts`)
|
|
1282
|
+
*
|
|
1283
|
+
* @param pattern - Route pattern (e.g., `/admin/**`)
|
|
1284
|
+
* @param routePath - Actual route path (e.g., `/admin/users`)
|
|
1285
|
+
* @returns Whether the pattern matches the path
|
|
1286
|
+
*/
|
|
1287
|
+
function matchRoutePattern(pattern, routePath) {
|
|
1288
|
+
const normalizedPattern = pattern.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
1289
|
+
const normalizedPath = routePath.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
1290
|
+
let regexPattern = normalizedPattern.replace(/\/\*\*/g, "___DOUBLE_WILDCARD___").replace(/\*/g, "___SINGLE_WILDCARD___").replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/___DOUBLE_WILDCARD___/g, "(?:/.*)?").replace(/___SINGLE_WILDCARD___/g, "[^/]+");
|
|
1291
|
+
return (/* @__PURE__ */ new RegExp(`^${regexPattern}$`)).test(normalizedPath);
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Find matching route rules for a given path
|
|
1295
|
+
*
|
|
1296
|
+
* @param routeRules - Route rules configuration
|
|
1297
|
+
* @param routePath - Route path to match
|
|
1298
|
+
* @returns Array of matching rules in order of specificity (most specific first)
|
|
1299
|
+
*/
|
|
1300
|
+
function findMatchingRules(routeRules, routePath) {
|
|
1301
|
+
const matches = [];
|
|
1302
|
+
for (const [pattern, rule] of Object.entries(routeRules)) if (matchRoutePattern(pattern, routePath)) {
|
|
1303
|
+
let specificity = 0;
|
|
1304
|
+
if (!pattern.includes("*")) specificity = 1e3;
|
|
1305
|
+
else if (pattern.includes("**")) specificity = pattern.split("/").length;
|
|
1306
|
+
else specificity = pattern.split("/").length * 10;
|
|
1307
|
+
matches.push({
|
|
1308
|
+
pattern,
|
|
1309
|
+
rule,
|
|
1310
|
+
specificity
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
matches.sort((a, b) => b.specificity - a.specificity);
|
|
1314
|
+
return matches.map(({ pattern, rule }) => ({
|
|
1315
|
+
pattern,
|
|
1316
|
+
rule
|
|
1317
|
+
}));
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Merge multiple route rules into a single rule
|
|
1321
|
+
*
|
|
1322
|
+
* Later rules override earlier rules for same properties.
|
|
1323
|
+
*
|
|
1324
|
+
* @param rules - Array of route rules to merge
|
|
1325
|
+
* @returns Merged route rule
|
|
1326
|
+
*/
|
|
1327
|
+
function mergeRouteRules(rules) {
|
|
1328
|
+
const merged = {};
|
|
1329
|
+
for (const rule of rules) {
|
|
1330
|
+
if (rule.redirect !== void 0) merged.redirect = rule.redirect;
|
|
1331
|
+
if (rule.prerender !== void 0) merged.prerender = rule.prerender;
|
|
1332
|
+
if (rule.cache !== void 0) if (typeof rule.cache === "boolean") merged.cache = rule.cache;
|
|
1333
|
+
else if (typeof merged.cache === "object" && typeof rule.cache === "object") merged.cache = {
|
|
1334
|
+
...merged.cache,
|
|
1335
|
+
...rule.cache
|
|
1336
|
+
};
|
|
1337
|
+
else merged.cache = rule.cache;
|
|
1338
|
+
if (rule.headers) merged.headers = {
|
|
1339
|
+
...merged.headers,
|
|
1340
|
+
...rule.headers
|
|
1341
|
+
};
|
|
1342
|
+
if (rule.cors !== void 0) if (typeof rule.cors === "boolean") merged.cors = rule.cors;
|
|
1343
|
+
else if (typeof merged.cors === "object" && typeof rule.cors === "object") merged.cors = {
|
|
1344
|
+
...merged.cors,
|
|
1345
|
+
...rule.cors
|
|
1346
|
+
};
|
|
1347
|
+
else merged.cors = rule.cors;
|
|
1348
|
+
if (rule.meta) merged.meta = {
|
|
1349
|
+
...merged.meta,
|
|
1350
|
+
...rule.meta
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
return merged;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Get the effective route rule for a path
|
|
1357
|
+
*
|
|
1358
|
+
* @param config - Kimesh configuration
|
|
1359
|
+
* @param routePath - Route path to get rule for
|
|
1360
|
+
* @returns Merged route rule or undefined if no rules match
|
|
1361
|
+
*/
|
|
1362
|
+
function getRouteRule(config, routePath) {
|
|
1363
|
+
if (!config.routeRules) return;
|
|
1364
|
+
const matches = findMatchingRules(config.routeRules, routePath);
|
|
1365
|
+
if (matches.length === 0) return;
|
|
1366
|
+
return mergeRouteRules(matches.reverse().map((m) => m.rule));
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Check if a route should be redirected
|
|
1370
|
+
*
|
|
1371
|
+
* @param rule - Route rule
|
|
1372
|
+
* @returns Redirect information or undefined
|
|
1373
|
+
*/
|
|
1374
|
+
function getRedirectInfo(rule) {
|
|
1375
|
+
if (!rule.redirect) return;
|
|
1376
|
+
if (typeof rule.redirect === "string") return {
|
|
1377
|
+
to: rule.redirect,
|
|
1378
|
+
statusCode: 302
|
|
1379
|
+
};
|
|
1380
|
+
return {
|
|
1381
|
+
to: rule.redirect.to,
|
|
1382
|
+
statusCode: rule.redirect.statusCode ?? 302
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Generate route rules manifest for build output
|
|
1387
|
+
*
|
|
1388
|
+
* @param config - Kimesh configuration
|
|
1389
|
+
* @returns Route rules manifest object
|
|
1390
|
+
*/
|
|
1391
|
+
function generateRouteRulesManifest(config) {
|
|
1392
|
+
return config.routeRules ?? {};
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
//#endregion
|
|
1396
|
+
//#region src/kit/debug.ts
|
|
1397
|
+
/**
|
|
1398
|
+
* @kimesh/kit - Debug Utilities
|
|
1399
|
+
*
|
|
1400
|
+
* Utilities for debug mode logging and diagnostics.
|
|
1401
|
+
*/
|
|
1402
|
+
let cachedDebugConfig = null;
|
|
1403
|
+
/**
|
|
1404
|
+
* Set the debug configuration
|
|
1405
|
+
*
|
|
1406
|
+
* @param config - Kimesh configuration
|
|
1407
|
+
*/
|
|
1408
|
+
function setDebugConfig(config) {
|
|
1409
|
+
if (!config.debug) {
|
|
1410
|
+
cachedDebugConfig = null;
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
if (config.debug === true) cachedDebugConfig = {
|
|
1414
|
+
hooks: true,
|
|
1415
|
+
modules: true,
|
|
1416
|
+
layers: true,
|
|
1417
|
+
config: true,
|
|
1418
|
+
vite: true,
|
|
1419
|
+
routes: true,
|
|
1420
|
+
imports: true
|
|
1421
|
+
};
|
|
1422
|
+
else cachedDebugConfig = config.debug;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Check if debug mode is enabled for a specific category
|
|
1426
|
+
*
|
|
1427
|
+
* @param category - Debug category to check
|
|
1428
|
+
* @returns Whether debug is enabled for this category
|
|
1429
|
+
*/
|
|
1430
|
+
function isDebug(category) {
|
|
1431
|
+
if (!cachedDebugConfig) return false;
|
|
1432
|
+
if (!category) return true;
|
|
1433
|
+
return !!cachedDebugConfig[category];
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Debug logger that only logs when debug mode is enabled
|
|
1437
|
+
*/
|
|
1438
|
+
const debug = {
|
|
1439
|
+
hook(hookName, ...args) {
|
|
1440
|
+
if (!isDebug("hooks")) return;
|
|
1441
|
+
consola.debug(pc.gray(`[hook] ${pc.cyan(hookName)}`), ...args);
|
|
1442
|
+
},
|
|
1443
|
+
module(moduleName, message, ...args) {
|
|
1444
|
+
if (!isDebug("modules")) return;
|
|
1445
|
+
consola.debug(pc.gray(`[module] ${pc.magenta(moduleName)}: ${message}`), ...args);
|
|
1446
|
+
},
|
|
1447
|
+
layer(layerName, message, ...args) {
|
|
1448
|
+
if (!isDebug("layers")) return;
|
|
1449
|
+
consola.debug(pc.gray(`[layer] ${pc.yellow(layerName)}: ${message}`), ...args);
|
|
1450
|
+
},
|
|
1451
|
+
config(message, ...args) {
|
|
1452
|
+
if (!isDebug("config")) return;
|
|
1453
|
+
consola.debug(pc.gray(`[config] ${message}`), ...args);
|
|
1454
|
+
},
|
|
1455
|
+
vite(message, ...args) {
|
|
1456
|
+
if (!isDebug("vite")) return;
|
|
1457
|
+
consola.debug(pc.gray(`[vite] ${message}`), ...args);
|
|
1458
|
+
},
|
|
1459
|
+
route(routePath, message, ...args) {
|
|
1460
|
+
if (!isDebug("routes")) return;
|
|
1461
|
+
consola.debug(pc.gray(`[route] ${pc.green(routePath)}: ${message}`), ...args);
|
|
1462
|
+
},
|
|
1463
|
+
import(name, message, ...args) {
|
|
1464
|
+
if (!isDebug("imports")) return;
|
|
1465
|
+
consola.debug(pc.gray(`[import] ${pc.blue(name)}: ${message}`), ...args);
|
|
1466
|
+
},
|
|
1467
|
+
timing(label, startTime) {
|
|
1468
|
+
if (!cachedDebugConfig) return;
|
|
1469
|
+
const duration = performance.now() - startTime;
|
|
1470
|
+
consola.debug(pc.gray(`[timing] ${label}: ${pc.bold(duration.toFixed(2))}ms`));
|
|
1471
|
+
},
|
|
1472
|
+
startTiming(label) {
|
|
1473
|
+
const start = performance.now();
|
|
1474
|
+
return () => debug.timing(label, start);
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
/**
|
|
1478
|
+
* Create a scoped debug logger for a specific module
|
|
1479
|
+
*
|
|
1480
|
+
* @param scope - Module or component name
|
|
1481
|
+
* @returns Scoped debug logger
|
|
1482
|
+
*/
|
|
1483
|
+
function createDebugLogger(scope) {
|
|
1484
|
+
return {
|
|
1485
|
+
log(message, ...args) {
|
|
1486
|
+
if (!cachedDebugConfig) return;
|
|
1487
|
+
consola.debug(pc.gray(`[${scope}] ${message}`), ...args);
|
|
1488
|
+
},
|
|
1489
|
+
info(message, ...args) {
|
|
1490
|
+
if (!cachedDebugConfig) return;
|
|
1491
|
+
consola.info(pc.gray(`[${scope}] ${message}`), ...args);
|
|
1492
|
+
},
|
|
1493
|
+
warn(message, ...args) {
|
|
1494
|
+
if (!cachedDebugConfig) return;
|
|
1495
|
+
consola.warn(`[${scope}] ${message}`, ...args);
|
|
1496
|
+
},
|
|
1497
|
+
error(message, ...args) {
|
|
1498
|
+
consola.error(`[${scope}] ${message}`, ...args);
|
|
1499
|
+
},
|
|
1500
|
+
timing: debug.startTiming
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Log a debug table (formatted key-value pairs)
|
|
1505
|
+
*
|
|
1506
|
+
* @param title - Table title
|
|
1507
|
+
* @param data - Data to display
|
|
1508
|
+
*/
|
|
1509
|
+
function debugTable(title, data) {
|
|
1510
|
+
if (!cachedDebugConfig) return;
|
|
1511
|
+
consola.debug(pc.bold(pc.cyan(`\n${title}:`)));
|
|
1512
|
+
const maxKeyLength = Math.max(...Object.keys(data).map((k) => k.length));
|
|
1513
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1514
|
+
const paddedKey = key.padEnd(maxKeyLength);
|
|
1515
|
+
const formattedValue = typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
|
|
1516
|
+
consola.debug(pc.gray(` ${paddedKey} : ${formattedValue}`));
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
//#endregion
|
|
1521
|
+
//#region src/kit/tsconfig-generator.ts
|
|
1522
|
+
/**
|
|
1523
|
+
* @kimesh/kit - TypeScript Configuration Generator
|
|
1524
|
+
*
|
|
1525
|
+
* Auto-generates TypeScript configurations with alias path mappings.
|
|
1526
|
+
* Similar to Nuxt's .nuxt/tsconfig.json generation.
|
|
1527
|
+
*/
|
|
1528
|
+
/**
|
|
1529
|
+
* Generate tsconfig.json content for .kimesh directory
|
|
1530
|
+
*
|
|
1531
|
+
* This generates a tsconfig that can be extended by the project's tsconfig.json,
|
|
1532
|
+
* providing automatic TypeScript support for all aliases.
|
|
1533
|
+
*
|
|
1534
|
+
* @param options - Generation options
|
|
1535
|
+
* @returns TSConfig JSON object
|
|
1536
|
+
*/
|
|
1537
|
+
function generateTsConfig(options) {
|
|
1538
|
+
const { rootDir, srcDir, buildDir, aliases, layerAliases = {}, moduleAliases = {}, internalAliases = {}, include = ["../src/**/*", "./**/*"], exclude = ["../node_modules"] } = options;
|
|
1539
|
+
const allAliases = {
|
|
1540
|
+
...aliases,
|
|
1541
|
+
...layerAliases,
|
|
1542
|
+
...moduleAliases,
|
|
1543
|
+
...internalAliases
|
|
1544
|
+
};
|
|
1545
|
+
const paths = {};
|
|
1546
|
+
for (const [alias, absolutePath] of Object.entries(allAliases)) {
|
|
1547
|
+
const relativePath = relative(buildDir, absolutePath);
|
|
1548
|
+
const normalizedPath = relativePath.startsWith("..") ? relativePath : "./" + relativePath;
|
|
1549
|
+
paths[alias] = [normalizedPath];
|
|
1550
|
+
paths[`${alias}/*`] = [`${normalizedPath}/*`];
|
|
1551
|
+
}
|
|
1552
|
+
return {
|
|
1553
|
+
compilerOptions: {
|
|
1554
|
+
target: "ESNext",
|
|
1555
|
+
module: "ESNext",
|
|
1556
|
+
moduleResolution: "bundler",
|
|
1557
|
+
strict: true,
|
|
1558
|
+
jsx: "preserve",
|
|
1559
|
+
sourceMap: true,
|
|
1560
|
+
resolveJsonModule: true,
|
|
1561
|
+
esModuleInterop: true,
|
|
1562
|
+
lib: ["ESNext", "DOM"],
|
|
1563
|
+
skipLibCheck: true,
|
|
1564
|
+
noEmit: true,
|
|
1565
|
+
baseUrl: ".",
|
|
1566
|
+
paths
|
|
1567
|
+
},
|
|
1568
|
+
include,
|
|
1569
|
+
exclude
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Write generated tsconfig.json to .kimesh directory
|
|
1574
|
+
*
|
|
1575
|
+
* @param options - Generation options
|
|
1576
|
+
*/
|
|
1577
|
+
function writeTsConfig(options) {
|
|
1578
|
+
const { buildDir } = options;
|
|
1579
|
+
mkdirSync(buildDir, { recursive: true });
|
|
1580
|
+
const tsconfig = generateTsConfig(options);
|
|
1581
|
+
writeFileSync(join$1(buildDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2), "utf-8");
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
//#endregion
|
|
1585
|
+
//#region src/kit/phase2-utils.ts
|
|
1586
|
+
/**
|
|
1587
|
+
* @kimesh/kit - Phase 2 Configuration Utilities
|
|
1588
|
+
*
|
|
1589
|
+
* Utility functions for Phase 2 configuration options:
|
|
1590
|
+
* - Directory resolution (2.1)
|
|
1591
|
+
* - Build config defaults (2.2)
|
|
1592
|
+
* - Dev server config merging (2.3)
|
|
1593
|
+
* - TypeScript config extension (2.4)
|
|
1594
|
+
* - Watch pattern handling (2.5)
|
|
1595
|
+
*/
|
|
1596
|
+
/**
|
|
1597
|
+
* Default source directory
|
|
1598
|
+
*/
|
|
1599
|
+
const DEFAULT_SRC_DIR = "src";
|
|
1600
|
+
/**
|
|
1601
|
+
* Default build directory
|
|
1602
|
+
*/
|
|
1603
|
+
const DEFAULT_BUILD_DIR = ".kimesh";
|
|
1604
|
+
/**
|
|
1605
|
+
* Resolve all directory paths from configuration.
|
|
1606
|
+
*
|
|
1607
|
+
* @param config - Kimesh configuration
|
|
1608
|
+
* @param rootDir - Project root directory
|
|
1609
|
+
* @returns Resolved absolute paths for all directories
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```ts
|
|
1613
|
+
* const dirs = resolveDirectories(config, process.cwd())
|
|
1614
|
+
* console.log(dirs.srcDir) // '/path/to/project/src'
|
|
1615
|
+
* console.log(dirs.buildDir) // '/path/to/project/.kimesh'
|
|
1616
|
+
* ```
|
|
1617
|
+
*/
|
|
1618
|
+
function resolveDirectories(config, rootDir) {
|
|
1619
|
+
const srcDirName = config.srcDir ?? DEFAULT_SRC_DIR;
|
|
1620
|
+
const srcDir = isAbsolute$1(srcDirName) ? srcDirName : resolve$1(rootDir, srcDirName);
|
|
1621
|
+
const buildDirName = config.buildDir ?? DEFAULT_BUILD_DIR;
|
|
1622
|
+
const buildDir = isAbsolute$1(buildDirName) ? buildDirName : resolve$1(rootDir, buildDirName);
|
|
1623
|
+
const dirConfig = {
|
|
1624
|
+
assets: config.dir?.assets ?? "assets",
|
|
1625
|
+
plugins: config.dir?.plugins ?? "plugins",
|
|
1626
|
+
public: config.dir?.public ?? "public",
|
|
1627
|
+
shared: config.dir?.shared ?? "shared"
|
|
1628
|
+
};
|
|
1629
|
+
return {
|
|
1630
|
+
rootDir,
|
|
1631
|
+
srcDir,
|
|
1632
|
+
buildDir,
|
|
1633
|
+
assetsDir: resolve$1(srcDir, dirConfig.assets),
|
|
1634
|
+
pluginsDir: resolve$1(srcDir, dirConfig.plugins),
|
|
1635
|
+
sharedDir: resolve$1(srcDir, dirConfig.shared),
|
|
1636
|
+
publicDir: resolve$1(rootDir, dirConfig.public)
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Normalize analyze configuration to consistent object format.
|
|
1641
|
+
*
|
|
1642
|
+
* @param analyze - Build analyze config (boolean or object)
|
|
1643
|
+
* @returns Normalized analyze config object
|
|
1644
|
+
*
|
|
1645
|
+
* @example
|
|
1646
|
+
* ```ts
|
|
1647
|
+
* normalizeAnalyzeConfig(true) // { enabled: true, openAnalyzer: false, reportFilename: 'report.html' }
|
|
1648
|
+
* normalizeAnalyzeConfig(false) // { enabled: false, openAnalyzer: false, reportFilename: 'report.html' }
|
|
1649
|
+
* ```
|
|
1650
|
+
*/
|
|
1651
|
+
function normalizeAnalyzeConfig(analyze) {
|
|
1652
|
+
if (typeof analyze === "boolean") return {
|
|
1653
|
+
enabled: analyze,
|
|
1654
|
+
openAnalyzer: false,
|
|
1655
|
+
reportFilename: "report.html"
|
|
1656
|
+
};
|
|
1657
|
+
return {
|
|
1658
|
+
enabled: analyze?.enabled ?? false,
|
|
1659
|
+
openAnalyzer: analyze?.openAnalyzer ?? false,
|
|
1660
|
+
reportFilename: analyze?.reportFilename ?? "report.html"
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Resolve build configuration with defaults.
|
|
1665
|
+
*
|
|
1666
|
+
* @param config - Build configuration
|
|
1667
|
+
* @returns Resolved build config with all defaults applied
|
|
1668
|
+
*
|
|
1669
|
+
* @example
|
|
1670
|
+
* ```ts
|
|
1671
|
+
* const buildConfig = resolveBuildConfig({ analyze: true })
|
|
1672
|
+
* console.log(buildConfig.target) // 'esnext'
|
|
1673
|
+
* ```
|
|
1674
|
+
*/
|
|
1675
|
+
function resolveBuildConfig(config) {
|
|
1676
|
+
return {
|
|
1677
|
+
analyze: normalizeAnalyzeConfig(config?.analyze),
|
|
1678
|
+
sourcemap: config?.sourcemap ?? false,
|
|
1679
|
+
target: config?.target ?? "esnext",
|
|
1680
|
+
minify: config?.minify ?? "esbuild"
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Normalize HTTPS configuration.
|
|
1685
|
+
*
|
|
1686
|
+
* @param https - HTTPS config (boolean or object)
|
|
1687
|
+
* @returns Normalized HTTPS config
|
|
1688
|
+
*/
|
|
1689
|
+
function normalizeHttpsConfig(https) {
|
|
1690
|
+
if (typeof https === "boolean") return { enabled: https };
|
|
1691
|
+
if (https) return {
|
|
1692
|
+
enabled: true,
|
|
1693
|
+
key: https.key,
|
|
1694
|
+
cert: https.cert
|
|
1695
|
+
};
|
|
1696
|
+
return { enabled: false };
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* Normalize CORS configuration.
|
|
1700
|
+
*
|
|
1701
|
+
* @param cors - CORS config (boolean or object)
|
|
1702
|
+
* @returns Normalized CORS config
|
|
1703
|
+
*/
|
|
1704
|
+
function normalizeCorsConfig(cors) {
|
|
1705
|
+
if (typeof cors === "boolean") return {
|
|
1706
|
+
enabled: cors,
|
|
1707
|
+
origin: cors ? "*" : void 0
|
|
1708
|
+
};
|
|
1709
|
+
if (cors) return {
|
|
1710
|
+
enabled: true,
|
|
1711
|
+
origin: cors.origin,
|
|
1712
|
+
methods: cors.methods,
|
|
1713
|
+
allowedHeaders: cors.allowedHeaders,
|
|
1714
|
+
exposedHeaders: cors.exposedHeaders,
|
|
1715
|
+
credentials: cors.credentials,
|
|
1716
|
+
maxAge: cors.maxAge
|
|
1717
|
+
};
|
|
1718
|
+
return { enabled: false };
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Resolve dev server configuration with defaults.
|
|
1722
|
+
*
|
|
1723
|
+
* @param config - Dev server configuration
|
|
1724
|
+
* @returns Resolved dev config with all defaults applied
|
|
1725
|
+
*/
|
|
1726
|
+
function resolveDevConfig(config) {
|
|
1727
|
+
return {
|
|
1728
|
+
port: config?.port ?? 3e3,
|
|
1729
|
+
host: config?.host ?? "localhost",
|
|
1730
|
+
open: config?.open ?? false,
|
|
1731
|
+
https: normalizeHttpsConfig(config?.https),
|
|
1732
|
+
proxy: config?.proxy ?? {},
|
|
1733
|
+
cors: normalizeCorsConfig(config?.cors),
|
|
1734
|
+
strictPort: config?.strictPort ?? false
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Resolve watch configuration with defaults.
|
|
1739
|
+
*
|
|
1740
|
+
* @param watch - Watch patterns array
|
|
1741
|
+
* @param watchers - Watchers configuration
|
|
1742
|
+
* @returns Resolved watch config
|
|
1743
|
+
*/
|
|
1744
|
+
function resolveWatchConfig(watch, watchers) {
|
|
1745
|
+
return {
|
|
1746
|
+
patterns: watch ?? [],
|
|
1747
|
+
chokidar: watchers?.chokidar ?? {}
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Merge chokidar options with defaults.
|
|
1752
|
+
*
|
|
1753
|
+
* @param options - User-provided chokidar options
|
|
1754
|
+
* @returns Merged chokidar options
|
|
1755
|
+
*/
|
|
1756
|
+
function mergeChokidarOptions(options) {
|
|
1757
|
+
return defu(options ?? {}, {
|
|
1758
|
+
persistent: true,
|
|
1759
|
+
usePolling: false,
|
|
1760
|
+
interval: 100,
|
|
1761
|
+
binaryInterval: 300,
|
|
1762
|
+
atomic: false
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Convert watch config to Vite watch options.
|
|
1767
|
+
*
|
|
1768
|
+
* @param config - Resolved watch configuration
|
|
1769
|
+
* @returns Vite-compatible watch options
|
|
1770
|
+
*/
|
|
1771
|
+
function toViteWatchOptions(config) {
|
|
1772
|
+
const merged = mergeChokidarOptions(config.chokidar);
|
|
1773
|
+
return {
|
|
1774
|
+
usePolling: merged.usePolling,
|
|
1775
|
+
interval: merged.interval,
|
|
1776
|
+
binaryInterval: merged.binaryInterval,
|
|
1777
|
+
ignored: merged.ignored
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
//#endregion
|
|
1782
|
+
//#region src/vite/entry.ts
|
|
1783
|
+
/**
|
|
1784
|
+
* Generate the entry point code for a Kimesh app
|
|
1785
|
+
*/
|
|
1786
|
+
function generateEntryCode(options) {
|
|
1787
|
+
const { hasContext, hasPlugins: hasPlugins$1 } = options;
|
|
1788
|
+
const imports = [
|
|
1789
|
+
`import { createKimeshApp } from '@kimesh/router-runtime'`,
|
|
1790
|
+
`import { routes } from '#kimesh/routes'`,
|
|
1791
|
+
`import App from '#kimesh/app'`
|
|
1792
|
+
];
|
|
1793
|
+
if (hasContext) imports.push(`import createContext from '#kimesh/context'`);
|
|
1794
|
+
if (hasPlugins$1) imports.push(`import { plugins } from '#kimesh/plugins'`);
|
|
1795
|
+
const optionLines = [` runtimeConfig: __KIMESH_CONFIG__,`, ` layersConfig: __KIMESH_LAYERS_CONFIG__,`];
|
|
1796
|
+
if (hasContext) optionLines.push(` context: createContext(),`);
|
|
1797
|
+
if (hasPlugins$1) optionLines.push(` plugins,`);
|
|
1798
|
+
const optionsBlock = `\n${optionLines.join("\n")}`;
|
|
1799
|
+
return `${imports.join("\n")}
|
|
1800
|
+
|
|
1801
|
+
declare const __KIMESH_CONFIG__: Record<string, unknown>
|
|
1802
|
+
declare const __KIMESH_LAYERS_CONFIG__: Record<string, Record<string, unknown>>
|
|
1803
|
+
|
|
1804
|
+
async function bootstrap() {
|
|
1805
|
+
const app = await createKimeshApp({
|
|
1806
|
+
rootComponent: App,
|
|
1807
|
+
routes,${optionsBlock}
|
|
1808
|
+
})
|
|
1809
|
+
|
|
1810
|
+
app.mount('#app')
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
bootstrap()
|
|
1814
|
+
`;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Generate context type declaration file for host app
|
|
1818
|
+
*/
|
|
1819
|
+
function generateContextTypes() {
|
|
1820
|
+
return `/* eslint-disable */
|
|
1821
|
+
/* prettier-ignore */
|
|
1822
|
+
// Auto-generated by @kimesh/kit
|
|
1823
|
+
// Do not edit this file manually
|
|
1824
|
+
|
|
1825
|
+
import type { AppContext } from '../src/app.context'
|
|
1826
|
+
|
|
1827
|
+
declare module '@kimesh/router-runtime' {
|
|
1828
|
+
interface KimeshContext extends AppContext {}
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
export {}
|
|
1832
|
+
`;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Generate context type declaration file for a layer
|
|
1836
|
+
* @param hostContextPath - Relative path from layer's .kimesh to host's app.context.ts
|
|
1837
|
+
*/
|
|
1838
|
+
function generateLayerContextTypes(hostContextPath) {
|
|
1839
|
+
return `/* eslint-disable */
|
|
1840
|
+
/* prettier-ignore */
|
|
1841
|
+
// Auto-generated by @kimesh/kit
|
|
1842
|
+
// Do not edit this file manually
|
|
1843
|
+
|
|
1844
|
+
import type { AppContext } from '${hostContextPath}'
|
|
1845
|
+
|
|
1846
|
+
declare module '@kimesh/router-runtime' {
|
|
1847
|
+
interface KimeshContext extends AppContext {}
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
export {}
|
|
1851
|
+
`;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
//#endregion
|
|
1855
|
+
//#region src/vite/html.ts
|
|
1856
|
+
/**
|
|
1857
|
+
* Generate the HTML template for a Kimesh app
|
|
1858
|
+
*/
|
|
1859
|
+
function generateHtml(title) {
|
|
1860
|
+
return `<!DOCTYPE html>
|
|
1861
|
+
<html lang="en">
|
|
1862
|
+
<head>
|
|
1863
|
+
<meta charset="UTF-8">
|
|
1864
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1865
|
+
<title>${title}</title>
|
|
1866
|
+
</head>
|
|
1867
|
+
<body>
|
|
1868
|
+
<div id="app"></div>
|
|
1869
|
+
<script type="module" src="/.kimesh/entry.ts"><\/script>
|
|
1870
|
+
</body>
|
|
1871
|
+
</html>`;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
//#endregion
|
|
1875
|
+
//#region src/hmr/watcher.ts
|
|
1876
|
+
/**
|
|
1877
|
+
* Create HMR watcher for Kimesh layers
|
|
1878
|
+
*/
|
|
1879
|
+
function createHMRWatcher(options) {
|
|
1880
|
+
const { server, layers, debug: debug$1 } = options;
|
|
1881
|
+
const log = (...args) => {
|
|
1882
|
+
if (debug$1) consola.debug("[kimesh:hmr]", ...args);
|
|
1883
|
+
};
|
|
1884
|
+
/**
|
|
1885
|
+
* Detect which layer a file belongs to
|
|
1886
|
+
*/
|
|
1887
|
+
function detectLayer(filePath) {
|
|
1888
|
+
const sortedLayers = [...layers].sort((a, b) => b.path.length - a.path.length);
|
|
1889
|
+
for (const layer of sortedLayers) if (filePath.startsWith(layer.path)) return layer;
|
|
1890
|
+
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Check if path contains a directory segment (cross-platform)
|
|
1893
|
+
*/
|
|
1894
|
+
function pathContains(filePath, segment) {
|
|
1895
|
+
return filePath.includes(`/${segment}/`) || filePath.includes(`\\${segment}\\`);
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Detect file category based on path
|
|
1899
|
+
*/
|
|
1900
|
+
function detectCategory(filePath) {
|
|
1901
|
+
if (pathContains(filePath, "routes")) return "route";
|
|
1902
|
+
if (pathContains(filePath, "components")) return "component";
|
|
1903
|
+
if (pathContains(filePath, "composables")) return "composable";
|
|
1904
|
+
if (filePath.includes("kimesh.config")) return "config";
|
|
1905
|
+
return "other";
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Handle file change
|
|
1909
|
+
*/
|
|
1910
|
+
async function handleChange(file, type) {
|
|
1911
|
+
const startTime = performance.now();
|
|
1912
|
+
const layer = detectLayer(file);
|
|
1913
|
+
const category = detectCategory(file);
|
|
1914
|
+
const context = {
|
|
1915
|
+
file,
|
|
1916
|
+
type,
|
|
1917
|
+
layer,
|
|
1918
|
+
category
|
|
1919
|
+
};
|
|
1920
|
+
log(`File ${type}: ${file}`, layer ? `[${layer.name}]` : "[app]", `(${category})`);
|
|
1921
|
+
switch (category) {
|
|
1922
|
+
case "route":
|
|
1923
|
+
await handleRouteChange(context);
|
|
1924
|
+
break;
|
|
1925
|
+
case "component":
|
|
1926
|
+
await handleComponentChange(context);
|
|
1927
|
+
break;
|
|
1928
|
+
case "composable":
|
|
1929
|
+
await handleComposableChange(context);
|
|
1930
|
+
break;
|
|
1931
|
+
case "config":
|
|
1932
|
+
await handleConfigChange(context);
|
|
1933
|
+
break;
|
|
1934
|
+
default: break;
|
|
1935
|
+
}
|
|
1936
|
+
log(`HMR handled in ${(performance.now() - startTime).toFixed(1)}ms`);
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Handle route file change
|
|
1940
|
+
*/
|
|
1941
|
+
async function handleRouteChange(context) {
|
|
1942
|
+
const routesModule = server.moduleGraph.getModuleById("\0virtual:kimesh-routes");
|
|
1943
|
+
if (routesModule) {
|
|
1944
|
+
server.moduleGraph.invalidateModule(routesModule);
|
|
1945
|
+
log("Invalidated virtual:kimesh-routes");
|
|
1946
|
+
}
|
|
1947
|
+
if (context.type === "add" || context.type === "unlink") {
|
|
1948
|
+
server.ws.send({
|
|
1949
|
+
type: "full-reload",
|
|
1950
|
+
path: "*"
|
|
1951
|
+
});
|
|
1952
|
+
log("Triggered full reload for route structure change");
|
|
1953
|
+
} else {
|
|
1954
|
+
const module = server.moduleGraph.getModuleById(context.file);
|
|
1955
|
+
if (module) {
|
|
1956
|
+
server.moduleGraph.invalidateModule(module);
|
|
1957
|
+
server.ws.send({
|
|
1958
|
+
type: "update",
|
|
1959
|
+
updates: [{
|
|
1960
|
+
type: "js-update",
|
|
1961
|
+
path: context.file,
|
|
1962
|
+
acceptedPath: context.file,
|
|
1963
|
+
timestamp: Date.now()
|
|
1964
|
+
}]
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Handle component file change
|
|
1971
|
+
*/
|
|
1972
|
+
async function handleComponentChange(context) {
|
|
1973
|
+
if (context.type === "add" || context.type === "unlink") {
|
|
1974
|
+
const autoImportModule = server.moduleGraph.getModuleById("\0virtual:kimesh-auto-imports");
|
|
1975
|
+
if (autoImportModule) {
|
|
1976
|
+
server.moduleGraph.invalidateModule(autoImportModule);
|
|
1977
|
+
log("Invalidated auto-import registry for component change");
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Handle composable file change
|
|
1983
|
+
*/
|
|
1984
|
+
async function handleComposableChange(context) {
|
|
1985
|
+
const module = server.moduleGraph.getModuleById(context.file);
|
|
1986
|
+
if (module) {
|
|
1987
|
+
const importers = collectImporters(module);
|
|
1988
|
+
for (const importer of importers) server.moduleGraph.invalidateModule(importer);
|
|
1989
|
+
server.moduleGraph.invalidateModule(module);
|
|
1990
|
+
log(`Invalidated ${importers.size + 1} modules for composable change`);
|
|
1991
|
+
server.ws.send({
|
|
1992
|
+
type: "update",
|
|
1993
|
+
updates: Array.from(importers).map((m) => ({
|
|
1994
|
+
type: "js-update",
|
|
1995
|
+
path: m.file,
|
|
1996
|
+
acceptedPath: m.file,
|
|
1997
|
+
timestamp: Date.now()
|
|
1998
|
+
}))
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
if (context.type === "add" || context.type === "unlink") {
|
|
2002
|
+
const autoImportModule = server.moduleGraph.getModuleById("\0virtual:kimesh-auto-imports");
|
|
2003
|
+
if (autoImportModule) server.moduleGraph.invalidateModule(autoImportModule);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Handle config file change
|
|
2008
|
+
*/
|
|
2009
|
+
async function handleConfigChange(context) {
|
|
2010
|
+
consola.info(`[kimesh] Config changed${context.layer ? ` in ${context.layer.name}` : ""}, restart required`);
|
|
2011
|
+
server.ws.send({
|
|
2012
|
+
type: "full-reload",
|
|
2013
|
+
path: "*"
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Collect all modules that import a given module (recursive)
|
|
2018
|
+
*/
|
|
2019
|
+
function collectImporters(module, seen = /* @__PURE__ */ new Set()) {
|
|
2020
|
+
const importers = /* @__PURE__ */ new Set();
|
|
2021
|
+
for (const importer of module.importers) if (!seen.has(importer)) {
|
|
2022
|
+
seen.add(importer);
|
|
2023
|
+
importers.add(importer);
|
|
2024
|
+
const nestedImporters = collectImporters(importer, seen);
|
|
2025
|
+
for (const nested of nestedImporters) importers.add(nested);
|
|
2026
|
+
}
|
|
2027
|
+
return importers;
|
|
2028
|
+
}
|
|
2029
|
+
return {
|
|
2030
|
+
handleChange,
|
|
2031
|
+
detectLayer,
|
|
2032
|
+
detectCategory
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region src/errors/formatter.ts
|
|
2038
|
+
/**
|
|
2039
|
+
* @kimesh/kit - Error Formatter
|
|
2040
|
+
*
|
|
2041
|
+
* Enhanced error messages with layer context and suggestions.
|
|
2042
|
+
*/
|
|
2043
|
+
/**
|
|
2044
|
+
* Format an error with layer context
|
|
2045
|
+
*/
|
|
2046
|
+
function formatError(error, context) {
|
|
2047
|
+
const title = pc.red(pc.bold(`✖ ${error.name || "Error"}`));
|
|
2048
|
+
let message = error.message;
|
|
2049
|
+
let location;
|
|
2050
|
+
const suggestions = [];
|
|
2051
|
+
if (context?.file) {
|
|
2052
|
+
const layerInfo = context.layer ? pc.dim(` [${context.layer.name}]`) : "";
|
|
2053
|
+
if (context.line) location = pc.cyan(`${context.file}:${context.line}${context.column ? `:${context.column}` : ""}`) + layerInfo;
|
|
2054
|
+
else location = pc.cyan(context.file) + layerInfo;
|
|
2055
|
+
}
|
|
2056
|
+
if (error.message.includes("Cannot find module")) {
|
|
2057
|
+
suggestions.push("Check if the import path is correct");
|
|
2058
|
+
suggestions.push("Ensure the dependency is installed");
|
|
2059
|
+
if (context?.layer) suggestions.push(`Try using the layer alias: #${context.layer.name}/...`);
|
|
2060
|
+
}
|
|
2061
|
+
if (error.message.includes("is not defined")) {
|
|
2062
|
+
suggestions.push("Add the missing import or ensure auto-import is enabled");
|
|
2063
|
+
suggestions.push("Check if the export exists in the source file");
|
|
2064
|
+
}
|
|
2065
|
+
if (error.message.includes("Duplicate route")) {
|
|
2066
|
+
suggestions.push("Routes in the app take precedence over layer routes");
|
|
2067
|
+
suggestions.push("Use a different path or remove the conflicting route");
|
|
2068
|
+
}
|
|
2069
|
+
let formatted = `\n${title}\n`;
|
|
2070
|
+
if (location) formatted += `\n${pc.dim("Location:")} ${location}\n`;
|
|
2071
|
+
formatted += `\n${message}\n`;
|
|
2072
|
+
if (context?.snippet) formatted += `\n${pc.dim("Code:")}\n${context.snippet}\n`;
|
|
2073
|
+
if (suggestions.length > 0) {
|
|
2074
|
+
formatted += `\n${pc.yellow("Suggestions:")}\n`;
|
|
2075
|
+
for (const suggestion of suggestions) formatted += ` ${pc.dim("•")} ${suggestion}\n`;
|
|
2076
|
+
}
|
|
2077
|
+
return {
|
|
2078
|
+
title: error.name || "Error",
|
|
2079
|
+
message,
|
|
2080
|
+
location,
|
|
2081
|
+
suggestions: suggestions.length > 0 ? suggestions : void 0,
|
|
2082
|
+
formatted
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Format a warning message
|
|
2087
|
+
*/
|
|
2088
|
+
function formatWarning(message, context) {
|
|
2089
|
+
let output = pc.yellow(pc.bold("⚠ Warning"));
|
|
2090
|
+
if (context?.layer) output += pc.dim(` [${context.layer.name}]`);
|
|
2091
|
+
output += `\n${message}`;
|
|
2092
|
+
if (context?.file) output += pc.dim(`\n at ${context.file}`);
|
|
2093
|
+
return output;
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Get suggestions for resolving a conflict based on type
|
|
2097
|
+
*/
|
|
2098
|
+
function getConflictSuggestions(type, name, winner) {
|
|
2099
|
+
if (type === "route") return ["Use different route paths", "Configure basePath in layer config"];
|
|
2100
|
+
return [`Use prefixed imports: ${name} → ${winner}${capitalize(name)}`, `Add prefix in layer config: components.prefix: '${capitalize(winner)}'`];
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Format a conflict warning
|
|
2104
|
+
*/
|
|
2105
|
+
function formatConflictWarning(type, name, sources, winner) {
|
|
2106
|
+
let output = pc.yellow(pc.bold(`⚠ ${capitalize(type)} Conflict`));
|
|
2107
|
+
output += `\n\n${pc.bold(name)} is defined in multiple layers:\n`;
|
|
2108
|
+
for (const source of sources) {
|
|
2109
|
+
const isWinner = source.layer === winner;
|
|
2110
|
+
const marker = isWinner ? pc.green("✓") : pc.dim("○");
|
|
2111
|
+
const layerName = isWinner ? pc.green(source.layer) : pc.dim(source.layer);
|
|
2112
|
+
output += ` ${marker} ${layerName}\n`;
|
|
2113
|
+
output += pc.dim(` ${source.path}\n`);
|
|
2114
|
+
}
|
|
2115
|
+
output += `\n${pc.dim("Using:")} ${pc.green(winner)} (highest priority)\n`;
|
|
2116
|
+
output += `\n${pc.yellow("Suggestions:")}\n`;
|
|
2117
|
+
for (const suggestion of getConflictSuggestions(type, name, winner)) output += ` ${pc.dim("•")} ${suggestion}\n`;
|
|
2118
|
+
return output;
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Format timing info for debug
|
|
2122
|
+
*/
|
|
2123
|
+
function formatTiming(operation, timeMs, details) {
|
|
2124
|
+
const color = timeMs < 50 ? pc.green : timeMs < 200 ? pc.yellow : pc.red;
|
|
2125
|
+
let output = `${pc.dim("[timing]")} ${operation}: ${color(`${timeMs.toFixed(1)}ms`)}`;
|
|
2126
|
+
if (details) {
|
|
2127
|
+
const detailStr = Object.entries(details).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
2128
|
+
output += pc.dim(` (${detailStr})`);
|
|
2129
|
+
}
|
|
2130
|
+
return output;
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Create a timing logger
|
|
2134
|
+
*/
|
|
2135
|
+
function createTimer(operation, debug$1 = false) {
|
|
2136
|
+
const start = performance.now();
|
|
2137
|
+
return { end(details) {
|
|
2138
|
+
const elapsed = performance.now() - start;
|
|
2139
|
+
if (debug$1) console.log(formatTiming(operation, elapsed, details));
|
|
2140
|
+
return elapsed;
|
|
2141
|
+
} };
|
|
2142
|
+
}
|
|
2143
|
+
function capitalize(str) {
|
|
2144
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
//#endregion
|
|
2148
|
+
//#region src/vite/plugin.ts
|
|
2149
|
+
/**
|
|
2150
|
+
* Find workspace root by traversing up and looking for workspaces config
|
|
2151
|
+
*/
|
|
2152
|
+
function findWorkspaceRoot(startDir) {
|
|
2153
|
+
let current = startDir;
|
|
2154
|
+
for (let i = 0; i < 10; i++) {
|
|
2155
|
+
const pkgPath = join$1(current, "package.json");
|
|
2156
|
+
if (existsSync(pkgPath)) try {
|
|
2157
|
+
if (JSON.parse(readFileSync(pkgPath, "utf-8")).workspaces || existsSync(join$1(current, "pnpm-workspace.yaml"))) return current;
|
|
2158
|
+
} catch {}
|
|
2159
|
+
const parent = resolve$1(current, "..");
|
|
2160
|
+
if (parent === current) break;
|
|
2161
|
+
current = parent;
|
|
2162
|
+
}
|
|
2163
|
+
return startDir;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Process all configured modules using the new v2 system
|
|
2167
|
+
*/
|
|
2168
|
+
async function processModules(kimesh, debug$1) {
|
|
2169
|
+
const modules = kimesh.options.config.modules;
|
|
2170
|
+
if (!modules || modules.length === 0) return;
|
|
2171
|
+
const moduleTimer = createTimer("Module processing", debug$1);
|
|
2172
|
+
try {
|
|
2173
|
+
await executeModules(modules, kimesh);
|
|
2174
|
+
moduleTimer.end({ count: modules.length });
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
moduleTimer.end({ error: true });
|
|
2177
|
+
consola.error("[Kimesh] Module processing failed:", error);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Extract module names from config (filters out inline module objects)
|
|
2182
|
+
*/
|
|
2183
|
+
function extractModuleNames$1(modules) {
|
|
2184
|
+
if (!modules) return [];
|
|
2185
|
+
const names = [];
|
|
2186
|
+
for (const mod of modules) if (typeof mod === "string") names.push(mod);
|
|
2187
|
+
else if (Array.isArray(mod) && typeof mod[0] === "string") names.push(mod[0]);
|
|
2188
|
+
return names;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Generate modules.ts that imports all configured modules
|
|
2192
|
+
* AND references their augment.d.ts files for type declarations
|
|
2193
|
+
*/
|
|
2194
|
+
function generateModulesTypeDeclaration(modules, buildDir) {
|
|
2195
|
+
const moduleNames = extractModuleNames$1(modules);
|
|
2196
|
+
if (moduleNames.length === 0) return;
|
|
2197
|
+
const imports = moduleNames.map((name) => `import "${name}";`).join("\n");
|
|
2198
|
+
const content = `/**
|
|
2199
|
+
* Module type declarations - Auto-generated by Kimesh
|
|
2200
|
+
* This file imports all modules to enable their type augmentations.
|
|
2201
|
+
*
|
|
2202
|
+
* DO NOT EDIT - This file is regenerated when modules change.
|
|
2203
|
+
*/
|
|
2204
|
+
|
|
2205
|
+
${moduleNames.map((name) => `/// <reference path="../node_modules/${name}/augment.d.ts" />`).join("\n")}
|
|
2206
|
+
|
|
2207
|
+
${imports}
|
|
2208
|
+
`;
|
|
2209
|
+
mkdirSync(buildDir, { recursive: true });
|
|
2210
|
+
writeFileSync(join$1(buildDir, "modules.ts"), content, "utf-8");
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Kimesh Vite plugin
|
|
2214
|
+
* Sets up Vue with Vite 8 + Rolldown + File-based routing + Layers + Auto-Import
|
|
2215
|
+
*/
|
|
2216
|
+
function kimeshPlugin(options = {}) {
|
|
2217
|
+
const config = options.config || {};
|
|
2218
|
+
const debug$1 = options.debug ?? false;
|
|
2219
|
+
const { plugins: userPlugins = [], ...userViteOptions } = config.vite || {};
|
|
2220
|
+
const allUserPlugins = [...userPlugins, ...options.additionalPlugins || []];
|
|
2221
|
+
const state = {
|
|
2222
|
+
resolvedLayers: [],
|
|
2223
|
+
root: "",
|
|
2224
|
+
generatedDir: "",
|
|
2225
|
+
kimesh: null,
|
|
2226
|
+
hasPlugins: false
|
|
2227
|
+
};
|
|
2228
|
+
const layersConfig = {
|
|
2229
|
+
enabled: options.layers?.enabled ?? config.layers?.enabled ?? "all",
|
|
2230
|
+
excluded: options.layers?.excluded ?? config.layers?.excluded ?? []
|
|
2231
|
+
};
|
|
2232
|
+
const mainPlugin = {
|
|
2233
|
+
name: "kimesh:main",
|
|
2234
|
+
enforce: "pre",
|
|
2235
|
+
async config(viteConfig, { command, mode }) {
|
|
2236
|
+
consola.debug(`[Kimesh] Configuring for ${command} mode`);
|
|
2237
|
+
const configRoot = viteConfig.root || process.cwd();
|
|
2238
|
+
state.root = configRoot;
|
|
2239
|
+
const resolvedDirs = resolveDirectories(config, configRoot);
|
|
2240
|
+
state.generatedDir = resolvedDirs.buildDir;
|
|
2241
|
+
const resolvedBuildConfig = resolveBuildConfig(config.build);
|
|
2242
|
+
const resolvedDevConfig = resolveDevConfig(config.dev);
|
|
2243
|
+
const resolvedWatchConfig = resolveWatchConfig(config.watch, config.watchers);
|
|
2244
|
+
if (debug$1) {
|
|
2245
|
+
consola.info(`[Kimesh] Resolved directories:`);
|
|
2246
|
+
consola.info(` - srcDir: ${resolvedDirs.srcDir}`);
|
|
2247
|
+
consola.info(` - buildDir: ${resolvedDirs.buildDir}`);
|
|
2248
|
+
consola.info(` - pluginsDir: ${resolvedDirs.pluginsDir}`);
|
|
2249
|
+
}
|
|
2250
|
+
const envVars = loadEnv(mode, configRoot, "KIMESH_");
|
|
2251
|
+
const layerTimer = createTimer("Layer resolution", debug$1);
|
|
2252
|
+
try {
|
|
2253
|
+
state.resolvedLayers = await prepareLayers$1(configRoot, {
|
|
2254
|
+
enabled: layersConfig.enabled,
|
|
2255
|
+
excluded: layersConfig.excluded
|
|
2256
|
+
});
|
|
2257
|
+
layerTimer.end({ count: state.resolvedLayers.length });
|
|
2258
|
+
if (debug$1 || state.resolvedLayers.length > 1) {
|
|
2259
|
+
consola.info(`[Kimesh] Resolved ${state.resolvedLayers.length} layers:`);
|
|
2260
|
+
for (const layer of state.resolvedLayers) consola.info(` - ${layer.name} (priority: ${layer.priority}, path: ${layer.path})`);
|
|
2261
|
+
}
|
|
2262
|
+
} catch (error) {
|
|
2263
|
+
layerTimer.end({ error: true });
|
|
2264
|
+
consola.warn(`[Kimesh] Layer resolution failed: ${error}`);
|
|
2265
|
+
state.resolvedLayers = [];
|
|
2266
|
+
}
|
|
2267
|
+
const layerAliases = generateLayerAliases$1(state.resolvedLayers);
|
|
2268
|
+
state.kimesh = createKimesh({
|
|
2269
|
+
config,
|
|
2270
|
+
layers: state.resolvedLayers,
|
|
2271
|
+
root: configRoot,
|
|
2272
|
+
buildDir: state.generatedDir,
|
|
2273
|
+
dev: command === "serve"
|
|
2274
|
+
});
|
|
2275
|
+
await processModules(state.kimesh, debug$1);
|
|
2276
|
+
if (config.app?.head) {
|
|
2277
|
+
const headPluginSrc = "@kimesh/head/plugin";
|
|
2278
|
+
if (!state.kimesh._registries.runtimePlugins.some((p) => p.src === headPluginSrc)) {
|
|
2279
|
+
state.kimesh._registries.runtimePlugins.unshift({
|
|
2280
|
+
src: headPluginSrc,
|
|
2281
|
+
name: "head"
|
|
2282
|
+
});
|
|
2283
|
+
if (debug$1) consola.info("[Kimesh] Auto-registered @kimesh/head plugin (app.head configured)");
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
let discoveredPlugins = await scanPluginsDir(resolvedDirs.pluginsDir);
|
|
2287
|
+
for (const layer of state.resolvedLayers) {
|
|
2288
|
+
const layerPlugins = await scanPluginsDir(join$1(layer.path, "src", "plugins"));
|
|
2289
|
+
discoveredPlugins = discoveredPlugins.concat(layerPlugins);
|
|
2290
|
+
}
|
|
2291
|
+
const hasRegisteredPlugins = state.kimesh._registries.runtimePlugins.length > 0;
|
|
2292
|
+
state.hasPlugins = discoveredPlugins.length > 0 || hasRegisteredPlugins;
|
|
2293
|
+
if (state.hasPlugins) {
|
|
2294
|
+
addPluginsTemplate(state.kimesh, discoveredPlugins);
|
|
2295
|
+
if (debug$1) {
|
|
2296
|
+
consola.info(`[Kimesh] Found ${discoveredPlugins.length} auto-discovered plugins`);
|
|
2297
|
+
consola.info(`[Kimesh] Found ${state.kimesh._registries.runtimePlugins.length} module-registered plugins`);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
generateModulesTypeDeclaration(config.modules, state.generatedDir);
|
|
2301
|
+
await writeTemplates(state.kimesh);
|
|
2302
|
+
const userAliases = buildAliases(config, resolvedDirs.srcDir, configRoot);
|
|
2303
|
+
const moduleAliases = {};
|
|
2304
|
+
for (const alias of state.kimesh._registries.aliases) if (typeof alias.find === "string") moduleAliases[alias.find] = alias.replacement;
|
|
2305
|
+
const appVuePath = resolve$1(resolvedDirs.srcDir, "app.vue");
|
|
2306
|
+
const internalAliases = {
|
|
2307
|
+
"#kimesh/routes": join$1(resolvedDirs.buildDir, "routes.gen.ts"),
|
|
2308
|
+
"#kimesh/app": existsSync(appVuePath) ? appVuePath : "@kimesh/router-runtime/default-app",
|
|
2309
|
+
"#kimesh/context": join$1(resolvedDirs.srcDir, "app.context.ts"),
|
|
2310
|
+
"#kimesh/plugins": join$1(resolvedDirs.buildDir, "plugins.mjs")
|
|
2311
|
+
};
|
|
2312
|
+
writeTsConfig({
|
|
2313
|
+
rootDir: configRoot,
|
|
2314
|
+
srcDir: resolvedDirs.srcDir,
|
|
2315
|
+
buildDir: state.generatedDir,
|
|
2316
|
+
aliases: userAliases,
|
|
2317
|
+
layerAliases,
|
|
2318
|
+
moduleAliases,
|
|
2319
|
+
internalAliases,
|
|
2320
|
+
include: [
|
|
2321
|
+
"../src/**/*",
|
|
2322
|
+
"./**/*",
|
|
2323
|
+
"../kimesh.config.ts"
|
|
2324
|
+
],
|
|
2325
|
+
exclude: ["../node_modules"]
|
|
2326
|
+
});
|
|
2327
|
+
if (debug$1) consola.info(`[Kimesh] Generated .kimesh/tsconfig.json with ${Object.keys(userAliases).length} aliases`);
|
|
2328
|
+
const layerDirs = state.resolvedLayers.map((layer) => layer.path);
|
|
2329
|
+
const workspaceRoot = findWorkspaceRoot(configRoot);
|
|
2330
|
+
const workspacePackages = resolve$1(workspaceRoot, "packages");
|
|
2331
|
+
const nodeModules = resolve$1(workspaceRoot, "node_modules");
|
|
2332
|
+
if (command === "build") {
|
|
2333
|
+
if (!existsSync(resolvedDirs.buildDir)) mkdirSync(resolvedDirs.buildDir, { recursive: true });
|
|
2334
|
+
writeFileSync(join$1(resolvedDirs.buildDir, "index.html"), generateHtml(config.name || "Kimesh App"), "utf-8");
|
|
2335
|
+
const hasContext = existsSync(join$1(resolvedDirs.srcDir, "app.context.ts"));
|
|
2336
|
+
writeFileSync(join$1(resolvedDirs.buildDir, "entry.ts"), generateEntryCode({
|
|
2337
|
+
hasContext,
|
|
2338
|
+
hasPlugins: state.hasPlugins
|
|
2339
|
+
}), "utf-8");
|
|
2340
|
+
if (hasContext) writeFileSync(join$1(resolvedDirs.buildDir, "context.d.ts"), generateContextTypes(), "utf-8");
|
|
2341
|
+
}
|
|
2342
|
+
const layerRuntimeConfigs = state.resolvedLayers.slice().sort((a, b) => b.priority - a.priority).map((layer) => layer.runtimeConfig).filter(Boolean);
|
|
2343
|
+
const processedRuntimeConfig = applyEnv(defu(config.runtimeConfig || createDefaultRuntimeConfig(), ...layerRuntimeConfigs), { env: envVars });
|
|
2344
|
+
const layerConfigMap = {};
|
|
2345
|
+
for (const layer of state.resolvedLayers) if (layer.runtimeConfig) layerConfigMap[layer.name] = layer.runtimeConfig;
|
|
2346
|
+
return mergeConfig({
|
|
2347
|
+
define: {
|
|
2348
|
+
__KIMESH_DEV__: command === "serve",
|
|
2349
|
+
__KIMESH_NAME__: JSON.stringify(config.name || "kimesh-app"),
|
|
2350
|
+
__KIMESH_LAYERS__: JSON.stringify(state.resolvedLayers.map((l) => l.name)),
|
|
2351
|
+
__KIMESH_CONFIG__: JSON.stringify(processedRuntimeConfig),
|
|
2352
|
+
__KIMESH_LAYERS_CONFIG__: JSON.stringify(layerConfigMap)
|
|
2353
|
+
},
|
|
2354
|
+
resolve: { alias: {
|
|
2355
|
+
"#kimesh/routes": join$1(resolvedDirs.buildDir, "routes.gen.ts"),
|
|
2356
|
+
"#kimesh/app": existsSync(appVuePath) ? appVuePath : "@kimesh/router-runtime/default-app",
|
|
2357
|
+
"#kimesh/context": join$1(resolvedDirs.srcDir, "app.context.ts"),
|
|
2358
|
+
"#kimesh/plugins": join$1(resolvedDirs.buildDir, "plugins.mjs"),
|
|
2359
|
+
...userAliases,
|
|
2360
|
+
...layerAliases,
|
|
2361
|
+
...moduleAliases
|
|
2362
|
+
} },
|
|
2363
|
+
server: {
|
|
2364
|
+
port: resolvedDevConfig.port,
|
|
2365
|
+
host: resolvedDevConfig.host,
|
|
2366
|
+
open: resolvedDevConfig.open,
|
|
2367
|
+
strictPort: resolvedDevConfig.strictPort,
|
|
2368
|
+
fs: { allow: [
|
|
2369
|
+
configRoot,
|
|
2370
|
+
...layerDirs,
|
|
2371
|
+
workspaceRoot,
|
|
2372
|
+
workspacePackages,
|
|
2373
|
+
nodeModules
|
|
2374
|
+
] },
|
|
2375
|
+
watch: toViteWatchOptions(resolvedWatchConfig)
|
|
2376
|
+
},
|
|
2377
|
+
build: {
|
|
2378
|
+
target: resolvedBuildConfig.target,
|
|
2379
|
+
sourcemap: resolvedBuildConfig.sourcemap,
|
|
2380
|
+
minify: resolvedBuildConfig.minify,
|
|
2381
|
+
rollupOptions: command === "build" ? { input: join$1(resolvedDirs.buildDir, "index.html") } : void 0
|
|
2382
|
+
}
|
|
2383
|
+
}, userViteOptions);
|
|
2384
|
+
},
|
|
2385
|
+
configResolved(resolvedConfig) {
|
|
2386
|
+
state.root = resolvedConfig.root;
|
|
2387
|
+
state.generatedDir = resolveDirectories(config, state.root).buildDir;
|
|
2388
|
+
consola.debug(`[Kimesh] Config resolved, mode: ${resolvedConfig.mode}`);
|
|
2389
|
+
},
|
|
2390
|
+
async buildStart() {
|
|
2391
|
+
if (!existsSync(state.generatedDir)) mkdirSync(state.generatedDir, { recursive: true });
|
|
2392
|
+
const contextPath = join$1(state.root, "src", "app.context.ts");
|
|
2393
|
+
const hasContext = existsSync(contextPath);
|
|
2394
|
+
const entryPath = join$1(state.generatedDir, "entry.ts");
|
|
2395
|
+
writeFileSync(entryPath, generateEntryCode({
|
|
2396
|
+
hasContext,
|
|
2397
|
+
hasPlugins: state.hasPlugins
|
|
2398
|
+
}), "utf-8");
|
|
2399
|
+
consola.debug(`[Kimesh] Generated entry file: ${entryPath}`);
|
|
2400
|
+
if (hasContext) {
|
|
2401
|
+
const contextTypesPath = join$1(state.generatedDir, "context.d.ts");
|
|
2402
|
+
writeFileSync(contextTypesPath, generateContextTypes(), "utf-8");
|
|
2403
|
+
consola.debug(`[Kimesh] Generated context types: ${contextTypesPath}`);
|
|
2404
|
+
for (const layer of state.resolvedLayers) {
|
|
2405
|
+
if (layer.isApp) continue;
|
|
2406
|
+
let layerRoot = layer.path;
|
|
2407
|
+
try {
|
|
2408
|
+
layerRoot = realpathSync(layer.path);
|
|
2409
|
+
} catch {}
|
|
2410
|
+
const layerKimeshDir = join$1(layerRoot, ".kimesh");
|
|
2411
|
+
if (!existsSync(layerKimeshDir)) mkdirSync(layerKimeshDir, { recursive: true });
|
|
2412
|
+
const hostContextPath = relative(layerKimeshDir, contextPath).replace(/\.ts$/, "");
|
|
2413
|
+
const layerContextTypesPath = join$1(layerKimeshDir, "context.d.ts");
|
|
2414
|
+
writeFileSync(layerContextTypesPath, generateLayerContextTypes(hostContextPath), "utf-8");
|
|
2415
|
+
consola.debug(`[Kimesh] Generated layer context types: ${layerContextTypesPath}`);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
const htmlPath = join$1(state.generatedDir, "index.html");
|
|
2419
|
+
writeFileSync(htmlPath, generateHtml(config.name || "Kimesh App"), "utf-8");
|
|
2420
|
+
consola.debug(`[Kimesh] Generated HTML file: ${htmlPath}`);
|
|
2421
|
+
},
|
|
2422
|
+
configureServer(server) {
|
|
2423
|
+
const hmrWatcher = createHMRWatcher({
|
|
2424
|
+
server,
|
|
2425
|
+
layers: state.resolvedLayers,
|
|
2426
|
+
debug: debug$1
|
|
2427
|
+
});
|
|
2428
|
+
for (const layer of state.resolvedLayers) if (!layer.isApp) {
|
|
2429
|
+
server.watcher.add(layer.path);
|
|
2430
|
+
consola.debug(`[Kimesh] Watching layer: ${layer.name}`);
|
|
2431
|
+
}
|
|
2432
|
+
const handleWatchEvent = (event) => (file) => {
|
|
2433
|
+
const layer = hmrWatcher.detectLayer(file);
|
|
2434
|
+
if (layer && !layer.isApp) hmrWatcher.handleChange(file, event);
|
|
2435
|
+
};
|
|
2436
|
+
server.watcher.on("change", handleWatchEvent("change"));
|
|
2437
|
+
server.watcher.on("add", handleWatchEvent("add"));
|
|
2438
|
+
server.watcher.on("unlink", handleWatchEvent("unlink"));
|
|
2439
|
+
server.middlewares.use(async (req, res, next) => {
|
|
2440
|
+
const url = req.url || "/";
|
|
2441
|
+
if (url.includes(".") || url.startsWith("/@") || url.startsWith("/__") || url.startsWith("/node_modules")) return next();
|
|
2442
|
+
try {
|
|
2443
|
+
const html = readFileSync(join$1(state.generatedDir, "index.html"), "utf-8");
|
|
2444
|
+
const transformed = await server.transformIndexHtml(url, html);
|
|
2445
|
+
res.setHeader("Content-Type", "text/html");
|
|
2446
|
+
res.end(transformed);
|
|
2447
|
+
} catch (e) {
|
|
2448
|
+
next(e);
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
};
|
|
2453
|
+
const routerPlugin = kimeshRouterGenerator({
|
|
2454
|
+
routesDir: config.router?.routesDir ?? "routes",
|
|
2455
|
+
importMode: config.router?.importMode ?? "async",
|
|
2456
|
+
debug: debug$1,
|
|
2457
|
+
getLayerRoutes: () => {
|
|
2458
|
+
return state.resolvedLayers.filter((l) => !l.isApp).map((layer) => {
|
|
2459
|
+
const routesFolder = layer.config.routes?.folder || "routes";
|
|
2460
|
+
const routesDir = join$1(layer.path, "src", routesFolder);
|
|
2461
|
+
return {
|
|
2462
|
+
layerName: layer.name,
|
|
2463
|
+
routesDir,
|
|
2464
|
+
layerPath: layer.path,
|
|
2465
|
+
basePath: layer.config.routes?.basePath,
|
|
2466
|
+
priority: layer.priority
|
|
2467
|
+
};
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
const internalPlugins = [
|
|
2472
|
+
mainPlugin,
|
|
2473
|
+
kimeshAutoImport$1({
|
|
2474
|
+
getSources: () => {
|
|
2475
|
+
return state.resolvedLayers.map((layer) => ({
|
|
2476
|
+
layer: layer.name,
|
|
2477
|
+
priority: layer.priority,
|
|
2478
|
+
layerPath: layer.path,
|
|
2479
|
+
config: {
|
|
2480
|
+
composables: { dirs: layer.config.composables?.dirs || ["composables"] },
|
|
2481
|
+
components: {
|
|
2482
|
+
dirs: layer.config.components?.dirs || ["components"],
|
|
2483
|
+
prefix: layer.isApp ? void 0 : layer.config.components?.prefix
|
|
2484
|
+
},
|
|
2485
|
+
utils: { dirs: layer.config.utils?.dirs || ["utils"] },
|
|
2486
|
+
stores: { dirs: layer.config.stores?.dirs || ["stores"] },
|
|
2487
|
+
presets: layer.isApp ? config.autoImport?.presets || [
|
|
2488
|
+
"vue",
|
|
2489
|
+
"vue-router",
|
|
2490
|
+
"kimesh"
|
|
2491
|
+
] : void 0
|
|
2492
|
+
}
|
|
2493
|
+
}));
|
|
2494
|
+
},
|
|
2495
|
+
getLayers: () => {
|
|
2496
|
+
return state.resolvedLayers.map((layer) => ({
|
|
2497
|
+
name: layer.name,
|
|
2498
|
+
path: layer.path,
|
|
2499
|
+
isApp: layer.isApp
|
|
2500
|
+
}));
|
|
2501
|
+
},
|
|
2502
|
+
dts: config.autoImport?.dts !== false ? state.generatedDir || ".kimesh" : false,
|
|
2503
|
+
debug: debug$1
|
|
2504
|
+
}),
|
|
2505
|
+
vue(),
|
|
2506
|
+
routerPlugin
|
|
2507
|
+
];
|
|
2508
|
+
const modulePluginsWrapper = {
|
|
2509
|
+
name: "kimesh:module-plugins-wrapper",
|
|
2510
|
+
async resolveId(id, importer, options$1) {
|
|
2511
|
+
const modulePlugins = state.kimesh?._registries.vitePlugins ?? [];
|
|
2512
|
+
for (const entry of modulePlugins) {
|
|
2513
|
+
const plugin = entry.plugin;
|
|
2514
|
+
if (typeof plugin.resolveId === "function") {
|
|
2515
|
+
const result = await plugin.resolveId.call(this, id, importer, options$1);
|
|
2516
|
+
if (result != null) return result;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
return null;
|
|
2520
|
+
},
|
|
2521
|
+
async load(id, options$1) {
|
|
2522
|
+
const modulePlugins = state.kimesh?._registries.vitePlugins ?? [];
|
|
2523
|
+
for (const entry of modulePlugins) {
|
|
2524
|
+
const plugin = entry.plugin;
|
|
2525
|
+
if (typeof plugin.load === "function") {
|
|
2526
|
+
const result = await plugin.load.call(this, id, options$1);
|
|
2527
|
+
if (result != null) return result;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
return null;
|
|
2531
|
+
},
|
|
2532
|
+
async transform(code, id, options$1) {
|
|
2533
|
+
let currentCode = code;
|
|
2534
|
+
const modulePlugins = state.kimesh?._registries.vitePlugins ?? [];
|
|
2535
|
+
for (const entry of modulePlugins) {
|
|
2536
|
+
const plugin = entry.plugin;
|
|
2537
|
+
if (typeof plugin.transform === "function") {
|
|
2538
|
+
const result = await plugin.transform.call(this, currentCode, id, options$1);
|
|
2539
|
+
if (result != null) currentCode = typeof result === "string" ? result : result.code;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return currentCode !== code ? currentCode : null;
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
return [
|
|
2546
|
+
...internalPlugins,
|
|
2547
|
+
modulePluginsWrapper,
|
|
2548
|
+
...allUserPlugins
|
|
2549
|
+
];
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
//#endregion
|
|
2553
|
+
//#region src/config.ts
|
|
2554
|
+
/**
|
|
2555
|
+
* Load kimesh.config.ts using c12
|
|
2556
|
+
*/
|
|
2557
|
+
async function loadConfig(options = {}) {
|
|
2558
|
+
const { config, configFile } = await loadConfig$1({
|
|
2559
|
+
name: "kimesh",
|
|
2560
|
+
cwd: options.root || process.cwd(),
|
|
2561
|
+
configFile: options.configFile,
|
|
2562
|
+
defaultConfig: {
|
|
2563
|
+
name: "kimesh-app",
|
|
2564
|
+
dev: {
|
|
2565
|
+
port: 3e3,
|
|
2566
|
+
host: "localhost"
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
});
|
|
2570
|
+
if (configFile) consola.debug(`[Kimesh] Config loaded from: ${configFile}`);
|
|
2571
|
+
return config || {};
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
//#endregion
|
|
2575
|
+
//#region src/prepare.ts
|
|
2576
|
+
/**
|
|
2577
|
+
* @kimesh/kit - Prepare Utilities
|
|
2578
|
+
*
|
|
2579
|
+
* Generates the .kimesh directory with TypeScript configurations,
|
|
2580
|
+
* type declarations, and other generated files needed for IDE support.
|
|
2581
|
+
*
|
|
2582
|
+
* Similar to `nuxt prepare` - run before IDE work for full type support.
|
|
2583
|
+
*/
|
|
2584
|
+
/**
|
|
2585
|
+
* Prepare the .kimesh directory for TypeScript support
|
|
2586
|
+
*
|
|
2587
|
+
* This function generates all necessary files in the .kimesh directory
|
|
2588
|
+
* to enable TypeScript autocompletion and type checking before running
|
|
2589
|
+
* the dev server.
|
|
2590
|
+
*
|
|
2591
|
+
* @param options - Prepare options
|
|
2592
|
+
* @returns Result with generated file information
|
|
2593
|
+
*
|
|
2594
|
+
* @example
|
|
2595
|
+
* ```ts
|
|
2596
|
+
* import { prepare } from '@kimesh/kit'
|
|
2597
|
+
*
|
|
2598
|
+
* await prepare({ root: process.cwd() })
|
|
2599
|
+
* ```
|
|
2600
|
+
*/
|
|
2601
|
+
async function prepare(options = {}) {
|
|
2602
|
+
const root = resolve$1(options.root || process.cwd());
|
|
2603
|
+
const buildDir = join$1(root, ".kimesh");
|
|
2604
|
+
const srcDir = join$1(root, "src");
|
|
2605
|
+
const verbose = options.verbose ?? false;
|
|
2606
|
+
const generatedFiles = [];
|
|
2607
|
+
if (verbose) consola.info(`[Kimesh] Preparing project at ${root}`);
|
|
2608
|
+
const config = await loadConfig({
|
|
2609
|
+
root,
|
|
2610
|
+
configFile: options.configFile
|
|
2611
|
+
});
|
|
2612
|
+
if (verbose) consola.info(`[Kimesh] Loaded config: ${config.name || "kimesh-app"}`);
|
|
2613
|
+
if (!existsSync(buildDir)) mkdirSync(buildDir, { recursive: true });
|
|
2614
|
+
let resolvedLayers = [];
|
|
2615
|
+
try {
|
|
2616
|
+
resolvedLayers = await prepareLayers$1(root, {
|
|
2617
|
+
enabled: config.layers?.enabled ?? "all",
|
|
2618
|
+
excluded: config.layers?.excluded ?? []
|
|
2619
|
+
});
|
|
2620
|
+
if (verbose) consola.info(`[Kimesh] Resolved ${resolvedLayers.length} layers`);
|
|
2621
|
+
} catch (error) {
|
|
2622
|
+
consola.warn(`[Kimesh] Layer resolution failed: ${error}`);
|
|
2623
|
+
}
|
|
2624
|
+
const layerAliases = generateLayerAliases$1(resolvedLayers);
|
|
2625
|
+
const userAliases = buildAliases(config, srcDir, root);
|
|
2626
|
+
const kimeshDir = resolve$1(root, ".kimesh");
|
|
2627
|
+
const appVuePath = resolve$1(root, "src/app.vue");
|
|
2628
|
+
const internalAliases = {
|
|
2629
|
+
"#kimesh/routes": join$1(kimeshDir, "routes.gen.ts"),
|
|
2630
|
+
"#kimesh/app": existsSync(appVuePath) ? appVuePath : "@kimesh/router-runtime/default-app",
|
|
2631
|
+
"#kimesh/context": join$1(root, "src", "app.context.ts"),
|
|
2632
|
+
"#kimesh/plugins": join$1(kimeshDir, "plugins.mjs")
|
|
2633
|
+
};
|
|
2634
|
+
writeTsConfig({
|
|
2635
|
+
rootDir: root,
|
|
2636
|
+
srcDir,
|
|
2637
|
+
buildDir,
|
|
2638
|
+
aliases: userAliases,
|
|
2639
|
+
layerAliases,
|
|
2640
|
+
moduleAliases: {},
|
|
2641
|
+
internalAliases,
|
|
2642
|
+
include: [
|
|
2643
|
+
"../src/**/*",
|
|
2644
|
+
"./**/*",
|
|
2645
|
+
"../kimesh.config.ts"
|
|
2646
|
+
],
|
|
2647
|
+
exclude: ["../node_modules"]
|
|
2648
|
+
});
|
|
2649
|
+
generatedFiles.push("tsconfig.json");
|
|
2650
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/tsconfig.json`);
|
|
2651
|
+
const routesStubPath = join$1(buildDir, "routes.gen.ts");
|
|
2652
|
+
if (!existsSync(routesStubPath)) {
|
|
2653
|
+
writeFileSync(routesStubPath, `/**
|
|
2654
|
+
* Kimesh Routes - Auto-generated
|
|
2655
|
+
*
|
|
2656
|
+
* This file is a stub generated by 'km prepare'.
|
|
2657
|
+
* It will be populated with actual routes when running 'km dev' or 'km build'.
|
|
2658
|
+
*
|
|
2659
|
+
* DO NOT EDIT - This file is regenerated automatically.
|
|
2660
|
+
*/
|
|
2661
|
+
|
|
2662
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
2663
|
+
|
|
2664
|
+
export const routes: RouteRecordRaw[] = []
|
|
2665
|
+
`, "utf-8");
|
|
2666
|
+
generatedFiles.push("routes.gen.ts");
|
|
2667
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/routes.gen.ts (stub)`);
|
|
2668
|
+
}
|
|
2669
|
+
const pluginsStubPath = join$1(buildDir, "plugins.mjs");
|
|
2670
|
+
if (!existsSync(pluginsStubPath)) {
|
|
2671
|
+
writeFileSync(pluginsStubPath, `/**
|
|
2672
|
+
* Kimesh Plugins - Auto-generated
|
|
2673
|
+
*
|
|
2674
|
+
* This file is a stub generated by 'km prepare'.
|
|
2675
|
+
* It will be populated with actual plugins when running 'km dev' or 'km build'.
|
|
2676
|
+
*
|
|
2677
|
+
* DO NOT EDIT - This file is regenerated automatically.
|
|
2678
|
+
*/
|
|
2679
|
+
|
|
2680
|
+
export default []
|
|
2681
|
+
`, "utf-8");
|
|
2682
|
+
generatedFiles.push("plugins.mjs");
|
|
2683
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/plugins.mjs (stub)`);
|
|
2684
|
+
}
|
|
2685
|
+
const modulesPath = join$1(buildDir, "modules.ts");
|
|
2686
|
+
const moduleNames = extractModuleNames(config.modules);
|
|
2687
|
+
if (moduleNames.length > 0) {
|
|
2688
|
+
const imports = moduleNames.map((name) => `import "${name}";`).join("\n");
|
|
2689
|
+
writeFileSync(modulesPath, `/**
|
|
2690
|
+
* Module type declarations - Auto-generated by Kimesh
|
|
2691
|
+
*
|
|
2692
|
+
* DO NOT EDIT - This file is regenerated when modules change.
|
|
2693
|
+
*/
|
|
2694
|
+
|
|
2695
|
+
${moduleNames.map((name) => `/// <reference path="../node_modules/${name}/augment.d.ts" />`).join("\n")}
|
|
2696
|
+
|
|
2697
|
+
${imports}
|
|
2698
|
+
`, "utf-8");
|
|
2699
|
+
generatedFiles.push("modules.ts");
|
|
2700
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/modules.ts`);
|
|
2701
|
+
}
|
|
2702
|
+
if (existsSync(join$1(root, "src", "app.context.ts"))) {
|
|
2703
|
+
writeFileSync(join$1(buildDir, "context.d.ts"), `/**
|
|
2704
|
+
* Kimesh Context Types - Auto-generated
|
|
2705
|
+
*
|
|
2706
|
+
* DO NOT EDIT - This file is regenerated automatically.
|
|
2707
|
+
*/
|
|
2708
|
+
|
|
2709
|
+
import type { AppContext as UserAppContext } from '../src/app.context'
|
|
2710
|
+
|
|
2711
|
+
declare module '@kimesh/router-runtime' {
|
|
2712
|
+
interface AppContext extends UserAppContext {}
|
|
2713
|
+
}
|
|
2714
|
+
`, "utf-8");
|
|
2715
|
+
generatedFiles.push("context.d.ts");
|
|
2716
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/context.d.ts`);
|
|
2717
|
+
}
|
|
2718
|
+
const autoImportsPath = join$1(buildDir, "auto-imports.d.ts");
|
|
2719
|
+
if (!existsSync(autoImportsPath)) {
|
|
2720
|
+
writeFileSync(autoImportsPath, `/**
|
|
2721
|
+
* Kimesh Auto-Imports - Auto-generated
|
|
2722
|
+
*
|
|
2723
|
+
* This file is a stub generated by 'km prepare'.
|
|
2724
|
+
* It will be populated with actual imports when running 'km dev' or 'km build'.
|
|
2725
|
+
*
|
|
2726
|
+
* DO NOT EDIT - This file is regenerated automatically.
|
|
2727
|
+
*/
|
|
2728
|
+
|
|
2729
|
+
export {}
|
|
2730
|
+
|
|
2731
|
+
declare global {
|
|
2732
|
+
// Vue imports will be auto-detected
|
|
2733
|
+
const ref: typeof import('vue')['ref']
|
|
2734
|
+
const reactive: typeof import('vue')['reactive']
|
|
2735
|
+
const computed: typeof import('vue')['computed']
|
|
2736
|
+
const watch: typeof import('vue')['watch']
|
|
2737
|
+
const watchEffect: typeof import('vue')['watchEffect']
|
|
2738
|
+
const onMounted: typeof import('vue')['onMounted']
|
|
2739
|
+
const onUnmounted: typeof import('vue')['onUnmounted']
|
|
2740
|
+
const defineProps: typeof import('vue')['defineProps']
|
|
2741
|
+
const defineEmits: typeof import('vue')['defineEmits']
|
|
2742
|
+
const withDefaults: typeof import('vue')['withDefaults']
|
|
2743
|
+
|
|
2744
|
+
// Vue Router imports
|
|
2745
|
+
const useRoute: typeof import('vue-router')['useRoute']
|
|
2746
|
+
const useRouter: typeof import('vue-router')['useRouter']
|
|
2747
|
+
}
|
|
2748
|
+
`, "utf-8");
|
|
2749
|
+
generatedFiles.push("auto-imports.d.ts");
|
|
2750
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/auto-imports.d.ts (stub)`);
|
|
2751
|
+
}
|
|
2752
|
+
const componentsPath = join$1(buildDir, "components.d.ts");
|
|
2753
|
+
if (!existsSync(componentsPath)) {
|
|
2754
|
+
writeFileSync(componentsPath, `/**
|
|
2755
|
+
* Kimesh Components - Auto-generated
|
|
2756
|
+
*
|
|
2757
|
+
* This file is a stub generated by 'km prepare'.
|
|
2758
|
+
* It will be populated with actual components when running 'km dev' or 'km build'.
|
|
2759
|
+
*
|
|
2760
|
+
* DO NOT EDIT - This file is regenerated automatically.
|
|
2761
|
+
*/
|
|
2762
|
+
|
|
2763
|
+
export {}
|
|
2764
|
+
|
|
2765
|
+
declare module 'vue' {
|
|
2766
|
+
export interface GlobalComponents {
|
|
2767
|
+
// Component types will be populated by auto-import
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
`, "utf-8");
|
|
2771
|
+
generatedFiles.push("components.d.ts");
|
|
2772
|
+
if (verbose) consola.success(`[Kimesh] Generated .kimesh/components.d.ts (stub)`);
|
|
2773
|
+
}
|
|
2774
|
+
const aliasCount = Object.keys(userAliases).length + Object.keys(layerAliases).length + Object.keys(internalAliases).length;
|
|
2775
|
+
return {
|
|
2776
|
+
buildDir,
|
|
2777
|
+
layerCount: resolvedLayers.length,
|
|
2778
|
+
aliasCount,
|
|
2779
|
+
generatedFiles
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Extract module names from config (filters out inline module objects)
|
|
2784
|
+
*/
|
|
2785
|
+
function extractModuleNames(modules) {
|
|
2786
|
+
if (!modules) return [];
|
|
2787
|
+
const names = [];
|
|
2788
|
+
for (const mod of modules) if (typeof mod === "string") names.push(mod);
|
|
2789
|
+
else if (Array.isArray(mod) && typeof mod[0] === "string") names.push(mod[0]);
|
|
2790
|
+
return names;
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
//#endregion
|
|
2794
|
+
export { DEFAULT_ALIASES, DEFAULT_IGNORE_PATTERNS, addAlias, addBuildPlugin, addComponent, addComponentResolver, addComponentsDir, addImports, addImportsDir, addImportsPreset, addPluginsTemplate, addRuntimePlugin, addTemplate, addTypeTemplate, addVitePlugin, applyEnv, buildAliases, buildImportRegistry, createDebugLogger, createDefaultRuntimeConfig, createHMRWatcher, createIgnoreFilter, createIgnoreMatcher, createKimesh, createResolver, createTimer, debug, debugTable, defineKimeshModule, defineKimeshPlugin, defineKmConfig, envToKey, executeModule, executeModules, filterIgnored, findMatchingRules, formatConflictWarning, formatError, formatTiming, formatWarning, generateDts, generateLayerAliases, generatePluginsTemplate, generateRouteRulesManifest, getIgnorePatterns, getRedirectInfo, getRouteRule, getRuntimePlugins, hasPlugins, hasRuntimePlugin, isDebug, isDebugEnabled, keyToEnv, kimeshAutoImport, kimeshPlugin, loadConfig, matchRoutePattern, mergeLayerConfigs, mergeRouteRules, normalizeDebugConfig, normalizeModuleInput, prepare, prepareLayers, removeRuntimePlugin, resolveAlias, resolveAliasPath, resolveLayers, resolvePathFromBuild, resolvePathFromRoot, scanExports, scanPluginsDir, setDebugConfig, shouldIgnore, toTsConfigPaths, toViteAliases, tryUseKimesh, updateTemplates, useKimesh, writeTemplates };
|
|
2795
|
+
//# sourceMappingURL=index.mjs.map
|