@postxl/cli 1.4.5 → 1.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.
@@ -42,6 +42,7 @@ const node_util_1 = require("node:util");
42
42
  const schema_1 = require("@postxl/schema");
43
43
  const utils_1 = require("@postxl/utils");
44
44
  const create_project_generator_1 = require("./create-project/create-project.generator");
45
+ const log_schema_error_1 = require("./helpers/log-schema-error");
45
46
  const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
46
47
  async function run(command, options) {
47
48
  const { stdout, stderr } = await execAsync(command, options);
@@ -57,10 +58,26 @@ function register(program) {
57
58
  .command('create-project')
58
59
  .alias('new')
59
60
  .summary('Creates a new PostXL project.')
60
- .description('Creates a new PostXL project with the simple schema template.')
61
+ .description(`Creates a new PostXL project.
62
+
63
+ Installation flow:
64
+ 1. Prompts for project name, slug, path, and schema (if not provided via options)
65
+ 2. Writes initial project files (schema, generate.ts, tsconfig, package.json, .env)
66
+ Uses the simple schema for the first pass even if a custom schema is provided
67
+ 3. Runs pnpm install to fetch initial dependencies
68
+ 4. Runs first code generation without formatting (as prettier/eslint not yet installed)
69
+ 5. Runs pnpm install again to fetch devDependencies added by the generated package.json
70
+ 6. Swaps in the custom schema (if provided) before the final generation
71
+ 7. Runs second code generation with full formatting (prettier, eslint)
72
+ 8. Generates TanStack Router route tree
73
+ 9. Generates Prisma client
74
+ 10. Initializes a git repository (unless --skip-git)
75
+
76
+ If --skip-generate is used with a custom schema, the custom schema is written directly.`)
61
77
  .option('-n, --name <name>', 'Project name')
62
78
  .option('-s, --slug <slug>', 'Project slug')
63
79
  .option('-p, --project-path <path>', 'Path where the generated project should be written. If not specified, user will be prompted')
80
+ .option('-S, --schema <path>', 'Path to an existing postxl-schema.json to use instead of the default simple schema')
64
81
  .option('--skip-git', 'Skip initialing a git repository in the generated project directory')
65
82
  .option('--skip-generate', 'Skip running the initial project generation')
66
83
  .option('-l, --link-postxl', 'Link project to local PostXL monorepo packages (for development purposes)')
@@ -70,12 +87,16 @@ function register(program) {
70
87
  const link = options.linkPostxl === true;
71
88
  const name = await getProjectName(options.name);
72
89
  const slug = await getProjectSlug(options.slug, name);
73
- const schema = getProjectSchema();
90
+ const customSchema = await loadCustomSchema(options.schema);
91
+ const schema = customSchema ?? getProjectSchema();
74
92
  const projectPath = await resolveProjectPath({ slug, projectPath: options.projectPath });
93
+ // For the first generator pass, use the simple schema (faster, result gets overwritten by second pass anyway).
94
+ // If --skip-generate is active, use the custom schema directly since there won't be a second pass.
95
+ const initialSchema = customSchema && !options.skipGenerate ? getProjectSchema() : schema;
75
96
  await createProjectStructure({
76
97
  name,
77
98
  slug,
78
- schema,
99
+ schema: initialSchema,
79
100
  projectPath,
80
101
  });
81
102
  // In case it is a workspace project, we install dependencies in the workspace part - else in the project
@@ -87,13 +108,23 @@ function register(program) {
87
108
  // After the generation, we now have the full package.json including devDependencies for prettier, eslint, etc.
88
109
  // So we install dependencies again to get those.
89
110
  await installDependencies({ targetPath: projectPath, link });
111
+ // If a custom schema was provided, write it now before the second (final) generation pass.
112
+ if (customSchema) {
113
+ await writeSchema({ name, slug, schema: customSchema, projectPath });
114
+ }
90
115
  // The second generation runs with transformations - setting up prettier, eslint, etc.
91
116
  await runGenerate({ projectPath, transform: true });
117
+ await generateTanStackRouter(projectPath);
92
118
  await generatePrismaClient(projectPath);
93
- if (!options.skipInitGit) {
119
+ if (!options.skipGit) {
94
120
  await initializeGitRepository(projectPath);
95
121
  }
96
122
  }
123
+ else if (customSchema) {
124
+ // With --skip-generate, createProjectStructure already wrote the custom schema,
125
+ // but we still need to apply name/slug/projectType overrides.
126
+ await writeSchema({ name, slug, schema: customSchema, projectPath });
127
+ }
97
128
  console.log(`\n✓ Project "${name}" created successfully at ${projectPath}`);
98
129
  console.log('\nNext steps:');
99
130
  console.log(` cd ${path.relative(process.cwd(), projectPath)}`);
@@ -133,6 +164,48 @@ async function getProjectSlug(providedSlug, projectName) {
133
164
  function getProjectSchema() {
134
165
  return schema_1.sampleSchemas.simple;
135
166
  }
167
+ async function loadCustomSchema(providedPath) {
168
+ if (!providedPath) {
169
+ return null;
170
+ }
171
+ const resolvedPath = path.resolve(providedPath);
172
+ let content;
173
+ try {
174
+ content = await fs.readFile(resolvedPath, 'utf-8');
175
+ }
176
+ catch (error) {
177
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
178
+ console.error(`Schema file not found: ${resolvedPath}`);
179
+ }
180
+ else {
181
+ console.error(`Failed to read schema file: ${error instanceof Error ? error.message : error}`);
182
+ }
183
+ process.exit(1);
184
+ }
185
+ let json;
186
+ try {
187
+ json = JSON.parse(content);
188
+ }
189
+ catch {
190
+ console.error(`Schema file is not valid JSON: ${resolvedPath}`);
191
+ process.exit(1);
192
+ }
193
+ const result = schema_1.zProjectSchema.safeParse(json);
194
+ if (!result.success) {
195
+ (0, log_schema_error_1.logSchemaValidationError)(result.error);
196
+ process.exit(1);
197
+ }
198
+ return json;
199
+ }
200
+ async function writeSchema({ name, slug, schema, projectPath, }) {
201
+ const updatedSchema = {
202
+ ...schema,
203
+ name: (0, schema_1.toProjectSchemaName)(name),
204
+ slug: (0, schema_1.toProjectSlug)(slug),
205
+ projectType: 'standalone',
206
+ };
207
+ await fs.writeFile(path.join(projectPath, 'postxl-schema.json'), JSON.stringify(updatedSchema, null, 2) + '\n');
208
+ }
136
209
  /**
137
210
  * If projectPath is not provided:
138
211
  * - Prompt user for path if it should be standalone (in ../{slug}) or workspace (in ./projects/{slug}). Default is standalone.
@@ -309,6 +382,17 @@ async function runGenerate({ projectPath, transform, }) {
309
382
  console.log('You can manually run "pnpm run generate" in the project directory.');
310
383
  }
311
384
  }
385
+ async function generateTanStackRouter(targetPath) {
386
+ console.log('\nGenerating TanStack Router route tree...');
387
+ try {
388
+ await run('pnpm run generate:tsr', { cwd: targetPath });
389
+ console.log('TanStack Router route tree generated successfully!');
390
+ }
391
+ catch (error) {
392
+ console.error('Failed to generate TanStack Router route tree:', error);
393
+ console.log('You can manually run "pnpm run generate:tsr" in the project directory.');
394
+ }
395
+ }
312
396
  async function generatePrismaClient(targetPath) {
313
397
  console.log('\nGenerating Prisma client...');
314
398
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/cli",
3
- "version": "1.4.5",
3
+ "version": "1.5.1",
4
4
  "description": "Command-line interface for PostXL code generation framework",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -45,8 +45,8 @@
45
45
  "dotenv": "17.3.1",
46
46
  "zod-validation-error": "5.0.0",
47
47
  "@postxl/generator": "^1.3.7",
48
- "@postxl/generators": "^1.22.0",
49
- "@postxl/schema": "^1.8.1",
48
+ "@postxl/generators": "^1.23.0",
49
+ "@postxl/schema": "^1.8.2",
50
50
  "@postxl/utils": "^1.4.0"
51
51
  },
52
52
  "devDependencies": {},