diff --git a/.changeset/cuddly-balloons-yawn.md b/.changeset/cuddly-balloons-yawn.md new file mode 100644 index 00000000..926c5cff --- /dev/null +++ b/.changeset/cuddly-balloons-yawn.md @@ -0,0 +1,6 @@ +--- +"vinxi": patch +"react-ssr-basic": patch +--- + +feat: bundle config file with esbuild before loading, adds support for ts, jsx, etc in config files diff --git a/packages/vinxi/lib/load-app.js b/packages/vinxi/lib/load-app.js index 1b9949cc..b2782da9 100644 --- a/packages/vinxi/lib/load-app.js +++ b/packages/vinxi/lib/load-app.js @@ -1,11 +1,12 @@ /// -import { loadConfig } from "c12"; import { existsSync } from "fs"; +import { rm } from "fs/promises"; +import { isBuiltin } from "module"; import { fileURLToPath, pathToFileURL } from "url"; import { createApp } from "./app.js"; import { log } from "./logger.js"; -import { join } from "./path.js"; +import { isAbsolute, join } from "./path.js"; function isBun() { return !!process.versions.bun; @@ -15,44 +16,97 @@ async function fileExists(path) { return isBun() ? Bun.file(path).exists() : existsSync(path); } +const bundleConfigFile = async (configFile, out) => { + const esbuild = await import("esbuild"); + + // const out = `app.config.timestamp_${Date.now()}.js`; + await esbuild.build({ + entryPoints: [configFile], + bundle: true, + outfile: out, + platform: "node", + format: "esm", + resolveExtensions: [".js", ".mjs", ".ts", ".jsx", ".tsx"], + plugins: [ + { + name: "externalize-deps", + setup(build) { + build.onResolve( + { filter: /^[^.].*/ }, + async ({ path: id, importer, kind }) => { + if ( + kind === "entry-point" || + isAbsolute(id) || + id.match(/node:.*/) + ) { + return; + } + + // With the `isNodeBuiltin` check above, this check captures if the builtin is a + // non-node built-in, which esbuild doesn't know how to handle. In that case, we + // externalize it so the non-node runtime handles it instead. + if (isBuiltin(id)) { + return { external: true }; + } + + return { external: true }; + }, + ); + }, + }, + ], + loader: { + ".js": "jsx", + ".ts": "tsx", + ".jsx": "jsx", + ".tsx": "tsx", + ".mjs": "jsx", + }, + }); +}; + async function loadFile({ ...options }) { if (options.name) { - for (const ext of ["js", "mjs", "ts"]) { - if (ext === "ts" && !isBun()) continue; - + for (const ext of ["js", "mjs", "ts", "tsx", "jsx"]) { const filepath = join(process.cwd(), `${options.name}.config.${ext}`); if (await fileExists(filepath)) { - return import( - pathToFileURL(filepath).href + `?time=${Date.now()}` - ).then((m) => ({ + let out = `${options.name}.config.timestamp_${Date.now()}.${ext}`; + await bundleConfigFile(`${options.name}.config.${ext}`, out); + const importedApp = import(pathToFileURL(out).href).then((m) => ({ config: m.default, })); + + await rm(out); + return importedApp; } } + + throw new Error(`Config file not found: ${options.name}`); } else if (options.configFile) { const ext = options.configFile.slice( options.configFile.lastIndexOf(".") + 1, ); - if (["js", "mjs", "ts"].includes(ext) && (ext !== "ts" || isBun())) { - const filepath = join(process.cwd(), options.configFile); + const configFileName = options.configFile.slice( + 0, + options.configFile.lastIndexOf("."), + ); + + if (["js", "mjs", "ts", "tsx", "jsx"].includes(ext)) { + const filepath = join(process.cwd(), `${configFileName}.${ext}`); if (await fileExists(filepath)) { - return import( - pathToFileURL(filepath).href + `?time=${Date.now()}` - ).then((m) => ({ + let out = `${configFileName}.timestamp_${Date.now()}.${ext}`; + await bundleConfigFile(options.configFile, out); + const importedApp = import(pathToFileURL(out).href).then((m) => ({ config: m.default, })); + await rm(out); + return importedApp; } } - } - return loadConfig({ - jitiOptions: { - esmResolve: true, - nativeModules: ["acorn"], - }, - ...options, - }); + throw new Error(`Config file not found: ${options.configFile}`); + } } /** diff --git a/packages/vinxi/lib/nitro-dev.js b/packages/vinxi/lib/nitro-dev.js index d31d81e5..024004b8 100644 --- a/packages/vinxi/lib/nitro-dev.js +++ b/packages/vinxi/lib/nitro-dev.js @@ -186,7 +186,6 @@ export async function createDevServer(nitro) { // Dev-only handlers for (const handler of nitro.options.devHandlers) { - console.log(handler); app.use( joinURL(nitro.options.runtimeConfig.app.baseURL, handler.route ?? "/"), handler.handler,