create-ej-app 30.0.0 → 32.0.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 (4) hide show
  1. package/README.md +21 -1
  2. package/cli.js +169 -35
  3. package/cli.ts +213 -38
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -59,7 +59,8 @@ You will be prompted for:
59
59
  - Project name
60
60
  - Description
61
61
  - Template (`nextjs`, `saas-starter`, or `api`)
62
- - Whether to initialize git
62
+ - Whether to initialize git. If yes, the CLI creates the `main` branch (or your configured default, unless it is `master`) and makes the initial commit, as long as `.env` is ignored.
63
+ - Whether to update packages to their latest versions. With Bun it runs `bun update -i`, so you pick which packages to update.
63
64
 
64
65
  Example:
65
66
 
@@ -68,8 +69,27 @@ Example:
68
69
  ? Enter a description for the project: Production-ready Next.js app
69
70
  ? Select a template: nextjs
70
71
  ? Initialize a git repository? yes
72
+ ? Update packages to their latest versions? The template can lag behind. yes
71
73
  ```
72
74
 
75
+ ### Without Prompts
76
+
77
+ Pass the answers as flags to skip the prompts, for example in scripts, CI or coding agents. Any answer you leave out is still asked:
78
+
79
+ ```bash
80
+ bunx create-ej-app@latest --name my-app --description "Production-ready Next.js app" --template nextjs --git --no-update
81
+ ```
82
+
83
+ | Flag | Answers |
84
+ | -------------------------------- | --------------------------------------------- |
85
+ | `-n, --name <name>` | Project name, also the folder name |
86
+ | `-d, --description <text>` | Description |
87
+ | `-t, --template <template>` | Template: `nextjs`, `saas-starter`, or `api` |
88
+ | `--git` or `--no-git` | Whether to initialize git and commit |
89
+ | `--update` or `--no-update` | Whether to update packages to latest versions |
90
+
91
+ Without a terminal, the CLI can't ask, so it exits and names the flags you still need to pass. `--update` without a terminal updates every package to its latest version, which can include major versions: run the type-check and tests afterwards.
92
+
73
93
  ---
74
94
 
75
95
  ## What you get
package/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process';
2
+ import { execFileSync, execSync } from 'child_process';
3
3
  import { program } from 'commander';
4
4
  import { randomBytes } from 'crypto';
5
5
  import enquirer from 'enquirer';
@@ -48,54 +48,167 @@ const getInstallCommand = (packageManager) => {
48
48
  return 'npm i';
49
49
  }
50
50
  };
51
+ /**
52
+ * Commands that update dependencies to their latest versions: `interactive`
53
+ * lets you pick packages in a terminal, `all` updates everything. Only Bun and
54
+ * pnpm have both; other package managers get instructions instead.
55
+ */
56
+ const getUpdateCommands = (packageManager) => {
57
+ switch (packageManager) {
58
+ case 'bun':
59
+ return { interactive: 'bun update -i', all: 'bun update --latest' };
60
+ case 'pnpm':
61
+ return {
62
+ interactive: 'pnpm update -i --latest',
63
+ all: 'pnpm update --latest'
64
+ };
65
+ default:
66
+ return null;
67
+ }
68
+ };
69
+ /**
70
+ * The branch name for a new repository: the one set in git's
71
+ * init.defaultBranch, unless that is unset or `master`, which become `main`.
72
+ */
73
+ const defaultBranch = () => {
74
+ let configured = '';
75
+ try {
76
+ configured = execSync('git config --get init.defaultBranch', {
77
+ stdio: 'pipe'
78
+ })
79
+ .toString()
80
+ .trim();
81
+ }
82
+ catch {
83
+ // Not set
84
+ }
85
+ return configured && configured !== 'master' ? configured : 'main';
86
+ };
87
+ /**
88
+ * Commits the new project once. Skips the commit when .env is not ignored, so
89
+ * the generated secret never lands in history, and explains a missing git
90
+ * identity instead of failing silently.
91
+ */
92
+ const commitEverything = (projectDir) => {
93
+ const run = (command) => execSync(command, { cwd: projectDir, stdio: 'pipe' });
94
+ try {
95
+ run('git check-ignore -q .env');
96
+ }
97
+ catch {
98
+ console.error('Skipped the initial commit: .env is not in .gitignore, and committing it would publish its secrets.');
99
+ return;
100
+ }
101
+ try {
102
+ run('git add -A');
103
+ run('git commit -q -m "initial commit: bootstrap new project with create-ej-app"');
104
+ console.log('Created the initial commit.');
105
+ }
106
+ catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ console.error(/user\.(name|email)|identity/i.test(message)
109
+ ? "Skipped the initial commit: git doesn't know who you are. Set git config user.name and user.email, then run 'git add -A && git commit'."
110
+ : `The initial commit failed: ${message}`);
111
+ }
112
+ };
51
113
  const getRunCommand = (packageManager, script) => packageManager === 'npm'
52
114
  ? `npm run ${script}`
53
115
  : `${packageManager} run ${script}`;
54
- program.action(async () => {
55
- const response = await enquirer.prompt([
56
- {
116
+ // The name becomes a directory and the package.json name, so keep it to
117
+ // characters that are safe for both.
118
+ const PROJECT_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,213}$/;
119
+ const PROJECT_NAME_HINT = 'Use lowercase letters, numbers, dots, hyphens, and underscores.';
120
+ const TEMPLATES = [
121
+ {
122
+ name: 'nextjs',
123
+ message: 'Full-stack Next.js 16 app with Better Auth, an admin panel, shadcn/ui, and Drizzle'
124
+ },
125
+ {
126
+ name: 'saas-starter',
127
+ message: 'SaaS starter with organizations, Stripe, support tickets, Better Auth, shadcn/ui, and Drizzle'
128
+ },
129
+ {
130
+ name: 'api',
131
+ message: 'API server with Elysia, Drizzle, and TypeScript'
132
+ }
133
+ ];
134
+ program
135
+ .name('create-ej-app')
136
+ .description('Create a new app from a template. Flags answer the prompts; anything left out is asked.')
137
+ .option('-n, --name <name>', 'project name, also the folder name')
138
+ .option('-d, --description <text>', 'project description')
139
+ .option('-t, --template <template>', `template: ${TEMPLATES.map(t => t.name).join(', ')}`)
140
+ .option('--git', 'initialize a git repository')
141
+ .option('--no-git', 'skip git initialization')
142
+ .option('--update', 'update packages to their latest versions after creating the project')
143
+ .option('--no-update', 'keep the template package versions');
144
+ program.action(async (options) => {
145
+ if (options.name !== undefined && !PROJECT_NAME_PATTERN.test(options.name)) {
146
+ console.error(`Invalid project name '${options.name}'. ${PROJECT_NAME_HINT}`);
147
+ process.exit(1);
148
+ }
149
+ if (options.template !== undefined &&
150
+ !TEMPLATES.some(t => t.name === options.template)) {
151
+ console.error(`Unknown template '${options.template}'. Use one of: ${TEMPLATES.map(t => t.name).join(', ')}.`);
152
+ process.exit(1);
153
+ }
154
+ const questions = [
155
+ options.name === undefined && {
57
156
  type: 'input',
58
157
  name: 'projectName',
59
158
  message: 'Enter the project name:',
60
159
  initial: 'my-project',
61
- // The name becomes a directory and the package.json name, so keep it
62
- // to characters that are safe for both.
63
- validate: (value) => /^[a-z0-9][a-z0-9._-]{0,213}$/.test(value) ||
64
- 'Use lowercase letters, numbers, dots, hyphens, and underscores.'
160
+ validate: (value) => PROJECT_NAME_PATTERN.test(value) || PROJECT_NAME_HINT
65
161
  },
66
- {
162
+ options.description === undefined && {
67
163
  type: 'input',
68
164
  name: 'description',
69
165
  message: 'Enter a description for the project:',
70
166
  initial: 'A Legendary Project'
71
167
  },
72
- {
168
+ options.template === undefined && {
73
169
  type: 'select',
74
170
  name: 'template',
75
171
  message: 'Select a template:',
76
- choices: [
77
- {
78
- name: 'nextjs',
79
- message: 'Full-stack Next.js 16 app with Better Auth, an admin panel, shadcn/ui, and Drizzle'
80
- },
81
- {
82
- name: 'saas-starter',
83
- message: 'SaaS starter with organizations, Stripe, support tickets, Better Auth, shadcn/ui, and Drizzle'
84
- },
85
- {
86
- name: 'api',
87
- message: 'API server with Elysia, Drizzle, and TypeScript'
88
- }
89
- ]
172
+ choices: TEMPLATES
90
173
  },
91
- {
174
+ options.git === undefined && {
92
175
  type: 'select',
93
176
  name: 'git',
94
177
  message: 'Initialize a git repository?',
95
178
  choices: ['yes', 'no']
179
+ },
180
+ options.update === undefined && {
181
+ type: 'select',
182
+ name: 'update',
183
+ message: 'Update packages to their latest versions? The template can lag behind.',
184
+ choices: ['yes', 'no']
96
185
  }
97
- ]);
98
- const { projectName, description, template, git } = response;
186
+ ].filter(q => q !== false);
187
+ // Without a terminal the prompts would wait forever, for example when an
188
+ // agent or a script runs the CLI, so name the missing flags instead.
189
+ if (questions.length && !process.stdin.isTTY) {
190
+ const flags = {
191
+ projectName: '--name',
192
+ description: '--description',
193
+ template: '--template',
194
+ git: '--git or --no-git',
195
+ update: '--update or --no-update'
196
+ };
197
+ console.error(`No terminal to ask questions in. Pass ${questions.map(q => flags[q.name]).join(', ')}.`);
198
+ process.exit(1);
199
+ }
200
+ const answers = questions.length
201
+ ? await enquirer.prompt(questions)
202
+ : {};
203
+ const projectName = options.name ?? answers.projectName;
204
+ const description = options.description ?? answers.description;
205
+ const template = options.template ?? answers.template;
206
+ const git = options.git === undefined ? answers.git : options.git ? 'yes' : 'no';
207
+ const update = options.update === undefined
208
+ ? answers.update
209
+ : options.update
210
+ ? 'yes'
211
+ : 'no';
99
212
  const projectDir = path.join(process.cwd(), projectName);
100
213
  if (fs.existsSync(projectDir)) {
101
214
  console.error(`Directory '${projectName}' already exists. Choose another project name or remove the directory.`);
@@ -146,18 +259,41 @@ program.action(async () => {
146
259
  if (!fs.existsSync(readmeFile)) {
147
260
  fs.writeFileSync(readmeFile, `# ${projectName}\n\n${description}\n`);
148
261
  }
262
+ const pm = detectPackageManager();
263
+ const installCommand = getInstallCommand(pm);
264
+ const devCommand = getRunCommand(pm, 'dev');
265
+ const dbPushCommand = getRunCommand(pm, 'db:push');
266
+ // Updating also installs, so the initial commit includes the lockfile
267
+ let installed = false;
268
+ if (update === 'yes') {
269
+ const commands = getUpdateCommands(pm);
270
+ if (!commands) {
271
+ console.log(`\nAutomatic updates support Bun and pnpm. After '${installCommand}', update packages with your package manager.`);
272
+ }
273
+ else {
274
+ const command = process.stdin.isTTY ? commands.interactive : commands.all;
275
+ console.log(`\nUpdating packages with '${command}'…`);
276
+ try {
277
+ execSync(command, { cwd: projectDir, stdio: 'inherit' });
278
+ installed = true;
279
+ }
280
+ catch {
281
+ console.error(`Updating packages failed, so the template versions stay. Run '${command}' in the project to try again.`);
282
+ }
283
+ }
284
+ }
149
285
  if (git === 'yes') {
150
286
  try {
151
- execSync('git init', { cwd: projectDir, stdio: 'inherit' });
287
+ execFileSync('git', ['init', '-b', defaultBranch()], {
288
+ cwd: projectDir,
289
+ stdio: 'inherit'
290
+ });
291
+ commitEverything(projectDir);
152
292
  }
153
293
  catch (err) {
154
294
  console.error('Error initializing git repository:', err);
155
295
  }
156
296
  }
157
- const pm = detectPackageManager();
158
- const installCommand = getInstallCommand(pm);
159
- const devCommand = getRunCommand(pm, 'dev');
160
- const dbPushCommand = getRunCommand(pm, 'db:push');
161
297
  console.log(`
162
298
  Created a new app called '${projectName}' 🎉
163
299
 
@@ -168,9 +304,7 @@ code ${projectName}
168
304
 
169
305
  To get started, run these commands:
170
306
  cd ${projectName}
171
-
172
- ${installCommand}
173
-
307
+ ${installed ? '' : `\n${installCommand}\n`}
174
308
  Fill in the values in .env, then create the database tables:
175
309
 
176
310
  ${dbPushCommand}
package/cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { execSync } from 'child_process'
3
+ import { execFileSync, execSync } from 'child_process'
4
4
  import { program } from 'commander'
5
5
  import { randomBytes } from 'crypto'
6
6
  import enquirer from 'enquirer'
@@ -15,6 +15,7 @@ interface Response {
15
15
  description: string
16
16
  template: string
17
17
  git: string
18
+ update: string
18
19
  }
19
20
 
20
21
  /**
@@ -61,60 +62,210 @@ const getInstallCommand = (packageManager: string) => {
61
62
  }
62
63
  }
63
64
 
65
+ /**
66
+ * Commands that update dependencies to their latest versions: `interactive`
67
+ * lets you pick packages in a terminal, `all` updates everything. Only Bun and
68
+ * pnpm have both; other package managers get instructions instead.
69
+ */
70
+ const getUpdateCommands = (packageManager: string) => {
71
+ switch (packageManager) {
72
+ case 'bun':
73
+ return { interactive: 'bun update -i', all: 'bun update --latest' }
74
+ case 'pnpm':
75
+ return {
76
+ interactive: 'pnpm update -i --latest',
77
+ all: 'pnpm update --latest'
78
+ }
79
+ default:
80
+ return null
81
+ }
82
+ }
83
+
84
+ /**
85
+ * The branch name for a new repository: the one set in git's
86
+ * init.defaultBranch, unless that is unset or `master`, which become `main`.
87
+ */
88
+ const defaultBranch = () => {
89
+ let configured = ''
90
+ try {
91
+ configured = execSync('git config --get init.defaultBranch', {
92
+ stdio: 'pipe'
93
+ })
94
+ .toString()
95
+ .trim()
96
+ } catch {
97
+ // Not set
98
+ }
99
+ return configured && configured !== 'master' ? configured : 'main'
100
+ }
101
+
102
+ /**
103
+ * Commits the new project once. Skips the commit when .env is not ignored, so
104
+ * the generated secret never lands in history, and explains a missing git
105
+ * identity instead of failing silently.
106
+ */
107
+ const commitEverything = (projectDir: string) => {
108
+ const run = (command: string) =>
109
+ execSync(command, { cwd: projectDir, stdio: 'pipe' })
110
+ try {
111
+ run('git check-ignore -q .env')
112
+ } catch {
113
+ console.error(
114
+ 'Skipped the initial commit: .env is not in .gitignore, and committing it would publish its secrets.'
115
+ )
116
+ return
117
+ }
118
+ try {
119
+ run('git add -A')
120
+ run('git commit -q -m "initial commit: bootstrap new project with create-ej-app"')
121
+ console.log('Created the initial commit.')
122
+ } catch (err) {
123
+ const message = err instanceof Error ? err.message : String(err)
124
+ console.error(
125
+ /user\.(name|email)|identity/i.test(message)
126
+ ? "Skipped the initial commit: git doesn't know who you are. Set git config user.name and user.email, then run 'git add -A && git commit'."
127
+ : `The initial commit failed: ${message}`
128
+ )
129
+ }
130
+ }
131
+
64
132
  const getRunCommand = (packageManager: string, script: string) =>
65
133
  packageManager === 'npm'
66
134
  ? `npm run ${script}`
67
135
  : `${packageManager} run ${script}`
68
136
 
69
- program.action(async () => {
70
- const response: Response = await enquirer.prompt([
71
- {
137
+ // The name becomes a directory and the package.json name, so keep it to
138
+ // characters that are safe for both.
139
+ const PROJECT_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,213}$/
140
+ const PROJECT_NAME_HINT =
141
+ 'Use lowercase letters, numbers, dots, hyphens, and underscores.'
142
+
143
+ const TEMPLATES = [
144
+ {
145
+ name: 'nextjs',
146
+ message:
147
+ 'Full-stack Next.js 16 app with Better Auth, an admin panel, shadcn/ui, and Drizzle'
148
+ },
149
+ {
150
+ name: 'saas-starter',
151
+ message:
152
+ 'SaaS starter with organizations, Stripe, support tickets, Better Auth, shadcn/ui, and Drizzle'
153
+ },
154
+ {
155
+ name: 'api',
156
+ message: 'API server with Elysia, Drizzle, and TypeScript'
157
+ }
158
+ ]
159
+
160
+ interface Options {
161
+ name?: string
162
+ description?: string
163
+ template?: string
164
+ git?: boolean
165
+ update?: boolean
166
+ }
167
+
168
+ program
169
+ .name('create-ej-app')
170
+ .description(
171
+ 'Create a new app from a template. Flags answer the prompts; anything left out is asked.'
172
+ )
173
+ .option('-n, --name <name>', 'project name, also the folder name')
174
+ .option('-d, --description <text>', 'project description')
175
+ .option(
176
+ '-t, --template <template>',
177
+ `template: ${TEMPLATES.map(t => t.name).join(', ')}`
178
+ )
179
+ .option('--git', 'initialize a git repository')
180
+ .option('--no-git', 'skip git initialization')
181
+ .option(
182
+ '--update',
183
+ 'update packages to their latest versions after creating the project'
184
+ )
185
+ .option('--no-update', 'keep the template package versions')
186
+
187
+ program.action(async (options: Options) => {
188
+ if (options.name !== undefined && !PROJECT_NAME_PATTERN.test(options.name)) {
189
+ console.error(`Invalid project name '${options.name}'. ${PROJECT_NAME_HINT}`)
190
+ process.exit(1)
191
+ }
192
+ if (
193
+ options.template !== undefined &&
194
+ !TEMPLATES.some(t => t.name === options.template)
195
+ ) {
196
+ console.error(
197
+ `Unknown template '${options.template}'. Use one of: ${TEMPLATES.map(t => t.name).join(', ')}.`
198
+ )
199
+ process.exit(1)
200
+ }
201
+
202
+ const questions = [
203
+ options.name === undefined && {
72
204
  type: 'input',
73
205
  name: 'projectName',
74
206
  message: 'Enter the project name:',
75
207
  initial: 'my-project',
76
- // The name becomes a directory and the package.json name, so keep it
77
- // to characters that are safe for both.
78
208
  validate: (value: string) =>
79
- /^[a-z0-9][a-z0-9._-]{0,213}$/.test(value) ||
80
- 'Use lowercase letters, numbers, dots, hyphens, and underscores.'
209
+ PROJECT_NAME_PATTERN.test(value) || PROJECT_NAME_HINT
81
210
  },
82
- {
211
+ options.description === undefined && {
83
212
  type: 'input',
84
213
  name: 'description',
85
214
  message: 'Enter a description for the project:',
86
215
  initial: 'A Legendary Project'
87
216
  },
88
- {
217
+ options.template === undefined && {
89
218
  type: 'select',
90
219
  name: 'template',
91
220
  message: 'Select a template:',
92
- choices: [
93
- {
94
- name: 'nextjs',
95
- message:
96
- 'Full-stack Next.js 16 app with Better Auth, an admin panel, shadcn/ui, and Drizzle'
97
- },
98
- {
99
- name: 'saas-starter',
100
- message:
101
- 'SaaS starter with organizations, Stripe, support tickets, Better Auth, shadcn/ui, and Drizzle'
102
- },
103
- {
104
- name: 'api',
105
- message: 'API server with Elysia, Drizzle, and TypeScript'
106
- }
107
- ]
221
+ choices: TEMPLATES
108
222
  },
109
- {
223
+ options.git === undefined && {
110
224
  type: 'select',
111
225
  name: 'git',
112
226
  message: 'Initialize a git repository?',
113
227
  choices: ['yes', 'no']
228
+ },
229
+ options.update === undefined && {
230
+ type: 'select',
231
+ name: 'update',
232
+ message:
233
+ 'Update packages to their latest versions? The template can lag behind.',
234
+ choices: ['yes', 'no']
114
235
  }
115
- ])
236
+ ].filter(q => q !== false)
237
+
238
+ // Without a terminal the prompts would wait forever, for example when an
239
+ // agent or a script runs the CLI, so name the missing flags instead.
240
+ if (questions.length && !process.stdin.isTTY) {
241
+ const flags: Record<string, string> = {
242
+ projectName: '--name',
243
+ description: '--description',
244
+ template: '--template',
245
+ git: '--git or --no-git',
246
+ update: '--update or --no-update'
247
+ }
248
+ console.error(
249
+ `No terminal to ask questions in. Pass ${questions.map(q => flags[q.name]).join(', ')}.`
250
+ )
251
+ process.exit(1)
252
+ }
116
253
 
117
- const { projectName, description, template, git } = response
254
+ const answers: Partial<Response> = questions.length
255
+ ? await enquirer.prompt(questions)
256
+ : {}
257
+
258
+ const projectName = options.name ?? answers.projectName!
259
+ const description = options.description ?? answers.description!
260
+ const template = options.template ?? answers.template!
261
+ const git =
262
+ options.git === undefined ? answers.git! : options.git ? 'yes' : 'no'
263
+ const update =
264
+ options.update === undefined
265
+ ? answers.update!
266
+ : options.update
267
+ ? 'yes'
268
+ : 'no'
118
269
 
119
270
  const projectDir = path.join(process.cwd(), projectName)
120
271
 
@@ -186,19 +337,45 @@ program.action(async () => {
186
337
  fs.writeFileSync(readmeFile, `# ${projectName}\n\n${description}\n`)
187
338
  }
188
339
 
340
+ const pm = detectPackageManager()
341
+ const installCommand = getInstallCommand(pm)
342
+ const devCommand = getRunCommand(pm, 'dev')
343
+ const dbPushCommand = getRunCommand(pm, 'db:push')
344
+
345
+ // Updating also installs, so the initial commit includes the lockfile
346
+ let installed = false
347
+ if (update === 'yes') {
348
+ const commands = getUpdateCommands(pm)
349
+ if (!commands) {
350
+ console.log(
351
+ `\nAutomatic updates support Bun and pnpm. After '${installCommand}', update packages with your package manager.`
352
+ )
353
+ } else {
354
+ const command = process.stdin.isTTY ? commands.interactive : commands.all
355
+ console.log(`\nUpdating packages with '${command}'…`)
356
+ try {
357
+ execSync(command, { cwd: projectDir, stdio: 'inherit' })
358
+ installed = true
359
+ } catch {
360
+ console.error(
361
+ `Updating packages failed, so the template versions stay. Run '${command}' in the project to try again.`
362
+ )
363
+ }
364
+ }
365
+ }
366
+
189
367
  if (git === 'yes') {
190
368
  try {
191
- execSync('git init', { cwd: projectDir, stdio: 'inherit' })
369
+ execFileSync('git', ['init', '-b', defaultBranch()], {
370
+ cwd: projectDir,
371
+ stdio: 'inherit'
372
+ })
373
+ commitEverything(projectDir)
192
374
  } catch (err) {
193
375
  console.error('Error initializing git repository:', err)
194
376
  }
195
377
  }
196
378
 
197
- const pm = detectPackageManager()
198
- const installCommand = getInstallCommand(pm)
199
- const devCommand = getRunCommand(pm, 'dev')
200
- const dbPushCommand = getRunCommand(pm, 'db:push')
201
-
202
379
  console.log(
203
380
  `
204
381
  Created a new app called '${projectName}' 🎉
@@ -210,9 +387,7 @@ code ${projectName}
210
387
 
211
388
  To get started, run these commands:
212
389
  cd ${projectName}
213
-
214
- ${installCommand}
215
-
390
+ ${installed ? '' : `\n${installCommand}\n`}
216
391
  Fill in the values in .env, then create the database tables:
217
392
 
218
393
  ${dbPushCommand}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ej-app",
3
- "version": "30.0.0",
3
+ "version": "32.0.0",
4
4
  "description": "Bootstrap a next.js app with my preferred setup",
5
5
  "main": "cli.js",
6
6
  "type": "module",