@4399ywkf/core 5.0.2 → 5.0.6

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.
@@ -1,7 +1,7 @@
1
- import { e as YwkfPlugin, b as PluginHooks, Y as YwkfConfig, c as PluginConfig, k as RouteItem } from '../schema-BuqmN_ra.js';
2
- export { B as BuildHooks, C as CodeInjection, n as DefinePlugin, j as GeneratedFile, G as GeneratorContext, i as GeneratorHooks, m as PluginAPI, f as PluginContext, l as PluginLogger, g as RouterHooks, h as RuntimeHooks } from '../schema-BuqmN_ra.js';
1
+ import { e as YwkfPlugin, b as PluginHooks, Y as YwkfConfig, c as PluginConfig, k as RouteItem } from '../schema-VPH72NAR.js';
2
+ export { B as BuildHooks, C as CodeInjection, n as DefinePlugin, j as GeneratedFile, G as GeneratorContext, i as GeneratorHooks, m as PluginAPI, f as PluginContext, l as PluginLogger, g as RouterHooks, h as RuntimeHooks } from '../schema-VPH72NAR.js';
3
3
  import { Configuration } from '@rspack/core';
4
- import { A as AppConfig, P as ProviderConfig } from '../types-BZV_2QtD.js';
4
+ import { A as AppConfig, P as ProviderConfig } from '../types-BztKUufh.js';
5
5
  import 'react';
6
6
  import 'react-router';
7
7
 
@@ -28,14 +28,15 @@ function composeHooks(...hooksList) {
28
28
  // src/plugin/manager.ts
29
29
  function createLogger(pluginName) {
30
30
  const prefix = `[${pluginName}]`;
31
+ const verbose = !!process.env.DEBUG;
31
32
  return {
32
- info: (msg) => console.log(`${prefix} ${msg}`),
33
+ info: (msg) => {
34
+ if (verbose) console.log(`${prefix} ${msg}`);
35
+ },
33
36
  warn: (msg) => console.warn(`${prefix} ${msg}`),
34
37
  error: (msg) => console.error(`${prefix} ${msg}`),
35
38
  debug: (msg) => {
36
- if (process.env.DEBUG) {
37
- console.debug(`${prefix} ${msg}`);
38
- }
39
+ if (verbose) console.debug(`${prefix} ${msg}`);
39
40
  }
40
41
  };
41
42
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugin/define.ts","../../src/plugin/manager.ts"],"sourcesContent":["import type { YwkfPlugin, PluginHooks, PluginContext } from \"./types.js\";\n\n/**\n * 定义插件的辅助函数\n * \n * @example\n * ```ts\n * export default definePlugin({\n * name: \"my-plugin\",\n * setup(context) {\n * return {\n * modifyRspackConfig(config) {\n * // 修改配置\n * return config;\n * },\n * };\n * },\n * });\n * ```\n */\nexport function definePlugin(plugin: YwkfPlugin): YwkfPlugin {\n return plugin;\n}\n\n/**\n * 创建带配置的插件工厂\n * \n * @example\n * ```ts\n * export default createPlugin<{ apiUrl: string }>((options) => ({\n * name: \"api-plugin\",\n * setup(context) {\n * console.log(\"API URL:\", options?.apiUrl);\n * return {};\n * },\n * }));\n * ```\n */\nexport function createPlugin<T = Record<string, unknown>>(\n factory: (options?: T) => YwkfPlugin\n): (options?: T) => YwkfPlugin {\n return factory;\n}\n\n/**\n * 组合多个插件钩子\n */\nexport function composeHooks(...hooksList: PluginHooks[]): PluginHooks {\n const composed: Record<string, Function> = {};\n\n for (const hooks of hooksList) {\n for (const [key, value] of Object.entries(hooks)) {\n if (value) {\n const existing = composed[key];\n if (existing) {\n composed[key] = async (...args: unknown[]) => {\n await existing(...args);\n return (value as Function)(...args);\n };\n } else {\n composed[key] = value as Function;\n }\n }\n }\n }\n\n return composed as PluginHooks;\n}\n","import type { Configuration as RspackConfig } from \"@rspack/core\";\nimport type { YwkfConfig } from \"../config/schema.js\";\nimport type { AppConfig, ProviderConfig } from \"../runtime/types.js\";\nimport type { RouteItem } from \"../router/types.js\";\nimport type {\n YwkfPlugin,\n PluginConfig,\n PluginContext,\n PluginHooks,\n PluginLogger,\n} from \"./types.js\";\n\n/**\n * 创建插件日志工具\n */\nfunction createLogger(pluginName: string): PluginLogger {\n const prefix = `[${pluginName}]`;\n\n return {\n info: (msg) => console.log(`${prefix} ${msg}`),\n warn: (msg) => console.warn(`${prefix} ${msg}`),\n error: (msg) => console.error(`${prefix} ${msg}`),\n debug: (msg) => {\n if (process.env.DEBUG) {\n console.debug(`${prefix} ${msg}`);\n }\n },\n };\n}\n\n/**\n * 解析插件配置\n */\nasync function resolvePlugin(\n pluginConfig: PluginConfig,\n cwd: string\n): Promise<{ plugin: YwkfPlugin; options: Record<string, unknown> }> {\n let plugin: YwkfPlugin;\n let options: Record<string, unknown> = {};\n\n if (typeof pluginConfig === \"string\") {\n // 字符串:插件包名\n const module = await import(pluginConfig);\n plugin = module.default || module;\n } else if (Array.isArray(pluginConfig)) {\n // 数组:[插件, 配置]\n const [pluginOrName, pluginOptions] = pluginConfig;\n options = pluginOptions;\n\n if (typeof pluginOrName === \"string\") {\n const module = await import(pluginOrName);\n plugin = module.default || module;\n } else {\n plugin = pluginOrName;\n }\n } else {\n // 插件对象\n plugin = pluginConfig;\n }\n\n // 如果是工厂函数,调用它\n if (typeof plugin === \"function\") {\n plugin = (plugin as (opts?: Record<string, unknown>) => YwkfPlugin)(options);\n }\n\n return { plugin, options };\n}\n\n/**\n * 插件管理器\n */\nexport class PluginManager {\n private plugins: Map<string, { plugin: YwkfPlugin; hooks: PluginHooks }> =\n new Map();\n private context: PluginContext;\n\n constructor(\n config: Required<YwkfConfig>,\n cwd: string,\n isDev: boolean\n ) {\n this.context = {\n cwd,\n isDev,\n isProd: !isDev,\n config,\n logger: createLogger(\"PluginManager\"),\n };\n }\n\n /**\n * 加载并初始化所有插件\n */\n async loadPlugins(pluginConfigs: PluginConfig[]): Promise<void> {\n for (const pluginConfig of pluginConfigs) {\n try {\n const { plugin } = await resolvePlugin(pluginConfig, this.context.cwd);\n\n if (this.plugins.has(plugin.name)) {\n this.context.logger.warn(`插件 ${plugin.name} 已加载,跳过重复加载`);\n continue;\n }\n\n // 创建插件专属 logger\n const pluginContext: PluginContext = {\n ...this.context,\n logger: createLogger(plugin.name),\n };\n\n // 调用插件 setup\n const hooks = await plugin.setup(pluginContext);\n\n this.plugins.set(plugin.name, { plugin, hooks });\n this.context.logger.info(`已加载插件: ${plugin.name}`);\n } catch (error) {\n this.context.logger.error(\n `加载插件失败: ${String(pluginConfig)} - ${error}`\n );\n }\n }\n }\n\n /**\n * 执行 modifyRspackConfig 钩子\n */\n async applyRspackConfigHooks(config: RspackConfig): Promise<RspackConfig> {\n let result = config;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyRspackConfig) {\n try {\n const modified = await hooks.modifyRspackConfig(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyRspackConfig 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 执行 modifyRoutes 钩子\n */\n async applyRoutesHooks(routes: RouteItem[]): Promise<RouteItem[]> {\n let result = routes;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyRoutes) {\n try {\n const modified = await hooks.modifyRoutes(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyRoutes 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 执行 modifyAppConfig 钩子\n */\n applyAppConfigHooks(appConfig: AppConfig): AppConfig {\n let result = appConfig;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyAppConfig) {\n try {\n const modified = hooks.modifyAppConfig(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyAppConfig 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 收集所有插件的 Provider\n */\n collectProviders(): ProviderConfig[] {\n const providers: ProviderConfig[] = [];\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.addProvider) {\n try {\n const result = hooks.addProvider(this.context);\n if (Array.isArray(result)) {\n providers.push(...result);\n } else if (result) {\n providers.push(result);\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} addProvider 执行失败: ${error}`\n );\n }\n }\n }\n\n return providers;\n }\n\n /**\n * 执行 beforeBuild 钩子\n */\n async runBeforeBuild(): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.beforeBuild) {\n try {\n await hooks.beforeBuild(this.context);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} beforeBuild 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 afterBuild 钩子\n */\n async runAfterBuild(stats: {\n success: boolean;\n errors?: string[];\n }): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.afterBuild) {\n try {\n await hooks.afterBuild(this.context, stats);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} afterBuild 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 beforeDevServer 钩子\n */\n async runBeforeDevServer(): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.beforeDevServer) {\n try {\n await hooks.beforeDevServer(this.context);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} beforeDevServer 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 afterDevServer 钩子\n */\n async runAfterDevServer(server: {\n port: number;\n host: string;\n }): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.afterDevServer) {\n try {\n await hooks.afterDevServer(this.context, server);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} afterDevServer 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 获取所有已加载的插件名称\n */\n getPluginNames(): string[] {\n return Array.from(this.plugins.keys());\n }\n\n /**\n * 获取所有已加载的插件\n */\n getPlugins(): YwkfPlugin[] {\n return Array.from(this.plugins.values()).map((p) => p.plugin);\n }\n\n /**\n * 获取所有已加载的插件钩子\n */\n getPluginHooks(): PluginHooks[] {\n return Array.from(this.plugins.values()).map((p) => p.hooks);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/plugin/define.ts","../../src/plugin/manager.ts"],"sourcesContent":["import type { YwkfPlugin, PluginHooks, PluginContext } from \"./types.js\";\n\n/**\n * 定义插件的辅助函数\n * \n * @example\n * ```ts\n * export default definePlugin({\n * name: \"my-plugin\",\n * setup(context) {\n * return {\n * modifyRspackConfig(config) {\n * // 修改配置\n * return config;\n * },\n * };\n * },\n * });\n * ```\n */\nexport function definePlugin(plugin: YwkfPlugin): YwkfPlugin {\n return plugin;\n}\n\n/**\n * 创建带配置的插件工厂\n * \n * @example\n * ```ts\n * export default createPlugin<{ apiUrl: string }>((options) => ({\n * name: \"api-plugin\",\n * setup(context) {\n * console.log(\"API URL:\", options?.apiUrl);\n * return {};\n * },\n * }));\n * ```\n */\nexport function createPlugin<T = Record<string, unknown>>(\n factory: (options?: T) => YwkfPlugin\n): (options?: T) => YwkfPlugin {\n return factory;\n}\n\n/**\n * 组合多个插件钩子\n */\nexport function composeHooks(...hooksList: PluginHooks[]): PluginHooks {\n const composed: Record<string, Function> = {};\n\n for (const hooks of hooksList) {\n for (const [key, value] of Object.entries(hooks)) {\n if (value) {\n const existing = composed[key];\n if (existing) {\n composed[key] = async (...args: unknown[]) => {\n await existing(...args);\n return (value as Function)(...args);\n };\n } else {\n composed[key] = value as Function;\n }\n }\n }\n }\n\n return composed as PluginHooks;\n}\n","import type { Configuration as RspackConfig } from \"@rspack/core\";\nimport type { YwkfConfig } from \"../config/schema.js\";\nimport type { AppConfig, ProviderConfig } from \"../runtime/types.js\";\nimport type { RouteItem } from \"../router/types.js\";\nimport type {\n YwkfPlugin,\n PluginConfig,\n PluginContext,\n PluginHooks,\n PluginLogger,\n} from \"./types.js\";\n\n/**\n * 创建插件日志工具\n */\nfunction createLogger(pluginName: string): PluginLogger {\n const prefix = `[${pluginName}]`;\n const verbose = !!process.env.DEBUG;\n\n return {\n info: (msg) => {\n if (verbose) console.log(`${prefix} ${msg}`);\n },\n warn: (msg) => console.warn(`${prefix} ${msg}`),\n error: (msg) => console.error(`${prefix} ${msg}`),\n debug: (msg) => {\n if (verbose) console.debug(`${prefix} ${msg}`);\n },\n };\n}\n\n/**\n * 解析插件配置\n */\nasync function resolvePlugin(\n pluginConfig: PluginConfig,\n cwd: string\n): Promise<{ plugin: YwkfPlugin; options: Record<string, unknown> }> {\n let plugin: YwkfPlugin;\n let options: Record<string, unknown> = {};\n\n if (typeof pluginConfig === \"string\") {\n // 字符串:插件包名\n const module = await import(pluginConfig);\n plugin = module.default || module;\n } else if (Array.isArray(pluginConfig)) {\n // 数组:[插件, 配置]\n const [pluginOrName, pluginOptions] = pluginConfig;\n options = pluginOptions;\n\n if (typeof pluginOrName === \"string\") {\n const module = await import(pluginOrName);\n plugin = module.default || module;\n } else {\n plugin = pluginOrName;\n }\n } else {\n // 插件对象\n plugin = pluginConfig;\n }\n\n // 如果是工厂函数,调用它\n if (typeof plugin === \"function\") {\n plugin = (plugin as (opts?: Record<string, unknown>) => YwkfPlugin)(options);\n }\n\n return { plugin, options };\n}\n\n/**\n * 插件管理器\n */\nexport class PluginManager {\n private plugins: Map<string, { plugin: YwkfPlugin; hooks: PluginHooks }> =\n new Map();\n private context: PluginContext;\n\n constructor(\n config: Required<YwkfConfig>,\n cwd: string,\n isDev: boolean\n ) {\n this.context = {\n cwd,\n isDev,\n isProd: !isDev,\n config,\n logger: createLogger(\"PluginManager\"),\n };\n }\n\n /**\n * 加载并初始化所有插件\n */\n async loadPlugins(pluginConfigs: PluginConfig[]): Promise<void> {\n for (const pluginConfig of pluginConfigs) {\n try {\n const { plugin } = await resolvePlugin(pluginConfig, this.context.cwd);\n\n if (this.plugins.has(plugin.name)) {\n this.context.logger.warn(`插件 ${plugin.name} 已加载,跳过重复加载`);\n continue;\n }\n\n // 创建插件专属 logger\n const pluginContext: PluginContext = {\n ...this.context,\n logger: createLogger(plugin.name),\n };\n\n // 调用插件 setup\n const hooks = await plugin.setup(pluginContext);\n\n this.plugins.set(plugin.name, { plugin, hooks });\n this.context.logger.info(`已加载插件: ${plugin.name}`);\n } catch (error) {\n this.context.logger.error(\n `加载插件失败: ${String(pluginConfig)} - ${error}`\n );\n }\n }\n }\n\n /**\n * 执行 modifyRspackConfig 钩子\n */\n async applyRspackConfigHooks(config: RspackConfig): Promise<RspackConfig> {\n let result = config;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyRspackConfig) {\n try {\n const modified = await hooks.modifyRspackConfig(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyRspackConfig 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 执行 modifyRoutes 钩子\n */\n async applyRoutesHooks(routes: RouteItem[]): Promise<RouteItem[]> {\n let result = routes;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyRoutes) {\n try {\n const modified = await hooks.modifyRoutes(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyRoutes 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 执行 modifyAppConfig 钩子\n */\n applyAppConfigHooks(appConfig: AppConfig): AppConfig {\n let result = appConfig;\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.modifyAppConfig) {\n try {\n const modified = hooks.modifyAppConfig(result, this.context);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} modifyAppConfig 执行失败: ${error}`\n );\n }\n }\n }\n\n return result;\n }\n\n /**\n * 收集所有插件的 Provider\n */\n collectProviders(): ProviderConfig[] {\n const providers: ProviderConfig[] = [];\n\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.addProvider) {\n try {\n const result = hooks.addProvider(this.context);\n if (Array.isArray(result)) {\n providers.push(...result);\n } else if (result) {\n providers.push(result);\n }\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} addProvider 执行失败: ${error}`\n );\n }\n }\n }\n\n return providers;\n }\n\n /**\n * 执行 beforeBuild 钩子\n */\n async runBeforeBuild(): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.beforeBuild) {\n try {\n await hooks.beforeBuild(this.context);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} beforeBuild 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 afterBuild 钩子\n */\n async runAfterBuild(stats: {\n success: boolean;\n errors?: string[];\n }): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.afterBuild) {\n try {\n await hooks.afterBuild(this.context, stats);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} afterBuild 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 beforeDevServer 钩子\n */\n async runBeforeDevServer(): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.beforeDevServer) {\n try {\n await hooks.beforeDevServer(this.context);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} beforeDevServer 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 执行 afterDevServer 钩子\n */\n async runAfterDevServer(server: {\n port: number;\n host: string;\n }): Promise<void> {\n for (const [name, { hooks }] of this.plugins) {\n if (hooks.afterDevServer) {\n try {\n await hooks.afterDevServer(this.context, server);\n } catch (error) {\n this.context.logger.error(\n `插件 ${name} afterDevServer 执行失败: ${error}`\n );\n }\n }\n }\n }\n\n /**\n * 获取所有已加载的插件名称\n */\n getPluginNames(): string[] {\n return Array.from(this.plugins.keys());\n }\n\n /**\n * 获取所有已加载的插件\n */\n getPlugins(): YwkfPlugin[] {\n return Array.from(this.plugins.values()).map((p) => p.plugin);\n }\n\n /**\n * 获取所有已加载的插件钩子\n */\n getPluginHooks(): PluginHooks[] {\n return Array.from(this.plugins.values()).map((p) => p.hooks);\n }\n}\n"],"mappings":"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","names":[]}
@@ -292,7 +292,7 @@ export default routes;
292
292
  mkdirSync(outputDir, { recursive: true });
293
293
  }
294
294
  writeFileSync(join(outputDir, "routes.tsx"), code, "utf-8");
295
- console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
295
+ if (process.env.DEBUG) console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
296
296
  }
297
297
  };
298
298
  function generateConventionalRoutes(options) {
@@ -372,7 +372,7 @@ var ConventionalRoutePlugin = class {
372
372
  clearTimeout(debounceTimer);
373
373
  }
374
374
  debounceTimer = setTimeout(() => {
375
- console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
375
+ if (process.env.DEBUG) console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
376
376
  this.generateRoutes();
377
377
  }, 500);
378
378
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/router/generator.ts","../../src/router/plugin.ts"],"sourcesContent":["import { existsSync, readdirSync, statSync, mkdirSync, writeFileSync } from \"fs\";\nimport { join, relative, basename } from \"path\";\n\n/**\n * 路由节点配置\n */\nexport interface RouteConfig {\n /** 路由路径 */\n path: string;\n /** 页面文件(page.tsx)相对路径 */\n file?: string;\n /** 布局文件(layout.tsx)相对路径 */\n layoutFile?: string;\n /** 错误边界文件(error.tsx)相对路径 */\n errorFile?: string;\n /** 加载状态文件(loading.tsx)相对路径 */\n loadingFile?: string;\n /** 通配路由文件($.tsx)相对路径 */\n catchAllFile?: string;\n /** 组件名称 */\n name: string;\n /** 子路由 */\n children?: RouteConfig[];\n /** 是否为 index 路由 */\n index?: boolean;\n /** 是否为布局路由 */\n isLayout?: boolean;\n /** 是否为无路径布局(__prefix) */\n pathless?: boolean;\n}\n\nexport interface GeneratorOptions {\n /** 页面目录 */\n pagesDir: string;\n /** 输出目录 */\n outputDir: string;\n /** 路由 basename */\n basename?: string;\n}\n\n/**\n * 不参与路由扫描的目录名\n */\nconst EXCLUDED_DIRS = new Set([\n \"components\",\n \"hooks\",\n \"utils\",\n \"services\",\n \"models\",\n \"assets\",\n \"types\",\n \"constants\",\n \"styles\",\n]);\n\n/**\n * 约定文件名匹配\n */\nconst CONVENTION_FILES = {\n page: /^page\\.(tsx?|jsx?)$/,\n layout: /^layout\\.(tsx?|jsx?)$/,\n error: /^error\\.(tsx?|jsx?)$/,\n loading: /^loading\\.(tsx?|jsx?)$/,\n catchAll: /^\\$\\.(tsx?|jsx?)$/,\n};\n\n/**\n * Modern.js 风格约定式路由生成器\n *\n * 文件约定:\n * - page.tsx — 页面内容(叶子组件)\n * - layout.tsx — 布局组件(使用 <Outlet> 渲染子路由)\n * - error.tsx — 错误边界组件\n * - loading.tsx — 加载状态组件\n * - $.tsx — 通配/404 路由\n *\n * 目录约定:\n * - [id]/ — 动态路由 → /:id\n * - [id$]/ — 可选动态路由 → /:id?\n * - [...slug]/ — 全匹配路由 → /*\n * - __auth/ — 无路径布局(不生成 URL 片段)\n * - user.profile/ — 扁平路由(. 分隔 → /user/profile)\n */\nexport class ConventionalRouteGenerator {\n private options: GeneratorOptions;\n\n constructor(options: GeneratorOptions) {\n this.options = options;\n }\n\n generate(): RouteConfig[] {\n const { pagesDir } = this.options;\n if (!existsSync(pagesDir)) {\n console.warn(`[ywkf] 页面目录不存在: ${pagesDir}`);\n return [];\n }\n return this.scanDirectory(pagesDir, \"/\");\n }\n\n /**\n * 扫描目录,生成路由树\n */\n private scanDirectory(dir: string, routePath: string): RouteConfig[] {\n const entries = readdirSync(dir);\n\n // 查找约定文件\n const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));\n const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));\n const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));\n const loadingFile = entries.find((e) => CONVENTION_FILES.loading.test(e));\n const catchAllFile = entries.find((e) => CONVENTION_FILES.catchAll.test(e));\n\n // 收集子目录\n const subDirs = entries.filter((e) => {\n if (e.startsWith(\".\")) return false;\n if (EXCLUDED_DIRS.has(e)) return false;\n return statSync(join(dir, e)).isDirectory();\n });\n\n // 递归子目录\n const childRoutes: RouteConfig[] = [];\n for (const subDir of subDirs) {\n const subDirPath = join(dir, subDir);\n\n // __prefix:无路径布局\n if (subDir.startsWith(\"__\")) {\n const pathlessRoutes = this.scanDirectory(subDirPath, routePath);\n // 如果该无路径目录有 layout,将子路由包装在其中\n const pathlessLayout = readdirSync(subDirPath).find((e) =>\n CONVENTION_FILES.layout.test(e)\n );\n if (pathlessLayout) {\n const pathlessChildren = this.scanDirectory(subDirPath, routePath);\n childRoutes.push({\n path: routePath,\n name: this.generateRouteName(subDir.slice(2)),\n layoutFile: relative(this.options.pagesDir, join(subDirPath, pathlessLayout)),\n isLayout: true,\n pathless: true,\n children: pathlessChildren.filter(\n (r) => r.layoutFile !== relative(this.options.pagesDir, join(subDirPath, pathlessLayout))\n ),\n });\n } else {\n childRoutes.push(...pathlessRoutes);\n }\n continue;\n }\n\n // 计算路由路径\n const subRoutePath = this.resolveRoutePath(subDir, routePath);\n childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));\n }\n\n // 构建当前目录的路由节点\n const routeName = this.generateRouteName(routePath);\n const relPath = (file: string) => relative(this.options.pagesDir, join(dir, file));\n\n // 有 layout.tsx → 创建布局路由节点,children 为子路由\n if (layoutFile) {\n const layoutChildren: RouteConfig[] = [];\n\n // page.tsx 作为 index 子路由\n if (pageFile) {\n layoutChildren.push({\n path: routePath,\n file: relPath(pageFile),\n name: routeName + \"Index\",\n index: true,\n });\n }\n\n // 通配路由\n if (catchAllFile) {\n layoutChildren.push({\n path: \"*\",\n file: relPath(catchAllFile),\n name: routeName + \"CatchAll\",\n });\n }\n\n layoutChildren.push(...childRoutes);\n\n return [\n {\n path: routePath,\n layoutFile: relPath(layoutFile),\n errorFile: errorFile ? relPath(errorFile) : undefined,\n loadingFile: loadingFile ? relPath(loadingFile) : undefined,\n name: routeName,\n isLayout: true,\n children: layoutChildren,\n },\n ];\n }\n\n // 无 layout → page.tsx 是叶子页面\n const result: RouteConfig[] = [];\n\n if (pageFile) {\n result.push({\n path: routePath,\n file: relPath(pageFile),\n errorFile: errorFile ? relPath(errorFile) : undefined,\n name: routeName,\n });\n }\n\n if (catchAllFile) {\n result.push({\n path: \"*\",\n file: relPath(catchAllFile),\n name: routeName + \"CatchAll\",\n });\n }\n\n result.push(...childRoutes);\n return result;\n }\n\n /**\n * 解析目录名到路由路径\n *\n * - [id] → :id\n * - [id$] → :id?\n * - [...slug] → *\n * - user.profile → user/profile\n */\n private resolveRoutePath(dirName: string, parentPath: string): string {\n let segment: string;\n\n // [...slug] → *\n if (dirName.match(/^\\[\\.\\.\\.(.+)\\]$/)) {\n segment = \"*\";\n }\n // [id$] → :id? (可选动态)\n else if (dirName.match(/^\\[(.+)\\$\\]$/)) {\n const param = dirName.slice(1, -2);\n segment = `:${param}?`;\n }\n // [id] → :id\n else if (dirName.match(/^\\[(.+)\\]$/)) {\n const param = dirName.slice(1, -1);\n segment = `:${param}`;\n }\n // user.profile → user/profile (扁平路由)\n else if (dirName.includes(\".\")) {\n segment = dirName.replace(/\\./g, \"/\");\n } else {\n segment = dirName;\n }\n\n return parentPath === \"/\"\n ? `/${segment}`\n : `${parentPath}/${segment}`;\n }\n\n /**\n * 生成有效的 JS 标识符名称\n */\n private generateRouteName(path: string): string {\n const name = path\n .split(\"/\")\n .filter(Boolean)\n .map((s) =>\n s\n .replace(/^:/, \"Param\")\n .replace(/\\?$/, \"Optional\")\n .replace(/^\\*$/, \"CatchAll\")\n )\n .map((s) => {\n const cleaned = s.replace(/[^a-zA-Z0-9]/g, \"\");\n if (!cleaned) return \"\";\n return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n })\n .join(\"\");\n\n if (!name || /^\\d/.test(name)) {\n return \"Page\" + (name || \"Root\");\n }\n return name;\n }\n\n // ===== 代码生成 =====\n\n generateCode(): string {\n const routes = this.generate();\n const lazyImports: string[] = [];\n const errorImports: string[] = [];\n const loadingImports: string[] = [];\n\n this.collectImports(routes, lazyImports, errorImports, loadingImports);\n\n return `// 此文件由 @4399ywkf/core 自动生成,请勿手动修改\n// Generated at: ${new Date().toISOString()}\n\nimport React, { lazy, Suspense } from \"react\";\nimport { createBrowserRouter, type RouteObject } from \"react-router\";\n\n// 懒加载页面组件\n${lazyImports.join(\"\\n\")}\n${errorImports.length > 0 ? \"\\n// 错误边界组件\\n\" + errorImports.join(\"\\n\") : \"\"}\n${loadingImports.length > 0 ? \"\\n// 加载状态组件\\n\" + loadingImports.join(\"\\n\") : \"\"}\n\n// 默认加载状态\nconst DefaultLoading = () => <div style={{ padding: 24, textAlign: \"center\" }}>加载中...</div>;\n\n// 懒加载包装\nfunction LazyRoute({\n Component,\n Loading = DefaultLoading,\n}: {\n Component: React.LazyExoticComponent<React.ComponentType<unknown>>;\n Loading?: React.ComponentType;\n}) {\n return (\n <Suspense fallback={<Loading />}>\n <Component />\n </Suspense>\n );\n}\n\n// 路由配置\nexport const routes: RouteObject[] = ${this.emitRouteArray(routes)};\n\n/**\n * 创建路由实例\n */\nexport function createRouter(basename?: string) {\n return createBrowserRouter(routes, { basename: basename || \"/\" });\n}\n\nexport default routes;\n`;\n }\n\n private collectImports(\n routes: RouteConfig[],\n lazyImports: string[],\n errorImports: string[],\n loadingImports: string[]\n ): void {\n for (const route of routes) {\n const name = route.name;\n const toImportPath = (f: string) => f.replace(/\\.(tsx?|jsx?)$/, \"\");\n\n if (route.isLayout && route.layoutFile) {\n lazyImports.push(\n `const ${name}Layout = lazy(() => import(\"@/pages/${toImportPath(route.layoutFile)}\"));`\n );\n }\n if (route.file) {\n lazyImports.push(\n `const ${name}Page = lazy(() => import(\"@/pages/${toImportPath(route.file)}\"));`\n );\n }\n if (route.errorFile) {\n errorImports.push(\n `const ${name}Error = lazy(() => import(\"@/pages/${toImportPath(route.errorFile)}\"));`\n );\n }\n if (route.loadingFile) {\n loadingImports.push(\n `const ${name}Loading = lazy(() => import(\"@/pages/${toImportPath(route.loadingFile)}\"));`\n );\n }\n\n if (route.children) {\n this.collectImports(route.children, lazyImports, errorImports, loadingImports);\n }\n }\n }\n\n private emitRouteArray(routes: RouteConfig[], parentPath?: string): string {\n const items = routes.map((r) => this.emitRouteObject(r, parentPath));\n return `[\\n ${items.join(\",\\n \")}\\n]`;\n }\n\n private emitRouteObject(route: RouteConfig, parentPath?: string): string {\n const parts: string[] = [];\n const name = route.name;\n\n // path\n if (route.index) {\n parts.push(\"index: true\");\n } else if (route.pathless) {\n // 无路径布局不输出 path\n } else if (route.path === \"*\") {\n parts.push(`path: \"*\"`);\n } else if (parentPath && route.path.startsWith(parentPath) && parentPath !== \"/\") {\n const rel = route.path.slice(parentPath.length + 1);\n parts.push(`path: \"${rel || \"\"}\"`);\n } else {\n parts.push(`path: \"${route.path}\"`);\n }\n\n // element\n if (route.isLayout && route.layoutFile) {\n const loadingProp = route.loadingFile ? ` Loading={${name}Loading}` : \"\";\n parts.push(`element: <LazyRoute Component={${name}Layout}${loadingProp} />`);\n } else if (route.file) {\n parts.push(`element: <LazyRoute Component={${name}Page} />`);\n }\n\n // errorElement\n if (route.errorFile) {\n parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);\n }\n\n // children\n if (route.children && route.children.length > 0) {\n const childParent = route.pathless ? parentPath : route.path;\n parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);\n }\n\n return `{\\n ${parts.join(\",\\n \")}\\n }`;\n }\n\n /**\n * 写入路由文件\n */\n write(): void {\n const { outputDir } = this.options;\n const code = this.generateCode();\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n writeFileSync(join(outputDir, \"routes.tsx\"), code, \"utf-8\");\n console.log(`[ywkf] 约定式路由已生成`);\n }\n}\n\n/**\n * 生成约定式路由\n */\nexport function generateConventionalRoutes(options: GeneratorOptions): void {\n new ConventionalRouteGenerator(options).write();\n}\n","import type { Compiler } from \"@rspack/core\";\nimport { ConventionalRouteGenerator } from \"./generator.js\";\nimport { join } from \"path\";\nimport { watch, existsSync, readFileSync } from \"fs\";\n\nexport interface ConventionalRoutePluginOptions {\n /** 页面目录 */\n pagesDir: string;\n /** 输出目录 */\n outputDir: string;\n /** 路由 basename */\n basename?: string;\n /** 是否监听文件变化(开发模式) */\n watch?: boolean;\n}\n\n/**\n * 约定式路由 Rspack 插件\n *\n * 在编译开始前扫描 src/pages 目录,生成 .ywkf/routes.tsx\n */\nexport class ConventionalRoutePlugin {\n private options: ConventionalRoutePluginOptions;\n private isWatching = false;\n private hasGenerated = false;\n private lastGeneratedContent = \"\";\n\n constructor(options: ConventionalRoutePluginOptions) {\n this.options = options;\n }\n\n apply(compiler: Compiler): void {\n const pluginName = \"ConventionalRoutePlugin\";\n\n // 只在首次编译时生成路由\n compiler.hooks.beforeCompile.tapAsync(pluginName, (params, callback) => {\n if (!this.hasGenerated) {\n this.generateRoutes();\n this.hasGenerated = true;\n }\n callback();\n });\n\n // 开发模式下监听文件变化\n if (this.options.watch && !this.isWatching) {\n this.watchPages();\n }\n }\n\n /**\n * 生成路由文件(带内容比对,避免不必要的重复生成)\n */\n private generateRoutes(): void {\n try {\n const generator = new ConventionalRouteGenerator({\n pagesDir: this.options.pagesDir,\n outputDir: this.options.outputDir,\n basename: this.options.basename,\n });\n\n const newContent = generator.generateCode();\n const outputPath = join(this.options.outputDir, \"routes.tsx\");\n\n // 比对内容,如果相同则跳过(去掉时间戳后比较)\n const normalizedNew = this.normalizeContent(newContent);\n const normalizedOld = this.normalizeContent(this.lastGeneratedContent);\n\n if (normalizedNew === normalizedOld) {\n return; // 内容相同,跳过生成\n }\n\n generator.write();\n this.lastGeneratedContent = newContent;\n } catch (error) {\n console.error(\"[ywkf] 生成路由失败:\", error);\n }\n }\n\n /**\n * 标准化内容(去掉时间戳等动态部分)\n */\n private normalizeContent(content: string): string {\n return content.replace(/\\/\\/ Generated at: .+/g, \"\");\n }\n\n /**\n * 监听页面目录变化\n */\n private watchPages(): void {\n if (!existsSync(this.options.pagesDir)) {\n return;\n }\n\n this.isWatching = true;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const watcher = watch(\n this.options.pagesDir,\n { recursive: true },\n (eventType, filename) => {\n // 只处理 tsx/jsx 文件\n if (!filename?.match(/\\.(tsx?|jsx?)$/)) {\n return;\n }\n\n // 防抖处理\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(() => {\n console.log(`[ywkf] 检测到页面变化: ${filename}`);\n this.generateRoutes();\n }, 500);\n }\n );\n\n // 进程退出时关闭监听\n process.on(\"exit\", () => {\n watcher.close();\n });\n }\n}\n"],"mappings":";AAAA,SAAS,YAAY,aAAa,UAAU,WAAW,qBAAqB;AAC5E,SAAS,MAAM,gBAA0B;AA0CzC,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,IAAM,mBAAmB;AAAA,EACvB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AACZ;AAmBO,IAAM,6BAAN,MAAiC;AAAA,EAC9B;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,WAA0B;AACxB,UAAM,EAAE,SAAS,IAAI,KAAK;AAC1B,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,cAAQ,KAAK,sDAAmB,QAAQ,EAAE;AAC1C,aAAO,CAAC;AAAA,IACV;AACA,WAAO,KAAK,cAAc,UAAU,GAAG;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,KAAa,WAAkC;AACnE,UAAM,UAAU,YAAY,GAAG;AAG/B,UAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,iBAAiB,OAAO,KAAK,CAAC,CAAC;AACtE,UAAM,WAAW,QAAQ,KAAK,CAAC,MAAM,iBAAiB,KAAK,KAAK,CAAC,CAAC;AAClE,UAAM,YAAY,QAAQ,KAAK,CAAC,MAAM,iBAAiB,MAAM,KAAK,CAAC,CAAC;AACpE,UAAM,cAAc,QAAQ,KAAK,CAAC,MAAM,iBAAiB,QAAQ,KAAK,CAAC,CAAC;AACxE,UAAM,eAAe,QAAQ,KAAK,CAAC,MAAM,iBAAiB,SAAS,KAAK,CAAC,CAAC;AAG1E,UAAM,UAAU,QAAQ,OAAO,CAAC,MAAM;AACpC,UAAI,EAAE,WAAW,GAAG,EAAG,QAAO;AAC9B,UAAI,cAAc,IAAI,CAAC,EAAG,QAAO;AACjC,aAAO,SAAS,KAAK,KAAK,CAAC,CAAC,EAAE,YAAY;AAAA,IAC5C,CAAC;AAGD,UAAM,cAA6B,CAAC;AACpC,eAAW,UAAU,SAAS;AAC5B,YAAM,aAAa,KAAK,KAAK,MAAM;AAGnC,UAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,cAAM,iBAAiB,KAAK,cAAc,YAAY,SAAS;AAE/D,cAAM,iBAAiB,YAAY,UAAU,EAAE;AAAA,UAAK,CAAC,MACnD,iBAAiB,OAAO,KAAK,CAAC;AAAA,QAChC;AACA,YAAI,gBAAgB;AAClB,gBAAM,mBAAmB,KAAK,cAAc,YAAY,SAAS;AACjE,sBAAY,KAAK;AAAA,YACf,MAAM;AAAA,YACN,MAAM,KAAK,kBAAkB,OAAO,MAAM,CAAC,CAAC;AAAA,YAC5C,YAAY,SAAS,KAAK,QAAQ,UAAU,KAAK,YAAY,cAAc,CAAC;AAAA,YAC5E,UAAU;AAAA,YACV,UAAU;AAAA,YACV,UAAU,iBAAiB;AAAA,cACzB,CAAC,MAAM,EAAE,eAAe,SAAS,KAAK,QAAQ,UAAU,KAAK,YAAY,cAAc,CAAC;AAAA,YAC1F;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,sBAAY,KAAK,GAAG,cAAc;AAAA,QACpC;AACA;AAAA,MACF;AAGA,YAAM,eAAe,KAAK,iBAAiB,QAAQ,SAAS;AAC5D,kBAAY,KAAK,GAAG,KAAK,cAAc,YAAY,YAAY,CAAC;AAAA,IAClE;AAGA,UAAM,YAAY,KAAK,kBAAkB,SAAS;AAClD,UAAM,UAAU,CAAC,SAAiB,SAAS,KAAK,QAAQ,UAAU,KAAK,KAAK,IAAI,CAAC;AAGjF,QAAI,YAAY;AACd,YAAM,iBAAgC,CAAC;AAGvC,UAAI,UAAU;AACZ,uBAAe,KAAK;AAAA,UAClB,MAAM;AAAA,UACN,MAAM,QAAQ,QAAQ;AAAA,UACtB,MAAM,YAAY;AAAA,UAClB,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAGA,UAAI,cAAc;AAChB,uBAAe,KAAK;AAAA,UAClB,MAAM;AAAA,UACN,MAAM,QAAQ,YAAY;AAAA,UAC1B,MAAM,YAAY;AAAA,QACpB,CAAC;AAAA,MACH;AAEA,qBAAe,KAAK,GAAG,WAAW;AAElC,aAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,YAAY,QAAQ,UAAU;AAAA,UAC9B,WAAW,YAAY,QAAQ,SAAS,IAAI;AAAA,UAC5C,aAAa,cAAc,QAAQ,WAAW,IAAI;AAAA,UAClD,MAAM;AAAA,UACN,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAGA,UAAM,SAAwB,CAAC;AAE/B,QAAI,UAAU;AACZ,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM,QAAQ,QAAQ;AAAA,QACtB,WAAW,YAAY,QAAQ,SAAS,IAAI;AAAA,QAC5C,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,QAAI,cAAc;AAChB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM,QAAQ,YAAY;AAAA,QAC1B,MAAM,YAAY;AAAA,MACpB,CAAC;AAAA,IACH;AAEA,WAAO,KAAK,GAAG,WAAW;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iBAAiB,SAAiB,YAA4B;AACpE,QAAI;AAGJ,QAAI,QAAQ,MAAM,kBAAkB,GAAG;AACrC,gBAAU;AAAA,IACZ,WAES,QAAQ,MAAM,cAAc,GAAG;AACtC,YAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE;AACjC,gBAAU,IAAI,KAAK;AAAA,IACrB,WAES,QAAQ,MAAM,YAAY,GAAG;AACpC,YAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE;AACjC,gBAAU,IAAI,KAAK;AAAA,IACrB,WAES,QAAQ,SAAS,GAAG,GAAG;AAC9B,gBAAU,QAAQ,QAAQ,OAAO,GAAG;AAAA,IACtC,OAAO;AACL,gBAAU;AAAA,IACZ;AAEA,WAAO,eAAe,MAClB,IAAI,OAAO,KACX,GAAG,UAAU,IAAI,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,MAAsB;AAC9C,UAAM,OAAO,KACV,MAAM,GAAG,EACT,OAAO,OAAO,EACd;AAAA,MAAI,CAAC,MACJ,EACG,QAAQ,MAAM,OAAO,EACrB,QAAQ,OAAO,UAAU,EACzB,QAAQ,QAAQ,UAAU;AAAA,IAC/B,EACC,IAAI,CAAC,MAAM;AACV,YAAM,UAAU,EAAE,QAAQ,iBAAiB,EAAE;AAC7C,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO,QAAQ,OAAO,CAAC,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC;AAAA,IAC1D,CAAC,EACA,KAAK,EAAE;AAEV,QAAI,CAAC,QAAQ,MAAM,KAAK,IAAI,GAAG;AAC7B,aAAO,UAAU,QAAQ;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,eAAuB;AACrB,UAAM,SAAS,KAAK,SAAS;AAC7B,UAAM,cAAwB,CAAC;AAC/B,UAAM,eAAyB,CAAC;AAChC,UAAM,iBAA2B,CAAC;AAElC,SAAK,eAAe,QAAQ,aAAa,cAAc,cAAc;AAErE,WAAO;AAAA,oBACQ,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzC,YAAY,KAAK,IAAI,CAAC;AAAA,EACtB,aAAa,SAAS,IAAI,gDAAkB,aAAa,KAAK,IAAI,IAAI,EAAE;AAAA,EACxE,eAAe,SAAS,IAAI,gDAAkB,eAAe,KAAK,IAAI,IAAI,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAqBvC,KAAK,eAAe,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhE;AAAA,EAEQ,eACN,QACA,aACA,cACA,gBACM;AACN,eAAW,SAAS,QAAQ;AAC1B,YAAM,OAAO,MAAM;AACnB,YAAM,eAAe,CAAC,MAAc,EAAE,QAAQ,kBAAkB,EAAE;AAElE,UAAI,MAAM,YAAY,MAAM,YAAY;AACtC,oBAAY;AAAA,UACV,SAAS,IAAI,uCAAuC,aAAa,MAAM,UAAU,CAAC;AAAA,QACpF;AAAA,MACF;AACA,UAAI,MAAM,MAAM;AACd,oBAAY;AAAA,UACV,SAAS,IAAI,qCAAqC,aAAa,MAAM,IAAI,CAAC;AAAA,QAC5E;AAAA,MACF;AACA,UAAI,MAAM,WAAW;AACnB,qBAAa;AAAA,UACX,SAAS,IAAI,sCAAsC,aAAa,MAAM,SAAS,CAAC;AAAA,QAClF;AAAA,MACF;AACA,UAAI,MAAM,aAAa;AACrB,uBAAe;AAAA,UACb,SAAS,IAAI,wCAAwC,aAAa,MAAM,WAAW,CAAC;AAAA,QACtF;AAAA,MACF;AAEA,UAAI,MAAM,UAAU;AAClB,aAAK,eAAe,MAAM,UAAU,aAAa,cAAc,cAAc;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAe,QAAuB,YAA6B;AACzE,UAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,KAAK,gBAAgB,GAAG,UAAU,CAAC;AACnE,WAAO;AAAA,IAAQ,MAAM,KAAK,OAAO,CAAC;AAAA;AAAA,EACpC;AAAA,EAEQ,gBAAgB,OAAoB,YAA6B;AACvE,UAAM,QAAkB,CAAC;AACzB,UAAM,OAAO,MAAM;AAGnB,QAAI,MAAM,OAAO;AACf,YAAM,KAAK,aAAa;AAAA,IAC1B,WAAW,MAAM,UAAU;AAAA,IAE3B,WAAW,MAAM,SAAS,KAAK;AAC7B,YAAM,KAAK,WAAW;AAAA,IACxB,WAAW,cAAc,MAAM,KAAK,WAAW,UAAU,KAAK,eAAe,KAAK;AAChF,YAAM,MAAM,MAAM,KAAK,MAAM,WAAW,SAAS,CAAC;AAClD,YAAM,KAAK,UAAU,OAAO,EAAE,GAAG;AAAA,IACnC,OAAO;AACL,YAAM,KAAK,UAAU,MAAM,IAAI,GAAG;AAAA,IACpC;AAGA,QAAI,MAAM,YAAY,MAAM,YAAY;AACtC,YAAM,cAAc,MAAM,cAAc,aAAa,IAAI,aAAa;AACtE,YAAM,KAAK,kCAAkC,IAAI,UAAU,WAAW,KAAK;AAAA,IAC7E,WAAW,MAAM,MAAM;AACrB,YAAM,KAAK,kCAAkC,IAAI,UAAU;AAAA,IAC7D;AAGA,QAAI,MAAM,WAAW;AACnB,YAAM,KAAK,uCAAuC,IAAI,WAAW;AAAA,IACnE;AAGA,QAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,YAAM,cAAc,MAAM,WAAW,aAAa,MAAM;AACxD,YAAM,KAAK,aAAa,KAAK,eAAe,MAAM,UAAU,WAAW,CAAC,EAAE;AAAA,IAC5E;AAEA,WAAO;AAAA,MAAU,MAAM,KAAK,SAAS,CAAC;AAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,UAAM,EAAE,UAAU,IAAI,KAAK;AAC3B,UAAM,OAAO,KAAK,aAAa;AAC/B,QAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,gBAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,IAC1C;AACA,kBAAc,KAAK,WAAW,YAAY,GAAG,MAAM,OAAO;AAC1D,YAAQ,IAAI,yDAAiB;AAAA,EAC/B;AACF;AAKO,SAAS,2BAA2B,SAAiC;AAC1E,MAAI,2BAA2B,OAAO,EAAE,MAAM;AAChD;;;ACnbA,SAAS,QAAAA,aAAY;AACrB,SAAS,OAAO,cAAAC,mBAAgC;AAkBzC,IAAM,0BAAN,MAA8B;AAAA,EAC3B;AAAA,EACA,aAAa;AAAA,EACb,eAAe;AAAA,EACf,uBAAuB;AAAA,EAE/B,YAAY,SAAyC;AACnD,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,UAA0B;AAC9B,UAAM,aAAa;AAGnB,aAAS,MAAM,cAAc,SAAS,YAAY,CAAC,QAAQ,aAAa;AACtE,UAAI,CAAC,KAAK,cAAc;AACtB,aAAK,eAAe;AACpB,aAAK,eAAe;AAAA,MACtB;AACA,eAAS;AAAA,IACX,CAAC;AAGD,QAAI,KAAK,QAAQ,SAAS,CAAC,KAAK,YAAY;AAC1C,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAuB;AAC7B,QAAI;AACF,YAAM,YAAY,IAAI,2BAA2B;AAAA,QAC/C,UAAU,KAAK,QAAQ;AAAA,QACvB,WAAW,KAAK,QAAQ;AAAA,QACxB,UAAU,KAAK,QAAQ;AAAA,MACzB,CAAC;AAED,YAAM,aAAa,UAAU,aAAa;AAC1C,YAAM,aAAaD,MAAK,KAAK,QAAQ,WAAW,YAAY;AAG5D,YAAM,gBAAgB,KAAK,iBAAiB,UAAU;AACtD,YAAM,gBAAgB,KAAK,iBAAiB,KAAK,oBAAoB;AAErE,UAAI,kBAAkB,eAAe;AACnC;AAAA,MACF;AAEA,gBAAU,MAAM;AAChB,WAAK,uBAAuB;AAAA,IAC9B,SAAS,OAAO;AACd,cAAQ,MAAM,gDAAkB,KAAK;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,SAAyB;AAChD,WAAO,QAAQ,QAAQ,0BAA0B,EAAE;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAmB;AACzB,QAAI,CAACC,YAAW,KAAK,QAAQ,QAAQ,GAAG;AACtC;AAAA,IACF;AAEA,SAAK,aAAa;AAClB,QAAI,gBAAsD;AAE1D,UAAM,UAAU;AAAA,MACd,KAAK,QAAQ;AAAA,MACb,EAAE,WAAW,KAAK;AAAA,MAClB,CAAC,WAAW,aAAa;AAEvB,YAAI,CAAC,UAAU,MAAM,gBAAgB,GAAG;AACtC;AAAA,QACF;AAGA,YAAI,eAAe;AACjB,uBAAa,aAAa;AAAA,QAC5B;AAEA,wBAAgB,WAAW,MAAM;AAC/B,kBAAQ,IAAI,sDAAmB,QAAQ,EAAE;AACzC,eAAK,eAAe;AAAA,QACtB,GAAG,GAAG;AAAA,MACR;AAAA,IACF;AAGA,YAAQ,GAAG,QAAQ,MAAM;AACvB,cAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH;AACF;","names":["join","existsSync"]}
1
+ {"version":3,"sources":["../../src/router/generator.ts","../../src/router/plugin.ts"],"sourcesContent":["import { existsSync, readdirSync, statSync, mkdirSync, writeFileSync } from \"fs\";\nimport { join, relative, basename } from \"path\";\n\n/**\n * 路由节点配置\n */\nexport interface RouteConfig {\n /** 路由路径 */\n path: string;\n /** 页面文件(page.tsx)相对路径 */\n file?: string;\n /** 布局文件(layout.tsx)相对路径 */\n layoutFile?: string;\n /** 错误边界文件(error.tsx)相对路径 */\n errorFile?: string;\n /** 加载状态文件(loading.tsx)相对路径 */\n loadingFile?: string;\n /** 通配路由文件($.tsx)相对路径 */\n catchAllFile?: string;\n /** 组件名称 */\n name: string;\n /** 子路由 */\n children?: RouteConfig[];\n /** 是否为 index 路由 */\n index?: boolean;\n /** 是否为布局路由 */\n isLayout?: boolean;\n /** 是否为无路径布局(__prefix) */\n pathless?: boolean;\n}\n\nexport interface GeneratorOptions {\n /** 页面目录 */\n pagesDir: string;\n /** 输出目录 */\n outputDir: string;\n /** 路由 basename */\n basename?: string;\n}\n\n/**\n * 不参与路由扫描的目录名\n */\nconst EXCLUDED_DIRS = new Set([\n \"components\",\n \"hooks\",\n \"utils\",\n \"services\",\n \"models\",\n \"assets\",\n \"types\",\n \"constants\",\n \"styles\",\n]);\n\n/**\n * 约定文件名匹配\n */\nconst CONVENTION_FILES = {\n page: /^page\\.(tsx?|jsx?)$/,\n layout: /^layout\\.(tsx?|jsx?)$/,\n error: /^error\\.(tsx?|jsx?)$/,\n loading: /^loading\\.(tsx?|jsx?)$/,\n catchAll: /^\\$\\.(tsx?|jsx?)$/,\n};\n\n/**\n * Modern.js 风格约定式路由生成器\n *\n * 文件约定:\n * - page.tsx — 页面内容(叶子组件)\n * - layout.tsx — 布局组件(使用 <Outlet> 渲染子路由)\n * - error.tsx — 错误边界组件\n * - loading.tsx — 加载状态组件\n * - $.tsx — 通配/404 路由\n *\n * 目录约定:\n * - [id]/ — 动态路由 → /:id\n * - [id$]/ — 可选动态路由 → /:id?\n * - [...slug]/ — 全匹配路由 → /*\n * - __auth/ — 无路径布局(不生成 URL 片段)\n * - user.profile/ — 扁平路由(. 分隔 → /user/profile)\n */\nexport class ConventionalRouteGenerator {\n private options: GeneratorOptions;\n\n constructor(options: GeneratorOptions) {\n this.options = options;\n }\n\n generate(): RouteConfig[] {\n const { pagesDir } = this.options;\n if (!existsSync(pagesDir)) {\n console.warn(`[ywkf] 页面目录不存在: ${pagesDir}`);\n return [];\n }\n return this.scanDirectory(pagesDir, \"/\");\n }\n\n /**\n * 扫描目录,生成路由树\n */\n private scanDirectory(dir: string, routePath: string): RouteConfig[] {\n const entries = readdirSync(dir);\n\n // 查找约定文件\n const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));\n const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));\n const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));\n const loadingFile = entries.find((e) => CONVENTION_FILES.loading.test(e));\n const catchAllFile = entries.find((e) => CONVENTION_FILES.catchAll.test(e));\n\n // 收集子目录\n const subDirs = entries.filter((e) => {\n if (e.startsWith(\".\")) return false;\n if (EXCLUDED_DIRS.has(e)) return false;\n return statSync(join(dir, e)).isDirectory();\n });\n\n // 递归子目录\n const childRoutes: RouteConfig[] = [];\n for (const subDir of subDirs) {\n const subDirPath = join(dir, subDir);\n\n // __prefix:无路径布局\n if (subDir.startsWith(\"__\")) {\n const pathlessRoutes = this.scanDirectory(subDirPath, routePath);\n // 如果该无路径目录有 layout,将子路由包装在其中\n const pathlessLayout = readdirSync(subDirPath).find((e) =>\n CONVENTION_FILES.layout.test(e)\n );\n if (pathlessLayout) {\n const pathlessChildren = this.scanDirectory(subDirPath, routePath);\n childRoutes.push({\n path: routePath,\n name: this.generateRouteName(subDir.slice(2)),\n layoutFile: relative(this.options.pagesDir, join(subDirPath, pathlessLayout)),\n isLayout: true,\n pathless: true,\n children: pathlessChildren.filter(\n (r) => r.layoutFile !== relative(this.options.pagesDir, join(subDirPath, pathlessLayout))\n ),\n });\n } else {\n childRoutes.push(...pathlessRoutes);\n }\n continue;\n }\n\n // 计算路由路径\n const subRoutePath = this.resolveRoutePath(subDir, routePath);\n childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));\n }\n\n // 构建当前目录的路由节点\n const routeName = this.generateRouteName(routePath);\n const relPath = (file: string) => relative(this.options.pagesDir, join(dir, file));\n\n // 有 layout.tsx → 创建布局路由节点,children 为子路由\n if (layoutFile) {\n const layoutChildren: RouteConfig[] = [];\n\n // page.tsx 作为 index 子路由\n if (pageFile) {\n layoutChildren.push({\n path: routePath,\n file: relPath(pageFile),\n name: routeName + \"Index\",\n index: true,\n });\n }\n\n // 通配路由\n if (catchAllFile) {\n layoutChildren.push({\n path: \"*\",\n file: relPath(catchAllFile),\n name: routeName + \"CatchAll\",\n });\n }\n\n layoutChildren.push(...childRoutes);\n\n return [\n {\n path: routePath,\n layoutFile: relPath(layoutFile),\n errorFile: errorFile ? relPath(errorFile) : undefined,\n loadingFile: loadingFile ? relPath(loadingFile) : undefined,\n name: routeName,\n isLayout: true,\n children: layoutChildren,\n },\n ];\n }\n\n // 无 layout → page.tsx 是叶子页面\n const result: RouteConfig[] = [];\n\n if (pageFile) {\n result.push({\n path: routePath,\n file: relPath(pageFile),\n errorFile: errorFile ? relPath(errorFile) : undefined,\n name: routeName,\n });\n }\n\n if (catchAllFile) {\n result.push({\n path: \"*\",\n file: relPath(catchAllFile),\n name: routeName + \"CatchAll\",\n });\n }\n\n result.push(...childRoutes);\n return result;\n }\n\n /**\n * 解析目录名到路由路径\n *\n * - [id] → :id\n * - [id$] → :id?\n * - [...slug] → *\n * - user.profile → user/profile\n */\n private resolveRoutePath(dirName: string, parentPath: string): string {\n let segment: string;\n\n // [...slug] → *\n if (dirName.match(/^\\[\\.\\.\\.(.+)\\]$/)) {\n segment = \"*\";\n }\n // [id$] → :id? (可选动态)\n else if (dirName.match(/^\\[(.+)\\$\\]$/)) {\n const param = dirName.slice(1, -2);\n segment = `:${param}?`;\n }\n // [id] → :id\n else if (dirName.match(/^\\[(.+)\\]$/)) {\n const param = dirName.slice(1, -1);\n segment = `:${param}`;\n }\n // user.profile → user/profile (扁平路由)\n else if (dirName.includes(\".\")) {\n segment = dirName.replace(/\\./g, \"/\");\n } else {\n segment = dirName;\n }\n\n return parentPath === \"/\"\n ? `/${segment}`\n : `${parentPath}/${segment}`;\n }\n\n /**\n * 生成有效的 JS 标识符名称\n */\n private generateRouteName(path: string): string {\n const name = path\n .split(\"/\")\n .filter(Boolean)\n .map((s) =>\n s\n .replace(/^:/, \"Param\")\n .replace(/\\?$/, \"Optional\")\n .replace(/^\\*$/, \"CatchAll\")\n )\n .map((s) => {\n const cleaned = s.replace(/[^a-zA-Z0-9]/g, \"\");\n if (!cleaned) return \"\";\n return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n })\n .join(\"\");\n\n if (!name || /^\\d/.test(name)) {\n return \"Page\" + (name || \"Root\");\n }\n return name;\n }\n\n // ===== 代码生成 =====\n\n generateCode(): string {\n const routes = this.generate();\n const lazyImports: string[] = [];\n const errorImports: string[] = [];\n const loadingImports: string[] = [];\n\n this.collectImports(routes, lazyImports, errorImports, loadingImports);\n\n return `// 此文件由 @4399ywkf/core 自动生成,请勿手动修改\n// Generated at: ${new Date().toISOString()}\n\nimport React, { lazy, Suspense } from \"react\";\nimport { createBrowserRouter, type RouteObject } from \"react-router\";\n\n// 懒加载页面组件\n${lazyImports.join(\"\\n\")}\n${errorImports.length > 0 ? \"\\n// 错误边界组件\\n\" + errorImports.join(\"\\n\") : \"\"}\n${loadingImports.length > 0 ? \"\\n// 加载状态组件\\n\" + loadingImports.join(\"\\n\") : \"\"}\n\n// 默认加载状态\nconst DefaultLoading = () => <div style={{ padding: 24, textAlign: \"center\" }}>加载中...</div>;\n\n// 懒加载包装\nfunction LazyRoute({\n Component,\n Loading = DefaultLoading,\n}: {\n Component: React.LazyExoticComponent<React.ComponentType<unknown>>;\n Loading?: React.ComponentType;\n}) {\n return (\n <Suspense fallback={<Loading />}>\n <Component />\n </Suspense>\n );\n}\n\n// 路由配置\nexport const routes: RouteObject[] = ${this.emitRouteArray(routes)};\n\n/**\n * 创建路由实例\n */\nexport function createRouter(basename?: string) {\n return createBrowserRouter(routes, { basename: basename || \"/\" });\n}\n\nexport default routes;\n`;\n }\n\n private collectImports(\n routes: RouteConfig[],\n lazyImports: string[],\n errorImports: string[],\n loadingImports: string[]\n ): void {\n for (const route of routes) {\n const name = route.name;\n const toImportPath = (f: string) => f.replace(/\\.(tsx?|jsx?)$/, \"\");\n\n if (route.isLayout && route.layoutFile) {\n lazyImports.push(\n `const ${name}Layout = lazy(() => import(\"@/pages/${toImportPath(route.layoutFile)}\"));`\n );\n }\n if (route.file) {\n lazyImports.push(\n `const ${name}Page = lazy(() => import(\"@/pages/${toImportPath(route.file)}\"));`\n );\n }\n if (route.errorFile) {\n errorImports.push(\n `const ${name}Error = lazy(() => import(\"@/pages/${toImportPath(route.errorFile)}\"));`\n );\n }\n if (route.loadingFile) {\n loadingImports.push(\n `const ${name}Loading = lazy(() => import(\"@/pages/${toImportPath(route.loadingFile)}\"));`\n );\n }\n\n if (route.children) {\n this.collectImports(route.children, lazyImports, errorImports, loadingImports);\n }\n }\n }\n\n private emitRouteArray(routes: RouteConfig[], parentPath?: string): string {\n const items = routes.map((r) => this.emitRouteObject(r, parentPath));\n return `[\\n ${items.join(\",\\n \")}\\n]`;\n }\n\n private emitRouteObject(route: RouteConfig, parentPath?: string): string {\n const parts: string[] = [];\n const name = route.name;\n\n // path\n if (route.index) {\n parts.push(\"index: true\");\n } else if (route.pathless) {\n // 无路径布局不输出 path\n } else if (route.path === \"*\") {\n parts.push(`path: \"*\"`);\n } else if (parentPath && route.path.startsWith(parentPath) && parentPath !== \"/\") {\n const rel = route.path.slice(parentPath.length + 1);\n parts.push(`path: \"${rel || \"\"}\"`);\n } else {\n parts.push(`path: \"${route.path}\"`);\n }\n\n // element\n if (route.isLayout && route.layoutFile) {\n const loadingProp = route.loadingFile ? ` Loading={${name}Loading}` : \"\";\n parts.push(`element: <LazyRoute Component={${name}Layout}${loadingProp} />`);\n } else if (route.file) {\n parts.push(`element: <LazyRoute Component={${name}Page} />`);\n }\n\n // errorElement\n if (route.errorFile) {\n parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);\n }\n\n // children\n if (route.children && route.children.length > 0) {\n const childParent = route.pathless ? parentPath : route.path;\n parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);\n }\n\n return `{\\n ${parts.join(\",\\n \")}\\n }`;\n }\n\n /**\n * 写入路由文件\n */\n write(): void {\n const { outputDir } = this.options;\n const code = this.generateCode();\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n writeFileSync(join(outputDir, \"routes.tsx\"), code, \"utf-8\");\n if (process.env.DEBUG) console.log(`[ywkf] 约定式路由已生成`);\n }\n}\n\n/**\n * 生成约定式路由\n */\nexport function generateConventionalRoutes(options: GeneratorOptions): void {\n new ConventionalRouteGenerator(options).write();\n}\n","import type { Compiler } from \"@rspack/core\";\nimport { ConventionalRouteGenerator } from \"./generator.js\";\nimport { join } from \"path\";\nimport { watch, existsSync, readFileSync } from \"fs\";\n\nexport interface ConventionalRoutePluginOptions {\n /** 页面目录 */\n pagesDir: string;\n /** 输出目录 */\n outputDir: string;\n /** 路由 basename */\n basename?: string;\n /** 是否监听文件变化(开发模式) */\n watch?: boolean;\n}\n\n/**\n * 约定式路由 Rspack 插件\n *\n * 在编译开始前扫描 src/pages 目录,生成 .ywkf/routes.tsx\n */\nexport class ConventionalRoutePlugin {\n private options: ConventionalRoutePluginOptions;\n private isWatching = false;\n private hasGenerated = false;\n private lastGeneratedContent = \"\";\n\n constructor(options: ConventionalRoutePluginOptions) {\n this.options = options;\n }\n\n apply(compiler: Compiler): void {\n const pluginName = \"ConventionalRoutePlugin\";\n\n // 只在首次编译时生成路由\n compiler.hooks.beforeCompile.tapAsync(pluginName, (params, callback) => {\n if (!this.hasGenerated) {\n this.generateRoutes();\n this.hasGenerated = true;\n }\n callback();\n });\n\n // 开发模式下监听文件变化\n if (this.options.watch && !this.isWatching) {\n this.watchPages();\n }\n }\n\n /**\n * 生成路由文件(带内容比对,避免不必要的重复生成)\n */\n private generateRoutes(): void {\n try {\n const generator = new ConventionalRouteGenerator({\n pagesDir: this.options.pagesDir,\n outputDir: this.options.outputDir,\n basename: this.options.basename,\n });\n\n const newContent = generator.generateCode();\n const outputPath = join(this.options.outputDir, \"routes.tsx\");\n\n // 比对内容,如果相同则跳过(去掉时间戳后比较)\n const normalizedNew = this.normalizeContent(newContent);\n const normalizedOld = this.normalizeContent(this.lastGeneratedContent);\n\n if (normalizedNew === normalizedOld) {\n return; // 内容相同,跳过生成\n }\n\n generator.write();\n this.lastGeneratedContent = newContent;\n } catch (error) {\n console.error(\"[ywkf] 生成路由失败:\", error);\n }\n }\n\n /**\n * 标准化内容(去掉时间戳等动态部分)\n */\n private normalizeContent(content: string): string {\n return content.replace(/\\/\\/ Generated at: .+/g, \"\");\n }\n\n /**\n * 监听页面目录变化\n */\n private watchPages(): void {\n if (!existsSync(this.options.pagesDir)) {\n return;\n }\n\n this.isWatching = true;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const watcher = watch(\n this.options.pagesDir,\n { recursive: true },\n (eventType, filename) => {\n // 只处理 tsx/jsx 文件\n if (!filename?.match(/\\.(tsx?|jsx?)$/)) {\n return;\n }\n\n // 防抖处理\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(() => {\n if (process.env.DEBUG) console.log(`[ywkf] 检测到页面变化: ${filename}`);\n this.generateRoutes();\n }, 500);\n }\n );\n\n // 进程退出时关闭监听\n process.on(\"exit\", () => {\n watcher.close();\n });\n }\n}\n"],"mappings":"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","names":["join","existsSync"]}
@@ -1,13 +1,15 @@
1
1
  import { Configuration } from '@rspack/core';
2
- import { Y as YwkfConfig } from '../schema-BuqmN_ra.js';
3
- import '../types-BZV_2QtD.js';
2
+ import { Y as YwkfConfig } from '../schema-VPH72NAR.js';
3
+ import '../types-BztKUufh.js';
4
4
  import 'react';
5
5
  import 'react-router';
6
6
 
7
7
  /**
8
8
  * 创建基础 Rspack 配置
9
9
  */
10
- declare function createBaseConfig(config: Required<YwkfConfig>, cwd: string): Configuration;
10
+ declare function createBaseConfig(config: Required<YwkfConfig>, cwd: string, options?: {
11
+ isDev?: boolean;
12
+ }): Configuration;
11
13
 
12
14
  /**
13
15
  * 创建开发环境 Rspack 配置
@@ -2,6 +2,7 @@
2
2
  import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
3
3
 
4
4
  // src/rspack/dev.ts
5
+ import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
5
6
  import { merge } from "webpack-merge";
6
7
  import { createRequire as createRequire2 } from "module";
7
8
 
@@ -325,7 +326,7 @@ export default routes;
325
326
  mkdirSync(outputDir, { recursive: true });
326
327
  }
327
328
  writeFileSync(join(outputDir, "routes.tsx"), code, "utf-8");
328
- console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
329
+ if (process.env.DEBUG) console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
329
330
  }
330
331
  };
331
332
 
@@ -339,17 +340,33 @@ function generateEntry(config, injections = {}) {
339
340
  const topLevel = injections.topLevel || [];
340
341
  const exports = injections.exports || [];
341
342
  const hasPluginExports = exports.length > 0;
342
- const startupCode = hasPluginExports ? `// \u72EC\u7ACB\u8FD0\u884C\u6A21\u5F0F
343
- if (shouldRunIndependently !== false) {
344
- runApp();
345
- }` : `// \u542F\u52A8\u5E94\u7528
346
- runApp();`;
343
+ const hasAsyncTopLevel = topLevel.some((line) => line.includes("await "));
344
+ let startupBody;
345
+ if (hasPluginExports) {
346
+ startupBody = [
347
+ ...topLevel,
348
+ ...exports,
349
+ ``,
350
+ `if (shouldRunIndependently !== false) {`,
351
+ ` runApp();`,
352
+ `}`
353
+ ].join("\n");
354
+ } else if (hasAsyncTopLevel) {
355
+ startupBody = [
356
+ `(async () => {`,
357
+ ...topLevel.map((l) => ` ${l}`),
358
+ ` runApp();`,
359
+ `})();`
360
+ ].join("\n");
361
+ } else {
362
+ startupBody = topLevel.length > 0 ? [...topLevel, `runApp();`].join("\n") : `runApp();`;
363
+ }
347
364
  return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
348
365
  // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
349
366
 
350
367
  ${imports.join("\n")}
351
368
 
352
- ${topLevel.length > 0 ? topLevel.join("\n") + "\n" : ""}${exports.length > 0 ? exports.join("\n") + "\n\n" : ""}${startupCode}
369
+ ${startupBody}
353
370
  `;
354
371
  }
355
372
 
@@ -398,12 +415,13 @@ async function getUserConfig(): Promise<Partial<AppConfig>> {
398
415
  * \u521B\u5EFA\u5E94\u7528\u914D\u7F6E
399
416
  */
400
417
  export function createAppConfig(userConfig: Partial<AppConfig> = {}): AppConfig {
401
- const router = createRouter(BASENAME);
418
+ const effectiveBasename = userConfig.basename || BASENAME;
419
+ const router = createRouter(effectiveBasename);
402
420
 
403
421
  const defaultConfig: AppConfig = {
404
422
  appName: APP_NAME,
405
423
  router,
406
- basename: BASENAME,
424
+ basename: effectiveBasename,
407
425
  rootId: APP_NAME,
408
426
  strictMode: true,
409
427
  antd: {
@@ -596,7 +614,7 @@ var YwkfGenerator = class {
596
614
  await hooks.afterGenerate(this.context);
597
615
  }
598
616
  }
599
- console.log(`[ywkf] \u5DF2\u751F\u6210 .ywkf \u76EE\u5F55`);
617
+ if (process.env.DEBUG) console.log(`[ywkf] \u5DF2\u751F\u6210 .ywkf \u76EE\u5F55`);
600
618
  }
601
619
  /**
602
620
  * 生成配置快照
@@ -822,13 +840,13 @@ var YwkfGeneratorPlugin = class {
822
840
  isProd: !isDev,
823
841
  config,
824
842
  logger: {
825
- info: (msg) => console.log(`[ywkf] ${msg}`),
843
+ info: (msg) => {
844
+ if (process.env.DEBUG) console.log(`[ywkf] ${msg}`);
845
+ },
826
846
  warn: (msg) => console.warn(`[ywkf] ${msg}`),
827
847
  error: (msg) => console.error(`[ywkf] ${msg}`),
828
848
  debug: (msg) => {
829
- if (process.env.DEBUG) {
830
- console.log(`[ywkf:debug] ${msg}`);
831
- }
849
+ if (process.env.DEBUG) console.log(`[ywkf:debug] ${msg}`);
832
850
  }
833
851
  }
834
852
  };
@@ -837,7 +855,7 @@ var YwkfGeneratorPlugin = class {
837
855
  if (plugin) {
838
856
  const hooks = await plugin.setup(context);
839
857
  this.pluginHooks.push(hooks);
840
- console.log(`[ywkf] \u63D2\u4EF6\u5DF2\u52A0\u8F7D: ${plugin.name}`);
858
+ if (process.env.DEBUG) console.log(`[ywkf] \u63D2\u4EF6\u5DF2\u52A0\u8F7D: ${plugin.name}`);
841
859
  }
842
860
  }
843
861
  this.generator = new YwkfGenerator(
@@ -891,7 +909,7 @@ var YwkfGeneratorPlugin = class {
891
909
  clearTimeout(debounceTimer);
892
910
  }
893
911
  debounceTimer = setTimeout(async () => {
894
- console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
912
+ if (process.env.DEBUG) console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
895
913
  if (this.generator) {
896
914
  await this.generator.generate();
897
915
  }
@@ -908,7 +926,8 @@ var YwkfGeneratorPlugin = class {
908
926
  var require2 = createRequire(import.meta.url);
909
927
  var __dirname = dirname(fileURLToPath(import.meta.url));
910
928
  var coreNodeModules = join5(__dirname, "../../node_modules");
911
- function createBaseConfig(config, cwd) {
929
+ function createBaseConfig(config, cwd, options = {}) {
930
+ const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
912
931
  const { resolveApp } = createPathResolver(cwd);
913
932
  const {
914
933
  appName,
@@ -939,12 +958,11 @@ function createBaseConfig(config, cwd) {
939
958
  entry: [join5(ywkfOutputDir, "index.tsx")],
940
959
  resolve: {
941
960
  extensions: [".ts", ".tsx", ".jsx", ".js", ".json", ".mjs"],
942
- symlinks: false,
961
+ symlinks: true,
943
962
  alias: {
944
963
  ...alias,
945
964
  "process/browser.js": require2.resolve("process/browser.js"),
946
965
  "process/browser": require2.resolve("process/browser.js"),
947
- // 确保 React 系列包从用户项目的 node_modules 解析
948
966
  react: resolveApp("node_modules/react"),
949
967
  "react-dom": resolveApp("node_modules/react-dom"),
950
968
  "react-router": resolveApp("node_modules/react-router")
@@ -1002,7 +1020,9 @@ function createBaseConfig(config, cwd) {
1002
1020
  },
1003
1021
  transform: {
1004
1022
  react: {
1005
- runtime: "automatic"
1023
+ runtime: "automatic",
1024
+ development: isDev,
1025
+ refresh: isDev
1006
1026
  }
1007
1027
  },
1008
1028
  target: "es2015"
@@ -1116,7 +1136,7 @@ function convertProxyToArray(proxy) {
1116
1136
  });
1117
1137
  }
1118
1138
  function createDevConfig(config, cwd) {
1119
- const baseConfig = createBaseConfig(config, cwd);
1139
+ const baseConfig = createBaseConfig(config, cwd, { isDev: true });
1120
1140
  const { resolveApp } = createPathResolver(cwd);
1121
1141
  const { dev, style } = config;
1122
1142
  const styleRules = {
@@ -1300,6 +1320,9 @@ function createDevConfig(config, cwd) {
1300
1320
  clean: false
1301
1321
  },
1302
1322
  stats: "errors-only",
1323
+ plugins: [
1324
+ new ReactRefreshPlugin()
1325
+ ],
1303
1326
  devServer: {
1304
1327
  host: dev.host,
1305
1328
  port: dev.port,
@@ -1322,7 +1345,7 @@ function createDevConfig(config, cwd) {
1322
1345
  import { rspack as rspack2 } from "@rspack/core";
1323
1346
  import { merge as merge2 } from "webpack-merge";
1324
1347
  function createProdConfig(config, cwd) {
1325
- const baseConfig = createBaseConfig(config, cwd);
1348
+ const baseConfig = createBaseConfig(config, cwd, { isDev: false });
1326
1349
  const { resolveApp } = createPathResolver(cwd);
1327
1350
  const { style, performance, output } = config;
1328
1351
  const styleRules = {