UNPKG

kirbyup

Version:

Zero-config bundler for Kirby Panel plugins

175 lines (173 loc) 6.09 kB
import { name, version } from "../package.js"; import { loadConfig, resolvePostCSSConfig } from "./config.js"; import { PrettyError, handleError } from "./errors.js"; import kirbyupBuildCleanupPlugin from "./plugins/build-cleanup.js"; import kirbyupGlobImportPlugin from "./plugins/glob-import.js"; import kirbyupHmrPlugin from "./plugins/hmr.js"; import { printFileInfo, toArray } from "./utils.js"; import * as fs from "node:fs"; import * as fsp from "node:fs/promises"; import vuePlugin from "@vitejs/plugin-vue2"; import vueJsxPlugin from "@vitejs/plugin-vue2-jsx"; import { consola } from "consola"; import { colors } from "consola/utils"; import { basename, dirname, resolve } from "pathe"; import { debounce } from "perfect-debounce"; import externalGlobals from "rollup-plugin-external-globals"; import { build as build$1, createLogger, createServer, mergeConfig } from "vite"; import fullReloadPlugin from "vite-plugin-full-reload"; import * as vueCompilerSfc from "vue/compiler-sfc"; //#region src/node/index.ts let resolvedKirbyupConfig; let resolvedPostCssConfig; const logLevel = "warn"; const logger = createLogger(logLevel); const loggerWarn = logger.warn; logger.warn = (msg, options) => { if (msg.includes("(!) build.outDir")) return; loggerWarn(msg, options); }; function getViteConfig(command, options) { const aliasDir = resolve(options.cwd, dirname(options.entry)); const { alias = {}, vite, extendViteConfig } = resolvedKirbyupConfig; const userConfig = vite ?? extendViteConfig ?? {}; const sharedConfig = { resolve: { alias: { "~/": `${aliasDir}/`, "@/": `${aliasDir}/`, ...alias } }, plugins: [ vuePlugin({ compiler: vueCompilerSfc }), vueJsxPlugin(), kirbyupGlobImportPlugin(), { ...externalGlobals({ vue: "Vue" }), enforce: "post" } ], build: { copyPublicDir: false }, ...resolvedPostCssConfig && { css: { postcss: resolvedPostCssConfig } }, envDir: options.cwd, envPrefix: ["VITE_", "KIRBYUP_"], customLogger: logger, logLevel }; if (command === "serve") { const { port, watch } = options; const serveConfig = mergeConfig(sharedConfig, { plugins: [kirbyupHmrPlugin(options), watch && fullReloadPlugin(watch)].filter(Boolean), build: { rollupOptions: { input: resolve(options.cwd, options.entry) } }, server: { port, strictPort: true, origin: `http://localhost:${port}` } }); return mergeConfig(serveConfig, userConfig); } const mode = options.watch ? "development" : "production"; const buildConfig = mergeConfig(sharedConfig, { mode, plugins: [kirbyupBuildCleanupPlugin(options)], build: { lib: { entry: resolve(options.cwd, options.entry), formats: ["iife"], name: "kirbyupExport", fileName: () => "index.js" }, minify: mode === "production", outDir: options.outDir, emptyOutDir: false, rollupOptions: { output: { assetFileNames: "index.[ext]" } } } }); return mergeConfig(buildConfig, userConfig); } async function generate(options) { const config = getViteConfig("build", options); let result; try { result = await build$1(config); } catch (error) { consola.error("Build failed"); if (config.mode === "production") throw error; } if (result && !options.watch) { const { output } = toArray(result)[0]; let maxLength = 0; for (const chunkFile in output) { const fileNameLength = output[chunkFile].fileName.length; if (fileNameLength > maxLength) maxLength = fileNameLength; } for (const { fileName, type, code } of output) { const content = code || await fsp.readFile(resolve(options.outDir, fileName), "utf8"); await printFileInfo({ root: options.cwd, outDir: options.outDir, filePath: fileName, content, type, maxLength }); } } return result; } async function build(options) { assertEntryExists(options); const { cwd } = options; const { config, configFile } = await loadConfig(cwd); resolvedKirbyupConfig = config ?? {}; resolvedPostCssConfig = await resolvePostCSSConfig(cwd); consola.log(colors.green(`${name} v${version}`)); consola.start(`Building ${colors.cyan(options.entry)}`); if (options.watch) consola.info("Running in watch mode"); const debouncedBuild = debounce(async () => { generate(options).catch(handleError); }, 100); const startWatcher = async () => { if (!options.watch) return; const { watch } = await import("chokidar"); const ignored = ["**/{.git,node_modules}/**", "index.{css,js}"]; const watchPaths = typeof options.watch === "boolean" ? dirname(options.entry) : Array.isArray(options.watch) ? options.watch.filter((path) => typeof path === "string") : options.watch; consola.info(`Watching for changes in ${toArray(watchPaths).map((i) => colors.cyan(i)).join(", ")}`); const watcher = watch(watchPaths, { ignoreInitial: true, ignorePermissionErrors: true, ignored, cwd }); if (configFile) watcher.add(configFile); watcher.on("all", async (type, file) => { const absolutePath = resolve(cwd, file); if (configFile === absolutePath) { resolvedKirbyupConfig = (await loadConfig(cwd)).config ?? {}; consola.info(`${colors.cyan(basename(file))} changed, setting new config`); } else consola.log(`${colors.green(type)} ${colors.white(colors.dim(file))}`); debouncedBuild(); }); }; await generate(options); consola.success("Build successful"); startWatcher(); } async function serve(options) { assertEntryExists(options); const { cwd } = options; const { config } = await loadConfig(cwd); resolvedKirbyupConfig = config ?? {}; resolvedPostCssConfig = await resolvePostCSSConfig(cwd); consola.log(colors.green(`${name} v${version}`)); consola.info("Starting development server..."); const server = await createServer(getViteConfig("serve", options)); await server.listen(); consola.success(`Server is listening on :${server.config.server.port}`); return server; } function assertEntryExists(options) { if (!fs.existsSync(resolve(options.cwd, options.entry))) throw new PrettyError(`Cannot find "${options.entry}"`); } //#endregion export { build, serve };