@pauldvlp/vp-pkg-shadcn 0.5.0 → 0.5.1

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 (2) hide show
  1. package/dist/index.js +39 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -101,6 +101,16 @@ function addIconDeps(pkg, iconLibrary) {
101
101
  const iconDeps = ICON_LIBS[iconLibrary] ?? ICON_LIBS.hugeicons;
102
102
  pkg.dependencies = { ...pkg.dependencies, ...iconDeps };
103
103
  }
104
+ function validateScope(value) {
105
+ return /^@?[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(value.trim()) ? void 0 : "must be a package scope/name, e.g. @acme";
106
+ }
107
+ function validatePreset(value) {
108
+ return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value.trim()) ? void 0 : "must be a style name or ui.shadcn.com code";
109
+ }
110
+ function validateComponents(value) {
111
+ const parts = value.split(",").map((c) => c.trim()).filter(Boolean);
112
+ return parts.every((c) => /^[a-z0-9][a-z0-9-]*$/.test(c)) ? void 0 : "comma-separated component names, e.g. button,card,dialog";
113
+ }
104
114
  function withRequiredComponents(csv, required = ["button", "badge"]) {
105
115
  const requested = csv.split(",").map((c) => c.trim()).filter(Boolean);
106
116
  return Array.from(/* @__PURE__ */ new Set([...required, ...requested]));
@@ -125,30 +135,36 @@ function uiComponentsJson(theme) {
125
135
  }
126
136
  };
127
137
  }
128
- function shadcnInitFlags(opts) {
138
+ function shadcnInitArgs(opts) {
129
139
  return [
130
- `--base ${opts.base}`,
131
- `--preset ${opts.preset}`,
140
+ "--base",
141
+ opts.base,
142
+ "--preset",
143
+ opts.preset,
132
144
  opts.cssVariables ? "--css-variables" : "--no-css-variables",
133
145
  opts.rtl ? "--rtl" : "--no-rtl",
134
146
  opts.pointer ? "--pointer" : "--no-pointer",
135
147
  "--no-reinstall",
136
148
  "-y",
137
149
  "-f"
138
- ].join(" ");
150
+ ];
139
151
  }
140
152
  function defineTemplate(config) {
141
153
  return config;
142
154
  }
143
- function writeTree(node, dir) {
155
+ function writeTree(node, dir, root = path.resolve(dir)) {
144
156
  for (const [name, value] of Object.entries(node)) {
145
- const p = path.join(dir, name);
157
+ const p = path.resolve(dir, name);
158
+ const rel = path.relative(root, p);
159
+ if (rel === "" || rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
160
+ throw new Error(`Refusing to write outside the target directory: ${name}`);
161
+ }
146
162
  if (typeof value === "string") {
147
163
  fs.mkdirSync(path.dirname(p), { recursive: true });
148
164
  fs.writeFileSync(p, value);
149
165
  } else {
150
166
  fs.mkdirSync(p, { recursive: true });
151
- writeTree(value, p);
167
+ writeTree(value, p, root);
152
168
  }
153
169
  }
154
170
  }
@@ -288,8 +304,9 @@ async function runTemplateCLI(template) {
288
304
  writeTree(creation.files, directory);
289
305
  if (creation.scripts?.length) {
290
306
  for (const step of [...creation.scripts].sort((a, b) => a.phase - b.phase)) {
291
- for (const command of step.commands) {
292
- execFileSync(command, { cwd: directory, stdio: "inherit", shell: true });
307
+ for (const [program, ...commandArgs] of step.commands) {
308
+ if (!program) continue;
309
+ execFileSync(program, commandArgs, { cwd: directory, stdio: "inherit" });
293
310
  }
294
311
  }
295
312
  }
@@ -302,75 +319,31 @@ async function runTemplateCLI(template) {
302
319
  }
303
320
 
304
321
  // src/template.ts
322
+ import { readFileSync } from "node:fs";
305
323
  import path2 from "node:path";
306
324
  import { fileURLToPath } from "node:url";
307
-
308
- // package.json
309
- var package_default = {
310
- name: "@pauldvlp/vp-pkg-shadcn",
311
- version: "0.5.0",
312
- description: "Add a shared, fully customizable shadcn UI package (packages/ui) into an existing Vite+ monorepo, themeable via shadcn presets.",
313
- author: "pauldvlp (https://github.com/pauldvlp/vp-templates)",
314
- license: "MIT",
315
- homepage: "https://github.com/pauldvlp/vp-templates",
316
- repository: {
317
- type: "git",
318
- url: "git+https://github.com/pauldvlp/vp-templates.git",
319
- directory: "packages/vp-pkg-shadcn"
320
- },
321
- bugs: "https://github.com/pauldvlp/vp-templates/issues",
322
- keywords: [
323
- "vite-plus-generator",
324
- "vite-plus",
325
- "shadcn",
326
- "react",
327
- "template"
328
- ],
329
- bin: "./dist/index.js",
330
- type: "module",
331
- files: [
332
- "dist",
333
- "template"
334
- ],
335
- scripts: {
336
- build: "esbuild bin/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --target=node22 --packages=external",
337
- dev: "node bin/index.ts",
338
- prepack: "pnpm run build"
339
- },
340
- dependencies: {
341
- "@clack/prompts": "^1.6.0"
342
- },
343
- devDependencies: {
344
- "@pauldvlp/template-kit": "workspace:*",
345
- "@types/node": "^24",
346
- esbuild: "^0.25.0",
347
- typescript: "^5"
348
- },
349
- engines: {
350
- node: ">=22.18.0"
351
- }
352
- };
353
-
354
- // src/template.ts
355
325
  var TEMPLATE_DIR = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "..", "template");
326
+ var { name: pkgName, description: pkgDescription } = JSON.parse(
327
+ readFileSync(new URL("../package.json", import.meta.url), "utf8")
328
+ );
356
329
  var template_default = defineTemplate({
357
330
  about: {
358
- name: package_default.name,
359
- description: package_default.description
331
+ name: pkgName,
332
+ description: pkgDescription
360
333
  },
361
334
  // This generator drops a package into an *existing* repo, so it must never create a git repo of its own.
362
335
  git: false,
363
336
  options: [
364
337
  // Default the scope to the surrounding monorepo's scope (e.g. `@acme`) so it tracks the repo
365
338
  // instead of a fixed `@app`. Falls back to `@app` when run outside a pnpm workspace.
366
- { key: "scope", type: "string", message: "npm scope for the workspace package, e.g. @acme", default: () => detectMonorepoScope() ?? "@app" },
339
+ { key: "scope", type: "string", message: "npm scope for the workspace package, e.g. @acme", default: () => detectMonorepoScope() ?? "@app", validate: validateScope },
367
340
  { key: "base", type: "select", message: "shadcn component library base (radix-ui or @base-ui)", choices: ["radix", "base"], default: "radix" },
368
- { key: "preset", type: "string", message: "shadcn preset: a style name (nova, vega, maia, lyra, mira, luma, sera, rhea) or a code from ui.shadcn.com", default: DEFAULT_PRESET },
341
+ { key: "preset", type: "string", message: "shadcn preset: a style name (nova, vega, maia, lyra, mira, luma, sera, rhea) or a code from ui.shadcn.com", default: DEFAULT_PRESET, validate: validatePreset },
369
342
  { key: "iconLibrary", type: "select", message: "Icon library", choices: ["lucide", "hugeicons", "radix", "tabler"], default: "hugeicons" },
370
343
  { key: "cssVariables", type: "boolean", message: "Use CSS variables for theming", default: true },
371
344
  { key: "rtl", type: "boolean", message: "Enable RTL support", default: false },
372
345
  { key: "pointer", type: "boolean", message: "Use pointer cursor on interactive elements", default: false },
373
- { key: "components", type: "string", message: "Comma-separated shadcn components to pre-install, e.g. button,card,dialog", default: "button,badge" },
346
+ { key: "components", type: "string", message: "Comma-separated shadcn components to pre-install, e.g. button,card,dialog", default: "button,badge", validate: validateComponents },
374
347
  { key: "install", type: "boolean", message: "Install deps and apply the shadcn theme after scaffolding", default: true }
375
348
  ],
376
349
  async produce({ options }) {
@@ -384,11 +357,11 @@ var template_default = defineTemplate({
384
357
  `);
385
358
  const adds = withRequiredComponents(options.components);
386
359
  const ui = `${scope}/ui`;
387
- const initFlags = shadcnInitFlags(options);
360
+ const initArgs = shadcnInitArgs(options);
388
361
  const scripts = options.install ? [
389
- { commands: ["pnpm install --silent"], phase: 0 },
390
- { commands: [`pnpm --filter ${ui} exec shadcn init ${initFlags}`], phase: 1 },
391
- { commands: [`pnpm --filter ${ui} exec shadcn add ${adds.join(" ")} -y`], phase: 2 }
362
+ { commands: [["pnpm", "install", "--silent"]], phase: 0 },
363
+ { commands: [["pnpm", "--filter", ui, "exec", "shadcn", "init", ...initArgs]], phase: 1 },
364
+ { commands: [["pnpm", "--filter", ui, "exec", "shadcn", "add", ...adds, "-y"]], phase: 2 }
392
365
  ] : [];
393
366
  return {
394
367
  files,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pauldvlp/vp-pkg-shadcn",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Add a shared, fully customizable shadcn UI package (packages/ui) into an existing Vite+ monorepo, themeable via shadcn presets.",
5
5
  "author": "pauldvlp (https://github.com/pauldvlp/vp-templates)",
6
6
  "license": "MIT",