@pauldvlp/vp-react-ts-shadcn 0.5.0 → 0.5.2

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.
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@ import * as prompts from "@clack/prompts";
8
8
  var RENAME = {
9
9
  _gitignore: ".gitignore",
10
10
  _dockerignore: ".dockerignore",
11
- "_env.example": ".env.example"
11
+ "_env.example": ".env.example",
12
+ "_package.json": "package.json"
12
13
  };
13
14
  var DEFAULT_PRESET = "b30557okNu";
14
15
  var ICON_LIBS = {
@@ -63,6 +64,19 @@ function addIconDeps(pkg, iconLibrary) {
63
64
  const iconDeps = ICON_LIBS[iconLibrary] ?? ICON_LIBS.hugeicons;
64
65
  pkg.dependencies = { ...pkg.dependencies, ...iconDeps };
65
66
  }
67
+ function validateScope(value) {
68
+ return /^@?[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(value.trim()) ? void 0 : "must be a package scope/name, e.g. @acme";
69
+ }
70
+ function validateName(value) {
71
+ return /^@?[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(value.trim()) ? void 0 : "must be a package name, e.g. my-app or @acme/app";
72
+ }
73
+ function validatePreset(value) {
74
+ return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value.trim()) ? void 0 : "must be a style name or ui.shadcn.com code";
75
+ }
76
+ function validateComponents(value) {
77
+ const parts = value.split(",").map((c) => c.trim()).filter(Boolean);
78
+ return parts.every((c) => /^[a-z0-9][a-z0-9-]*$/.test(c)) ? void 0 : "comma-separated component names, e.g. button,card,dialog";
79
+ }
66
80
  function withRequiredComponents(csv, required = ["button", "badge"]) {
67
81
  const requested = csv.split(",").map((c) => c.trim()).filter(Boolean);
68
82
  return Array.from(/* @__PURE__ */ new Set([...required, ...requested]));
@@ -106,30 +120,36 @@ function appComponentsJson(theme) {
106
120
  }
107
121
  };
108
122
  }
109
- function shadcnInitFlags(opts) {
123
+ function shadcnInitArgs(opts) {
110
124
  return [
111
- `--base ${opts.base}`,
112
- `--preset ${opts.preset}`,
125
+ "--base",
126
+ opts.base,
127
+ "--preset",
128
+ opts.preset,
113
129
  opts.cssVariables ? "--css-variables" : "--no-css-variables",
114
130
  opts.rtl ? "--rtl" : "--no-rtl",
115
131
  opts.pointer ? "--pointer" : "--no-pointer",
116
132
  "--no-reinstall",
117
133
  "-y",
118
134
  "-f"
119
- ].join(" ");
135
+ ];
120
136
  }
121
137
  function defineTemplate(config) {
122
138
  return config;
123
139
  }
124
- function writeTree(node, dir) {
140
+ function writeTree(node, dir, root = path.resolve(dir)) {
125
141
  for (const [name, value] of Object.entries(node)) {
126
- const p = path.join(dir, name);
142
+ const p = path.resolve(dir, name);
143
+ const rel = path.relative(root, p);
144
+ if (rel === "" || rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
145
+ throw new Error(`Refusing to write outside the target directory: ${name}`);
146
+ }
127
147
  if (typeof value === "string") {
128
148
  fs.mkdirSync(path.dirname(p), { recursive: true });
129
149
  fs.writeFileSync(p, value);
130
150
  } else {
131
151
  fs.mkdirSync(p, { recursive: true });
132
- writeTree(value, p);
152
+ writeTree(value, p, root);
133
153
  }
134
154
  }
135
155
  }
@@ -269,8 +289,9 @@ async function runTemplateCLI(template) {
269
289
  writeTree(creation.files, directory);
270
290
  if (creation.scripts?.length) {
271
291
  for (const step of [...creation.scripts].sort((a, b) => a.phase - b.phase)) {
272
- for (const command of step.commands) {
273
- execFileSync(command, { cwd: directory, stdio: "inherit", shell: true });
292
+ for (const [program, ...commandArgs] of step.commands) {
293
+ if (!program) continue;
294
+ execFileSync(program, commandArgs, { cwd: directory, stdio: "inherit" });
274
295
  }
275
296
  }
276
297
  }
@@ -283,75 +304,31 @@ async function runTemplateCLI(template) {
283
304
  }
284
305
 
285
306
  // src/template.ts
307
+ import { readFileSync } from "node:fs";
286
308
  import path2 from "node:path";
287
309
  import { fileURLToPath } from "node:url";
288
-
289
- // package.json
290
- var package_default = {
291
- name: "@pauldvlp/vp-react-ts-shadcn",
292
- version: "0.5.0",
293
- description: "Vite+ monorepo template: one frontend app (website) + a shared shadcn UI package, themeable via shadcn presets.",
294
- author: "pauldvlp (https://github.com/pauldvlp/vp-templates)",
295
- license: "MIT",
296
- homepage: "https://github.com/pauldvlp/vp-templates",
297
- repository: {
298
- type: "git",
299
- url: "git+https://github.com/pauldvlp/vp-templates.git",
300
- directory: "packages/vp-react-ts-shadcn"
301
- },
302
- bugs: "https://github.com/pauldvlp/vp-templates/issues",
303
- keywords: [
304
- "vite-plus-generator",
305
- "vite-plus",
306
- "shadcn",
307
- "react",
308
- "template"
309
- ],
310
- bin: "./dist/index.js",
311
- type: "module",
312
- files: [
313
- "dist",
314
- "template"
315
- ],
316
- scripts: {
317
- build: "esbuild bin/index.ts --bundle --outfile=dist/index.js --format=esm --platform=node --target=node22 --packages=external",
318
- dev: "node bin/index.ts",
319
- prepack: "pnpm run build"
320
- },
321
- dependencies: {
322
- "@clack/prompts": "^1.6.0"
323
- },
324
- devDependencies: {
325
- "@pauldvlp/template-kit": "workspace:*",
326
- "@types/node": "^24",
327
- esbuild: "^0.25.0",
328
- typescript: "^5"
329
- },
330
- engines: {
331
- node: ">=22.18.0"
332
- }
333
- };
334
-
335
- // src/template.ts
336
310
  var TEMPLATE_DIR = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "..", "template");
311
+ var { name: pkgName, description: pkgDescription } = JSON.parse(
312
+ readFileSync(new URL("../package.json", import.meta.url), "utf8")
313
+ );
337
314
  var template_default = defineTemplate({
338
315
  about: {
339
- name: package_default.name,
340
- description: package_default.description
316
+ name: pkgName,
317
+ description: pkgDescription
341
318
  },
342
319
  options: [
343
320
  // Default the name to the target directory the user chose, so they don't retype it.
344
- { key: "name", type: "string", message: "Root project / package name", default: ({ directory }) => dirName(directory) },
321
+ { key: "name", type: "string", message: "Root project / package name", default: ({ directory }) => dirName(directory), validate: validateName },
345
322
  // Lazily default the package scope to `@<name>` (prefixing `@` unless the name already has one),
346
323
  // so it tracks the project name instead of a fixed value. Falls back to `@app` when no name is set.
347
- { key: "scope", type: "string", message: "npm scope for workspace packages, e.g. @acme (defaults to @<name>)", default: ({ options }) => options.name ? toScope(options.name) : "@app" },
324
+ { key: "scope", type: "string", message: "npm scope for workspace packages, e.g. @acme (defaults to @<name>)", default: ({ options }) => options.name ? toScope(options.name) : "@app", validate: validateScope },
348
325
  { key: "base", type: "select", message: "shadcn component library base (radix-ui or @base-ui)", choices: ["radix", "base"], default: "radix" },
349
- { 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 },
326
+ { 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 },
350
327
  { key: "iconLibrary", type: "select", message: "Icon library", choices: ["lucide", "hugeicons", "radix", "tabler"], default: "hugeicons" },
351
328
  { key: "cssVariables", type: "boolean", message: "Use CSS variables for theming", default: true },
352
329
  { key: "rtl", type: "boolean", message: "Enable RTL support", default: false },
353
330
  { key: "pointer", type: "boolean", message: "Use pointer cursor on interactive elements", default: false },
354
- { key: "components", type: "string", message: "Comma-separated shadcn components to pre-install, e.g. button,card,dialog", default: "button,badge" },
331
+ { key: "components", type: "string", message: "Comma-separated shadcn components to pre-install, e.g. button,card,dialog", default: "button,badge", validate: validateComponents },
355
332
  { key: "install", type: "boolean", message: "Install deps and apply the shadcn theme after scaffolding", default: true }
356
333
  ],
357
334
  async produce({ options }) {
@@ -373,11 +350,11 @@ var template_default = defineTemplate({
373
350
  }
374
351
  const adds = withRequiredComponents(options.components);
375
352
  const ui = `${scope}/ui`;
376
- const initFlags = shadcnInitFlags(options);
353
+ const initArgs = shadcnInitArgs(options);
377
354
  const scripts = options.install ? [
378
- { commands: ["pnpm install --silent"], phase: 0 },
379
- { commands: [`pnpm --filter ${ui} exec shadcn init ${initFlags}`], phase: 1 },
380
- { commands: [`pnpm --filter ${ui} exec shadcn add ${adds.join(" ")} -y`], phase: 2 }
355
+ { commands: [["pnpm", "install", "--silent"]], phase: 0 },
356
+ { commands: [["pnpm", "--filter", ui, "exec", "shadcn", "init", ...initArgs]], phase: 1 },
357
+ { commands: [["pnpm", "--filter", ui, "exec", "shadcn", "add", ...adds, "-y"]], phase: 2 }
381
358
  ] : [];
382
359
  return {
383
360
  files,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pauldvlp/vp-react-ts-shadcn",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Vite+ monorepo template: one frontend app (website) + a shared shadcn UI package, themeable via shadcn presets.",
5
5
  "author": "pauldvlp (https://github.com/pauldvlp/vp-templates)",
6
6
  "license": "MIT",
File without changes