@chuckcchen/vite-plugin 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/factory.d.ts CHANGED
@@ -1,20 +1,17 @@
1
1
  /**
2
2
  * Adapter Factory - Simplifies the creation of framework adapters
3
3
  *
4
- * Provides factory functions and preset configurations to reduce boilerplate code in adapter development
4
+ * Provides factory functions and preset configurations to reduce boilerplate code
5
5
  */
6
- import type { BuildContext, FrameworkAdapter, AdapterHooks } from "./types.js";
6
+ import type { BuildContext, RouteInfo, FrameworkAdapter } from "./types.js";
7
+ import { type RouteSource } from "./route-parser.js";
7
8
  /**
8
9
  * Server wrapper preset configuration
9
10
  */
10
11
  export interface ServerWrapperPreset {
11
- /** Additional import statements */
12
12
  imports?: string;
13
- /** Handler initialization code */
14
13
  handlerSetup: string;
15
- /** Handler call expression */
16
14
  handlerCall: string;
17
- /** Whether this preset requires HTML template injection */
18
15
  requiresHtmlTemplate?: boolean;
19
16
  }
20
17
  /**
@@ -48,87 +45,96 @@ export declare const SERVER_WRAPPER_PRESETS: {
48
45
  * Build directory configuration
49
46
  */
50
47
  export interface BuildDirConfig {
51
- /** Client directory candidates */
52
48
  client: string[];
53
- /** Server directory candidates (SSR mode) */
54
49
  server?: string[];
55
50
  }
51
+ /**
52
+ * Route source configuration for declarative route parsing
53
+ */
54
+ export interface RouteSourceConfig {
55
+ /** Route sources to try in order */
56
+ sources?: RouteSource[];
57
+ /** Default routes if no sources match */
58
+ defaultRoutes?: string[];
59
+ /** Extra routes to add in SSR mode */
60
+ ssrExtraRoutes?: string[];
61
+ /** Transform function for parsed routes */
62
+ transform?: (routes: RouteInfo[], context: BuildContext) => RouteInfo[];
63
+ }
64
+ /**
65
+ * Server wrapper mode configuration
66
+ */
67
+ export interface ServerWrapperModeConfig {
68
+ /** Wrapper mode: inline (embed server code) or import (use import statement) */
69
+ mode?: "inline" | "import";
70
+ /** Additional exports when using import mode */
71
+ additionalExports?: string[];
72
+ /** Custom handler call expression */
73
+ handlerCall?: string;
74
+ }
75
+ /**
76
+ * esbuild configuration for server bundling
77
+ */
78
+ export interface EsbuildConfig {
79
+ /** Working directory for esbuild */
80
+ absWorkingDir?: string | ((context: BuildContext) => string);
81
+ /** External modules */
82
+ external?: string[];
83
+ /** Additional esbuild options */
84
+ options?: Record<string, unknown>;
85
+ }
56
86
  /**
57
87
  * Adapter factory configuration
58
88
  */
59
89
  export interface AdapterFactoryConfig {
60
- /** Adapter name */
61
90
  name: string;
62
- /** Config file detection list */
63
91
  configFiles: string[];
64
- /** Build directory candidates configuration */
65
92
  buildDirs: BuildDirConfig;
66
- /** Server entry file candidates */
67
93
  serverEntryFiles?: string[];
68
- /** Server wrapper configuration (use preset name or custom config) */
69
94
  serverWrapper?: keyof typeof SERVER_WRAPPER_PRESETS | ServerWrapperPreset;
70
- /** Default routes list */
95
+ serverWrapperMode?: ServerWrapperModeConfig;
96
+ /** Declarative route configuration */
97
+ routeConfig?: RouteSourceConfig;
98
+ /** Legacy: default routes (use routeConfig.defaultRoutes instead) */
71
99
  defaultRoutes?: string[];
72
- /** Extra routes for SSR mode */
100
+ /** Legacy: SSR extra routes (use routeConfig.ssrExtraRoutes instead) */
73
101
  ssrExtraRoutes?: string[];
74
- /** Custom ssr404 handling */
75
102
  ssr404?: boolean | ((isSSR: boolean) => boolean);
76
- /** HTML template path candidates (for viteSSR preset) */
77
103
  htmlTemplatePaths?: string[];
104
+ /** esbuild configuration */
105
+ esbuildConfig?: EsbuildConfig;
78
106
  }
79
107
  /**
80
108
  * Adapter factory runtime options
81
109
  */
82
110
  export interface AdapterFactoryOptions {
83
- /** User-specified client directory */
84
111
  clientBuildDir?: string;
85
- /** User-specified server directory */
86
112
  serverBuildDir?: string;
87
- /** User-specified server entry */
88
113
  serverEntry?: string;
89
- /** User-specified routes */
90
114
  routes?: string[];
91
115
  }
92
116
  /**
93
117
  * Create a framework adapter
94
- *
95
- * @example
96
- * ```ts
97
- * const adapter = createFrameworkAdapter({
98
- * name: "my-framework",
99
- * configFiles: ["my-framework.config.ts", "my-framework.config.js"],
100
- * buildDirs: {
101
- * client: ["dist/client", "dist"],
102
- * server: ["dist/server"],
103
- * },
104
- * serverWrapper: "generic",
105
- * });
106
- * ```
107
118
  */
108
119
  export declare function createFrameworkAdapter(config: AdapterFactoryConfig, options?: AdapterFactoryOptions, overrides?: Partial<FrameworkAdapter>): FrameworkAdapter;
109
120
  /**
110
- * Create a stateful adapter
111
- * Used for complex adapters that need to share state between different hooks
121
+ * Create a stateful adapter (for complex adapters needing shared state)
112
122
  */
113
123
  export declare function createStatefulAdapter<TState extends object>(initialState: TState, factory: (state: TState) => FrameworkAdapter): FrameworkAdapter;
114
124
  /**
115
- * Compose multiple hooks
125
+ * Combine multiple detector functions (returns true if any matches)
116
126
  */
117
- export declare function composeHooks(...hooksList: (AdapterHooks | undefined)[]): AdapterHooks;
127
+ export declare function combineDetectors(...detectors: ((context: BuildContext) => Promise<boolean> | boolean)[]): (context: BuildContext) => Promise<boolean>;
118
128
  /**
119
- * Create a detector function - based on config files
129
+ * Create detector based on config files
120
130
  */
121
131
  export declare function createConfigDetector(configFiles: string[]): (context: BuildContext) => Promise<boolean>;
122
132
  /**
123
- * Create a detector function - based on build directories
133
+ * Create detector based on build directories
124
134
  */
125
135
  export declare function createBuildDirDetector(buildDirs: string[]): (context: BuildContext) => Promise<boolean>;
126
136
  /**
127
- * Create a detector function - based on package.json dependencies
137
+ * Create detector based on package.json dependencies
128
138
  */
129
139
  export declare function createDependencyDetector(dependencies: string[]): (context: BuildContext) => Promise<boolean>;
130
- /**
131
- * Combine multiple detector functions (returns true if any matches)
132
- */
133
- export declare function combineDetectors(...detectors: ((context: BuildContext) => Promise<boolean> | boolean)[]): (context: BuildContext) => Promise<boolean>;
134
140
  //# sourceMappingURL=factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,YAAY,EAEZ,SAAS,EAET,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAWpB,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB;IACjC,0DAA0D;;;;;IAa1D,sCAAsC;;;;;IAatC,+CAA+C;;;;;;IAqB/C,wDAAwD;;;;;;CAoChD,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,KAAK,SAAS,EAAE,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC3B,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,MAAM,CAAC,CAAC;IAC7D,uBAAuB;IACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,cAAc,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,OAAO,sBAAsB,GAAG,mBAAmB,CAAC;IAC1E,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,sCAAsC;IACtC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,4BAA4B;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA+ND;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE,qBAA0B,EACnC,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GACpC,gBAAgB,CAqDlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,MAAM,EACzD,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,gBAAgB,GAC3C,gBAAgB,CAElB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,IAEzD,SAAS,YAAY,KAAG,OAAO,CAAC,OAAO,CAAC,CAQvD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAC1C,SAAS,YAAY,KAAG,OAAO,CAAC,OAAO,CAAC,CAGvD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAC1C,SAAS,YAAY,KAAG,OAAO,CAAC,OAAO,CAAC,CAGvD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,IAC/C,SAAS,YAAY,KAAG,OAAO,CAAC,OAAO,CAAC,CAcvD"}
package/dist/factory.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Adapter Factory - Simplifies the creation of framework adapters
3
3
  *
4
- * Provides factory functions and preset configurations to reduce boilerplate code in adapter development
4
+ * Provides factory functions and preset configurations to reduce boilerplate code
5
5
  */
6
6
  import path from "path";
7
- import { detectConfigFile, detectBuildDir, detectServerEntry, generateServerWrapperCode, createDefaultMeta, logBuildArtifacts, } from "./helpers.js";
8
- import { pathExists, readFile } from "./utils.js";
7
+ import { pathExists, readFile, detectConfigFile, detectBuildDir, detectServerEntry, generateServerWrapperCode, createDefaultMeta, logBuildArtifacts, } from "./utils.js";
8
+ import { parseRoutesFromSources, } from "./route-parser.js";
9
9
  /**
10
10
  * Built-in server wrapper presets
11
11
  */
@@ -58,7 +58,6 @@ const requestHandler = createRequestHandler(buildConfig);`.trim(),
58
58
  viteSSR: {
59
59
  requiresHtmlTemplate: true,
60
60
  handlerSetup: `
61
- // Get render function from server build
62
61
  const renderFn = typeof render === 'function'
63
62
  ? render
64
63
  : (typeof module !== 'undefined' && typeof module.render === 'function' ? module.render : null);
@@ -72,10 +71,7 @@ async function handleSSRRequest(request) {
72
71
  const pathname = url.pathname;
73
72
 
74
73
  try {
75
- // Call render function
76
74
  const rendered = await renderFn(pathname);
77
-
78
- // Inject rendered content into template
79
75
  let html = __HTML_TEMPLATE__
80
76
  .replace('<!--app-head-->', rendered.head ?? '')
81
77
  .replace('<!--app-html-->', rendered.html ?? '');
@@ -95,235 +91,270 @@ async function handleSSRRequest(request) {
95
91
  handlerCall: "handleSSRRequest(webRequest)",
96
92
  },
97
93
  };
94
+ // ============================================================================
95
+ // Internal Helper Functions
96
+ // ============================================================================
98
97
  /**
99
- * Create a framework adapter
100
- *
101
- * @example
102
- * ```ts
103
- * const adapter = createFrameworkAdapter({
104
- * name: "my-framework",
105
- * configFiles: ["my-framework.config.ts", "my-framework.config.js"],
106
- * buildDirs: {
107
- * client: ["dist/client", "dist"],
108
- * server: ["dist/server"],
109
- * },
110
- * serverWrapper: "generic",
111
- * });
112
- * ```
98
+ * Create default detect function based on config files
113
99
  */
114
- export function createFrameworkAdapter(config, options = {}, overrides) {
115
- const { name, configFiles, buildDirs, serverEntryFiles = ["index.js", "index.mjs", "entry.js", "main.js"], serverWrapper = "generic", defaultRoutes = ["/"], ssrExtraRoutes = [], ssr404, htmlTemplatePaths = ["dist/client/index.html", "dist/index.html"], } = config;
116
- const { clientBuildDir, serverBuildDir, serverEntry, routes = defaultRoutes, } = options;
117
- // Resolve server wrapper configuration
118
- const wrapperConfig = typeof serverWrapper === "string"
119
- ? SERVER_WRAPPER_PRESETS[serverWrapper]
120
- : serverWrapper;
121
- const baseAdapter = {
122
- name,
123
- async detect(context) {
124
- const configPath = await detectConfigFile(context.projectRoot, configFiles);
125
- return configPath !== null;
126
- },
127
- async getBuildArtifacts(context) {
128
- const { projectRoot, isSSR, logger } = context;
129
- // Detect client directory
130
- let clientDir = null;
131
- if (clientBuildDir) {
132
- clientDir = path.join(projectRoot, clientBuildDir);
100
+ function createDefaultDetector(configFiles) {
101
+ return async (context) => {
102
+ const configPath = await detectConfigFile(context.projectRoot, configFiles);
103
+ return configPath !== null;
104
+ };
105
+ }
106
+ /**
107
+ * Create default getBuildArtifacts function
108
+ */
109
+ function createDefaultBuildArtifactsGetter(name, buildDirs, serverEntryFiles, options) {
110
+ const { clientBuildDir, serverBuildDir, serverEntry } = options;
111
+ return async (context) => {
112
+ const { projectRoot, isSSR, logger } = context;
113
+ let clientDir = null;
114
+ if (clientBuildDir) {
115
+ clientDir = path.join(projectRoot, clientBuildDir);
116
+ }
117
+ else {
118
+ clientDir = await detectBuildDir(projectRoot, buildDirs.client);
119
+ }
120
+ let serverDir = null;
121
+ let serverEntryPath = null;
122
+ if (isSSR && buildDirs.server) {
123
+ if (serverBuildDir) {
124
+ serverDir = path.join(projectRoot, serverBuildDir);
133
125
  }
134
126
  else {
135
- const candidates = isSSR && buildDirs.server
136
- ? buildDirs.client
137
- : buildDirs.client;
138
- clientDir = await detectBuildDir(projectRoot, candidates);
127
+ serverDir = await detectBuildDir(projectRoot, buildDirs.server);
139
128
  }
140
- // Detect server directory and entry
141
- let serverDir = null;
142
- let serverEntryPath = null;
143
- if (isSSR && buildDirs.server) {
144
- if (serverBuildDir) {
145
- serverDir = path.join(projectRoot, serverBuildDir);
146
- }
147
- else {
148
- serverDir = await detectBuildDir(projectRoot, buildDirs.server);
149
- }
150
- if (serverEntry) {
151
- serverEntryPath = path.join(projectRoot, serverEntry);
129
+ if (serverEntry) {
130
+ serverEntryPath = path.join(projectRoot, serverEntry);
131
+ }
132
+ else if (serverDir) {
133
+ serverEntryPath = await detectServerEntry(serverDir, serverEntryFiles);
134
+ }
135
+ }
136
+ logBuildArtifacts(logger, name, {
137
+ clientDir,
138
+ serverDir,
139
+ serverEntry: serverEntryPath,
140
+ });
141
+ return {
142
+ clientDir,
143
+ serverDir,
144
+ serverEntry: serverEntryPath,
145
+ assetsDir: clientDir,
146
+ };
147
+ };
148
+ }
149
+ /**
150
+ * Create generateRoutes hook
151
+ */
152
+ function createGenerateRoutesHook(effectiveRouteConfig, fallbackRoutes) {
153
+ return async (context) => {
154
+ const { projectRoot, isSSR, logger } = context;
155
+ // Try declarative route sources first
156
+ if (effectiveRouteConfig.sources && effectiveRouteConfig.sources.length > 0) {
157
+ let parsedRoutes = await parseRoutesFromSources(projectRoot, effectiveRouteConfig.sources, { isSSR, logger });
158
+ if (parsedRoutes.length > 0) {
159
+ // Apply transform if provided
160
+ if (effectiveRouteConfig.transform) {
161
+ parsedRoutes = effectiveRouteConfig.transform(parsedRoutes, context);
152
162
  }
153
- else if (serverDir) {
154
- serverEntryPath = await detectServerEntry(serverDir, serverEntryFiles);
163
+ // Add SSR extra routes
164
+ if (isSSR && effectiveRouteConfig.ssrExtraRoutes) {
165
+ effectiveRouteConfig.ssrExtraRoutes.forEach((route) => {
166
+ parsedRoutes.push({ path: route, isStatic: false, srcRoute: route });
167
+ });
155
168
  }
169
+ return parsedRoutes;
156
170
  }
157
- logBuildArtifacts(logger, name, {
158
- clientDir,
159
- serverDir,
160
- serverEntry: serverEntryPath,
171
+ }
172
+ // Fallback to static route list
173
+ const routeList = fallbackRoutes.map((route) => ({
174
+ path: route,
175
+ isStatic: !isSSR,
176
+ srcRoute: route,
177
+ }));
178
+ if (isSSR && effectiveRouteConfig.ssrExtraRoutes) {
179
+ effectiveRouteConfig.ssrExtraRoutes.forEach((route) => {
180
+ routeList.push({ path: route, isStatic: false, srcRoute: route });
161
181
  });
162
- return {
163
- clientDir,
164
- serverDir,
165
- serverEntry: serverEntryPath,
166
- assetsDir: clientDir,
167
- };
168
- },
169
- hooks: {
170
- async generateRoutes(context) {
171
- const routeList = routes.map((route) => ({
172
- path: route,
173
- isStatic: !context.isSSR,
174
- srcRoute: route,
175
- }));
176
- // Add extra routes for SSR mode
177
- if (context.isSSR && ssrExtraRoutes.length > 0) {
178
- ssrExtraRoutes.forEach((route) => {
179
- routeList.push({ path: route, isStatic: false, srcRoute: route });
180
- });
181
- }
182
- return routeList;
183
- },
184
- async generateMeta(context, routeList) {
185
- const meta = createDefaultMeta(routeList, { isSSR: context.isSSR });
186
- // Custom ssr404 handling
187
- if (ssr404 !== undefined) {
188
- meta.conf.ssr404 = typeof ssr404 === "function"
189
- ? ssr404(context.isSSR)
190
- : ssr404;
191
- }
192
- return meta;
193
- },
194
- async generateServerWrapper(context, wrapperCfg) {
195
- let handlerSetup = wrapperConfig.handlerSetup;
196
- // Handle HTML template injection for viteSSR preset
197
- if (wrapperConfig.requiresHtmlTemplate) {
198
- const { projectRoot, outputDir, logger } = context;
199
- // Try to read HTML template
200
- let htmlTemplate = "";
201
- const templateCandidates = [
202
- ...htmlTemplatePaths.map(p => path.join(projectRoot, p)),
203
- path.join(projectRoot, outputDir, "assets/index.html"),
204
- ];
205
- for (const templatePath of templateCandidates) {
206
- if (await pathExists(templatePath)) {
207
- htmlTemplate = await readFile(templatePath);
208
- logger.verbose(`Found HTML template: ${templatePath}`);
209
- break;
210
- }
211
- }
212
- if (!htmlTemplate) {
213
- logger.warn("HTML template not found, using default template");
214
- htmlTemplate = "<!DOCTYPE html><html><head><!--app-head--></head><body><div id=\"app\"><!--app-html--></div></body></html>";
215
- }
216
- // Prepend HTML template definition
217
- const escapedTemplate = JSON.stringify(htmlTemplate);
218
- handlerSetup = `// HTML template embedded at build time\nconst __HTML_TEMPLATE__ = ${escapedTemplate};\n\n${handlerSetup}`;
182
+ }
183
+ return routeList;
184
+ };
185
+ }
186
+ /**
187
+ * Create generateMeta hook
188
+ */
189
+ function createGenerateMetaHook(ssr404) {
190
+ return async (context, routeList) => {
191
+ const meta = createDefaultMeta(routeList, { isSSR: context.isSSR });
192
+ if (ssr404 !== undefined) {
193
+ meta.conf.ssr404 = typeof ssr404 === "function"
194
+ ? ssr404(context.isSSR)
195
+ : ssr404;
196
+ }
197
+ return meta;
198
+ };
199
+ }
200
+ /**
201
+ * Create generateServerWrapper hook
202
+ */
203
+ function createGenerateServerWrapperHook(wrapperConfig, serverWrapperMode, htmlTemplatePaths) {
204
+ return async (context, wrapperCfg) => {
205
+ // Use serverWrapperMode if provided
206
+ if (serverWrapperMode) {
207
+ return generateServerWrapperCode({
208
+ serverEntryPath: wrapperCfg.serverEntryPath,
209
+ handlerCall: serverWrapperMode.handlerCall || wrapperConfig.handlerCall,
210
+ mode: serverWrapperMode.mode,
211
+ additionalExports: serverWrapperMode.additionalExports,
212
+ needsNodeRequestConversion: true,
213
+ });
214
+ }
215
+ let handlerSetup = wrapperConfig.handlerSetup;
216
+ if (wrapperConfig.requiresHtmlTemplate) {
217
+ const { projectRoot, outputDir, logger } = context;
218
+ let htmlTemplate = "";
219
+ const templateCandidates = [
220
+ ...htmlTemplatePaths.map(p => path.join(projectRoot, p)),
221
+ path.join(projectRoot, outputDir, "assets/index.html"),
222
+ ];
223
+ for (const templatePath of templateCandidates) {
224
+ if (await pathExists(templatePath)) {
225
+ htmlTemplate = await readFile(templatePath);
226
+ logger.verbose(`Found HTML template: ${templatePath}`);
227
+ break;
219
228
  }
220
- return generateServerWrapperCode({
221
- serverEntryPath: wrapperCfg.serverEntryPath,
222
- imports: wrapperConfig.imports,
223
- handlerSetup,
224
- handlerCall: wrapperConfig.handlerCall,
225
- });
229
+ }
230
+ if (!htmlTemplate) {
231
+ logger.warn("HTML template not found, using default template");
232
+ htmlTemplate = "<!DOCTYPE html><html><head><!--app-head--></head><body><div id=\"app\"><!--app-html--></div></body></html>";
233
+ }
234
+ const escapedTemplate = JSON.stringify(htmlTemplate);
235
+ handlerSetup = `const __HTML_TEMPLATE__ = ${escapedTemplate};\n\n${handlerSetup}`;
236
+ }
237
+ return generateServerWrapperCode({
238
+ serverEntryPath: wrapperCfg.serverEntryPath,
239
+ imports: wrapperConfig.imports,
240
+ handlerSetup,
241
+ handlerCall: wrapperConfig.handlerCall,
242
+ });
243
+ };
244
+ }
245
+ /**
246
+ * Create beforeBundle hook if esbuildConfig is provided
247
+ */
248
+ function createBeforeBundleHook(esbuildConfig) {
249
+ return async (context, bundleConfig) => {
250
+ const absWorkingDir = typeof esbuildConfig.absWorkingDir === "function"
251
+ ? esbuildConfig.absWorkingDir(context)
252
+ : esbuildConfig.absWorkingDir;
253
+ return {
254
+ ...bundleConfig,
255
+ external: esbuildConfig.external || bundleConfig.external,
256
+ esbuildOptions: {
257
+ ...bundleConfig.esbuildOptions,
258
+ ...esbuildConfig.options,
259
+ ...(absWorkingDir ? { absWorkingDir } : {}),
226
260
  },
227
- },
261
+ };
228
262
  };
229
- // Merge user overrides
230
- if (overrides) {
231
- return mergeAdapter(baseAdapter, overrides);
232
- }
233
- return baseAdapter;
234
263
  }
264
+ // ============================================================================
265
+ // Main Factory Function
266
+ // ============================================================================
235
267
  /**
236
- * Merge adapter configurations
268
+ * Create a framework adapter
237
269
  */
238
- function mergeAdapter(base, overrides) {
239
- const merged = {
240
- ...base,
241
- ...overrides,
242
- name: overrides.name || base.name,
270
+ export function createFrameworkAdapter(config, options = {}, overrides) {
271
+ const { name, configFiles, buildDirs, serverEntryFiles = ["index.js", "index.mjs", "entry.js", "main.js"], serverWrapper = "generic", serverWrapperMode, routeConfig, defaultRoutes = ["/"], ssrExtraRoutes = [], ssr404, htmlTemplatePaths = ["dist/client/index.html", "dist/index.html"], esbuildConfig, } = config;
272
+ const { routes = routeConfig?.defaultRoutes || defaultRoutes } = options;
273
+ // Merge route config with legacy options
274
+ const effectiveRouteConfig = {
275
+ sources: routeConfig?.sources,
276
+ defaultRoutes: routeConfig?.defaultRoutes || defaultRoutes,
277
+ ssrExtraRoutes: routeConfig?.ssrExtraRoutes || ssrExtraRoutes,
278
+ transform: routeConfig?.transform,
243
279
  };
244
- // Merge hooks
245
- if (base.hooks || overrides.hooks) {
246
- merged.hooks = {
247
- ...base.hooks,
248
- ...overrides.hooks,
280
+ const wrapperConfig = typeof serverWrapper === "string"
281
+ ? SERVER_WRAPPER_PRESETS[serverWrapper]
282
+ : serverWrapper;
283
+ // Build adapter using helper functions
284
+ const baseAdapter = {
285
+ name,
286
+ detect: createDefaultDetector(configFiles),
287
+ getBuildArtifacts: createDefaultBuildArtifactsGetter(name, buildDirs, serverEntryFiles, options),
288
+ hooks: {
289
+ generateRoutes: createGenerateRoutesHook(effectiveRouteConfig, routes),
290
+ generateMeta: createGenerateMetaHook(ssr404),
291
+ generateServerWrapper: createGenerateServerWrapperHook(wrapperConfig, serverWrapperMode, htmlTemplatePaths),
292
+ ...(esbuildConfig ? { beforeBundle: createBeforeBundleHook(esbuildConfig) } : {}),
293
+ },
294
+ };
295
+ if (overrides) {
296
+ return {
297
+ ...baseAdapter,
298
+ ...overrides,
299
+ name: overrides.name || baseAdapter.name,
300
+ hooks: { ...baseAdapter.hooks, ...overrides.hooks },
249
301
  };
250
302
  }
251
- return merged;
303
+ return baseAdapter;
252
304
  }
253
305
  /**
254
- * Create a stateful adapter
255
- * Used for complex adapters that need to share state between different hooks
306
+ * Create a stateful adapter (for complex adapters needing shared state)
256
307
  */
257
308
  export function createStatefulAdapter(initialState, factory) {
258
- const state = { ...initialState };
259
- return factory(state);
309
+ return factory({ ...initialState });
260
310
  }
261
311
  /**
262
- * Compose multiple hooks
312
+ * Combine multiple detector functions (returns true if any matches)
263
313
  */
264
- export function composeHooks(...hooksList) {
265
- const result = {};
266
- for (const hooks of hooksList) {
267
- if (!hooks)
268
- continue;
269
- Object.assign(result, hooks);
270
- }
271
- return result;
314
+ export function combineDetectors(...detectors) {
315
+ return async (context) => {
316
+ for (const detector of detectors) {
317
+ if (await detector(context)) {
318
+ return true;
319
+ }
320
+ }
321
+ return false;
322
+ };
272
323
  }
273
324
  /**
274
- * Create a detector function - based on config files
325
+ * Create detector based on config files
275
326
  */
276
327
  export function createConfigDetector(configFiles) {
277
328
  return async (context) => {
278
- const configPath = await detectConfigFile(context.projectRoot, configFiles);
279
- return configPath !== null;
329
+ return (await detectConfigFile(context.projectRoot, configFiles)) !== null;
280
330
  };
281
331
  }
282
332
  /**
283
- * Create a detector function - based on build directories
333
+ * Create detector based on build directories
284
334
  */
285
335
  export function createBuildDirDetector(buildDirs) {
286
336
  return async (context) => {
287
- for (const dir of buildDirs) {
288
- if (await pathExists(path.join(context.projectRoot, dir))) {
289
- return true;
290
- }
291
- }
292
- return false;
337
+ return (await detectBuildDir(context.projectRoot, buildDirs)) !== null;
293
338
  };
294
339
  }
295
340
  /**
296
- * Create a detector function - based on package.json dependencies
341
+ * Create detector based on package.json dependencies
297
342
  */
298
343
  export function createDependencyDetector(dependencies) {
299
344
  return async (context) => {
300
345
  const packageJsonPath = path.join(context.projectRoot, "package.json");
301
- if (!await pathExists(packageJsonPath)) {
346
+ if (!await pathExists(packageJsonPath))
302
347
  return false;
303
- }
304
348
  try {
305
- const { readFile } = await import("./utils.js");
306
349
  const content = await readFile(packageJsonPath);
307
350
  const packageJson = JSON.parse(content);
308
351
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
309
352
  return dependencies.some((dep) => dep in deps);
310
353
  }
311
- catch {
354
+ catch (error) {
355
+ context.logger?.verbose?.(`Failed to parse package.json: ${error instanceof Error ? error.message : String(error)}`);
312
356
  return false;
313
357
  }
314
358
  };
315
359
  }
316
- /**
317
- * Combine multiple detector functions (returns true if any matches)
318
- */
319
- export function combineDetectors(...detectors) {
320
- return async (context) => {
321
- for (const detector of detectors) {
322
- if (await detector(context)) {
323
- return true;
324
- }
325
- }
326
- return false;
327
- };
328
- }
329
360
  //# sourceMappingURL=factory.js.map