@cedarjs/vite 5.0.0-canary.13889 → 5.0.0-canary.13892

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 (32) hide show
  1. package/dist/apiDevMiddleware.d.ts +15 -0
  2. package/dist/apiDevMiddleware.d.ts.map +1 -0
  3. package/dist/{apiDevServer.js → apiDevMiddleware.js} +81 -101
  4. package/dist/build/build.d.ts +1 -1
  5. package/dist/build/build.d.ts.map +1 -1
  6. package/dist/build/build.js +2 -0
  7. package/dist/buildApp.d.ts +15 -0
  8. package/dist/buildApp.d.ts.map +1 -0
  9. package/dist/buildApp.js +150 -0
  10. package/dist/cedar-unified-dev.js +59 -5
  11. package/dist/cjs/{apiDevServer.js → apiDevMiddleware.js} +88 -104
  12. package/dist/cjs/build/build.js +3 -0
  13. package/dist/cjs/buildApp.js +181 -0
  14. package/dist/cjs/cedar-unified-dev.js +59 -5
  15. package/dist/cjs/index.js +3 -3
  16. package/dist/cjs/plugins/vite-plugin-cedar-cjs-compat.js +341 -0
  17. package/dist/cjs/plugins/vite-plugin-cedar-wait-for-api-server.js +2 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -2
  21. package/dist/plugins/vite-plugin-cedar-cjs-compat.d.ts +23 -0
  22. package/dist/plugins/vite-plugin-cedar-cjs-compat.d.ts.map +1 -0
  23. package/dist/plugins/vite-plugin-cedar-cjs-compat.js +307 -0
  24. package/dist/plugins/vite-plugin-cedar-wait-for-api-server.d.ts.map +1 -1
  25. package/dist/plugins/vite-plugin-cedar-wait-for-api-server.js +2 -1
  26. package/package.json +15 -19
  27. package/dist/apiDevServer.d.ts +0 -18
  28. package/dist/apiDevServer.d.ts.map +0 -1
  29. package/dist/cjs/plugins/vite-plugin-cedar-dev-dispatcher.js +0 -223
  30. package/dist/plugins/vite-plugin-cedar-dev-dispatcher.d.ts +0 -3
  31. package/dist/plugins/vite-plugin-cedar-dev-dispatcher.d.ts.map +0 -1
  32. package/dist/plugins/vite-plugin-cedar-dev-dispatcher.js +0 -189
@@ -0,0 +1,15 @@
1
+ import type { ViteDevServer } from 'vite';
2
+ export declare function loadApiFunctions(viteServer: ViteDevServer): Promise<void>;
3
+ export declare function createApiViteServer(): Promise<ViteDevServer>;
4
+ export declare function setupHmrHandlers(viteServer: ViteDevServer): void;
5
+ /**
6
+ * Creates a fetch-native handler for API requests.
7
+ * Routes GraphQL to Yoga and Lambda functions to their handlers.
8
+ */
9
+ export declare function createApiFetchHandler(): (request: Request) => Promise<Response>;
10
+ export declare function startApiDevMiddleware(): Promise<{
11
+ viteServer: ViteDevServer;
12
+ close: () => Promise<void>;
13
+ handler: (request: Request) => Promise<Response>;
14
+ }>;
15
+ //# sourceMappingURL=apiDevMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiDevMiddleware.d.ts","sourceRoot":"","sources":["../src/apiDevMiddleware.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,MAAM,CAAA;AA2BrD,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,aAAa,iBAe/D;AA0FD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC,CAuElE;AA0BD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,CA0EhE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,KAIrB,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAoEnD;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IACrD,UAAU,EAAE,aAAa,CAAA;IACzB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CACjD,CAAC,CAcD"}
@@ -1,12 +1,9 @@
1
1
  import { glob } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
- import fastifyUrlData from "@fastify/url-data";
5
4
  import ansis from "ansis";
6
- import fastify from "fastify";
7
- import fastifyRawBody from "fastify-raw-body";
8
- import { createServer as createViteServer, normalizePath } from "vite";
9
- import { requestHandler } from "@cedarjs/api-server/requestHandlers";
5
+ import { normalizePath } from "vite";
6
+ import { buildCedarContext, wrapLegacyHandler } from "@cedarjs/api/runtime";
10
7
  import {
11
8
  getApiSideBabelPlugins,
12
9
  transformWithBabel
@@ -56,13 +53,6 @@ async function internalLoadApiFunctions(viteServer) {
56
53
  } catch {
57
54
  srcFunctions = [];
58
55
  }
59
- const graphqlFunctionIndex = srcFunctions.findIndex(
60
- (f) => path.basename(f).startsWith("graphql.")
61
- );
62
- if (graphqlFunctionIndex > 0) {
63
- const [graphqlFn] = srcFunctions.splice(graphqlFunctionIndex, 1);
64
- srcFunctions.unshift(graphqlFn);
65
- }
66
56
  console.log(ansis.dim.italic("Importing Server Functions... "));
67
57
  const tsImport = Date.now();
68
58
  let extractedGraphqlOptions = null;
@@ -88,7 +78,7 @@ async function internalLoadApiFunctions(viteServer) {
88
78
  );
89
79
  } else {
90
80
  console.warn(
91
- `[apiDevServer] No handler export found in function: ${fnPath}`
81
+ `[apiDevMiddleware] No handler export found in function: ${fnPath}`
92
82
  );
93
83
  }
94
84
  if (routeName === "graphql" && "__rw_graphqlOptions" in mod) {
@@ -97,7 +87,7 @@ async function internalLoadApiFunctions(viteServer) {
97
87
  } catch (err) {
98
88
  viteServer.ssrFixStacktrace(err);
99
89
  console.error(
100
- `[apiDevServer] Failed to load function "${routeName}" from ${fnPath}:`,
90
+ `[apiDevMiddleware] Failed to load function "${routeName}" from ${fnPath}:`,
101
91
  err
102
92
  );
103
93
  }
@@ -106,56 +96,29 @@ async function internalLoadApiFunctions(viteServer) {
106
96
  if (extractedGraphqlOptions) {
107
97
  const { yoga } = await createGraphQLYoga(extractedGraphqlOptions);
108
98
  graphqlYoga = yoga;
99
+ } else {
100
+ graphqlYoga = null;
109
101
  }
110
102
  console.log(
111
103
  ansis.dim.italic("...Done importing in " + (Date.now() - tsImport) + " ms")
112
104
  );
113
105
  }
114
- function createFetchRequestFromFastify(req) {
115
- const requestBody = req.method === "GET" || req.method === "HEAD" ? void 0 : typeof req.body === "string" ? req.body : req.body ? JSON.stringify(req.body) : void 0;
116
- const href = `${req.protocol}://${req.host}${req.raw.url ?? "/"}`;
117
- return new Request(href, {
118
- method: req.method,
119
- headers: req.headers,
120
- body: requestBody
121
- });
122
- }
123
- async function startApiDevServer(port) {
106
+ async function createApiViteServer() {
124
107
  const cedarPaths = getPaths();
125
108
  const cedarConfig = getConfig();
126
- const apiPort = port || cedarConfig.api.port || 8911;
127
- const apiHost = cedarConfig.api.host || "::";
128
109
  const isEsm = projectSideIsEsm("api");
129
110
  const normalizedBase = normalizePath(cedarPaths.base);
130
- const normalizedApiSrc = normalizePath(cedarPaths.api.src);
131
- const normalizedApiBase = normalizePath(cedarPaths.api.base);
132
- const viteServer = await createViteDevServer(
133
- cedarPaths,
134
- cedarConfig,
135
- isEsm,
136
- normalizedBase
137
- );
138
- console.log(ansis.dim.italic("Starting API dev server..."));
139
- await loadApiFunctions(viteServer);
140
- setupHmrHandlers(viteServer, normalizedApiSrc, normalizedApiBase);
141
- const app = await createFastifyApp(apiPort, apiHost);
142
- const close = async () => {
143
- await app.close();
144
- await viteServer.close();
145
- };
146
- return { viteServer, close };
147
- }
148
- async function createViteDevServer(cedarPaths, cedarConfig, projectIsEsm, normalizedBase) {
149
111
  const babelPlugins = getApiSideBabelPlugins({
150
112
  openTelemetry: (cedarConfig.experimental?.opentelemetry?.enabled ?? false) && (cedarConfig.experimental?.opentelemetry?.wrapApi ?? false),
151
- projectIsEsm
113
+ projectIsEsm: isEsm
152
114
  });
153
115
  const workspacePkgSourceMap = Object.fromEntries(
154
116
  Object.entries(getWorkspacePackageAliases(cedarPaths, cedarConfig)).map(
155
117
  ([name, sourceFile]) => [name, normalizePath(sourceFile)]
156
118
  )
157
119
  );
158
- const viteServer = await createViteServer({
120
+ const { createServer: createViteServer } = await import("vite");
121
+ return createViteServer({
159
122
  configFile: false,
160
123
  root: cedarPaths.api.base,
161
124
  appType: "custom",
@@ -165,10 +128,6 @@ async function createViteDevServer(cedarPaths, cedarConfig, projectIsEsm, normal
165
128
  middlewareMode: true
166
129
  },
167
130
  resolve: {
168
- // Map workspace package names directly to their TypeScript source entry
169
- // files. This is processed by Vite's built-in alias plugin (enforce:
170
- // 'pre') which runs before vite:resolve and correctly intercepts imports
171
- // in the SSR module runner context.
172
131
  alias: workspacePkgSourceMap
173
132
  },
174
133
  plugins: [
@@ -203,7 +162,6 @@ async function createViteDevServer(cedarPaths, cedarConfig, projectIsEsm, normal
203
162
  }
204
163
  ]
205
164
  });
206
- return viteServer;
207
165
  }
208
166
  function invalidateApiModules(viteServer, normalizedApiSrc) {
209
167
  const invalidated = /* @__PURE__ */ new Set();
@@ -223,7 +181,10 @@ function invalidateApiModules(viteServer, normalizedApiSrc) {
223
181
  }
224
182
  }
225
183
  }
226
- function setupHmrHandlers(viteServer, normalizedApiSrc, normalizedApiBase) {
184
+ function setupHmrHandlers(viteServer) {
185
+ const cedarPaths = getPaths();
186
+ const normalizedApiSrc = normalizePath(cedarPaths.api.src);
187
+ const normalizedApiBase = normalizePath(cedarPaths.api.base);
227
188
  viteServer.watcher.on("change", async (filePath) => {
228
189
  const normalizedFilePath = normalizePath(filePath);
229
190
  if (!normalizedFilePath.startsWith(normalizedApiSrc)) {
@@ -276,60 +237,79 @@ function setupHmrHandlers(viteServer, normalizedApiSrc, normalizedApiBase) {
276
237
  await loadApiFunctions(viteServer);
277
238
  });
278
239
  }
279
- async function createFastifyApp(apiPort, apiHost) {
280
- const logLevel = process.env.NODE_ENV === "development" ? "debug" : "info";
281
- const app = fastify({ logger: { level: logLevel } });
282
- await app.register(fastifyRawBody);
283
- app.register(fastifyUrlData);
284
- app.addContentTypeParser(
285
- ["application/x-www-form-urlencoded", "multipart/form-data"],
286
- { parseAs: "string" },
287
- app.defaultTextParser
288
- );
289
- const lambdaRequestHandler = async (req, reply) => {
290
- const { routeName } = req.params;
240
+ function createApiFetchHandler() {
241
+ const cedarConfig = getConfig();
242
+ const apiUrlPrefix = cedarConfig.web.apiUrl.replace(/\/$/, "");
243
+ return async (request) => {
244
+ const url = new URL(request.url);
245
+ let pathname = url.pathname;
246
+ if (pathname.startsWith(apiUrlPrefix + "/")) {
247
+ pathname = pathname.slice(apiUrlPrefix.length);
248
+ } else if (pathname === apiUrlPrefix) {
249
+ pathname = "/";
250
+ }
251
+ if (pathname === "/graphql" || pathname.startsWith("/graphql/")) {
252
+ if (!graphqlYoga) {
253
+ return new Response(
254
+ JSON.stringify({ error: "GraphQL Yoga instance not initialized" }),
255
+ { status: 503, headers: { "Content-Type": "application/json" } }
256
+ );
257
+ }
258
+ const yoga = graphqlYoga;
259
+ return getAsyncStoreInstance().run(/* @__PURE__ */ new Map(), async () => {
260
+ return yoga.handle(request, { request });
261
+ });
262
+ }
263
+ const match = pathname.match(/^\/([^/]+)(?:\/.*)?$/);
264
+ if (!match) {
265
+ return new Response("Not Found", { status: 404 });
266
+ }
267
+ const routeName = match[1];
291
268
  const handler = LAMBDA_FUNCTIONS[routeName];
292
269
  if (!handler) {
293
- const errorMessage = `Function "${routeName}" was not found.`;
294
- req.log.error(errorMessage);
295
- reply.status(404);
296
- reply.send({
297
- error: errorMessage,
298
- availableFunctions: Object.keys(LAMBDA_FUNCTIONS)
270
+ return new Response(
271
+ JSON.stringify({
272
+ error: `Function "${routeName}" was not found.`,
273
+ availableFunctions: Object.keys(LAMBDA_FUNCTIONS)
274
+ }),
275
+ { status: 404, headers: { "Content-Type": "application/json" } }
276
+ );
277
+ }
278
+ try {
279
+ const ctx = await buildCedarContext(request, {
280
+ params: { routeName }
299
281
  });
300
- return;
282
+ const cedarHandler = wrapLegacyHandler(handler);
283
+ return await cedarHandler(request, ctx);
284
+ } catch (err) {
285
+ console.error(
286
+ `[apiDevMiddleware] Error handling function "${routeName}":`,
287
+ err
288
+ );
289
+ return new Response(
290
+ JSON.stringify({
291
+ error: err instanceof Error ? err.message : "Internal Server Error"
292
+ }),
293
+ { status: 500, headers: { "Content-Type": "application/json" } }
294
+ );
301
295
  }
302
- return requestHandler(req, reply, handler);
303
296
  };
304
- const graphqlHandler = async (req, reply) => {
305
- if (!graphqlYoga) {
306
- return reply.status(503).send({ error: "GraphQL Yoga instance not initialized" });
307
- }
308
- const request = createFetchRequestFromFastify(req);
309
- const yoga = graphqlYoga;
310
- const response = await getAsyncStoreInstance().run(/* @__PURE__ */ new Map(), async () => {
311
- return yoga.handle(request, { req, reply });
312
- });
313
- return response;
297
+ }
298
+ async function startApiDevMiddleware() {
299
+ const viteServer = await createApiViteServer();
300
+ console.log(ansis.dim.italic("Starting API dev server..."));
301
+ await loadApiFunctions(viteServer);
302
+ setupHmrHandlers(viteServer);
303
+ const close = async () => {
304
+ await viteServer.close();
314
305
  };
315
- app.all("/graphql", graphqlHandler);
316
- app.all("/graphql/*", graphqlHandler);
317
- app.all("/:routeName", lambdaRequestHandler);
318
- app.all("/:routeName/*", lambdaRequestHandler);
319
- app.addHook("onListen", (done) => {
320
- const addr = app.server.address();
321
- const listenPort = addr && typeof addr === "object" ? addr.port : apiPort;
322
- console.log(
323
- `API dev server listening at ${ansis.magenta(`http://localhost:${listenPort}/`)}`
324
- );
325
- console.log(
326
- `GraphQL endpoint at ${ansis.magenta(`http://localhost:${listenPort}/graphql`)}`
327
- );
328
- done();
329
- });
330
- await app.listen({ port: apiPort, host: apiHost });
331
- return app;
306
+ const handler = createApiFetchHandler();
307
+ return { viteServer, close, handler };
332
308
  }
333
309
  export {
334
- startApiDevServer
310
+ createApiFetchHandler,
311
+ createApiViteServer,
312
+ loadApiFunctions,
313
+ setupHmrHandlers,
314
+ startApiDevMiddleware
335
315
  };
@@ -1,3 +1,4 @@
1
+ export { buildCedarApp } from '../buildApp.js';
1
2
  interface BuildOptions {
2
3
  verbose?: boolean;
3
4
  }
@@ -6,5 +7,4 @@ interface BuildOptions {
6
7
  * because we want to set the process.cwd to web.base
7
8
  */
8
9
  export declare const buildWeb: ({ verbose }: BuildOptions) => Promise<import("rollup").RollupOutput | import("rollup").RollupOutput[] | import("rollup").RollupWatcher>;
9
- export {};
10
10
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/build/build.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAU,aAAa,YAAY,8GAoBvD,CAAA"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/build/build.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAU,aAAa,YAAY,8GAoBvD,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { getPaths } from "@cedarjs/project-config";
2
+ import { buildCedarApp } from "../buildApp.js";
2
3
  const buildWeb = async ({ verbose }) => {
3
4
  const { build } = await import("vite");
4
5
  const viteConfig = getPaths().web.viteConfig;
@@ -17,5 +18,6 @@ const buildWeb = async ({ verbose }) => {
17
18
  });
18
19
  };
19
20
  export {
21
+ buildCedarApp,
20
22
  buildWeb
21
23
  };
@@ -0,0 +1,15 @@
1
+ export interface BuildCedarAppOptions {
2
+ verbose?: boolean;
3
+ workspace?: string[];
4
+ }
5
+ /**
6
+ * Unified build for Cedar apps using Vite's builder API.
7
+ *
8
+ * Declares `client` and `api` environments and builds them in a single
9
+ * orchestrated pass. The web client is built from the project's web Vite
10
+ * config. The API is built as an SSR environment with Babel transforms and
11
+ * `src/` import redirection so it resolves correctly even though the builder
12
+ * root is the web source directory.
13
+ */
14
+ export declare function buildCedarApp({ verbose, workspace, }?: BuildCedarAppOptions): Promise<void>;
15
+ //# sourceMappingURL=buildApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildApp.d.ts","sourceRoot":"","sources":["../src/buildApp.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,EAClC,OAAe,EACf,SAA0B,GAC3B,GAAE,oBAAyB,iBA+J3B"}
@@ -0,0 +1,150 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createBuilder, normalizePath } from "vite";
4
+ import {
5
+ getApiSideBabelPlugins,
6
+ transformWithBabel
7
+ } from "@cedarjs/babel-config";
8
+ import { findApiFiles } from "@cedarjs/internal/dist/files.js";
9
+ import { getConfig, getPaths, projectSideIsEsm } from "@cedarjs/project-config";
10
+ import { getWorkspacePackageAliases } from "./lib/workspacePackageAliases.js";
11
+ function resolveWithExtensions(id) {
12
+ if (fs.existsSync(id)) {
13
+ return id;
14
+ }
15
+ for (const ext of [".js", ".ts", ".jsx", ".tsx", ".mjs", ".mts"]) {
16
+ const withExt = id + ext;
17
+ if (fs.existsSync(withExt)) {
18
+ return withExt;
19
+ }
20
+ }
21
+ return id;
22
+ }
23
+ async function buildCedarApp({
24
+ verbose = false,
25
+ workspace = ["api", "web"]
26
+ } = {}) {
27
+ const cedarPaths = getPaths();
28
+ const cedarConfig = getConfig();
29
+ const environments = {};
30
+ if (workspace.includes("web")) {
31
+ environments.client = {
32
+ build: {
33
+ outDir: cedarPaths.web.dist
34
+ }
35
+ };
36
+ }
37
+ if (workspace.includes("api")) {
38
+ const isEsm = projectSideIsEsm("api");
39
+ const format = isEsm ? "es" : "cjs";
40
+ const apiFiles = findApiFiles();
41
+ const input = {};
42
+ for (const f of apiFiles) {
43
+ const key = path.relative(cedarPaths.api.src, f).replace(/\.(ts|tsx|mts|js|jsx|mjs)$/, "");
44
+ input[key] = f;
45
+ }
46
+ environments.api = {
47
+ build: {
48
+ ssr: true,
49
+ sourcemap: true,
50
+ outDir: cedarPaths.api.dist,
51
+ emptyOutDir: true,
52
+ rollupOptions: {
53
+ input,
54
+ output: {
55
+ format,
56
+ preserveModules: true,
57
+ preserveModulesRoot: cedarPaths.api.src,
58
+ entryFileNames: "[name].js"
59
+ },
60
+ external: (id) => {
61
+ if (id.startsWith("node:")) {
62
+ return true;
63
+ }
64
+ if (!id.startsWith(".") && !path.isAbsolute(id)) {
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ }
70
+ }
71
+ };
72
+ }
73
+ const babelPlugins = workspace.includes("api") ? getApiSideBabelPlugins({
74
+ openTelemetry: (cedarConfig.experimental?.opentelemetry?.enabled ?? false) && (cedarConfig.experimental?.opentelemetry?.wrapApi ?? false),
75
+ projectIsEsm: projectSideIsEsm("api")
76
+ }) : null;
77
+ const workspacePkgSourceMap = workspace.includes("api") ? Object.fromEntries(
78
+ Object.entries(getWorkspacePackageAliases(cedarPaths, cedarConfig)).map(
79
+ ([name, sourceFile]) => [name, normalizePath(sourceFile)]
80
+ )
81
+ ) : {};
82
+ const plugins = [
83
+ {
84
+ name: "cedar-build-app-cleanup",
85
+ configResolved(config) {
86
+ if (!workspace.includes("web") && config.environments.client) {
87
+ delete config.environments.client;
88
+ }
89
+ if (!environments.ssr && config.environments.ssr) {
90
+ delete config.environments.ssr;
91
+ }
92
+ }
93
+ }
94
+ ];
95
+ if (workspace.includes("api")) {
96
+ plugins.push({
97
+ name: "cedar-api-src-redirect",
98
+ enforce: "pre",
99
+ resolveId(id, importer) {
100
+ if (!importer?.startsWith(cedarPaths.api.src)) {
101
+ return null;
102
+ }
103
+ if (id.startsWith("src/")) {
104
+ return resolveWithExtensions(
105
+ path.join(cedarPaths.api.src, id.slice(4))
106
+ );
107
+ }
108
+ return null;
109
+ }
110
+ });
111
+ if (babelPlugins) {
112
+ plugins.push({
113
+ name: "cedar-vite-api-babel-transform",
114
+ async transform(_code, id) {
115
+ if (!/\.(js|ts|tsx|jsx)$/.test(id)) {
116
+ return null;
117
+ }
118
+ if (id.includes("node_modules")) {
119
+ return null;
120
+ }
121
+ if (!normalizePath(id).startsWith(normalizePath(cedarPaths.api.base))) {
122
+ return null;
123
+ }
124
+ const transformedCode = await transformWithBabel(id, babelPlugins);
125
+ if (transformedCode?.code) {
126
+ return {
127
+ code: transformedCode.code,
128
+ map: transformedCode.map ?? null
129
+ };
130
+ }
131
+ return null;
132
+ }
133
+ });
134
+ }
135
+ }
136
+ const builder = await createBuilder({
137
+ configFile: cedarPaths.web.viteConfig,
138
+ envFile: false,
139
+ logLevel: verbose ? "info" : "warn",
140
+ environments,
141
+ resolve: {
142
+ alias: workspacePkgSourceMap
143
+ },
144
+ plugins
145
+ });
146
+ return builder.buildApp();
147
+ }
148
+ export {
149
+ buildCedarApp
150
+ };
@@ -1,9 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import { createServerAdapter } from "@whatwg-node/server";
2
3
  import { createServer } from "vite";
3
4
  import yargsParser from "yargs-parser";
4
5
  import { getPaths, getConfig } from "@cedarjs/project-config";
5
- import { startApiDevServer } from "./apiDevServer.js";
6
+ import { startApiDevMiddleware } from "./apiDevMiddleware.js";
7
+ function isViteInternalRequest(url) {
8
+ const pathname = url.split("?")[0];
9
+ return pathname.startsWith("/@") || pathname.startsWith("/__vite") || pathname.startsWith("/__hmr");
10
+ }
11
+ function isApiRequest(url, apiUrl, apiGqlUrl) {
12
+ return url === apiUrl || url.startsWith(apiUrl + "/") || url.startsWith(apiUrl + "?") || url === apiGqlUrl || url.startsWith(apiGqlUrl + "/") || url.startsWith(apiGqlUrl + "?");
13
+ }
6
14
  const startUnifiedDevServer = async () => {
15
+ process.env.__CEDAR_UNIFIED_DEV = "true";
7
16
  const rwPaths = getPaths();
8
17
  const cedarConfig = getConfig();
9
18
  const configFile = rwPaths.web.viteConfig;
@@ -14,7 +23,7 @@ const startUnifiedDevServer = async () => {
14
23
  force: forceOptimize,
15
24
  debug,
16
25
  port: portArg,
17
- apiPort: apiPortArg,
26
+ apiPort: _apiPortArg,
18
27
  _: _positional,
19
28
  ...serverArgs
20
29
  } = yargsParser(process.argv.slice(2), {
@@ -22,8 +31,8 @@ const startUnifiedDevServer = async () => {
22
31
  number: ["port", "apiPort"]
23
32
  });
24
33
  const webPort = portArg ?? cedarConfig.web.port ?? 8910;
25
- const apiPort = apiPortArg ?? cedarConfig.api.port ?? 8911;
26
- const { close: closeApi } = await startApiDevServer(apiPort);
34
+ const { close: closeApi, handler: apiHandler } = await startApiDevMiddleware();
35
+ const apiAdapter = createServerAdapter(apiHandler);
27
36
  const devServer = await createServer({
28
37
  configFile,
29
38
  // env file is handled by Cedar's plugins
@@ -36,7 +45,52 @@ const startUnifiedDevServer = async () => {
36
45
  port: webPort,
37
46
  ...serverArgs
38
47
  },
39
- logLevel: debug ? "info" : void 0
48
+ logLevel: debug ? "info" : void 0,
49
+ plugins: [
50
+ {
51
+ name: "cedar-api-middleware",
52
+ apply: "serve",
53
+ configureServer(server) {
54
+ const apiUrl = cedarConfig.web.apiUrl.replace(/\/$/, "");
55
+ const apiGqlUrl = cedarConfig.web.apiGraphQLUrl ?? apiUrl + "/graphql";
56
+ server.middlewares.use(
57
+ async (req, res, next) => {
58
+ const url = req.url ?? "/";
59
+ if (isViteInternalRequest(url)) {
60
+ return next();
61
+ }
62
+ if (!isApiRequest(url, apiUrl, apiGqlUrl)) {
63
+ return next();
64
+ }
65
+ try {
66
+ await apiAdapter(req, res);
67
+ } catch (err) {
68
+ console.error(
69
+ "[cedar-api-middleware] Error handling API request:",
70
+ err
71
+ );
72
+ if (!res.headersSent) {
73
+ res.writeHead(500, { "Content-Type": "application/json" });
74
+ }
75
+ res.end(
76
+ JSON.stringify(
77
+ {
78
+ errors: [
79
+ {
80
+ message: err instanceof Error ? err.message : "Internal Server Error"
81
+ }
82
+ ]
83
+ },
84
+ null,
85
+ 2
86
+ )
87
+ );
88
+ }
89
+ }
90
+ );
91
+ }
92
+ }
93
+ ]
40
94
  });
41
95
  await devServer.listen();
42
96
  process.stdin.on("data", async (data) => {