create-next-hydra 0.1.0 → 0.2.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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/cli.js +0 -0
  4. package/dist/composition/add.d.ts +14 -0
  5. package/dist/composition/add.d.ts.map +1 -0
  6. package/dist/composition/add.js +453 -0
  7. package/dist/composition/catalog.d.ts +23 -0
  8. package/dist/composition/catalog.d.ts.map +1 -0
  9. package/dist/composition/catalog.js +301 -0
  10. package/dist/composition/errors.d.ts +5 -0
  11. package/dist/composition/errors.d.ts.map +1 -0
  12. package/dist/composition/errors.js +8 -0
  13. package/dist/composition/format.d.ts +6 -0
  14. package/dist/composition/format.d.ts.map +1 -0
  15. package/dist/composition/format.js +63 -0
  16. package/dist/composition/install.d.ts +16 -0
  17. package/dist/composition/install.d.ts.map +1 -0
  18. package/dist/composition/install.js +175 -0
  19. package/dist/composition/packages.d.ts +6 -0
  20. package/dist/composition/packages.d.ts.map +1 -0
  21. package/dist/composition/packages.js +54 -0
  22. package/dist/composition/paths.d.ts +4 -0
  23. package/dist/composition/paths.d.ts.map +1 -0
  24. package/dist/composition/paths.js +47 -0
  25. package/dist/composition/planner.d.ts +4 -0
  26. package/dist/composition/planner.d.ts.map +1 -0
  27. package/dist/composition/planner.js +320 -0
  28. package/dist/composition/schema.d.ts +235 -0
  29. package/dist/composition/schema.d.ts.map +1 -0
  30. package/dist/composition/schema.js +115 -0
  31. package/dist/composition/types.d.ts +87 -0
  32. package/dist/composition/types.d.ts.map +1 -0
  33. package/dist/composition/types.js +6 -0
  34. package/dist/composition/use.d.ts +23 -0
  35. package/dist/composition/use.d.ts.map +1 -0
  36. package/dist/composition/use.js +182 -0
  37. package/dist/composition/workspace.d.ts +10 -0
  38. package/dist/composition/workspace.d.ts.map +1 -0
  39. package/dist/composition/workspace.js +208 -0
  40. package/dist/constants.d.ts +4 -1
  41. package/dist/constants.d.ts.map +1 -1
  42. package/dist/constants.js +26 -1
  43. package/dist/fs-utils.js +3 -3
  44. package/dist/git.d.ts.map +1 -1
  45. package/dist/git.js +12 -12
  46. package/dist/index.d.ts +5 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +55 -6
  49. package/dist/instruction-renderer.d.ts +17 -0
  50. package/dist/instruction-renderer.d.ts.map +1 -0
  51. package/dist/instruction-renderer.js +132 -0
  52. package/dist/logger.d.ts +2 -1
  53. package/dist/logger.d.ts.map +1 -1
  54. package/dist/logger.js +7 -3
  55. package/dist/prompts.d.ts +2 -0
  56. package/dist/prompts.d.ts.map +1 -1
  57. package/dist/prompts.js +25 -2
  58. package/dist/sanitize.d.ts +2 -1
  59. package/dist/sanitize.d.ts.map +1 -1
  60. package/dist/sanitize.js +13 -4
  61. package/dist/scaffold.d.ts +5 -1
  62. package/dist/scaffold.d.ts.map +1 -1
  63. package/dist/scaffold.js +247 -71
  64. package/dist/types.d.ts +5 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/version.d.ts +2 -0
  67. package/dist/version.d.ts.map +1 -0
  68. package/dist/version.js +2 -0
  69. package/package.json +35 -23
  70. package/schema/selection-definition.json +237 -0
  71. package/schema/source-registry.json +37 -0
package/dist/scaffold.js CHANGED
@@ -1,17 +1,28 @@
1
+ import { stat } from "node:fs/promises";
1
2
  import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { intro } from "@clack/prompts";
3
5
  import { cloneStarter } from "./clone.js";
6
+ import { addCatalogReferences, loadGitHubSourceRegistryCatalog, loadSourceRegistryCatalog, } from "./composition/catalog.js";
7
+ import { formatCompositionPlan } from "./composition/format.js";
8
+ import { installPreparedComposition, prepareComposition, validatePackageRequirementTargets, } from "./composition/install.js";
9
+ import { planComposition, selectionFromPreset } from "./composition/planner.js";
10
+ import { applyPackageRequirements, applyPnpmPatches, removeWorkspaceTargets, } from "./composition/workspace.js";
4
11
  import { DEFAULT_PACKAGE_MANAGER } from "./constants.js";
5
- import { ensureParentDirectory, isDirectoryEmpty, pathExists, removePath, toDisplayPath, } from "./fs-utils.js";
6
- import { CommandExecutionError, ensureGitInstalled, initializeGitRepository, } from "./git.js";
7
- import { createSpinner, finish, info, printNextSteps, success, warn, } from "./logger.js";
12
+ import { ensureParentDirectory, isDirectoryEmpty, pathExists, toDisplayPath, } from "./fs-utils.js";
13
+ import { CommandExecutionError, ensureGitInstalled, initializeGitRepository, runCommand, } from "./git.js";
14
+ import { createSpinner, finish, info, printInstructions, success, warn, } from "./logger.js";
15
+ import { promptForProvider } from "./prompts.js";
8
16
  import { sanitizeStarter } from "./sanitize.js";
9
17
  const SHELL_NEEDS_QUOTING_REGEX = /[\s"'\\]/;
18
+ const GITHUB_SCP_REPOSITORY = /^(?:git@|ssh:\/\/git@)github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/;
19
+ const LEADING_SLASH = /^\//;
20
+ const GIT_SUFFIX = /\.git$/;
10
21
  function quotePathForShell(value) {
11
22
  if (!SHELL_NEEDS_QUOTING_REGEX.test(value)) {
12
23
  return value;
13
24
  }
14
- return `'${value.replace(/'/g, `'\"'\"'`)}'`;
25
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
15
26
  }
16
27
  async function resolveAndValidateTarget(inputTargetDir) {
17
28
  const targetDir = inputTargetDir.trim();
@@ -20,8 +31,7 @@ async function resolveAndValidateTarget(inputTargetDir) {
20
31
  if (!targetName) {
21
32
  throw new Error("Please provide a valid target folder.");
22
33
  }
23
- const existedBefore = await pathExists(targetPath);
24
- if (existedBefore) {
34
+ if (await pathExists(targetPath)) {
25
35
  const isEmpty = await isDirectoryEmpty(targetPath);
26
36
  if (!isEmpty) {
27
37
  throw new Error(`Target directory is not empty: ${toDisplayPath(targetPath)}`);
@@ -30,46 +40,117 @@ async function resolveAndValidateTarget(inputTargetDir) {
30
40
  else {
31
41
  await ensureParentDirectory(targetPath);
32
42
  }
33
- return { targetDir, targetName, targetPath, existedBefore };
43
+ return { targetDir, targetName, targetPath };
34
44
  }
35
45
  function formatGitError(error) {
36
- const isCloneCommand = error.command.startsWith("git clone ");
37
46
  const stderr = error.stderr.trim();
38
- if (stderr) {
39
- if (!isCloneCommand) {
40
- return `${error.command}\n\n${stderr}`;
41
- }
42
- return [
43
- error.command,
44
- "",
45
- stderr,
47
+ const stdout = error.stdout.trim();
48
+ const detail = stderr || stdout || error.message;
49
+ const hint = error.command.startsWith("git clone ")
50
+ ? [
46
51
  "",
47
52
  "Hint: If the starter repo is private or you are testing locally, use `--repo-url`.",
48
53
  "Example: create-next-hydra my-app --repo-url /path/to/next-hydra",
49
- ].join("\n");
54
+ ]
55
+ : [];
56
+ return [error.command, "", detail, ...hint].join("\n");
57
+ }
58
+ async function localRepositoryPath(repoUrl) {
59
+ let candidate;
60
+ if (repoUrl.startsWith("file:")) {
61
+ candidate = fileURLToPath(repoUrl);
50
62
  }
51
- const stdout = error.stdout.trim();
52
- if (stdout) {
53
- if (!isCloneCommand) {
54
- return `${error.command}\n\n${stdout}`;
63
+ else if (path.isAbsolute(repoUrl) ||
64
+ repoUrl.startsWith(".") ||
65
+ !repoUrl.includes(":")) {
66
+ candidate = path.resolve(repoUrl);
67
+ }
68
+ else {
69
+ return null;
70
+ }
71
+ try {
72
+ return (await stat(candidate)).isDirectory() ? candidate : null;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ function githubRepository(repoUrl) {
79
+ const scpMatch = GITHUB_SCP_REPOSITORY.exec(repoUrl);
80
+ if (scpMatch?.[1] && scpMatch[2]) {
81
+ return `${scpMatch[1]}/${scpMatch[2]}`;
82
+ }
83
+ try {
84
+ const url = new URL(repoUrl);
85
+ if (url.hostname !== "github.com") {
86
+ return null;
55
87
  }
56
- return [
57
- error.command,
58
- "",
59
- stdout,
60
- "",
61
- "Hint: If the starter repo is private or you are testing locally, use `--repo-url`.",
62
- "Example: create-next-hydra my-app --repo-url /path/to/next-hydra",
63
- ].join("\n");
88
+ const parts = url.pathname
89
+ .replace(LEADING_SLASH, "")
90
+ .replace(GIT_SUFFIX, "")
91
+ .split("/");
92
+ return parts.length === 2 && parts[0] && parts[1]
93
+ ? `${parts[0]}/${parts[1]}`
94
+ : null;
95
+ }
96
+ catch {
97
+ return null;
64
98
  }
65
- return `${error.command}\n\n${error.message}`;
66
99
  }
67
- export async function scaffoldProject(options) {
100
+ async function requestedSelection(options, catalog) {
101
+ if (options.preset && (options.auth || options.cms || options.commerce)) {
102
+ throw new Error("`--preset` cannot be combined with provider flags.");
103
+ }
104
+ if (options.preset) {
105
+ const preset = selectionFromPreset(catalog, options.preset);
106
+ return {
107
+ ...preset,
108
+ addOns: [...new Set([...preset.addOns, ...(options.addOns ?? [])])],
109
+ };
110
+ }
111
+ if (options.yes && !(options.auth && options.cms && options.commerce)) {
112
+ throw new Error("`--yes` requires `--auth`, `--cms`, and `--commerce`, or one `--preset`.");
113
+ }
114
+ const auth = options.auth ?? (await promptForProvider("auth", "workos"));
115
+ const cms = options.cms ?? (await promptForProvider("cms", "drupal"));
116
+ const commerce = options.commerce ?? (await promptForProvider("commerce", "commercetools"));
117
+ return {
118
+ addOns: options.addOns ?? [],
119
+ providers: { auth, cms, commerce },
120
+ };
121
+ }
122
+ function explicitSelectionReferences(options) {
123
+ return [
124
+ options.auth,
125
+ options.cms,
126
+ options.commerce,
127
+ options.preset,
128
+ ...(options.addOns ?? []),
129
+ ].filter((value) => Boolean(value));
130
+ }
131
+ function scaffoldFailure(error, targetPath, failedStep, completed, pending) {
132
+ let cause = String(error);
133
+ if (error instanceof Error) {
134
+ cause = error.message;
135
+ }
136
+ if (error instanceof CommandExecutionError) {
137
+ cause = formatGitError(error);
138
+ }
139
+ return new Error([
140
+ `Scaffolding stopped while ${failedStep}.`,
141
+ `Target: ${targetPath}`,
142
+ `Completed: ${completed.length > 0 ? completed.join(", ") : "none"}.`,
143
+ `Not attempted: ${pending.length > 0 ? pending.join(", ") : "none"}.`,
144
+ "The partial project has been left exactly as it stands for inspection or repair.",
145
+ "",
146
+ cause,
147
+ ].join("\n"), { cause: error });
148
+ }
149
+ export async function scaffoldProject(options, dependencies = {}) {
68
150
  intro("create-next-hydra");
69
151
  const spin = createSpinner();
70
152
  const { repoUrl, ref, skipGit, commit, verbose } = options;
71
- const targetDir = options.targetDir;
72
- if (!targetDir) {
153
+ if (!options.targetDir) {
73
154
  throw new Error("Missing target directory.");
74
155
  }
75
156
  spin.start("Checking git availability");
@@ -77,40 +158,124 @@ export async function scaffoldProject(options) {
77
158
  await ensureGitInstalled(verbose);
78
159
  spin.stop("Git detected");
79
160
  }
80
- catch {
161
+ catch (error) {
81
162
  spin.stop("Git not available");
82
- throw new Error("Git is required to scaffold a project. Please install git and try again.");
163
+ throw new Error("Git is required to scaffold a project. Please install git and try again.", { cause: error });
164
+ }
165
+ const sourcePath = await localRepositoryPath(repoUrl);
166
+ const remoteRepository = sourcePath ? null : githubRepository(repoUrl);
167
+ let sourceCatalog;
168
+ if (sourcePath) {
169
+ sourceCatalog = await loadSourceRegistryCatalog(sourcePath);
83
170
  }
84
- const { targetPath, targetName, existedBefore } = await resolveAndValidateTarget(targetDir);
85
- let cloneCompleted = false;
171
+ else if (remoteRepository) {
172
+ sourceCatalog = await loadGitHubSourceRegistryCatalog(remoteRepository, ref);
173
+ }
174
+ else {
175
+ throw new Error("Composition preflight supports a local starter path or a public GitHub repository. Use one of those forms with `--repo-url`.");
176
+ }
177
+ sourceCatalog = await addCatalogReferences(sourceCatalog, explicitSelectionReferences(options), process.cwd());
178
+ const selection = await requestedSelection(options, sourceCatalog);
179
+ sourceCatalog = await addCatalogReferences(sourceCatalog, [...Object.values(selection.providers), ...selection.addOns], process.cwd());
180
+ planComposition(sourceCatalog, selection);
181
+ const { targetPath, targetName } = await resolveAndValidateTarget(options.targetDir);
182
+ const completed = [];
183
+ const pending = [
184
+ "clone the starter",
185
+ "resolve the composition",
186
+ "remove variable provider source",
187
+ "install selected source",
188
+ "update package aliases",
189
+ "update pnpm patches",
190
+ "remove maintainer-only files",
191
+ "install dependencies",
192
+ "initialize Git",
193
+ ];
194
+ let currentStep = "clone the starter";
195
+ const runStep = async (label, operation) => {
196
+ currentStep = label;
197
+ pending.shift();
198
+ const result = await operation();
199
+ completed.push(label);
200
+ return result;
201
+ };
86
202
  try {
87
203
  spin.start("Cloning next-hydra starter");
88
- await cloneStarter({
89
- repoUrl,
90
- targetPath,
91
- ref,
92
- verbose,
204
+ await runStep("clone the starter", async () => {
205
+ await cloneStarter({ ref, repoUrl, targetPath, verbose });
93
206
  });
94
- cloneCompleted = true;
95
207
  spin.stop("Starter cloned");
96
- spin.start("Sanitizing starter");
97
- const { packageName } = await sanitizeStarter({ targetPath, targetName });
98
- spin.stop("Starter sanitized");
208
+ spin.start("Resolving workspace composition");
209
+ const { catalog, plan, prepared } = await runStep("resolve the composition", async () => {
210
+ let targetCatalog = await loadSourceRegistryCatalog(targetPath);
211
+ targetCatalog = await addCatalogReferences(targetCatalog, [
212
+ ...explicitSelectionReferences(options),
213
+ ...Object.values(selection.providers),
214
+ ...selection.addOns,
215
+ ], process.cwd());
216
+ const targetPlan = planComposition(targetCatalog, selection);
217
+ const targetPrepared = await prepareComposition(targetCatalog, targetPlan);
218
+ await validatePackageRequirementTargets(targetPath, targetPlan, targetPrepared, targetPlan.variableTargets);
219
+ return {
220
+ catalog: targetCatalog,
221
+ plan: targetPlan,
222
+ prepared: targetPrepared,
223
+ };
224
+ });
225
+ spin.stop("Composition resolved");
226
+ info(formatCompositionPlan(plan));
227
+ spin.start("Removing unselected provider source");
228
+ await runStep("remove variable provider source", async () => {
229
+ await removeWorkspaceTargets(targetPath, plan.variableTargets);
230
+ });
231
+ spin.stop("Variable provider source removed");
232
+ spin.start("Installing selected provider source");
233
+ await runStep("install selected source", async () => {
234
+ await installPreparedComposition(targetPath, prepared);
235
+ });
236
+ spin.stop("Selected provider source installed");
237
+ await runStep("update package aliases", async () => {
238
+ await applyPackageRequirements(targetPath, plan);
239
+ });
240
+ await runStep("update pnpm patches", async () => {
241
+ await applyPnpmPatches(targetPath, plan);
242
+ });
243
+ spin.start("Removing maintainer-only files");
244
+ const { packageName } = await runStep("remove maintainer-only files", async () => {
245
+ const result = await sanitizeStarter({
246
+ registryAuthoringPaths: catalog.authoringPaths,
247
+ targetName,
248
+ targetPath,
249
+ });
250
+ await removeWorkspaceTargets(targetPath, catalog.authoringPaths);
251
+ return result;
252
+ });
253
+ spin.stop("Maintainer-only files removed");
254
+ spin.start("Installing dependencies");
255
+ await runStep("install dependencies", async () => {
256
+ if (dependencies.install) {
257
+ await dependencies.install(targetPath, verbose);
258
+ return;
259
+ }
260
+ await runCommand(DEFAULT_PACKAGE_MANAGER, ["install"], {
261
+ cwd: targetPath,
262
+ verbose,
263
+ });
264
+ });
265
+ spin.stop("Dependencies installed");
99
266
  let gitInitialized = false;
100
267
  let committed = false;
101
268
  if (skipGit) {
269
+ pending.shift();
270
+ completed.push("skip Git initialization");
102
271
  info("Skipped git initialization (--skip-git).");
103
272
  }
104
273
  else {
105
274
  spin.start(commit
106
275
  ? "Initializing git repo and creating initial commit"
107
276
  : "Initializing git repo");
108
- const gitResult = await initializeGitRepository(targetPath, {
109
- commit,
110
- verbose,
111
- });
112
- gitInitialized = gitResult.gitInitialized;
113
- committed = gitResult.committed;
277
+ const gitResult = await runStep("initialize Git", async () => await initializeGitRepository(targetPath, { commit, verbose }));
278
+ ({ committed, gitInitialized } = gitResult);
114
279
  spin.stop(committed
115
280
  ? "Git repo initialized with initial commit"
116
281
  : "Git repo initialized");
@@ -119,31 +284,42 @@ export async function scaffoldProject(options) {
119
284
  }
120
285
  }
121
286
  const displayTarget = toDisplayPath(targetPath);
122
- const cdTarget = quotePathForShell(displayTarget);
123
- printNextSteps([
124
- `cd ${cdTarget}`,
125
- `${DEFAULT_PACKAGE_MANAGER} install`,
126
- `${DEFAULT_PACKAGE_MANAGER} dev`,
127
- ].join("\n"));
287
+ printInstructions([
288
+ ...(plan.instructions.length > 0
289
+ ? [
290
+ {
291
+ entries: plan.instructions.map((text) => ({
292
+ kind: "text",
293
+ text,
294
+ })),
295
+ title: "Provider setup",
296
+ },
297
+ ]
298
+ : []),
299
+ {
300
+ entries: [
301
+ {
302
+ command: `cd ${quotePathForShell(displayTarget)}`,
303
+ kind: "command",
304
+ },
305
+ {
306
+ command: `${DEFAULT_PACKAGE_MANAGER} dev`,
307
+ kind: "command",
308
+ },
309
+ ],
310
+ title: "Next steps",
311
+ },
312
+ ]);
128
313
  success(`Created project in ${displayTarget}`);
129
314
  finish("Scaffold complete.");
130
315
  return {
131
- projectPath: targetPath,
132
- packageName,
133
- gitInitialized,
134
316
  committed,
317
+ gitInitialized,
318
+ packageName,
319
+ projectPath: targetPath,
135
320
  };
136
321
  }
137
322
  catch (error) {
138
- if (cloneCompleted === false && existedBefore === false) {
139
- await removePath(targetPath);
140
- }
141
- if (error instanceof CommandExecutionError) {
142
- throw new Error(formatGitError(error));
143
- }
144
- if (error instanceof Error) {
145
- throw error;
146
- }
147
- throw new Error("Failed to scaffold project.");
323
+ throw scaffoldFailure(error, targetPath, currentStep, completed, pending);
148
324
  }
149
325
  }
package/dist/types.d.ts CHANGED
@@ -6,6 +6,11 @@ export type CreateOptions = {
6
6
  ref?: string;
7
7
  repoUrl: string;
8
8
  verbose: boolean;
9
+ auth?: string;
10
+ cms?: string;
11
+ commerce?: string;
12
+ addOns?: string[];
13
+ preset?: string;
9
14
  };
10
15
  export type ResolvedCreateOptions = CreateOptions & {
11
16
  targetDir: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const CLI_VERSION: string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,QAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import packageManifest from "../package.json" with { type: "json" };
2
+ export const CLI_VERSION = packageManifest.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-next-hydra",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI to scaffold a new next-hydra project",
5
5
  "keywords": [
6
6
  "cli",
@@ -13,14 +13,11 @@
13
13
  ],
14
14
  "license": "MIT",
15
15
  "type": "module",
16
- "bin": {
17
- "create-next-hydra": "dist/cli.js"
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/jakala-na/next-hydra",
19
+ "directory": "packages/create-next-hydra"
18
20
  },
19
- "files": [
20
- "dist"
21
- ],
22
- "main": "./dist/index.js",
23
- "types": "./dist/index.d.ts",
24
21
  "exports": {
25
22
  ".": {
26
23
  "types": "./dist/index.d.ts",
@@ -30,27 +27,42 @@
30
27
  "import": "./dist/cli.js"
31
28
  }
32
29
  },
33
- "publishConfig": {
34
- "access": "public"
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "bin": {
33
+ "create-next-hydra": "dist/cli.js"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "schema"
38
+ ],
39
+ "dependencies": {
40
+ "@clack/prompts": "^0.11.0",
41
+ "commander": "^14.0.0",
42
+ "shadcn": "^4.16.2",
43
+ "yaml": "^2.9.0",
44
+ "zod": "^3.25.28"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "24.13.3",
48
+ "typescript": "npm:@typescript/typescript6@6.0.2",
49
+ "vitest": "4.1.11",
50
+ "@repo/typescript-config": "0.0.0"
35
51
  },
36
52
  "engines": {
37
- "node": ">=18"
53
+ "node": "24.x"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
38
57
  },
39
58
  "scripts": {
59
+ "prebuild": "node scripts/clean-dist.js",
40
60
  "build": "tsc -p tsconfig.json",
41
- "typecheck": "tsc --noEmit -p tsconfig.json",
42
61
  "pack": "npm pack",
43
62
  "publish:dry-run": "npm publish --access public --dry-run",
44
63
  "publish:public": "npm publish --access public",
45
- "prepublishOnly": "pnpm run build"
46
- },
47
- "dependencies": {
48
- "@clack/prompts": "^0.11.0",
49
- "commander": "^14.0.0"
50
- },
51
- "devDependencies": {
52
- "@repo/typescript-config": "workspace:*",
53
- "@types/node": "^22.15.21",
54
- "typescript": "^5.8.3"
64
+ "test": "vitest run --exclude 'tests/e2e/**'",
65
+ "test:e2e": "vitest run tests/e2e",
66
+ "typecheck": "pnpm --workspace-root exec tsc --noEmit --project packages/create-next-hydra/tsconfig.json"
55
67
  }
56
- }
68
+ }