@edgeone/vite-core 0.0.1-beta.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.
Files changed (78) hide show
  1. package/README.md +13 -0
  2. package/dist/bundler.d.ts +14 -0
  3. package/dist/bundler.d.ts.map +1 -0
  4. package/dist/bundler.js +691 -0
  5. package/dist/bundler.js.map +1 -0
  6. package/dist/constants.d.ts +12 -0
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/constants.js +29 -0
  9. package/dist/constants.js.map +1 -0
  10. package/dist/core.d.ts +11 -0
  11. package/dist/core.d.ts.map +1 -0
  12. package/dist/core.js +547 -0
  13. package/dist/core.js.map +1 -0
  14. package/dist/factory/detectors.d.ts +13 -0
  15. package/dist/factory/detectors.d.ts.map +1 -0
  16. package/dist/factory/detectors.js +46 -0
  17. package/dist/factory/detectors.js.map +1 -0
  18. package/dist/factory/hooks.d.ts +29 -0
  19. package/dist/factory/hooks.d.ts.map +1 -0
  20. package/dist/factory/hooks.js +154 -0
  21. package/dist/factory/hooks.js.map +1 -0
  22. package/dist/factory/index.d.ts +23 -0
  23. package/dist/factory/index.d.ts.map +1 -0
  24. package/dist/factory/index.js +47 -0
  25. package/dist/factory/index.js.map +1 -0
  26. package/dist/factory/presets.d.ts +27 -0
  27. package/dist/factory/presets.d.ts.map +1 -0
  28. package/dist/factory/presets.js +186 -0
  29. package/dist/factory/presets.js.map +1 -0
  30. package/dist/factory.d.ts +183 -0
  31. package/dist/factory.d.ts.map +1 -0
  32. package/dist/factory.js +482 -0
  33. package/dist/factory.js.map +1 -0
  34. package/dist/helpers.d.ts +53 -0
  35. package/dist/helpers.d.ts.map +1 -0
  36. package/dist/helpers.js +177 -0
  37. package/dist/helpers.js.map +1 -0
  38. package/dist/index.d.ts +16 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +12 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/route/index.d.ts +7 -0
  43. package/dist/route/index.d.ts.map +1 -0
  44. package/dist/route/index.js +6 -0
  45. package/dist/route/index.js.map +1 -0
  46. package/dist/route/parser.d.ts +18 -0
  47. package/dist/route/parser.d.ts.map +1 -0
  48. package/dist/route/parser.js +187 -0
  49. package/dist/route/parser.js.map +1 -0
  50. package/dist/route/regex.d.ts +31 -0
  51. package/dist/route/regex.d.ts.map +1 -0
  52. package/dist/route/regex.js +145 -0
  53. package/dist/route/regex.js.map +1 -0
  54. package/dist/route/regex.test.d.ts +7 -0
  55. package/dist/route/regex.test.d.ts.map +1 -0
  56. package/dist/route/regex.test.js +666 -0
  57. package/dist/route/regex.test.js.map +1 -0
  58. package/dist/route/types.d.ts +58 -0
  59. package/dist/route/types.d.ts.map +1 -0
  60. package/dist/route/types.js +5 -0
  61. package/dist/route/types.js.map +1 -0
  62. package/dist/route-parser.d.ts +8 -0
  63. package/dist/route-parser.d.ts.map +1 -0
  64. package/dist/route-parser.js +8 -0
  65. package/dist/route-parser.js.map +1 -0
  66. package/dist/types.d.ts +142 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +5 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils.d.ts +41 -0
  71. package/dist/utils.d.ts.map +1 -0
  72. package/dist/utils.js +264 -0
  73. package/dist/utils.js.map +1 -0
  74. package/dist/vite-config-parser.d.ts +62 -0
  75. package/dist/vite-config-parser.d.ts.map +1 -0
  76. package/dist/vite-config-parser.js +229 -0
  77. package/dist/vite-config-parser.js.map +1 -0
  78. package/package.json +52 -0
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Adapter Factory - Simplifies the creation of framework adapters
3
+ *
4
+ * Provides factory functions and preset configurations to reduce boilerplate code
5
+ */
6
+ import type { BuildContext, RouteInfo, FrameworkAdapter } from "./types.js";
7
+ import { type RouteSource } from "./route-parser.js";
8
+ /**
9
+ * Server wrapper preset configuration
10
+ */
11
+ export interface ServerWrapperPreset {
12
+ imports?: string;
13
+ handlerSetup: string;
14
+ handlerCall: string;
15
+ requiresHtmlTemplate?: boolean;
16
+ }
17
+ /**
18
+ * Built-in server wrapper presets (generic, framework-agnostic)
19
+ *
20
+ * Note: All presets use `serverBuild` (namespace import) and `serverExports` (rest exports)
21
+ * which are automatically provided by generateServerWrapperCode()
22
+ *
23
+ * Framework-specific presets should be defined in their respective adapter packages.
24
+ */
25
+ export declare const SERVER_WRAPPER_PRESETS: {
26
+ /** Generic handler - detects default or handler export */
27
+ readonly generic: {
28
+ readonly handlerSetup: string;
29
+ readonly handlerCall: "handler(webRequest, ...args)";
30
+ };
31
+ /** Hono style - supports app.fetch */
32
+ readonly hono: {
33
+ readonly handlerSetup: string;
34
+ readonly handlerCall: "serverHandler(webRequest, ...args)";
35
+ };
36
+ /** Vite SSR - standard Vite SSR with render function */
37
+ readonly viteSSR: {
38
+ readonly requiresHtmlTemplate: true;
39
+ readonly handlerSetup: string;
40
+ readonly handlerCall: "handleSSRRequest(webRequest)";
41
+ };
42
+ };
43
+ /**
44
+ * Build directory configuration
45
+ */
46
+ export interface BuildDirConfig {
47
+ client: string[];
48
+ server?: string[];
49
+ }
50
+ /**
51
+ * Route source configuration for declarative route parsing
52
+ */
53
+ export interface RouteSourceConfig {
54
+ /** Route sources to try in order */
55
+ sources?: RouteSource[];
56
+ /** Default routes if no sources match */
57
+ defaultRoutes?: string[];
58
+ /** Extra routes to add in SSR mode */
59
+ ssrExtraRoutes?: string[];
60
+ /** Transform function for parsed routes */
61
+ transform?: (routes: RouteInfo[], context: BuildContext) => RouteInfo[];
62
+ }
63
+ /**
64
+ * esbuild configuration for server bundling
65
+ */
66
+ export interface EsbuildConfig {
67
+ /** Working directory for esbuild */
68
+ absWorkingDir?: string | ((context: BuildContext) => string);
69
+ /** External modules */
70
+ external?: string[];
71
+ /** Additional esbuild options */
72
+ options?: Record<string, unknown>;
73
+ }
74
+ /**
75
+ * Adapter factory configuration
76
+ */
77
+ export interface AdapterFactoryConfig {
78
+ name: string;
79
+ configFiles: string[];
80
+ buildDirs: BuildDirConfig;
81
+ serverEntryFiles?: string[];
82
+ serverWrapper?: keyof typeof SERVER_WRAPPER_PRESETS | ServerWrapperPreset;
83
+ /** Declarative route configuration */
84
+ routeConfig?: RouteSourceConfig;
85
+ /** Legacy: default routes (use routeConfig.defaultRoutes instead) */
86
+ defaultRoutes?: string[];
87
+ /** Legacy: SSR extra routes (use routeConfig.ssrExtraRoutes instead) */
88
+ ssrExtraRoutes?: string[];
89
+ ssr404?: boolean | ((isSSR: boolean) => boolean);
90
+ htmlTemplatePaths?: string[];
91
+ /** esbuild configuration */
92
+ esbuildConfig?: EsbuildConfig;
93
+ }
94
+ /**
95
+ * Adapter factory runtime options
96
+ */
97
+ export interface AdapterFactoryOptions {
98
+ clientBuildDir?: string;
99
+ serverBuildDir?: string;
100
+ serverEntry?: string;
101
+ routes?: string[];
102
+ }
103
+ /**
104
+ * Create a framework adapter
105
+ */
106
+ export declare function createFrameworkAdapter(config: AdapterFactoryConfig, options?: AdapterFactoryOptions, overrides?: Partial<FrameworkAdapter>): FrameworkAdapter;
107
+ /**
108
+ * Create a stateful adapter (for complex adapters needing shared state)
109
+ */
110
+ export declare function createStatefulAdapter<TState extends object>(initialState: TState, factory: (state: TState) => FrameworkAdapter): FrameworkAdapter;
111
+ /**
112
+ * Result of extending server wrapper presets
113
+ */
114
+ export interface ExtendedPresetsResult<TCustom extends Record<string, ServerWrapperPreset>> {
115
+ /** Extended presets object (core + custom) */
116
+ presets: typeof SERVER_WRAPPER_PRESETS & TCustom;
117
+ /** Type-safe preset resolver function */
118
+ resolvePreset: (preset: keyof (typeof SERVER_WRAPPER_PRESETS & TCustom) | ServerWrapperPreset) => ServerWrapperPreset;
119
+ /** Get all available preset names */
120
+ getPresetNames: () => (keyof (typeof SERVER_WRAPPER_PRESETS & TCustom))[];
121
+ }
122
+ /**
123
+ * Create a server wrapper preset
124
+ *
125
+ * Helper function to create a properly typed preset with validation and normalization.
126
+ * Automatically trims handlerSetup and validates required fields.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const myPreset = createServerWrapperPreset({
131
+ * imports: `import { handler } from 'my-framework';`,
132
+ * handlerSetup: `const requestHandler = handler(serverExports);`,
133
+ * handlerCall: "requestHandler(webRequest)",
134
+ * });
135
+ * ```
136
+ */
137
+ export declare function createServerWrapperPreset(config: ServerWrapperPreset): ServerWrapperPreset;
138
+ /**
139
+ * Extend core server wrapper presets with custom presets
140
+ *
141
+ * This utility provides a standardized way for adapters to:
142
+ * 1. Add framework-specific presets while keeping core presets
143
+ * 2. Get a type-safe resolver function
144
+ * 3. Export extended presets for external use
145
+ *
146
+ * Custom presets are automatically validated and normalized using createServerWrapperPreset().
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * // In vite-adapter - can pass raw config, will be validated automatically
151
+ * const { presets, resolvePreset } = extendServerWrapperPresets({
152
+ * vike: {
153
+ * imports: `import { renderPage } from 'vike/server';`,
154
+ * handlerSetup: `...`,
155
+ * handlerCall: "handleVikeRequest(webRequest)",
156
+ * },
157
+ * });
158
+ *
159
+ * export const SERVER_WRAPPER_PRESETS = presets;
160
+ * export type ServerWrapperPresetName = keyof typeof presets;
161
+ *
162
+ * // Use in adapter
163
+ * const resolved = resolvePreset(options.serverWrapper || "viteSSR");
164
+ * ```
165
+ */
166
+ export declare function extendServerWrapperPresets<TCustom extends Record<string, ServerWrapperPreset>>(customPresets: TCustom): ExtendedPresetsResult<TCustom>;
167
+ /**
168
+ * Combine multiple detector functions (returns true if any matches)
169
+ */
170
+ export declare function combineDetectors(...detectors: ((context: BuildContext) => Promise<boolean> | boolean)[]): (context: BuildContext) => Promise<boolean>;
171
+ /**
172
+ * Create detector based on config files
173
+ */
174
+ export declare function createConfigDetector(configFiles: string[]): (context: BuildContext) => Promise<boolean>;
175
+ /**
176
+ * Create detector based on build directories
177
+ */
178
+ export declare function createBuildDirDetector(buildDirs: string[]): (context: BuildContext) => Promise<boolean>;
179
+ /**
180
+ * Create detector based on package.json dependencies
181
+ */
182
+ export declare function createDependencyDetector(dependencies: string[]): (context: BuildContext) => Promise<boolean>;
183
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Adapter Factory - Simplifies the creation of framework adapters
3
+ *
4
+ * Provides factory functions and preset configurations to reduce boilerplate code
5
+ */
6
+ import path from "path";
7
+ import { pathExists, readFile, detectConfigFile, detectBuildDir, detectServerEntry, generateServerWrapperCode, createDefaultMeta, logBuildArtifacts, } from "./utils.js";
8
+ import { ViteConfigParser, } from "./vite-config-parser.js";
9
+ import { parseRoutesFromSources, } from "./route-parser.js";
10
+ /**
11
+ * Built-in server wrapper presets (generic, framework-agnostic)
12
+ *
13
+ * Note: All presets use `serverBuild` (namespace import) and `serverExports` (rest exports)
14
+ * which are automatically provided by generateServerWrapperCode()
15
+ *
16
+ * Framework-specific presets should be defined in their respective adapter packages.
17
+ */
18
+ export const SERVER_WRAPPER_PRESETS = {
19
+ /** Generic handler - detects default or handler export */
20
+ generic: {
21
+ handlerSetup: `
22
+ const handler = typeof defaultExport === 'function'
23
+ ? defaultExport
24
+ : (typeof serverExports.handler === 'function' ? serverExports.handler : null);
25
+
26
+ if (!handler) {
27
+ throw new Error('No handler found in server build');
28
+ }`.trim(),
29
+ handlerCall: "handler(webRequest, ...args)",
30
+ },
31
+ /** Hono style - supports app.fetch */
32
+ hono: {
33
+ handlerSetup: `
34
+ const getHandler = () => {
35
+ if (typeof defaultExport === 'function') return defaultExport;
36
+ if (typeof serverExports.fetch === 'function') return serverExports.fetch;
37
+ if (typeof serverExports.app === 'object' && typeof serverExports.app.fetch === 'function') {
38
+ return serverExports.app.fetch.bind(serverExports.app);
39
+ }
40
+ throw new Error('No handler found in server build');
41
+ };
42
+ const serverHandler = getHandler();`.trim(),
43
+ handlerCall: "serverHandler(webRequest, ...args)",
44
+ },
45
+ /** Vite SSR - standard Vite SSR with render function */
46
+ viteSSR: {
47
+ requiresHtmlTemplate: true,
48
+ handlerSetup: `
49
+ const renderFn = typeof serverExports.render === 'function'
50
+ ? serverExports.render
51
+ : (typeof defaultExport === 'function' ? defaultExport : null);
52
+
53
+ if (!renderFn) {
54
+ throw new Error('No render function found in server build. Expected export: render(url)');
55
+ }
56
+
57
+ async function handleSSRRequest(request) {
58
+ const url = new URL(request.url);
59
+ const pathname = url.pathname;
60
+
61
+ try {
62
+ const rendered = await renderFn(pathname);
63
+ let html = __HTML_TEMPLATE__
64
+ .replace('<!--app-head-->', rendered.head ?? '')
65
+ .replace('<!--app-html-->', rendered.html ?? '');
66
+
67
+ return new Response(html, {
68
+ status: 200,
69
+ headers: { 'Content-Type': 'text/html; charset=utf-8' }
70
+ });
71
+ } catch (error) {
72
+ console.error('SSR render error:', error);
73
+ return new Response('Internal Server Error: ' + error.message, {
74
+ status: 500,
75
+ headers: { 'Content-Type': 'text/plain' }
76
+ });
77
+ }
78
+ }`.trim(),
79
+ handlerCall: "handleSSRRequest(webRequest)",
80
+ },
81
+ };
82
+ // ============================================================================
83
+ // Internal Helper Functions
84
+ // ============================================================================
85
+ /**
86
+ * Create default detect function based on config files
87
+ */
88
+ function createDefaultDetector(configFiles) {
89
+ return async (context) => {
90
+ const configPath = await detectConfigFile(context.projectRoot, configFiles);
91
+ return configPath !== null;
92
+ };
93
+ }
94
+ /**
95
+ * Create default getBuildArtifacts function
96
+ * Uses ViteConfigParser for comprehensive Vite config support:
97
+ * - build.outDir: custom output directory
98
+ * - build.assetsDir: static assets directory name
99
+ * - build.ssr: SSR entry point
100
+ * - build.manifest: manifest.json generation
101
+ * - base: public base path
102
+ * - rollupOptions.input: entry file(s)
103
+ * - rollupOptions.output.dir: output directory override
104
+ * - rollupOptions.output.entryFileNames: output filename pattern
105
+ * - rollupOptions.output.assetFileNames: asset filename pattern
106
+ * - rollupOptions.output.chunkFileNames: chunk filename pattern
107
+ */
108
+ function createDefaultBuildArtifactsGetter(name, buildDirs, serverEntryFiles, options) {
109
+ const { clientBuildDir, serverBuildDir, serverEntry } = options;
110
+ return async (context) => {
111
+ const { projectRoot, isSSR, logger, viteConfig } = context;
112
+ // Resolve client directory
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
+ // Use ViteConfigParser for comprehensive config support
124
+ if (viteConfig) {
125
+ const configParser = new ViteConfigParser(viteConfig, projectRoot, {
126
+ logger,
127
+ serverEntryCandidates: serverEntryFiles,
128
+ });
129
+ // Priority 1: Explicit serverBuildDir option
130
+ if (serverBuildDir) {
131
+ serverDir = path.join(projectRoot, serverBuildDir);
132
+ }
133
+ else {
134
+ // Priority 2: Get from Vite config using parser
135
+ serverDir = configParser.getServerDir();
136
+ }
137
+ // Priority 3: Fall back to predefined build directories
138
+ if (!serverDir) {
139
+ serverDir = await detectBuildDir(projectRoot, buildDirs.server);
140
+ }
141
+ // Find server entry
142
+ if (serverEntry) {
143
+ // Explicit server entry path provided
144
+ serverEntryPath = path.join(projectRoot, serverEntry);
145
+ }
146
+ else if (serverDir) {
147
+ // Priority 1: Try to get server entry from Vite config using parser
148
+ serverEntryPath = await configParser.findServerEntry(serverDir);
149
+ if (serverEntryPath) {
150
+ logger.verbose(`Using server entry from Vite config: ${serverEntryPath}`);
151
+ }
152
+ // Priority 2: Fall back to file detection with recursive search
153
+ if (!serverEntryPath) {
154
+ serverEntryPath = await detectServerEntry(serverDir, serverEntryFiles, {
155
+ recursive: true,
156
+ logger,
157
+ });
158
+ }
159
+ }
160
+ }
161
+ else {
162
+ // No Vite config available, use fallback detection
163
+ if (serverBuildDir) {
164
+ serverDir = path.join(projectRoot, serverBuildDir);
165
+ }
166
+ else {
167
+ serverDir = await detectBuildDir(projectRoot, buildDirs.server);
168
+ }
169
+ if (serverEntry) {
170
+ serverEntryPath = path.join(projectRoot, serverEntry);
171
+ }
172
+ else if (serverDir) {
173
+ serverEntryPath = await detectServerEntry(serverDir, serverEntryFiles, {
174
+ recursive: true,
175
+ logger,
176
+ });
177
+ }
178
+ }
179
+ }
180
+ logBuildArtifacts(logger, name, {
181
+ clientDir,
182
+ serverDir,
183
+ serverEntry: serverEntryPath,
184
+ });
185
+ return {
186
+ clientDir,
187
+ serverDir,
188
+ serverEntry: serverEntryPath,
189
+ assetsDir: clientDir,
190
+ };
191
+ };
192
+ }
193
+ /**
194
+ * Create generateRoutes hook
195
+ */
196
+ function createGenerateRoutesHook(effectiveRouteConfig, fallbackRoutes) {
197
+ return async (context) => {
198
+ const { projectRoot, isSSR, logger } = context;
199
+ // Try declarative route sources first
200
+ if (effectiveRouteConfig.sources && effectiveRouteConfig.sources.length > 0) {
201
+ let parsedRoutes = await parseRoutesFromSources(projectRoot, effectiveRouteConfig.sources, { isSSR, logger });
202
+ if (parsedRoutes.length > 0) {
203
+ // Apply transform if provided
204
+ if (effectiveRouteConfig.transform) {
205
+ parsedRoutes = effectiveRouteConfig.transform(parsedRoutes, context);
206
+ }
207
+ // Add SSR extra routes
208
+ if (isSSR && effectiveRouteConfig.ssrExtraRoutes) {
209
+ effectiveRouteConfig.ssrExtraRoutes.forEach((route) => {
210
+ parsedRoutes.push({ path: route, isStatic: false, srcRoute: route });
211
+ });
212
+ }
213
+ return parsedRoutes;
214
+ }
215
+ }
216
+ // Fallback to static route list
217
+ const routeList = fallbackRoutes.map((route) => ({
218
+ path: route,
219
+ isStatic: !isSSR,
220
+ srcRoute: route,
221
+ }));
222
+ if (isSSR && effectiveRouteConfig.ssrExtraRoutes) {
223
+ effectiveRouteConfig.ssrExtraRoutes.forEach((route) => {
224
+ routeList.push({ path: route, isStatic: false, srcRoute: route });
225
+ });
226
+ }
227
+ return routeList;
228
+ };
229
+ }
230
+ /**
231
+ * Create generateMeta hook
232
+ */
233
+ function createGenerateMetaHook(ssr404) {
234
+ return async (context, routeList) => {
235
+ const meta = createDefaultMeta(routeList, { isSSR: context.isSSR });
236
+ if (ssr404 !== undefined) {
237
+ meta.conf.ssr404 = typeof ssr404 === "function"
238
+ ? ssr404(context.isSSR)
239
+ : ssr404;
240
+ }
241
+ return meta;
242
+ };
243
+ }
244
+ /**
245
+ * Create generateServerWrapper hook
246
+ */
247
+ function createGenerateServerWrapperHook(wrapperConfig, htmlTemplatePaths) {
248
+ return async (context, wrapperCfg) => {
249
+ let handlerSetup = wrapperConfig.handlerSetup;
250
+ if (wrapperConfig.requiresHtmlTemplate) {
251
+ const { projectRoot, outputDir, logger } = context;
252
+ let htmlTemplate = "";
253
+ const templateCandidates = [
254
+ ...htmlTemplatePaths.map(p => path.join(projectRoot, p)),
255
+ path.join(projectRoot, outputDir, "assets/index.html"),
256
+ ];
257
+ for (const templatePath of templateCandidates) {
258
+ if (await pathExists(templatePath)) {
259
+ htmlTemplate = await readFile(templatePath);
260
+ logger.verbose(`Found HTML template: ${templatePath}`);
261
+ break;
262
+ }
263
+ }
264
+ if (!htmlTemplate) {
265
+ logger.warn("HTML template not found, using default template");
266
+ htmlTemplate = "<!DOCTYPE html><html><head><!--app-head--></head><body><div id=\"app\"><!--app-html--></div></body></html>";
267
+ }
268
+ const escapedTemplate = JSON.stringify(htmlTemplate);
269
+ handlerSetup = `const __HTML_TEMPLATE__ = ${escapedTemplate};\n\n${handlerSetup}`;
270
+ }
271
+ return generateServerWrapperCode({
272
+ serverEntryPath: wrapperCfg.serverEntryPath,
273
+ imports: wrapperConfig.imports,
274
+ handlerSetup,
275
+ handlerCall: wrapperConfig.handlerCall,
276
+ });
277
+ };
278
+ }
279
+ /**
280
+ * Create beforeBundle hook if esbuildConfig is provided
281
+ */
282
+ function createBeforeBundleHook(esbuildConfig) {
283
+ return async (context, bundleConfig) => {
284
+ const absWorkingDir = typeof esbuildConfig.absWorkingDir === "function"
285
+ ? esbuildConfig.absWorkingDir(context)
286
+ : esbuildConfig.absWorkingDir;
287
+ return {
288
+ ...bundleConfig,
289
+ external: esbuildConfig.external || bundleConfig.external,
290
+ esbuildOptions: {
291
+ ...bundleConfig.esbuildOptions,
292
+ ...esbuildConfig.options,
293
+ ...(absWorkingDir ? { absWorkingDir } : {}),
294
+ },
295
+ };
296
+ };
297
+ }
298
+ // ============================================================================
299
+ // Main Factory Function
300
+ // ============================================================================
301
+ /**
302
+ * Create a framework adapter
303
+ */
304
+ export function createFrameworkAdapter(config, options = {}, overrides) {
305
+ const { name, configFiles, buildDirs, serverEntryFiles = ["index.js", "index.mjs", "entry.js", "main.js"], serverWrapper = "generic", routeConfig, defaultRoutes = ["/"], ssrExtraRoutes = [], ssr404, htmlTemplatePaths = ["dist/client/index.html", "dist/index.html"], esbuildConfig, } = config;
306
+ const { routes = routeConfig?.defaultRoutes || defaultRoutes } = options;
307
+ // Merge route config with legacy options
308
+ const effectiveRouteConfig = {
309
+ sources: routeConfig?.sources,
310
+ defaultRoutes: routeConfig?.defaultRoutes || defaultRoutes,
311
+ ssrExtraRoutes: routeConfig?.ssrExtraRoutes || ssrExtraRoutes,
312
+ transform: routeConfig?.transform,
313
+ };
314
+ const wrapperConfig = typeof serverWrapper === "string"
315
+ ? SERVER_WRAPPER_PRESETS[serverWrapper]
316
+ : serverWrapper;
317
+ // Build adapter using helper functions
318
+ const baseAdapter = {
319
+ name,
320
+ detect: createDefaultDetector(configFiles),
321
+ getBuildArtifacts: createDefaultBuildArtifactsGetter(name, buildDirs, serverEntryFiles, options),
322
+ hooks: {
323
+ generateRoutes: createGenerateRoutesHook(effectiveRouteConfig, routes),
324
+ generateMeta: createGenerateMetaHook(ssr404),
325
+ generateServerWrapper: createGenerateServerWrapperHook(wrapperConfig, htmlTemplatePaths),
326
+ ...(esbuildConfig ? { beforeBundle: createBeforeBundleHook(esbuildConfig) } : {}),
327
+ },
328
+ };
329
+ if (overrides) {
330
+ return {
331
+ ...baseAdapter,
332
+ ...overrides,
333
+ name: overrides.name || baseAdapter.name,
334
+ hooks: { ...baseAdapter.hooks, ...overrides.hooks },
335
+ };
336
+ }
337
+ return baseAdapter;
338
+ }
339
+ /**
340
+ * Create a stateful adapter (for complex adapters needing shared state)
341
+ */
342
+ export function createStatefulAdapter(initialState, factory) {
343
+ return factory({ ...initialState });
344
+ }
345
+ /**
346
+ * Create a server wrapper preset
347
+ *
348
+ * Helper function to create a properly typed preset with validation and normalization.
349
+ * Automatically trims handlerSetup and validates required fields.
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * const myPreset = createServerWrapperPreset({
354
+ * imports: `import { handler } from 'my-framework';`,
355
+ * handlerSetup: `const requestHandler = handler(serverExports);`,
356
+ * handlerCall: "requestHandler(webRequest)",
357
+ * });
358
+ * ```
359
+ */
360
+ export function createServerWrapperPreset(config) {
361
+ if (!config.handlerSetup) {
362
+ throw new Error("Server wrapper preset must have handlerSetup");
363
+ }
364
+ if (!config.handlerCall) {
365
+ throw new Error("Server wrapper preset must have handlerCall");
366
+ }
367
+ return {
368
+ imports: config.imports,
369
+ handlerSetup: config.handlerSetup.trim(),
370
+ handlerCall: config.handlerCall,
371
+ requiresHtmlTemplate: config.requiresHtmlTemplate,
372
+ };
373
+ }
374
+ /**
375
+ * Extend core server wrapper presets with custom presets
376
+ *
377
+ * This utility provides a standardized way for adapters to:
378
+ * 1. Add framework-specific presets while keeping core presets
379
+ * 2. Get a type-safe resolver function
380
+ * 3. Export extended presets for external use
381
+ *
382
+ * Custom presets are automatically validated and normalized using createServerWrapperPreset().
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * // In vite-adapter - can pass raw config, will be validated automatically
387
+ * const { presets, resolvePreset } = extendServerWrapperPresets({
388
+ * vike: {
389
+ * imports: `import { renderPage } from 'vike/server';`,
390
+ * handlerSetup: `...`,
391
+ * handlerCall: "handleVikeRequest(webRequest)",
392
+ * },
393
+ * });
394
+ *
395
+ * export const SERVER_WRAPPER_PRESETS = presets;
396
+ * export type ServerWrapperPresetName = keyof typeof presets;
397
+ *
398
+ * // Use in adapter
399
+ * const resolved = resolvePreset(options.serverWrapper || "viteSSR");
400
+ * ```
401
+ */
402
+ export function extendServerWrapperPresets(customPresets) {
403
+ // Validate and normalize all custom presets
404
+ const normalizedCustomPresets = {};
405
+ for (const [key, preset] of Object.entries(customPresets)) {
406
+ normalizedCustomPresets[key] =
407
+ createServerWrapperPreset(preset);
408
+ }
409
+ const extendedPresets = {
410
+ ...SERVER_WRAPPER_PRESETS,
411
+ ...normalizedCustomPresets,
412
+ };
413
+ function resolvePreset(preset) {
414
+ if (typeof preset === "string") {
415
+ const resolved = extendedPresets[preset];
416
+ if (!resolved) {
417
+ throw new Error(`Unknown server wrapper preset: "${preset}". Available presets: ${Object.keys(extendedPresets).join(", ")}`);
418
+ }
419
+ return resolved;
420
+ }
421
+ // Validate and normalize inline preset
422
+ return createServerWrapperPreset(preset);
423
+ }
424
+ function getPresetNames() {
425
+ return Object.keys(extendedPresets);
426
+ }
427
+ return {
428
+ presets: extendedPresets,
429
+ resolvePreset,
430
+ getPresetNames,
431
+ };
432
+ }
433
+ /**
434
+ * Combine multiple detector functions (returns true if any matches)
435
+ */
436
+ export function combineDetectors(...detectors) {
437
+ return async (context) => {
438
+ for (const detector of detectors) {
439
+ if (await detector(context)) {
440
+ return true;
441
+ }
442
+ }
443
+ return false;
444
+ };
445
+ }
446
+ /**
447
+ * Create detector based on config files
448
+ */
449
+ export function createConfigDetector(configFiles) {
450
+ return async (context) => {
451
+ return (await detectConfigFile(context.projectRoot, configFiles)) !== null;
452
+ };
453
+ }
454
+ /**
455
+ * Create detector based on build directories
456
+ */
457
+ export function createBuildDirDetector(buildDirs) {
458
+ return async (context) => {
459
+ return (await detectBuildDir(context.projectRoot, buildDirs)) !== null;
460
+ };
461
+ }
462
+ /**
463
+ * Create detector based on package.json dependencies
464
+ */
465
+ export function createDependencyDetector(dependencies) {
466
+ return async (context) => {
467
+ const packageJsonPath = path.join(context.projectRoot, "package.json");
468
+ if (!await pathExists(packageJsonPath))
469
+ return false;
470
+ try {
471
+ const content = await readFile(packageJsonPath);
472
+ const packageJson = JSON.parse(content);
473
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
474
+ return dependencies.some((dep) => dep in deps);
475
+ }
476
+ catch (error) {
477
+ context.logger?.verbose?.(`Failed to parse package.json: ${error instanceof Error ? error.message : String(error)}`);
478
+ return false;
479
+ }
480
+ };
481
+ }
482
+ //# sourceMappingURL=factory.js.map