Skip to content

Commit

Permalink
feat: support app config (#1022)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Mar 6, 2023
1 parent 6260f9a commit d2b34e0
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 15 deletions.
22 changes: 21 additions & 1 deletion src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as rollup from "rollup";
import fse from "fs-extra";
import { defu } from "defu";
import { watch } from "chokidar";
import { scanDirExports } from "unimport";
import { genTypeImport } from "knitwork";
import { debounce } from "perfect-debounce";
import type { TSConfig } from "pkg-types";
import type { RollupError } from "rollup";
Expand Down Expand Up @@ -128,6 +128,26 @@ export async function writeTypes(nitro: Nitro) {
" }",
"}",
...autoImportedTypes,
`
// App Config
import type { Defu } from 'defu'
${nitro.options.appConfigFiles
.map((file, index) =>
genTypeImport(file.replace(/\.\w+$/, ""), [
{ name: "default", as: `appConfig${index}` },
])
)
.join("\n")}
type UserAppConfig = Defu<{}, [${nitro.options.appConfigFiles
.map((_, index: number) => `typeof appConfig${index}`)
.join(", ")}]>
declare module 'nitropack' {
interface AppConfig extends UserAppConfig {}
}
`,
// Makes this a module for augmentation purposes
"export {}",
];
Expand Down
1 change: 1 addition & 0 deletions src/imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const nitroImports: Preset[] = [
"nitroPlugin",
"defineRenderHandler",
"getRouteRules",
"useAppConfig",
],
},
];
22 changes: 21 additions & 1 deletion src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import escapeRE from "escape-string-regexp";
import { withLeadingSlash, withoutTrailingSlash, withTrailingSlash } from "ufo";
import { isTest, isDebug } from "std-env";
import { findWorkspaceDir } from "pkg-types";
import { resolvePath, detectTarget, provideFallbackValues } from "./utils";
import {
resolvePath,
resolveFile,
detectTarget,
provideFallbackValues,
} from "./utils";
import type {
NitroConfig,
NitroOptions,
Expand All @@ -24,6 +29,8 @@ const NitroDefaults: NitroConfig = {
debug: isDebug,
logLevel: isTest ? 1 : 3,
runtimeConfig: { app: {}, nitro: {} },
appConfig: {},
appConfigFiles: [],

// Dirs
scanDirs: [],
Expand Down Expand Up @@ -238,6 +245,19 @@ export async function loadOptions(
);
}

// Normalize app.config file paths
options.appConfigFiles = options.appConfigFiles
.map((file) => resolveFile(resolvePath(file, options)))
.filter(Boolean);

// Detect app.config from scanDirs
for (const dir of options.scanDirs) {
const configFile = resolveFile("app.config", dir);
if (configFile && !options.appConfigFiles.includes(configFile)) {
options.appConfigFiles.push(configFile);
}
}

// Backward compatibility for options.routes
options.routeRules = defu(options.routeRules, (options as any).routes || {});

Expand Down
4 changes: 4 additions & 0 deletions src/rollup/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { esbuild } from "./plugins/esbuild";
import { raw } from "./plugins/raw";
import { storage } from "./plugins/storage";
import { importMeta } from "./plugins/import-meta";
import { appConfig } from "./plugins/app-config";

export type RollupConfig = InputOptions & { output: OutputOptions };

Expand Down Expand Up @@ -245,6 +246,9 @@ export const getRollupConfig = (nitro: Nitro) => {
// Storage
rollupConfig.plugins.push(storage(nitro));

// App.config
rollupConfig.plugins.push(appConfig(nitro));

// Handlers
rollupConfig.plugins.push(handlers(nitro));

Expand Down
25 changes: 25 additions & 0 deletions src/rollup/plugins/app-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { genImport } from "knitwork";
import type { Nitro } from "../../types";
import { virtual } from "./virtual";

export function appConfig(nitro: Nitro) {
return virtual(
{
"#internal/nitro/virtual/app-config": () => `
import { defuFn } from 'defu';
const inlineAppConfig = ${JSON.stringify(nitro.options.appConfig, null, 2)};
${nitro.options.appConfigFiles
.map((file, i) => genImport(file, "appConfig" + i) + ";")
.join("\n")}
export const appConfig = defuFn(${[
...nitro.options.appConfigFiles.map((_, i) => "appConfig" + i),
"inlineAppConfig",
].join(", ")});
`,
},
nitro.vfs
);
}
29 changes: 17 additions & 12 deletions src/runtime/config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import destr from "destr";
import { snakeCase } from "scule";
import { appConfig as _appConfig } from "#internal/nitro/virtual/app-config";

// Bundled runtime config (injected by nitro)
// Runtime config
const _runtimeConfig = process.env.RUNTIME_CONFIG as any;

const ENV_PREFIX = "NITRO_";
const ENV_PREFIX_ALT =
_runtimeConfig.nitro.envPrefix ?? process.env.NITRO_ENV_PREFIX ?? "_";
overrideConfig(_runtimeConfig);

const runtimeConfig = deepFreeze(_runtimeConfig);
export default runtimeConfig; // TODO: Remove in next major version
export const useRuntimeConfig = () => runtimeConfig;

// App config
const appConfig = deepFreeze(_appConfig);
export const useAppConfig = () => appConfig;

// --- Utils ---

// Allow override from process.env and deserialize
const getEnv = (key: string) => {
function getEnv(key: string) {
const envKey = snakeCase(key).toUpperCase();
return destr(
process.env[ENV_PREFIX + envKey] ?? process.env[ENV_PREFIX_ALT + envKey]
);
};
}

function isObject(input: unknown) {
return typeof input === "object" && !Array.isArray(input);
}

function overrideConfig(obj: object, parentKey = "") {
for (const key in obj) {
const subKey = parentKey ? `${parentKey}_${key}` : key;
Expand All @@ -32,14 +44,7 @@ function overrideConfig(obj: object, parentKey = "") {
}
}
}
overrideConfig(_runtimeConfig);

// Named exports
const config = deepFreeze(_runtimeConfig);
export const useRuntimeConfig = () => config;
export default config;

// Utils
function deepFreeze(object: Record<string, any>) {
const propNames = Object.getOwnPropertyNames(object);
for (const name of propNames) {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { useStorage } from "#internal/nitro/virtual/storage";
export { useRuntimeConfig } from "./config";
export { useRuntimeConfig, useAppConfig } from "./config";
export * from "./cache";
export { useNitroApp } from "./app";
export * from "./plugin";
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/virtual/app-config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { AppConfig } from "nitropack";

export const appConfig: AppConfig;
6 changes: 6 additions & 0 deletions src/types/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export interface NitroConfig
rollupConfig?: Partial<RollupConfig>;
}

export interface AppConfig {
[key: string]: any;
}

export interface PublicAssetDir {
baseURL?: string;
fallthrough?: boolean;
Expand Down Expand Up @@ -154,6 +158,8 @@ export interface NitroOptions extends PresetOptions {
};
[key: string]: any;
};
appConfig: AppConfig;
appConfigFiles: string[];

// Dirs
workspaceDir: string;
Expand Down
17 changes: 17 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ export function resolvePath(
return resolve(base || nitroOptions.srcDir, path);
}

export function resolveFile(
path: string,
base = ".",
extensions = [".js", ".ts", ".mjs", ".cjs", ".json"]
): string | undefined {
path = resolve(base, path);
if (existsSync(path)) {
return path;
}
for (const ext of extensions) {
const p = path + ext;
if (existsSync(p)) {
return p;
}
}
}

export function replaceAll(input: string, from: string, to: string) {
return input.replace(new RegExp(from, "g"), to);
}
Expand Down
3 changes: 3 additions & 0 deletions test/fixture/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
"app-config": true,
};
4 changes: 4 additions & 0 deletions test/fixture/nitro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export default defineNitroConfig({
dir: "files",
},
],
appConfig: {
"nitro-config": true,
},
appConfigFiles: ["~/server.config.ts"],
publicAssets: [
{
baseURL: "build",
Expand Down
7 changes: 7 additions & 0 deletions test/fixture/routes/app-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default eventHandler(() => {
const appConfig = useAppConfig();

return {
appConfig,
};
});
3 changes: 3 additions & 0 deletions test/fixture/server.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
"server-config": true,
};
15 changes: 15 additions & 0 deletions test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,21 @@ export function testNitro(
}
}

it("app config", async () => {
const { data } = await callHandler({
url: "/app-config",
});
expect(data).toMatchInlineSnapshot(`
{
"appConfig": {
"app-config": true,
"nitro-config": true,
"server-config": true,
},
}
`);
});

if (ctx.nitro!.options.timing) {
it("set server timing header", async () => {
const { data, status, headers } = await callHandler({
Expand Down

0 comments on commit d2b34e0

Please sign in to comment.