Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: config hmr support for routeRules and rutimeConfig #1175

Merged
merged 4 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ async function _watch(nitro: Nitro, rollupConfig: RollupConfig) {
reloadWacher.close();
});

nitro.hooks.hook("rollup:reload", () => reload());

await reload();
}

Expand Down
11 changes: 9 additions & 2 deletions src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -35,16 +37,21 @@ 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.every((e) => hmrKeyRe.test(e.key))
? reload() // Full reload
: nitro.updateConfig(newConfig.config)); // Hot reload
},
},
}
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export * from "./build";
export * from "./nitro";
export * from "./scan";
export * from "./dev/server";
export * from "./options";
export * from "./types";
export * from "./prerender";
export * from "./preset";

export { loadOptions } from "./options";
export type { LoadConfigOptions } from "./options";
19 changes: 17 additions & 2 deletions src/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ 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,
normalizeRouteRules,
normalizeRuntimeConfig,
} from "./options";
import { scanPlugins } from "./scan";
import { createStorage } from "./storage";

Expand All @@ -25,6 +30,16 @@ export async function createNitro(
scannedHandlers: [],
close: () => nitro.hooks.callHook("close"),
storage: undefined,
async updateConfig(config: NitroDynamicConfig) {
nitro.options.routeRules = normalizeRouteRules(
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
Expand Down
132 changes: 72 additions & 60 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, NitroRouteRules> {
const normalizedRules: Record<string, NitroRouteRules> = {};
for (const path in config.routeRules) {
const routeConfig = config.routeRules[path] as NitroRouteConfig;
const routeRules: NitroRouteRules = {
...routeConfig,
redirect: undefined,
Expand Down Expand Up @@ -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;
}
3 changes: 2 additions & 1 deletion src/rollup/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -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}`,
Expand Down
7 changes: 7 additions & 0 deletions src/types/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand All @@ -34,6 +39,7 @@ export interface Nitro {
logger: ConsolaInstance;
storage: Storage;
close: () => Promise<void>;
updateConfig: (config: NitroDynamicConfig) => void | Promise<void>;

/* @internal */
_prerenderedRoutes?: PrerenderGenerateRoute[];
Expand All @@ -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<string>) => HookResult;
Expand Down