UNPKG

kirbyup

Version:

Zero-config bundler for Kirby Panel plugins

929 lines (890 loc) 28.4 kB
import * as fs from 'node:fs'; import { existsSync } from 'node:fs'; import * as fsp from 'node:fs/promises'; import { readFile } 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 { resolve, join, normalize, relative, dirname, basename } from 'pathe'; import { debounce } from 'perfect-debounce'; import externalGlobals from 'rollup-plugin-external-globals'; import { createLogger, createServer, build as build$1, mergeConfig } from 'vite'; import fullReloadPlugin from 'vite-plugin-full-reload'; import * as vueCompilerSfc from 'vue/compiler-sfc'; import { loadConfig as loadConfig$1 } from 'c12'; import postcssrc from 'postcss-load-config'; import MagicString from 'magic-string'; import { createRequire } from 'node:module'; import 'child_process'; import 'path'; import 'process'; import 'stream'; import 'readline'; import { Buffer as Buffer$1 } from 'node:buffer'; import { promisify } from 'node:util'; import { gzip } from 'node:zlib'; const name = "kirbyup"; const version = "3.3.0"; function loadConfig(cwd = process.cwd()) { return loadConfig$1({ cwd, name: "kirbyup", rcFile: false, packageJson: false }); } async function resolvePostCSSConfig(cwd) { try { return await postcssrc(void 0, void 0, { stopDir: cwd }); } catch (error) { if (!error.message.includes("No PostCSS Config found")) throw error; } } class PrettyError extends Error { constructor(message) { super(message); this.name = this.constructor.name; if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(this, this.constructor); else this.stack = new Error(message).stack; } } function handleError(error) { consola.error(error.message); process.exitCode = 1; } function kirbyupBuildCleanupPlugin(options) { let config; let devIndexPath; return { name: "kirbyup:build-cleanup", configResolved(resolvedConfig) { config = resolvedConfig; devIndexPath = resolve(config.root, options.outDir, "index.dev.mjs"); }, writeBundle() { if (fs.existsSync(devIndexPath)) fs.unlinkSync(devIndexPath); } }; } const multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//g; const singlelineCommentsRE = /\/\/.*/g; const HMR_RUNTIME_ID = "\0plugin-vue2:hmr-runtime"; const JSX_HMR_RUNTIME_ID = "plugin-vue2-jsx:hmr-runtime"; function isHmrRuntimeId(id) { return id === HMR_RUNTIME_ID || id === JSX_HMR_RUNTIME_ID; } const __INJECTED_HMR_CODE__ = ` /** - injected by kirbyup - */ for (const methodName of ['rerender', 'reload']) { const original = __VUE_HMR_RUNTIME__[methodName] __VUE_HMR_RUNTIME__[methodName] = function (id, updatedDef) { const key = updatedDef?.__file ? '__file' : updatedDef?.__hmrId ? '__hmrId' : null if (key) { const pluginComponents = window.panel.plugins.components // const usedComponentDefs = window.panel.app.$options.components const usedComponentDefs = window.panel.app._vnode.componentInstance.$options.components // #33 for (const componentName in pluginComponents) { if (updatedDef[key] === pluginComponents[componentName][key]) { const usedDefinition = usedComponentDefs[componentName].options if (map[id].options !== usedDefinition) map[id].options = usedDefinition if (typeof map[id].options.$_isSection !== 'boolean') map[id].options.$_isSection = /^k-.*-section$/.test(componentName) break } } } return original.apply(this, arguments) } } function $_applyKirbyModifications(activeDef, newDef) { const usedComponentDefs = window.panel.app.$options.components if (newDef.template) newDef.render = null if (activeDef.$_isSection) newDef.$_isSection = true if (newDef.$_isSection && !newDef.mixins?.[0]?.methods?.load) newDef.mixins = [activeDef.mixins[0], ...(newDef.mixins || [])] if (typeof newDef.extends === 'string') { if (newDef.extends === activeDef.extends?.options?.name) { newDef.extends = activeDef.extends } else if (usedComponentDefs[newDef.extends]) { newDef.extends = usedComponentDefs[newDef.extends].extend({ options: newDef, components: { ...usedComponentDefs, ...(newDef.components || {}) }, }) } else { newDef.extends = null } } } /** -- */ `; function kirbyupGlobImportPlugin() { let config; return { name: "kirbyup:glob-import", configResolved(resolvedConfig) { config = resolvedConfig; }, async transform(code) { if (!code.includes("kirbyup.import")) return; const kirbyupImportRE = /\bkirbyup\.import\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*\)/g; const noCommentsCode = code.replace(multilineCommentsRE, (m) => " ".repeat(m.length)).replace(singlelineCommentsRE, (m) => " ".repeat(m.length)); let s; let match; while (match = kirbyupImportRE.exec(noCommentsCode)) { const { 0: exp, 1: rawPath, index } = match; if (!s) s = new MagicString(code); s.overwrite( index, index + exp.length, `kirbyup.import(import.meta.glob(${rawPath}, { eager: true }))` ); } if (s) { return { code: s.toString(), map: config.build.sourcemap ? s.generateMap({ hires: true }) : void 0 }; } } }; } const require = createRequire(import.meta.url); var St = Object.create; var $ = Object.defineProperty; var kt = Object.getOwnPropertyDescriptor; var Tt = Object.getOwnPropertyNames; var At = Object.getPrototypeOf, Rt = Object.prototype.hasOwnProperty; var h = /* @__PURE__ */ ((t) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(t, { get: (e, n) => (typeof require < "u" ? require : e)[n] }) : t)(function(t) { if (typeof require < "u") return require.apply(this, arguments); throw Error('Dynamic require of "' + t + '" is not supported'); }); var l = (t, e) => () => (e || t((e = { exports: {} }).exports, e), e.exports); var $t = (t, e, n, r) => { if (e && typeof e == "object" || typeof e == "function") for (let s of Tt(e)) !Rt.call(t, s) && s !== n && $(t, s, { get: () => e[s], enumerable: !(r = kt(e, s)) || r.enumerable }); return t; }; var Nt = (t, e, n) => (n = t != null ? St(At(t)) : {}, $t( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. $(n, "default", { value: t, enumerable: true }) , t )); // node_modules/isexe/windows.js var W = l((Se, H) => { H.exports = z; z.sync = Wt; var j = h("fs"); function Ht(t, e) { var n = e.pathExt !== void 0 ? e.pathExt : process.env.PATHEXT; if (!n || (n = n.split(";"), n.indexOf("") !== -1)) return true; for (var r = 0; r < n.length; r++) { var s = n[r].toLowerCase(); if (s && t.substr(-s.length).toLowerCase() === s) return true; } return false; } function F(t, e, n) { return !t.isSymbolicLink() && !t.isFile() ? false : Ht(e, n); } function z(t, e, n) { j.stat(t, function(r, s) { n(r, r ? false : F(s, t, e)); }); } function Wt(t, e) { return F(j.statSync(t), t, e); } }); // node_modules/isexe/mode.js var X = l((ke, B) => { B.exports = K; K.sync = Dt; var D = h("fs"); function K(t, e, n) { D.stat(t, function(r, s) { n(r, r ? false : M(s, e)); }); } function Dt(t, e) { return M(D.statSync(t), e); } function M(t, e) { return t.isFile() && Kt(t, e); } function Kt(t, e) { var n = t.mode, r = t.uid, s = t.gid, o = e.uid !== void 0 ? e.uid : process.getuid && process.getuid(), i = e.gid !== void 0 ? e.gid : process.getgid && process.getgid(), a = parseInt("100", 8), c = parseInt("010", 8), u = parseInt("001", 8), f = a | c, p = n & u || n & c && s === i || n & a && r === o || n & f && o === 0; return p; } }); // node_modules/isexe/index.js var U = l((Ae, G) => { h("fs"); var v; process.platform === "win32" || global.TESTING_WINDOWS ? v = W() : v = X(); G.exports = y; y.sync = Mt; function y(t, e, n) { if (typeof e == "function" && (n = e, e = {}), !n) { if (typeof Promise != "function") throw new TypeError("callback not provided"); return new Promise(function(r, s) { y(t, e || {}, function(o, i) { o ? s(o) : r(i); }); }); } v(t, e || {}, function(r, s) { r && (r.code === "EACCES" || e && e.ignoreErrors) && (r = null, s = false), n(r, s); }); } function Mt(t, e) { try { return v.sync(t, e || {}); } catch (n) { if (e && e.ignoreErrors || n.code === "EACCES") return false; throw n; } } }); // node_modules/which/which.js var et = l((Re, tt) => { var g = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys", Y = h("path"), Bt = g ? ";" : ":", V = U(), J = (t) => Object.assign(new Error(`not found: ${t}`), { code: "ENOENT" }), Q = (t, e) => { let n = e.colon || Bt, r = t.match(/\//) || g && t.match(/\\/) ? [""] : [ // windows always checks the cwd first ...g ? [process.cwd()] : [], ...(e.path || process.env.PATH || /* istanbul ignore next: very unusual */ "").split(n) ], s = g ? e.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "", o = g ? s.split(n) : [""]; return g && t.indexOf(".") !== -1 && o[0] !== "" && o.unshift(""), { pathEnv: r, pathExt: o, pathExtExe: s }; }, Z = (t, e, n) => { typeof e == "function" && (n = e, e = {}), e || (e = {}); let { pathEnv: r, pathExt: s, pathExtExe: o } = Q(t, e), i = [], a = (u) => new Promise((f, p) => { if (u === r.length) return e.all && i.length ? f(i) : p(J(t)); let d = r[u], w = /^".*"$/.test(d) ? d.slice(1, -1) : d, m = Y.join(w, t), b = !w && /^\.[\\\/]/.test(t) ? t.slice(0, 2) + m : m; f(c(b, u, 0)); }), c = (u, f, p) => new Promise((d, w) => { if (p === s.length) return d(a(f + 1)); let m = s[p]; V(u + m, { pathExt: o }, (b, Ot) => { if (!b && Ot) if (e.all) i.push(u + m); else return d(u + m); return d(c(u, f, p + 1)); }); }); return n ? a(0).then((u) => n(null, u), n) : a(0); }, Xt = (t, e) => { e = e || {}; let { pathEnv: n, pathExt: r, pathExtExe: s } = Q(t, e), o = []; for (let i = 0; i < n.length; i++) { let a = n[i], c = /^".*"$/.test(a) ? a.slice(1, -1) : a, u = Y.join(c, t), f = !c && /^\.[\\\/]/.test(t) ? t.slice(0, 2) + u : u; for (let p = 0; p < r.length; p++) { let d = f + r[p]; try { if (V.sync(d, { pathExt: s })) if (e.all) o.push(d); else return d; } catch { } } } if (e.all && o.length) return o; if (e.nothrow) return null; throw J(t); }; tt.exports = Z; Z.sync = Xt; }); // node_modules/path-key/index.js var rt = l(($e, _) => { var nt = (t = {}) => { let e = t.env || process.env; return (t.platform || process.platform) !== "win32" ? "PATH" : Object.keys(e).reverse().find((r) => r.toUpperCase() === "PATH") || "Path"; }; _.exports = nt; _.exports.default = nt; }); // node_modules/cross-spawn/lib/util/resolveCommand.js var ct = l((Ne, it) => { var st = h("path"), Gt = et(), Ut = rt(); function ot(t, e) { let n = t.options.env || process.env, r = process.cwd(), s = t.options.cwd != null, o = s && process.chdir !== void 0 && !process.chdir.disabled; if (o) try { process.chdir(t.options.cwd); } catch { } let i; try { i = Gt.sync(t.command, { path: n[Ut({ env: n })], pathExt: e ? st.delimiter : void 0 }); } catch { } finally { o && process.chdir(r); } return i && (i = st.resolve(s ? t.options.cwd : "", i)), i; } function Yt(t) { return ot(t) || ot(t, true); } it.exports = Yt; }); // node_modules/cross-spawn/lib/util/escape.js var ut = l((qe, P) => { var C = /([()\][%!^"`<>&|;, *?])/g; function Vt(t) { return t = t.replace(C, "^$1"), t; } function Jt(t, e) { return t = `${t}`, t = t.replace(/(\\*)"/g, '$1$1\\"'), t = t.replace(/(\\*)$/, "$1$1"), t = `"${t}"`, t = t.replace(C, "^$1"), e && (t = t.replace(C, "^$1")), t; } P.exports.command = Vt; P.exports.argument = Jt; }); // node_modules/shebang-regex/index.js var lt = l((Ie, at) => { at.exports = /^#!(.*)/; }); // node_modules/shebang-command/index.js var dt = l((Le, pt) => { var Qt = lt(); pt.exports = (t = "") => { let e = t.match(Qt); if (!e) return null; let [n, r] = e[0].replace(/#! ?/, "").split(" "), s = n.split("/").pop(); return s === "env" ? r : r ? `${s} ${r}` : s; }; }); // node_modules/cross-spawn/lib/util/readShebang.js var ht = l((je, ft) => { var O = h("fs"), Zt = dt(); function te(t) { let n = Buffer.alloc(150), r; try { r = O.openSync(t, "r"), O.readSync(r, n, 0, 150, 0), O.closeSync(r); } catch { } return Zt(n.toString()); } ft.exports = te; }); // node_modules/cross-spawn/lib/parse.js var wt = l((Fe, Et) => { var ee = h("path"), mt = ct(), gt = ut(), ne = ht(), re = process.platform === "win32", se = /\.(?:com|exe)$/i, oe = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; function ie(t) { t.file = mt(t); let e = t.file && ne(t.file); return e ? (t.args.unshift(t.file), t.command = e, mt(t)) : t.file; } function ce(t) { if (!re) return t; let e = ie(t), n = !se.test(e); if (t.options.forceShell || n) { let r = oe.test(e); t.command = ee.normalize(t.command), t.command = gt.command(t.command), t.args = t.args.map((o) => gt.argument(o, r)); let s = [t.command].concat(t.args).join(" "); t.args = ["/d", "/s", "/c", `"${s}"`], t.command = process.env.comspec || "cmd.exe", t.options.windowsVerbatimArguments = true; } return t; } function ue(t, e, n) { e && !Array.isArray(e) && (n = e, e = null), e = e ? e.slice(0) : [], n = Object.assign({}, n); let r = { command: t, args: e, options: n, file: void 0, original: { command: t, args: e } }; return n.shell ? r : ce(r); } Et.exports = ue; }); // node_modules/cross-spawn/lib/enoent.js var bt = l((ze, vt) => { var S = process.platform === "win32"; function k(t, e) { return Object.assign(new Error(`${e} ${t.command} ENOENT`), { code: "ENOENT", errno: "ENOENT", syscall: `${e} ${t.command}`, path: t.command, spawnargs: t.args }); } function ae(t, e) { if (!S) return; let n = t.emit; t.emit = function(r, s) { if (r === "exit") { let o = xt(s, e); if (o) return n.call(t, "error", o); } return n.apply(t, arguments); }; } function xt(t, e) { return S && t === 1 && !e.file ? k(e.original, "spawn") : null; } function le(t, e) { return S && t === 1 && !e.file ? k(e.original, "spawnSync") : null; } vt.exports = { hookChildProcess: ae, verifyENOENT: xt, verifyENOENTSync: le, notFoundError: k }; }); // node_modules/cross-spawn/index.js var Ct = l((He, E) => { var yt = h("child_process"), T = wt(), A = bt(); function _t(t, e, n) { let r = T(t, e, n), s = yt.spawn(r.command, r.args, r.options); return A.hookChildProcess(s, r), s; } function pe(t, e, n) { let r = T(t, e, n), s = yt.spawnSync(r.command, r.args, r.options); return s.error = s.error || A.verifyENOENTSync(s.status, r), s; } E.exports = _t; E.exports.spawn = _t; E.exports.sync = pe; E.exports._parse = T; E.exports._enoent = A; }); // src/main.ts Nt(Ct()); async function findup(cwd, match, options = {}) { const segments = normalize(cwd).split("/"); while (segments.length > 0) { const path = segments.join("/") || "/"; const result = await match(path); if (result || !options.includeParentDirs) { return result; } segments.pop(); } } function parsePackageManagerField(packageManager) { const [name, _version] = (packageManager || "").split("@"); const [version, buildMeta] = _version?.split("+") || []; if (name && name !== "-" && /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name)) { return { name, version, buildMeta }; } const sanitized = name.replace(/\W+/g, ""); const warnings = [ `Abnormal characters found in \`packageManager\` field, sanitizing from \`${name}\` to \`${sanitized}\`` ]; return { name: sanitized, version, buildMeta, warnings }; } const packageManagers = [ { name: "npm", command: "npm", lockFile: "package-lock.json" }, { name: "pnpm", command: "pnpm", lockFile: "pnpm-lock.yaml", files: ["pnpm-workspace.yaml"] }, { name: "bun", command: "bun", lockFile: ["bun.lockb", "bun.lock"] }, { name: "yarn", command: "yarn", lockFile: "yarn.lock", files: [".yarnrc.yml"] }, { name: "deno", command: "deno", lockFile: "deno.lock", files: ["deno.json"] } ]; async function detectPackageManager(cwd, options = {}) { const detected = await findup( resolve(cwd || "."), async (path) => { if (!options.ignorePackageJSON) { const packageJSONPath = join(path, "package.json"); if (existsSync(packageJSONPath)) { const packageJSON = JSON.parse( await readFile(packageJSONPath, "utf8") ); if (packageJSON?.packageManager) { const { name, version = "0.0.0", buildMeta, warnings } = parsePackageManagerField(packageJSON.packageManager); if (name) { const majorVersion = version.split(".")[0]; const packageManager = packageManagers.find( (pm) => pm.name === name && pm.majorVersion === majorVersion ) || packageManagers.find((pm) => pm.name === name); return { name, command: name, version, majorVersion, buildMeta, warnings, files: packageManager?.files, lockFile: packageManager?.lockFile }; } } } const denoJSONPath = join(path, "deno.json"); if (existsSync(denoJSONPath)) { return packageManagers.find((pm) => pm.name === "deno"); } } if (!options.ignoreLockFile) { for (const packageManager of packageManagers) { const detectionsFiles = [ packageManager.lockFile, packageManager.files ].flat().filter(Boolean); if (detectionsFiles.some((file) => existsSync(resolve(path, file)))) { return { ...packageManager }; } } } }, { includeParentDirs: options.includeParentDirs ?? true } ); if (!detected && !options.ignoreArgv) { const scriptArg = process.argv[1]; if (scriptArg) { for (const packageManager of packageManagers) { const re = new RegExp(`[/\\\\]\\.?${packageManager.command}`); if (re.test(scriptArg)) { return packageManager; } } } } return detected; } function kirbyupHmrPlugin(options) { let config; let entry; let devIndexPath; return { name: "kirbyup:hmr", apply: "serve", configResolved(resolvedConfig) { config = resolvedConfig; entry = resolve(config.root, options.entry); devIndexPath = resolve(config.root, options.outDir || "", "index.dev.mjs"); }, transform(code, id) { if (isHmrRuntimeId(id)) { return code.replace( // https://github.com/vitejs/vite-plugin-vue2/blob/8de73ea6b8a1df4c14308da2885db195dacc2b14/src/utils/hmrRuntime.ts#L173 /^.*=\s*record\.Ctor\.super\.extend\(options\)/m, "$_applyKirbyModifications(record.Ctor.options, options) // injected by kirbyup\n$&" ) + __INJECTED_HMR_CODE__; } }, configureServer(server) { if (!server.httpServer) return; server.httpServer.once("listening", async () => { const entryPath = entry.replace(`${config.root}/`, ""); const { address, family, port } = server.httpServer.address(); const hostname = family === "IPv6" ? `[${address}]` : address; const baseUrl = `http://${hostname}:${port}${config.base}`; const entryUrl = new URL(entryPath, baseUrl).href; const pm = await detectPackageManager(config.root); await fsp.writeFile(devIndexPath, getViteProxyModule(entryUrl, pm)); }); }, closeBundle() { if (fs.existsSync(devIndexPath)) fs.unlinkSync(devIndexPath); } }; } function getViteProxyModule(entryUrl, packageManager) { const pm = packageManager?.name || "npm"; return ` try { await import("${entryUrl}"); } catch (err) { console.error( "[kirbyup] Couldn't connect to the development server. Run \`${pm} run serve\` to start Vite or build the plugin with \`${pm} run build\` so Kirby uses the production version." ); throw err; } `.trimStart(); } function toArray(array) { array ??= []; return Array.isArray(array) ? array : [array]; } const compress = promisify(gzip); async function getCompressedSize(code) { const size = (await compress(typeof code === "string" ? code : Buffer$1.from(code))).length / 1024; return ` / gzip: ${size.toFixed(2)} KiB`; } async function printFileInfo(root, outDir, filePath, content, type, maxLength) { const prettyOutDir = `${normalize(relative(root, resolve(root, outDir)))}/`; const kibs = content.length / 1024; const compressedSize = await getCompressedSize(content); const writeColor = type === "chunk" ? colors.cyan : colors.magenta; consola.log( colors.white(colors.dim(prettyOutDir)) + writeColor(filePath.padEnd(maxLength + 2)) + colors.dim(`${kibs.toFixed(2)} kB${compressedSize}`) ); } 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: [ // Explicitly pass the compiler, since the plugin's resolving of the compiler // looks in the current directory and breaks `npx kirbyup` 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), // Input needs to be specified so dependency pre-bundling works build: { rollupOptions: { input: resolve(options.cwd, options.entry) } }, // Specify origin so asset URLs include Vite server host 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 longest = 0; for (const file in output) { const l = output[file].fileName.length; if (l > longest) longest = l; } for (const { fileName, type, code } of output) { await printFileInfo( options.cwd, options.outDir, fileName, code ?? await fsp.readFile(resolve(options.outDir, fileName), "utf8"), type, longest ); } } 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}/**", // Always ignore dist files "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}"`); } export { build as b, handleError as h, name as n, serve as s, version as v };