UNPKG

@nuxt/module-builder

Version:

Complete solution for building Nuxt modules

218 lines (215 loc) 9.49 kB
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs"; import { n as name, r as version } from "./package-1Cra3U-o.mjs"; import { n as sharedArgs, t as resolveCwdArg } from "./_shared-CfHVT2WD.mjs"; import { defineCommand } from "citty"; import { consola } from "consola"; import { existsSync, promises } from "node:fs"; import { pathToFileURL } from "node:url"; import { basename, dirname, extname, join, normalize, resolve } from "pathe"; import { filename } from "pathe/utils"; import { readPackageJSON } from "pkg-types"; import { parse } from "tsconfck"; import { defu } from "defu"; import { createJiti } from "jiti"; import { anyOf, createRegExp } from "magic-regexp"; import { findExports, findTypeExports, resolvePath } from "mlly"; import { convertCompilerOptionsFromJson } from "typescript"; //#region src/commands/build.ts var build_exports = /* @__PURE__ */ __exportAll({ default: () => build_default }); var build_default = defineCommand({ meta: { name: "build", description: "Build module for distribution" }, args: { ...sharedArgs, outDir: { type: "string", default: "dist", description: "Build directory" }, sourcemap: { type: "boolean", default: false, description: "Generate sourcemaps" }, stub: { type: "boolean", default: false, description: "Stub dist instead of actually building it for development" } }, async run(context) { const { build } = await import("unbuild"); const cwd = resolveCwdArg(context.args); const jiti = createJiti(cwd); await build(cwd, false, { declaration: "node16", sourcemap: context.args.sourcemap, stub: context.args.stub, stubOptions: { absoluteJitiPath: true }, outDir: context.args.outDir, entries: ["src/module", { input: "src/runtime/", outDir: `${context.args.outDir}/runtime`, addRelativeDeclarationExtensions: true, ext: "js", pattern: [ "**", "!**/*.stories.{js,cts,mts,ts,jsx,tsx}", "!**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}" ], esbuild: { jsxImportSource: "vue", jsx: "automatic", jsxFactory: "h" } }], rollup: { esbuild: { target: "esnext" }, emitCJS: false, cjsBridge: false }, externals: [ /dist[\\/]runtime[\\/]/, "@nuxt/schema", "@nuxt/schema-nightly", "@nuxt/schema-edge", "@nuxt/kit", "@nuxt/kit-nightly", "@nuxt/kit-edge", "#app", "#app/nuxt", "nuxt", "nuxt-nightly", "nuxt-edge", "nuxt3", "vue", "vue-demi" ], hooks: { async "mkdist:entry:options"(_ctx, entry, options) { options.typescript = defu(options.typescript, { compilerOptions: await loadTSCompilerOptions(entry.input) }); }, async "rollup:options"(ctx, options) { const [entry] = ctx.buildEntries; const mergedCompilerOptions = defu({ noEmit: false, paths: { "#app/nuxt": ["./node_modules/nuxt/dist/app/nuxt"] } }, ctx.options.rollup.dts.compilerOptions, await loadTSCompilerOptions(entry.path)); ctx.options.rollup.dts.compilerOptions = convertCompilerOptionsFromJson(mergedCompilerOptions, entry.path).options; options.plugins ||= []; if (!Array.isArray(options.plugins)) options.plugins = [options.plugins]; const runtimeEntries = ctx.options.entries.filter((entry) => entry.builder === "mkdist"); const RUNTIME_RE = createRegExp(anyOf(...runtimeEntries.map((entry) => basename(entry.input))).and(anyOf("/", "\\"))); options.plugins.unshift({ name: "nuxt-module-builder:runtime-externals", async resolveId(id, importer) { if (!RUNTIME_RE.test(id)) return; const resolved = await this.resolve(id, importer, { skipSelf: true }); if (!resolved) return; const normalizedId = normalize(resolved.id); for (const entry of runtimeEntries) { if (!entry.outDir || !normalizedId.includes(entry.input)) continue; const name = filename(normalizedId) || basename(normalizedId, extname(normalizedId)); const distFile = await resolvePath(join(dirname(pathToFileURL(normalizedId).href.replace(entry.input, entry.outDir)), name)); if (distFile) return { external: true, id: distFile }; } } }); }, async "rollup:done"(ctx) { const moduleEntryPath = resolve(ctx.options.outDir, "module.mjs"); const moduleFn = await jiti.import(pathToFileURL(moduleEntryPath).toString(), { default: true }).catch((err) => { consola.error(err); consola.error("Cannot load module. Please check dist:", moduleEntryPath); return null; }); if (!moduleFn) return; const moduleMeta = await moduleFn.getMeta?.() || {}; if (ctx.pkg) { if (!moduleMeta.name) moduleMeta.name = ctx.pkg.name; if (!moduleMeta.version) moduleMeta.version = ctx.pkg.version; } moduleMeta.builder = { [name]: version, unbuild: await readPackageJSON("unbuild").then((r) => r.version).catch(() => "unknown") }; const metaFile = resolve(ctx.options.outDir, "module.json"); await promises.writeFile(metaFile, JSON.stringify(moduleMeta, null, 2), "utf8"); await writeTypes(ctx.options.outDir, ctx.options.stub); }, async "build:done"(ctx) { const logs = [...ctx.warnings].filter((l) => l.startsWith("Potential missing package.json files:")); if (logs.filter((l) => l.match(/\.d\.ts/)).length > 0) consola.warn(`\`@nuxt/module-builder\` will no longer generate \`.d.ts\` declaration files. You can update these paths to use the \`.d.mts\` extension instead.`); if (logs.filter((l) => l.match(/module\.cjs/)).length > 0) consola.warn(`\`@nuxt/module-builder\` will no longer generate \`module.cjs\` as this is not required for Nuxt v3+. You can safely remove replace this with \`module.mjs\` in your \`package.json\`.`); const pkg = await readPackageJSON(cwd); if (pkg?.types && !existsSync(resolve(cwd, pkg.types))) consola.warn(`Please remove the \`types\` field from package.json as it is no longer required for Bundler TypeScript module resolution. Instead, you can use \`typesVersions\` to support subpath export types for Node10, if required.`); } } }); } }); async function writeTypes(distDir, isStub) { const dtsFile = resolve(distDir, "types.d.mts"); if (existsSync(dtsFile)) return; const moduleReExports = []; if (!isStub) { const moduleTypesFile = resolve(distDir, "module.d.mts"); const normalisedModuleTypes = (await promises.readFile(moduleTypesFile, "utf8").catch(() => "")).replace(/export\s*\{.*?\}/gs, (match) => match.replace(/\b(type|interface)\b/g, "")); for (const e of findExports(normalisedModuleTypes)) moduleReExports.push(e); for (const i of findTypeExports(normalisedModuleTypes)) moduleReExports.push(i); } const appShims = []; const schemaShims = []; const moduleImports = []; const schemaImports = []; const moduleExports = []; const hasTypeExport = (name) => isStub || moduleReExports.find((exp) => exp.names?.includes(name)); if (!hasTypeExport("ModuleOptions")) { schemaImports.push("NuxtModule"); moduleImports.push("default as Module"); moduleExports.push(`export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>`); } if (hasTypeExport("ModuleHooks")) { moduleImports.push("ModuleHooks"); schemaShims.push(" interface NuxtHooks extends ModuleHooks {}"); } if (hasTypeExport("ModuleRuntimeHooks")) { moduleImports.push("ModuleRuntimeHooks"); appShims.push(` interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}`); } if (hasTypeExport("ModuleRuntimeConfig")) { moduleImports.push("ModuleRuntimeConfig"); schemaShims.push(" interface RuntimeConfig extends ModuleRuntimeConfig {}"); } if (hasTypeExport("ModulePublicRuntimeConfig")) { moduleImports.push("ModulePublicRuntimeConfig"); schemaShims.push(" interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}"); } const dtsContents = ` ${schemaImports.length ? `import type { ${schemaImports.join(", ")} } from '@nuxt/schema'` : ""} ${moduleImports.length ? `import type { ${moduleImports.join(", ")} } from './module.mjs'` : ""} ${appShims.length ? `declare module '#app' {\n${appShims.join("\n")}\n}\n` : ""} ${schemaShims.length ? `declare module '@nuxt/schema' {\n${schemaShims.join("\n")}\n}\n` : ""} ${moduleExports.length ? `\n${moduleExports.join("\n")}` : ""} ${isStub ? "export * from \"./module.mjs\"" : ""} ${moduleReExports.filter((e) => e.type === "named" || e.type === "default").map((e) => `\nexport { ${e.names.map((n) => (n === "default" ? "" : "type ") + n).join(", ")} } from '${e.specifier || "./module.mjs"}'`).join("\n")} ${moduleReExports.filter((e) => e.type === "star").map((e) => `\nexport * from '${e.specifier || "./module.mjs"}'`).join("\n")} `.trim().replace(/[\n\r]{3,}/g, "\n\n") + "\n"; await promises.writeFile(dtsFile, dtsContents, "utf8"); } async function loadTSCompilerOptions(path) { const config = await parse(path); const resolvedCompilerOptions = config?.tsconfig.compilerOptions || {}; for (const { tsconfig, tsconfigFile } of config.extended || []) for (const alias in tsconfig.compilerOptions?.paths || {}) resolvedCompilerOptions.paths[alias] = resolvedCompilerOptions.paths[alias].map((p) => { if (!/^\.{1,2}(?:\/|$)/.test(p)) return p; return resolve(dirname(tsconfigFile), p); }); return resolvedCompilerOptions; } //#endregion export { build_exports as n, build_default as t };