@forinda/kickjs-cli 8.12.0 → 8.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/{agent-docs-CuD2-X3w.mjs → agent-docs-D0qgHBA8.mjs} +25 -21
  2. package/dist/{agent-docs-ykxoPqXX.mjs → agent-docs-DD0hT2pE.mjs} +18 -14
  3. package/dist/agent-docs-DD0hT2pE.mjs.map +1 -0
  4. package/dist/{build-C-AsmCh1.mjs → build-C1ZgYqj7.mjs} +2 -2
  5. package/dist/{build-DQnV0GCD.mjs → build-DEdLI54B.mjs} +3 -3
  6. package/dist/{build-DQnV0GCD.mjs.map → build-DEdLI54B.mjs.map} +1 -1
  7. package/dist/{builtins-BsYCQU80.mjs → builtins-BWtf5CEP.mjs} +2 -2
  8. package/dist/{builtins-Cjp9FT1m.mjs → builtins-DZ-kQSyt.mjs} +135 -124
  9. package/dist/cli.mjs +2 -2
  10. package/dist/{config-DbnBakh8.mjs → config-CHRM7WLZ.mjs} +3 -3
  11. package/dist/{config-DbnBakh8.mjs.map → config-CHRM7WLZ.mjs.map} +1 -1
  12. package/dist/{config-DMFwBdWg.mjs → config-CQGH0FTE.mjs} +2 -2
  13. package/dist/{doctor-rEbeoAoO.mjs → doctor-D0uazKD7.mjs} +34 -23
  14. package/dist/doctor-D0uazKD7.mjs.map +1 -0
  15. package/dist/fullstack-C8k03oM8.mjs +110 -0
  16. package/dist/fullstack-Cr5tath7.mjs +111 -0
  17. package/dist/fullstack-Cr5tath7.mjs.map +1 -0
  18. package/dist/index.d.mts +8 -2
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +2 -2
  21. package/dist/package-manager-B-BW1RBj.mjs +11 -0
  22. package/dist/package-manager-mqxVziQ9.mjs +12 -0
  23. package/dist/package-manager-mqxVziQ9.mjs.map +1 -0
  24. package/dist/{plugin--9hxxgsD.mjs → plugin-DSgKyJSD.mjs} +2 -2
  25. package/dist/{plugin-CvlUt9NG.mjs → plugin-MPu_4sWN.mjs} +2 -2
  26. package/dist/project--T8NPgfc.mjs +133 -0
  27. package/dist/{project-root-BXmtd-te.mjs → project-root-BsIxM6Gj.mjs} +2 -2
  28. package/dist/{project-root-FMt83NHV.mjs → project-root-C4lAZePJ.mjs} +3 -3
  29. package/dist/{project-root-FMt83NHV.mjs.map → project-root-C4lAZePJ.mjs.map} +1 -1
  30. package/dist/project-w-OcvS26.mjs +134 -0
  31. package/dist/project-w-OcvS26.mjs.map +1 -0
  32. package/dist/{rolldown-runtime-C4d34LEE.mjs → rolldown-runtime-BVrPnwAV.mjs} +1 -1
  33. package/dist/{run-plugins-9tjbMqS6.mjs → run-plugins-cqNounm7.mjs} +108 -108
  34. package/dist/run-plugins-cqNounm7.mjs.map +1 -0
  35. package/dist/{typegen-Lf0UPhdl.mjs → typegen-BfFiyGeh.mjs} +4 -4
  36. package/dist/{typegen-AGdhxyRc.mjs → typegen-Ctafx65M.mjs} +5 -5
  37. package/dist/{typegen-AGdhxyRc.mjs.map → typegen-Ctafx65M.mjs.map} +1 -1
  38. package/dist/{types-B5dY2mTz.mjs → types-CH5itn9u.mjs} +1 -1
  39. package/package.json +3 -3
  40. package/templates/base/files/README.md +11 -0
  41. package/templates/template-inertia/feature.json +1 -3
  42. package/templates/template-inertia/files/vite.client.config.ts +4 -1
  43. package/templates/web-kick/files/web/vite.config.ts +4 -1
  44. package/dist/agent-docs-ykxoPqXX.mjs.map +0 -1
  45. package/dist/doctor-rEbeoAoO.mjs.map +0 -1
  46. package/dist/fullstack-DdSScENL.mjs +0 -99
  47. package/dist/fullstack-XhuwRQEb.mjs +0 -100
  48. package/dist/fullstack-XhuwRQEb.mjs.map +0 -1
  49. package/dist/package-manager-C8ekNwsa.mjs +0 -11
  50. package/dist/package-manager-DvXi-NGm.mjs +0 -12
  51. package/dist/package-manager-DvXi-NGm.mjs.map +0 -1
  52. package/dist/project-BN1DnrN3.mjs +0 -134
  53. package/dist/project-BN1DnrN3.mjs.map +0 -1
  54. package/dist/project-DBmrFgdd.mjs +0 -133
  55. package/dist/prompts-DJBDNwHt.mjs +0 -12
  56. package/dist/prompts-DJBDNwHt.mjs.map +0 -1
  57. package/dist/run-plugins-9tjbMqS6.mjs.map +0 -1
  58. package/templates/host-config/files/vercel.json +0 -5
  59. /package/templates/{host-config → deploy-netlify}/files/netlify.toml +0 -0
  60. /package/templates/{fullstack-root → deploy-netlify-workspace}/files/netlify.toml +0 -0
  61. /package/templates/{base → deploy-serverless}/files/src/serverless.ts +0 -0
  62. /package/templates/{fullstack-root → deploy-vercel}/files/vercel.json +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v8.12.0
2
+ * @forinda/kickjs-cli v8.13.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,6 +8,6 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as e}from"./rolldown-runtime-C4d34LEE.mjs";import{dirname as t,isAbsolute as n,join as r,relative as i,resolve as a}from"node:path";import{cpSync as o,existsSync as s,mkdirSync as c,statSync as l,writeFileSync as u}from"node:fs";import{glob as d}from"glob";import{groupAssetKeys as f}from"@forinda/kickjs";var p=/* @__PURE__ */ e({ASSET_MANIFEST_VERSION:()=>1,buildAssets:()=>m});async function m(e,t){let{cwd:n,silent:o=!1}=t,s=t.distDir??e?.build?.outDir??`dist`,l=e?.assetMap;if(!l||Object.keys(l).length===0)return null;let d=o?()=>{}:console.log,f=a(n,s);c(f,{recursive:!0});let p=[],m={};for(let[e,t]of Object.entries(l)){let r=await h(e,t,n,f);p.push(r.entrySummary),Object.assign(m,r.manifestSlice),d(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let g={version:1,entries:m},_=r(f,`.kickjs-assets.json`);return u(_,JSON.stringify(g,null,2)+`
11
+ import{t as e}from"./rolldown-runtime-BVrPnwAV.mjs";import{dirname as t,isAbsolute as n,join as r,relative as i,resolve as a}from"node:path";import{cpSync as o,existsSync as s,mkdirSync as c,statSync as l,writeFileSync as u}from"node:fs";import{glob as d}from"glob";import{groupAssetKeys as f}from"@forinda/kickjs";var p=/* @__PURE__ */ e({ASSET_MANIFEST_VERSION:()=>1,buildAssets:()=>m});async function m(e,t){let{cwd:n,silent:o=!1}=t,s=t.distDir??e?.build?.outDir??`dist`,l=e?.assetMap;if(!l||Object.keys(l).length===0)return null;let d=o?()=>{}:console.log,f=a(n,s);c(f,{recursive:!0});let p=[],m={};for(let[e,t]of Object.entries(l)){let r=await h(e,t,n,f);p.push(r.entrySummary),Object.assign(m,r.manifestSlice),d(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let g={version:1,entries:m},_=r(f,`.kickjs-assets.json`);return u(_,JSON.stringify(g,null,2)+`
12
12
  `,`utf-8`),d(` ✓ wrote manifest → ${i(n,_)} (${Object.keys(m).length} entries)`),{manifestPath:_,entries:p,manifest:g}}async function h(e,n,l,u){let p=a(l,n.src),m=n.dest?a(l,n.dest):r(u,e);if(v(m,l))return console.warn(` ⚠ assetMap.${e}.dest ('${n.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:n.src,dest:i(l,m),filesCopied:0},manifestSlice:{}};if(!s(p)||!y(p))return{entrySummary:{namespace:e,src:n.src,dest:i(l,m),filesCopied:0},manifestSlice:{}};let h=n.glob??`**/*`,b=await d(h,{cwd:p,nodir:!0,dot:!1,posix:!0});c(m,{recursive:!0});let x={},S=[...b].toSorted(),{pairs:C,collisionGroupsResolved:w}=f(e,S,{strategy:n.keys??`auto`}),T=0;for(let{rel:e,key:n}of C){let i=r(p,e),a=r(m,e);x[n]=_(u,a),!g(i,a)&&(c(t(a),{recursive:!0}),o(i,a),T++)}return w>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${w} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:n.src,dest:i(l,m),filesCopied:T},manifestSlice:x}}function g(e,t){if(!s(t))return!1;try{let n=l(e),r=l(t);return r.size===n.size&&r.mtimeMs>=n.mtimeMs}catch{return!1}}function _(e,t){return i(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function v(e,t){let r=i(t,e);return r===``?!1:r.startsWith(`..`)||n(r)}function y(e){try{return l(e).isDirectory()}catch{return!1}}export{p as n,m as t};
13
- //# sourceMappingURL=build-DQnV0GCD.mjs.map
13
+ //# sourceMappingURL=build-DEdLI54B.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-DQnV0GCD.mjs","names":["__exportAll","build_exports","buildAssets","opts","distDir","config","map","log","silent","distAbs","resolve","cwd","mkdirSync","summary","manifestEntries","namespace","entry","result","processEntry","manifest","manifestPath","join","writeFileSync","relative","srcAbs","destAbs","escapesRoot","existsSync","isDirectorySync","pattern","matches","glob","manifestSlice","sorted","groupAssetKeys","filesCopied","relPath","pairs","srcFile","destFile","key","toManifestRelative","dirname","cpSync","collisionGroupsResolved","isUpToDate","s","statSync","d","manifestDir","rel","root","path","isAbsolute"],"sources":["../src/asset-manager/build.ts"],"sourcesContent":["/**\n * Build pipeline for `assetMap` entries (asset-manager PR 2).\n *\n * For each entry, walks `src/<...>` matching the configured `glob`,\n * copies matches into `dest` (default `dist/<name>/`), then emits a\n * `dist/.kickjs-assets.json` manifest mapping logical\n * `<namespace>/<key>` keys to repo-relative paths inside `dist/`.\n *\n * Pure function on top of `node:fs` + `glob` — no shell, no side\n * effects beyond the configured directory writes. The build entry-\n * point in `commands/run.ts` calls `buildAssets` after the existing\n * `copyDirs` step.\n *\n * @module @forinda/kickjs-cli/asset-manager/build\n */\n\nimport { cpSync, existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { glob } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry, KickConfig } from '../config'\n\n/** Wire-format version for `dist/.kickjs-assets.json`. Bump on shape change. */\nexport const ASSET_MANIFEST_VERSION = 1 as const\n\n/** On-disk manifest format (`dist/.kickjs-assets.json`). */\nexport interface AssetManifest {\n version: typeof ASSET_MANIFEST_VERSION\n /**\n * Logical key → manifest-relative path. Logical key is\n * `<namespace>/<key>` where `<key>` is the file path under `src`\n * with the extension stripped + path separators normalised.\n *\n * Path values are relative to the manifest file's directory so the\n * runtime can resolve them with a single `path.resolve(manifestDir,\n * entry)` regardless of where dist/ lives.\n */\n entries: Record<string, string>\n}\n\nexport interface BuildAssetsOptions {\n /** Project root — resolved for every relative path in the entry. */\n cwd: string\n /**\n * Output dir for the manifest + per-namespace asset copies. When\n * omitted, falls back to `config.build?.outDir` (resolved against\n * `cwd`), then to `dist/` under cwd. Adopters with a custom Vite\n * `build.outDir` should set `kick.config.ts.build.outDir` to match.\n */\n distDir?: string\n /** Suppress per-entry log lines. Default: false. */\n silent?: boolean\n}\n\n/** One entry in the per-build summary returned by `buildAssets`. */\nexport interface BuildAssetsEntryResult {\n namespace: string\n src: string\n dest: string\n /**\n * Number of files actually written this run. On an incremental\n * rebuild where nothing changed this is 0 even though the manifest\n * still lists every matched file.\n */\n filesCopied: number\n}\n\n/** Aggregated outcome of `buildAssets`. */\nexport interface BuildAssetsResult {\n manifestPath: string\n entries: BuildAssetsEntryResult[]\n /** `entries` merged into a single record — useful for tests + tooling. */\n manifest: AssetManifest\n}\n\n/**\n * Run the full asset build for a loaded config:\n *\n * 1. For each `assetMap` entry, glob → copy → manifest stub.\n * 2. Write `dist/.kickjs-assets.json`.\n *\n * Returns a summary including the manifest contents. No-op (and no\n * manifest written) when `assetMap` is empty / missing — the build\n * pipeline shouldn't litter `dist/` with empty manifests for\n * adopters who don't use the feature.\n */\nexport async function buildAssets(\n config: KickConfig | null,\n opts: BuildAssetsOptions,\n): Promise<BuildAssetsResult | null> {\n const { cwd, silent = false } = opts\n // Resolution order: explicit opts.distDir → config.build.outDir → 'dist'.\n // The CLI build command passes nothing explicit, so adopters control\n // the output via kick.config.ts.build.outDir alone.\n const distDir = opts.distDir ?? config?.build?.outDir ?? 'dist'\n const map = config?.assetMap\n if (!map || Object.keys(map).length === 0) return null\n\n const log = silent ? () => {} : console.log\n\n const distAbs = resolve(cwd, distDir)\n mkdirSync(distAbs, { recursive: true })\n\n const summary: BuildAssetsEntryResult[] = []\n const manifestEntries: Record<string, string> = {}\n\n for (const [namespace, entry] of Object.entries(map)) {\n const result = await processEntry(namespace, entry, cwd, distAbs)\n summary.push(result.entrySummary)\n Object.assign(manifestEntries, result.manifestSlice)\n log(\n ` ✓ ${namespace}: ${result.entrySummary.filesCopied} file(s) → ${result.entrySummary.dest}`,\n )\n }\n\n const manifest: AssetManifest = {\n version: ASSET_MANIFEST_VERSION,\n entries: manifestEntries,\n }\n const manifestPath = join(distAbs, '.kickjs-assets.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n log(\n ` ✓ wrote manifest → ${relative(cwd, manifestPath)} (${Object.keys(manifestEntries).length} entries)`,\n )\n\n return { manifestPath, entries: summary, manifest }\n}\n\n/** Per-entry inner pipeline — extracted for unit-test reuse. */\nasync function processEntry(\n namespace: string,\n entry: AssetMapEntry,\n cwd: string,\n distAbs: string,\n): Promise<{\n entrySummary: BuildAssetsEntryResult\n manifestSlice: Record<string, string>\n}> {\n const srcAbs = resolve(cwd, entry.src)\n const destAbs = entry.dest ? resolve(cwd, entry.dest) : join(distAbs, namespace)\n\n // Defensive: refuse to write outside the project root (cwd) even\n // though validateAssetMap warned about it at config-load time. The\n // build step shouldn't trust upstream warnings — a typo like\n // `dest: '../../'` would otherwise sprinkle files outside the\n // workspace despite the warning being printed.\n if (escapesRoot(destAbs, cwd)) {\n console.warn(\n ` ⚠ assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — skipping copy`,\n )\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n // Treat src-not-a-directory the same as src-missing — `glob` would\n // throw if pointed at a file, surfacing as a generic build failure\n // instead of a clean 0-files entry. Matches the validator's warning\n // shape (already emitted at config-load time for the missing case).\n if (!existsSync(srcAbs) || !isDirectorySync(srcAbs)) {\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n const pattern = entry.glob ?? '**/*'\n // `glob` returns paths relative to `cwd` when `cwd` is set —\n // exactly the slugs we want for the manifest keys.\n const matches = await glob(pattern, {\n cwd: srcAbs,\n nodir: true,\n dot: false,\n posix: true,\n })\n\n mkdirSync(destAbs, { recursive: true })\n\n const manifestSlice: Record<string, string> = {}\n // Sort the walk so the manifest is byte-stable across platforms.\n // The `groupAssetKeys` helper preserves input order, so a stable\n // sort here makes the resulting manifest stable too.\n const sorted = [...matches].toSorted()\n const { pairs, collisionGroupsResolved } = groupAssetKeys(namespace, sorted, {\n strategy: entry.keys ?? 'auto',\n })\n\n // Copy the files and write the keyed manifest slice. Both come from\n // the same `pairs` order so the on-disk layout matches the\n // manifest's iteration order — easier to grep in cold-start\n // debugging.\n //\n // Incremental: skip the copy when the destination is already\n // up-to-date (exists, same size, mtime ≥ source). `cpSync` stamps the\n // copy with the current time, so an unchanged source always satisfies\n // `dest.mtime ≥ src.mtime` on the next run — turning a re-build into a\n // pure stat sweep instead of re-copying every asset. The manifest\n // slice is written unconditionally so the manifest stays complete.\n let filesCopied = 0\n for (const { rel: relPath, key } of pairs) {\n const srcFile = join(srcAbs, relPath)\n const destFile = join(destAbs, relPath)\n manifestSlice[key] = toManifestRelative(distAbs, destFile)\n if (isUpToDate(srcFile, destFile)) continue\n mkdirSync(dirname(destFile), { recursive: true })\n cpSync(srcFile, destFile)\n filesCopied++\n }\n\n if (collisionGroupsResolved > 0) {\n console.log(\n ` ℹ assetMap.${namespace}: auto-resolved ${collisionGroupsResolved} basename collision(s) by keeping extensions ` +\n `(set 'keys: \"strip\"' to opt back into legacy last-write-wins behaviour, or 'keys: \"with-extension\"' to keep all keys verbose).`,\n )\n }\n\n return {\n entrySummary: {\n namespace,\n src: entry.src,\n dest: relative(cwd, destAbs),\n filesCopied,\n },\n manifestSlice,\n }\n}\n\n/**\n * Is `destFile` already a current copy of `srcFile`? True when the\n * destination exists with the same byte size and an mtime no older than\n * the source. Used to skip redundant copies on an incremental rebuild.\n */\nfunction isUpToDate(srcFile: string, destFile: string): boolean {\n if (!existsSync(destFile)) return false\n try {\n const s = statSync(srcFile)\n const d = statSync(destFile)\n return d.size === s.size && d.mtimeMs >= s.mtimeMs\n } catch {\n return false\n }\n}\n\n/**\n * Make `destFile` relative to the manifest's directory + force POSIX\n * separators so the manifest is byte-stable across platforms.\n */\nfunction toManifestRelative(manifestDir: string, destFile: string): string {\n const rel = relative(manifestDir, destFile)\n // path.relative returns OS-native separators; the manifest is JSON\n // and the runtime uses path.resolve which handles either, but a\n // forward-slash manifest is grep-friendly + diff-stable.\n return rel.split(/[\\\\/]/).filter(Boolean).join('/')\n}\n\n/**\n * Pure manifest writer — handy for tests that want to assert against\n * a hand-crafted manifest without exercising the full pipeline.\n */\nexport function writeAssetManifest(distDir: string, manifest: AssetManifest): string {\n const path = join(distDir, '.kickjs-assets.json')\n mkdirSync(distDir, { recursive: true })\n writeFileSync(path, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n return path\n}\n\n/**\n * Read + parse a manifest from disk. Returns `null` on missing or\n * malformed file rather than throwing — the runtime resolver wants\n * to fall through to dev-mode lookup in that case.\n */\nexport function readAssetManifest(distDir: string): AssetManifest | null {\n const path = join(distDir, '.kickjs-assets.json')\n if (!existsSync(path)) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs')\n const raw = fs.readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<AssetManifest>\n if (parsed.version !== ASSET_MANIFEST_VERSION) return null\n if (!parsed.entries || typeof parsed.entries !== 'object') return null\n return parsed as AssetManifest\n } catch {\n return null\n }\n}\n\n/**\n * Project-root escape check that's safe across symlinks + drive letters.\n * `path.relative` returns `..` segments when the target sits above root,\n * and an absolute path when the two live on different roots (Windows).\n * `startsWith(root)` would miss both cases.\n */\nfunction escapesRoot(path: string, root: string): boolean {\n const rel = relative(root, path)\n if (rel === '') return false\n return rel.startsWith('..') || isAbsolute(rel)\n}\n\n/** Pure helper — `false` for missing, non-dir, or unreadable paths. */\nfunction isDirectorySync(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"build-DEdLI54B.mjs","names":["__exportAll","build_exports","buildAssets","opts","distDir","config","map","log","silent","distAbs","resolve","cwd","mkdirSync","summary","manifestEntries","namespace","entry","result","processEntry","manifest","manifestPath","join","writeFileSync","relative","srcAbs","destAbs","escapesRoot","existsSync","isDirectorySync","pattern","matches","glob","manifestSlice","sorted","groupAssetKeys","filesCopied","relPath","pairs","srcFile","destFile","key","toManifestRelative","dirname","cpSync","collisionGroupsResolved","isUpToDate","s","statSync","d","manifestDir","rel","root","path","isAbsolute"],"sources":["../src/asset-manager/build.ts"],"sourcesContent":["/**\n * Build pipeline for `assetMap` entries (asset-manager PR 2).\n *\n * For each entry, walks `src/<...>` matching the configured `glob`,\n * copies matches into `dest` (default `dist/<name>/`), then emits a\n * `dist/.kickjs-assets.json` manifest mapping logical\n * `<namespace>/<key>` keys to repo-relative paths inside `dist/`.\n *\n * Pure function on top of `node:fs` + `glob` — no shell, no side\n * effects beyond the configured directory writes. The build entry-\n * point in `commands/run.ts` calls `buildAssets` after the existing\n * `copyDirs` step.\n *\n * @module @forinda/kickjs-cli/asset-manager/build\n */\n\nimport { cpSync, existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { glob } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry, KickConfig } from '../config'\n\n/** Wire-format version for `dist/.kickjs-assets.json`. Bump on shape change. */\nexport const ASSET_MANIFEST_VERSION = 1 as const\n\n/** On-disk manifest format (`dist/.kickjs-assets.json`). */\nexport interface AssetManifest {\n version: typeof ASSET_MANIFEST_VERSION\n /**\n * Logical key → manifest-relative path. Logical key is\n * `<namespace>/<key>` where `<key>` is the file path under `src`\n * with the extension stripped + path separators normalised.\n *\n * Path values are relative to the manifest file's directory so the\n * runtime can resolve them with a single `path.resolve(manifestDir,\n * entry)` regardless of where dist/ lives.\n */\n entries: Record<string, string>\n}\n\nexport interface BuildAssetsOptions {\n /** Project root — resolved for every relative path in the entry. */\n cwd: string\n /**\n * Output dir for the manifest + per-namespace asset copies. When\n * omitted, falls back to `config.build?.outDir` (resolved against\n * `cwd`), then to `dist/` under cwd. Adopters with a custom Vite\n * `build.outDir` should set `kick.config.ts.build.outDir` to match.\n */\n distDir?: string\n /** Suppress per-entry log lines. Default: false. */\n silent?: boolean\n}\n\n/** One entry in the per-build summary returned by `buildAssets`. */\nexport interface BuildAssetsEntryResult {\n namespace: string\n src: string\n dest: string\n /**\n * Number of files actually written this run. On an incremental\n * rebuild where nothing changed this is 0 even though the manifest\n * still lists every matched file.\n */\n filesCopied: number\n}\n\n/** Aggregated outcome of `buildAssets`. */\nexport interface BuildAssetsResult {\n manifestPath: string\n entries: BuildAssetsEntryResult[]\n /** `entries` merged into a single record — useful for tests + tooling. */\n manifest: AssetManifest\n}\n\n/**\n * Run the full asset build for a loaded config:\n *\n * 1. For each `assetMap` entry, glob → copy → manifest stub.\n * 2. Write `dist/.kickjs-assets.json`.\n *\n * Returns a summary including the manifest contents. No-op (and no\n * manifest written) when `assetMap` is empty / missing — the build\n * pipeline shouldn't litter `dist/` with empty manifests for\n * adopters who don't use the feature.\n */\nexport async function buildAssets(\n config: KickConfig | null,\n opts: BuildAssetsOptions,\n): Promise<BuildAssetsResult | null> {\n const { cwd, silent = false } = opts\n // Resolution order: explicit opts.distDir → config.build.outDir → 'dist'.\n // The CLI build command passes nothing explicit, so adopters control\n // the output via kick.config.ts.build.outDir alone.\n const distDir = opts.distDir ?? config?.build?.outDir ?? 'dist'\n const map = config?.assetMap\n if (!map || Object.keys(map).length === 0) return null\n\n const log = silent ? () => {} : console.log\n\n const distAbs = resolve(cwd, distDir)\n mkdirSync(distAbs, { recursive: true })\n\n const summary: BuildAssetsEntryResult[] = []\n const manifestEntries: Record<string, string> = {}\n\n for (const [namespace, entry] of Object.entries(map)) {\n const result = await processEntry(namespace, entry, cwd, distAbs)\n summary.push(result.entrySummary)\n Object.assign(manifestEntries, result.manifestSlice)\n log(\n ` ✓ ${namespace}: ${result.entrySummary.filesCopied} file(s) → ${result.entrySummary.dest}`,\n )\n }\n\n const manifest: AssetManifest = {\n version: ASSET_MANIFEST_VERSION,\n entries: manifestEntries,\n }\n const manifestPath = join(distAbs, '.kickjs-assets.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n log(\n ` ✓ wrote manifest → ${relative(cwd, manifestPath)} (${Object.keys(manifestEntries).length} entries)`,\n )\n\n return { manifestPath, entries: summary, manifest }\n}\n\n/** Per-entry inner pipeline — extracted for unit-test reuse. */\nasync function processEntry(\n namespace: string,\n entry: AssetMapEntry,\n cwd: string,\n distAbs: string,\n): Promise<{\n entrySummary: BuildAssetsEntryResult\n manifestSlice: Record<string, string>\n}> {\n const srcAbs = resolve(cwd, entry.src)\n const destAbs = entry.dest ? resolve(cwd, entry.dest) : join(distAbs, namespace)\n\n // Defensive: refuse to write outside the project root (cwd) even\n // though validateAssetMap warned about it at config-load time. The\n // build step shouldn't trust upstream warnings — a typo like\n // `dest: '../../'` would otherwise sprinkle files outside the\n // workspace despite the warning being printed.\n if (escapesRoot(destAbs, cwd)) {\n console.warn(\n ` ⚠ assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — skipping copy`,\n )\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n // Treat src-not-a-directory the same as src-missing — `glob` would\n // throw if pointed at a file, surfacing as a generic build failure\n // instead of a clean 0-files entry. Matches the validator's warning\n // shape (already emitted at config-load time for the missing case).\n if (!existsSync(srcAbs) || !isDirectorySync(srcAbs)) {\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n const pattern = entry.glob ?? '**/*'\n // `glob` returns paths relative to `cwd` when `cwd` is set —\n // exactly the slugs we want for the manifest keys.\n const matches = await glob(pattern, {\n cwd: srcAbs,\n nodir: true,\n dot: false,\n posix: true,\n })\n\n mkdirSync(destAbs, { recursive: true })\n\n const manifestSlice: Record<string, string> = {}\n // Sort the walk so the manifest is byte-stable across platforms.\n // The `groupAssetKeys` helper preserves input order, so a stable\n // sort here makes the resulting manifest stable too.\n const sorted = [...matches].toSorted()\n const { pairs, collisionGroupsResolved } = groupAssetKeys(namespace, sorted, {\n strategy: entry.keys ?? 'auto',\n })\n\n // Copy the files and write the keyed manifest slice. Both come from\n // the same `pairs` order so the on-disk layout matches the\n // manifest's iteration order — easier to grep in cold-start\n // debugging.\n //\n // Incremental: skip the copy when the destination is already\n // up-to-date (exists, same size, mtime ≥ source). `cpSync` stamps the\n // copy with the current time, so an unchanged source always satisfies\n // `dest.mtime ≥ src.mtime` on the next run — turning a re-build into a\n // pure stat sweep instead of re-copying every asset. The manifest\n // slice is written unconditionally so the manifest stays complete.\n let filesCopied = 0\n for (const { rel: relPath, key } of pairs) {\n const srcFile = join(srcAbs, relPath)\n const destFile = join(destAbs, relPath)\n manifestSlice[key] = toManifestRelative(distAbs, destFile)\n if (isUpToDate(srcFile, destFile)) continue\n mkdirSync(dirname(destFile), { recursive: true })\n cpSync(srcFile, destFile)\n filesCopied++\n }\n\n if (collisionGroupsResolved > 0) {\n console.log(\n ` ℹ assetMap.${namespace}: auto-resolved ${collisionGroupsResolved} basename collision(s) by keeping extensions ` +\n `(set 'keys: \"strip\"' to opt back into legacy last-write-wins behaviour, or 'keys: \"with-extension\"' to keep all keys verbose).`,\n )\n }\n\n return {\n entrySummary: {\n namespace,\n src: entry.src,\n dest: relative(cwd, destAbs),\n filesCopied,\n },\n manifestSlice,\n }\n}\n\n/**\n * Is `destFile` already a current copy of `srcFile`? True when the\n * destination exists with the same byte size and an mtime no older than\n * the source. Used to skip redundant copies on an incremental rebuild.\n */\nfunction isUpToDate(srcFile: string, destFile: string): boolean {\n if (!existsSync(destFile)) return false\n try {\n const s = statSync(srcFile)\n const d = statSync(destFile)\n return d.size === s.size && d.mtimeMs >= s.mtimeMs\n } catch {\n return false\n }\n}\n\n/**\n * Make `destFile` relative to the manifest's directory + force POSIX\n * separators so the manifest is byte-stable across platforms.\n */\nfunction toManifestRelative(manifestDir: string, destFile: string): string {\n const rel = relative(manifestDir, destFile)\n // path.relative returns OS-native separators; the manifest is JSON\n // and the runtime uses path.resolve which handles either, but a\n // forward-slash manifest is grep-friendly + diff-stable.\n return rel.split(/[\\\\/]/).filter(Boolean).join('/')\n}\n\n/**\n * Pure manifest writer — handy for tests that want to assert against\n * a hand-crafted manifest without exercising the full pipeline.\n */\nexport function writeAssetManifest(distDir: string, manifest: AssetManifest): string {\n const path = join(distDir, '.kickjs-assets.json')\n mkdirSync(distDir, { recursive: true })\n writeFileSync(path, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n return path\n}\n\n/**\n * Read + parse a manifest from disk. Returns `null` on missing or\n * malformed file rather than throwing — the runtime resolver wants\n * to fall through to dev-mode lookup in that case.\n */\nexport function readAssetManifest(distDir: string): AssetManifest | null {\n const path = join(distDir, '.kickjs-assets.json')\n if (!existsSync(path)) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs')\n const raw = fs.readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<AssetManifest>\n if (parsed.version !== ASSET_MANIFEST_VERSION) return null\n if (!parsed.entries || typeof parsed.entries !== 'object') return null\n return parsed as AssetManifest\n } catch {\n return null\n }\n}\n\n/**\n * Project-root escape check that's safe across symlinks + drive letters.\n * `path.relative` returns `..` segments when the target sits above root,\n * and an absolute path when the two live on different roots (Windows).\n * `startsWith(root)` would miss both cases.\n */\nfunction escapesRoot(path: string, root: string): boolean {\n const rel = relative(root, path)\n if (rel === '') return false\n return rel.startsWith('..') || isAbsolute(rel)\n}\n\n/** Pure helper — `false` for missing, non-dir, or unreadable paths. */\nfunction isDirectorySync(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v8.12.0
2
+ * @forinda/kickjs-cli v8.13.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- export{t as builtinCliPlugins}from"./run-plugins-9tjbMqS6.mjs";
11
+ export{t as builtinCliPlugins}from"./run-plugins-cqNounm7.mjs";