nuxt-kql
Version:
Kirby's Query Language API for Nuxt
188 lines (181 loc) • 5.94 kB
JavaScript
import process from 'node:process';
import { useLogger, defineNuxtModule, createResolver, addServerHandler, addImports, addTemplate } from '@nuxt/kit';
import { defu } from 'defu';
import { join } from 'pathe';
import { pascalCase } from 'scule';
import { withLeadingSlash } from 'ufo';
import { ofetch } from 'ofetch';
import { createAuthHeader } from '../dist/runtime/utils.js';
const name = "nuxt-kql";
const logger = useLogger("nuxt-kql");
async function prefetchQueries(options) {
const results = /* @__PURE__ */ new Map();
if (!options.prefetch || Object.keys(options.prefetch).length === 0)
return results;
if (!options.url) {
logger.error("Skipping KQL prefetch, since no Kirby base URL is provided");
return results;
}
const start = Date.now();
for (const [key, query] of Object.entries(options.prefetch)) {
const language = "language" in query ? query.language : void 0;
if (language && !query.query) {
logger.error(`Prefetch KQL query "${key}" requires the "query" property in multi-language mode`);
continue;
}
try {
results.set(
key,
await ofetch(options.prefix, {
baseURL: options.url,
method: "POST",
body: language ? query.query : query,
headers: {
...createAuthHeader(options),
...language && { "X-Language": language }
}
})
);
} catch (error) {
const _error = error;
logger.error(
`Prefetch KQL query "${key}" failed${_error.status ? ` with status code ${_error.status}:
${JSON.stringify(_error.data, void 0, 2)}` : `: ${_error}`}`
);
}
}
if (results.size > 0) {
const firstKey = results.keys().next().value;
logger.info(
`Prefetched ${results.size === 1 ? "" : `${results.size} `}KQL ${results.size === 1 ? `query "${firstKey}"` : "queries"} in ${Date.now() - start}ms`
);
}
return results;
}
const KIRBY_TYPES_PKG_EXPORT_NAMES = [
"KirbyApiResponse",
"KirbyBlock",
"KirbyDefaultBlockType",
"KirbyDefaultBlocks",
"KirbyLayout",
"KirbyLayoutColumn",
"KirbyQuery",
"KirbyQueryChain",
"KirbyQueryModel",
"KirbyQueryRequest",
"KirbyQueryResponse",
"KirbyQuerySchema"
];
const module = defineNuxtModule({
meta: {
name,
configKey: "kql",
compatibility: {
nuxt: ">=3.17"
}
},
defaults: {
url: process.env.KIRBY_BASE_URL || "",
prefix: "",
auth: "basic",
token: process.env.KIRBY_API_TOKEN || "",
credentials: {
username: process.env.KIRBY_API_USERNAME || "",
password: process.env.KIRBY_API_PASSWORD || ""
},
client: false,
prefetch: {},
server: {
cache: false,
storage: "cache",
swr: false,
maxAge: 1,
verboseErrors: false
}
},
async setup(options, nuxt) {
const moduleName = name;
if (!options.url)
logger.error("Missing `KIRBY_BASE_URL` environment variable");
if (options.auth === "basic" && (!options.credentials || !options.credentials.username || !options.credentials.password))
logger.error("Missing `KIRBY_API_USERNAME` and `KIRBY_API_PASSWORD` environment variable for basic authentication");
if (options.auth === "bearer" && !options.token)
logger.error("Missing `KIRBY_API_TOKEN` environment variable for bearer authentication");
if (!options.prefix) {
if (options.auth === "basic")
options.prefix = "api/query";
else if (options.auth === "bearer")
options.prefix = "api/kql";
}
if (!nuxt.options.ssr) {
logger.info("KQL requests are client-only because SSR is disabled");
options.client = true;
}
if (options.server) {
options.server.storage ||= "cache";
options.server.storage = withLeadingSlash(options.server.storage);
}
nuxt.options.runtimeConfig.kql = defu(
nuxt.options.runtimeConfig.kql,
options
);
nuxt.options.runtimeConfig.public.kql = defu(
nuxt.options.runtimeConfig.public.kql,
options.client ? options : { client: false }
);
const { resolve } = createResolver(import.meta.url);
nuxt.options.build.transpile.push(resolve("runtime"));
addServerHandler({
route: "/api/__kirby__/:key",
handler: resolve("runtime/server/handler"),
method: "post"
});
addImports(
["$kirby", "$kql", "useKirbyData", "useKql"].map((name2) => ({
name: name2,
as: name2,
from: resolve(`runtime/composables/${name2}`)
}))
);
nuxt.hooks.hook("nitro:config", (config) => {
config.externals ||= {};
config.externals.inline ||= [];
config.externals.inline.push(resolve("runtime/utils"));
config.imports = defu(config.imports, {
presets: [{
from: resolve("runtime/server/imports"),
imports: ["$kirby", "$kql"]
}]
});
});
nuxt.options.alias[`#${moduleName}`] = join(nuxt.options.buildDir, `module/${moduleName}`);
const prefetchedQueries = await prefetchQueries(options);
addTemplate({
filename: `module/${moduleName}.mjs`,
write: true,
getContents() {
return `
// Generated by ${moduleName}
${[...prefetchedQueries.entries()].map(([key, response]) => `
export const ${key} = ${JSON.stringify(response?.result || null, void 0, 2)}
`.trimStart()).join("") || `export {}
`}`.trimStart();
}
});
addTemplate({
filename: `module/${moduleName}.d.ts`,
write: true,
getContents() {
return `
// Generated by ${moduleName}
export type { ${KIRBY_TYPES_PKG_EXPORT_NAMES.join(", ")} } from 'kirby-types'
${[...prefetchedQueries.entries()].map(([key, response]) => `
export declare const ${key}: ${JSON.stringify(response?.result || null, void 0, 2)}
export type ${pascalCase(key)} = typeof ${key}
`.trimStart()).join("") || `export {}
`}`.trimStart();
}
});
}
});
export { module as default };