From 406fe42e69ef73ca3620405ac3a9d5c1bc8a861b Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Apr 2023 20:47:14 +0200 Subject: [PATCH 1/4] feat: hmr support for `routeRules` and `rutimeConfig` --- src/build.ts | 2 + src/cli/commands/dev.ts | 11 +++- src/index.ts | 2 +- src/nitro.ts | 18 +++++- src/options.ts | 132 ++++++++++++++++++++++------------------ src/rollup/config.ts | 3 +- src/types/nitro.ts | 7 +++ 7 files changed, 109 insertions(+), 66 deletions(-) diff --git a/src/build.ts b/src/build.ts index 2d40a2e023..f145dd3430 100644 --- a/src/build.ts +++ b/src/build.ts @@ -377,6 +377,8 @@ async function _watch(nitro: Nitro, rollupConfig: RollupConfig) { reloadWacher.close(); }); + nitro.hooks.hook("rollup:reload", () => reload()); + await reload(); } diff --git a/src/cli/commands/dev.ts b/src/cli/commands/dev.ts index 56246e93aa..2fdf59d474 100644 --- a/src/cli/commands/dev.ts +++ b/src/cli/commands/dev.ts @@ -35,16 +35,23 @@ export default defineCommand({ { watch: true, c12: { - async onUpdate({ getDiff }) { + async onUpdate({ getDiff, newConfig }) { const diff = getDiff(); + if (diff.length === 0) { return; // No changes } + consola.info( "Nitro config updated:\n" + diff.map((entry) => ` ${entry.toString()}`).join("\n") ); - await reload(); + + await (diff.some((e) => + ["routeRules", "runtimeConfig"].includes(e.key) + ) + ? reload() // Full reload + : nitro.updateConfig(newConfig.config)); // Hot reload }, }, } diff --git a/src/index.ts b/src/index.ts index 84ecc1e4d8..0a77da9051 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export * from "./build"; export * from "./nitro"; export * from "./scan"; export * from "./dev/server"; -export * from "./options"; +export { LoadConfigOptions, loadOptions } from "./options"; export * from "./types"; export * from "./prerender"; export * from "./preset"; diff --git a/src/nitro.ts b/src/nitro.ts index f7569f7c50..292f89e1af 100644 --- a/src/nitro.ts +++ b/src/nitro.ts @@ -4,8 +4,12 @@ import { createHooks, createDebugger } from "hookable"; import { createUnimport } from "unimport"; import { defu } from "defu"; import { consola } from "consola"; -import type { NitroConfig, Nitro } from "./types"; -import { LoadConfigOptions, loadOptions } from "./options"; +import type { NitroConfig, Nitro, NitroDynamicConfig } from "./types"; +import { + LoadConfigOptions, + loadOptions, + normalizeRuntimeConfig, +} from "./options"; import { scanPlugins } from "./scan"; import { createStorage } from "./storage"; @@ -25,6 +29,16 @@ export async function createNitro( scannedHandlers: [], close: () => nitro.hooks.callHook("close"), storage: undefined, + async updateConfig(config: NitroDynamicConfig) { + nitro.options.routeRules = normalizeRuntimeConfig( + config.routeRules ? config : nitro.options + ); + nitro.options.runtimeConfig = normalizeRuntimeConfig( + config.runtimeConfig ? config : nitro.options + ); + await nitro.hooks.callHook("rollup:reload"); + consola.success("Nitro config hot reloaded!"); + }, }; // Storage diff --git a/src/options.ts b/src/options.ts index 1d87c188c3..7e71b257b9 100644 --- a/src/options.ts +++ b/src/options.ts @@ -281,10 +281,77 @@ export async function loadOptions( // Backward compatibility for options.routes options.routeRules = defu(options.routeRules, (options as any).routes || {}); - // Normalize route rules (NitroRouteConfig => NitroRouteRules) - const normalizedRules: { [p: string]: NitroRouteRules } = {}; - for (const path in options.routeRules) { - const routeConfig = options.routeRules[path] as NitroRouteConfig; + // Normalize route rules + options.routeRules = normalizeRouteRules(options); + + options.baseURL = withLeadingSlash(withTrailingSlash(options.baseURL)); + + // Normalize runtime config + options.runtimeConfig = normalizeRuntimeConfig(options); + + for (const publicAsset of options.publicAssets) { + publicAsset.dir = resolve(options.srcDir, publicAsset.dir); + publicAsset.baseURL = withLeadingSlash( + withoutTrailingSlash(publicAsset.baseURL || "/") + ); + } + + for (const serverAsset of options.serverAssets) { + serverAsset.dir = resolve(options.srcDir, serverAsset.dir); + } + + for (const pkg of ["defu", "h3", "radix3"]) { + if (!options.alias[pkg]) { + options.alias[pkg] = await resolveModule(pkg, { url: import.meta.url }); + } + } + + // Build-only storage + const fsMounts = { + root: resolve(options.rootDir), + src: resolve(options.srcDir), + build: resolve(options.buildDir), + cache: resolve(options.buildDir, "cache"), + }; + for (const p in fsMounts) { + options.devStorage[p] = options.devStorage[p] || { + driver: "fs", + readOnly: p === "root" || p === "src", + base: fsMounts[p], + }; + } + + // Resolve plugin paths + options.plugins = options.plugins.map((p) => resolvePath(p, options)); + + return options; +} + +/** + * @deprecated Please import `defineNitroConfig` from nitropack/config instead + */ +export function defineNitroConfig(config: NitroConfig): NitroConfig { + return config; +} + +export function normalizeRuntimeConfig(config: NitroConfig) { + provideFallbackValues(config.runtimeConfig); + const runtimeConfig = defu(config.runtimeConfig, { + app: { + baseURL: config.baseURL, + }, + nitro: {}, + }); + runtimeConfig.nitro.routeRules = config.routeRules; + return runtimeConfig; +} + +export function normalizeRouteRules( + config: NitroConfig +): Record { + const normalizedRules: Record = {}; + for (const path in config.routeRules) { + const routeConfig = config.routeRules[path] as NitroRouteConfig; const routeRules: NitroRouteRules = { ...routeConfig, redirect: undefined, @@ -335,60 +402,5 @@ export async function loadOptions( } normalizedRules[path] = routeRules; } - options.routeRules = normalizedRules; - - options.baseURL = withLeadingSlash(withTrailingSlash(options.baseURL)); - - provideFallbackValues(options.runtimeConfig); - options.runtimeConfig = defu(options.runtimeConfig, { - app: { - baseURL: options.baseURL, - }, - nitro: {}, - }); - options.runtimeConfig.nitro.routeRules = options.routeRules; - - for (const publicAsset of options.publicAssets) { - publicAsset.dir = resolve(options.srcDir, publicAsset.dir); - publicAsset.baseURL = withLeadingSlash( - withoutTrailingSlash(publicAsset.baseURL || "/") - ); - } - - for (const serverAsset of options.serverAssets) { - serverAsset.dir = resolve(options.srcDir, serverAsset.dir); - } - - for (const pkg of ["defu", "h3", "radix3"]) { - if (!options.alias[pkg]) { - options.alias[pkg] = await resolveModule(pkg, { url: import.meta.url }); - } - } - - // Build-only storage - const fsMounts = { - root: resolve(options.rootDir), - src: resolve(options.srcDir), - build: resolve(options.buildDir), - cache: resolve(options.buildDir, "cache"), - }; - for (const p in fsMounts) { - options.devStorage[p] = options.devStorage[p] || { - driver: "fs", - readOnly: p === "root" || p === "src", - base: fsMounts[p], - }; - } - - // Resolve plugin paths - options.plugins = options.plugins.map((p) => resolvePath(p, options)); - - return options; -} - -/** - * @deprecated Please import `defineNitroConfig` from nitropack/config instead - */ -export function defineNitroConfig(config: NitroConfig): NitroConfig { - return config; + return normalizedRules; } diff --git a/src/rollup/config.ts b/src/rollup/config.ts index 673161ee80..0a8f11490d 100644 --- a/src/rollup/config.ts +++ b/src/rollup/config.ts @@ -169,7 +169,6 @@ export const getRollupConfig = (nitro: Nitro): RollupConfig => { server: true, client: false, dev: String(nitro.options.dev), - RUNTIME_CONFIG: nitro.options.runtimeConfig, DEBUG: nitro.options.dev, }; @@ -183,6 +182,8 @@ export const getRollupConfig = (nitro: Nitro): RollupConfig => { values: { "typeof window": '"undefined"', _import_meta_url_: "import.meta.url", + "process.env.RUNTIME_CONFIG": () => + JSON.stringify(nitro.options.runtimeConfig, null, 2), ...Object.fromEntries( [".", ";", ")", "[", "]", "}", " "].map((d) => [ `import.meta${d}`, diff --git a/src/types/nitro.ts b/src/types/nitro.ts index 348b65741e..5b70b947eb 100644 --- a/src/types/nitro.ts +++ b/src/types/nitro.ts @@ -25,6 +25,11 @@ import type { import type { PresetOptions } from "./presets"; import type { KebabCase } from "./utils"; +export type NitroDynamicConfig = Pick< + NitroConfig, + "runtimeConfig" | "routeRules" +>; + export interface Nitro { options: NitroOptions; scannedHandlers: NitroEventHandler[]; @@ -34,6 +39,7 @@ export interface Nitro { logger: ConsolaInstance; storage: Storage; close: () => Promise; + updateConfig: (config: NitroDynamicConfig) => void | Promise; /* @internal */ _prerenderedRoutes?: PrerenderGenerateRoute[]; @@ -57,6 +63,7 @@ export interface NitroHooks { "rollup:before": (nitro: Nitro, config: RollupConfig) => HookResult; compiled: (nitro: Nitro) => HookResult; "dev:reload": () => HookResult; + "rollup:reload": () => HookResult; restart: () => HookResult; close: () => HookResult; "prerender:routes": (routes: Set) => HookResult; From 5f127635e5fb270b7bfd6d3cae81a47bc6d62b4e Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Apr 2023 20:51:30 +0200 Subject: [PATCH 2/4] fix hmr key check --- src/cli/commands/dev.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/commands/dev.ts b/src/cli/commands/dev.ts index 2fdf59d474..add7815249 100644 --- a/src/cli/commands/dev.ts +++ b/src/cli/commands/dev.ts @@ -7,6 +7,8 @@ import { createDevServer } from "../../dev/server"; import { commonArgs } from "../common"; import type { Nitro } from "../../types"; +const hmrKeyRe = /^runtimeConfig\.|routeRules\./; + export default defineCommand({ meta: { name: "dev", @@ -47,9 +49,7 @@ export default defineCommand({ diff.map((entry) => ` ${entry.toString()}`).join("\n") ); - await (diff.some((e) => - ["routeRules", "runtimeConfig"].includes(e.key) - ) + await (!diff.every((e) => hmrKeyRe.test(e.key)) ? reload() // Full reload : nitro.updateConfig(newConfig.config)); // Hot reload }, From b25c0c7472d1c64e9894692a2c572ab6cdf71081 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Apr 2023 20:54:28 +0200 Subject: [PATCH 3/4] fix typo --- src/nitro.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nitro.ts b/src/nitro.ts index 292f89e1af..a8a92cd122 100644 --- a/src/nitro.ts +++ b/src/nitro.ts @@ -8,6 +8,7 @@ import type { NitroConfig, Nitro, NitroDynamicConfig } from "./types"; import { LoadConfigOptions, loadOptions, + normalizeRouteRules, normalizeRuntimeConfig, } from "./options"; import { scanPlugins } from "./scan"; @@ -30,7 +31,7 @@ export async function createNitro( close: () => nitro.hooks.callHook("close"), storage: undefined, async updateConfig(config: NitroDynamicConfig) { - nitro.options.routeRules = normalizeRuntimeConfig( + nitro.options.routeRules = normalizeRouteRules( config.routeRules ? config : nitro.options ); nitro.options.runtimeConfig = normalizeRuntimeConfig( From b395474367ae206e47c90f6ce64b5286b1697bf4 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Apr 2023 20:59:51 +0200 Subject: [PATCH 4/4] fix type export --- src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 0a77da9051..9e9093a483 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,9 @@ export * from "./build"; export * from "./nitro"; export * from "./scan"; export * from "./dev/server"; -export { LoadConfigOptions, loadOptions } from "./options"; export * from "./types"; export * from "./prerender"; export * from "./preset"; + +export { loadOptions } from "./options"; +export type { LoadConfigOptions } from "./options";