create-adonisjs 3.0.0-next.1 → 3.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.
package/README.md CHANGED
@@ -10,9 +10,10 @@ Scaffold a new AdonisJS application using starter kits
10
10
 
11
11
  You can use between one of the following official starter kits, or bring your own using the `--kit` flag.
12
12
 
13
- - `hypermedia` : A fullstack application using server-side templates, alongside Alpine.js and optionally Unpoly.
13
+ - `hypermedia`: A fullstack application using server-side templates, alongside Alpine.js and optionally Unpoly.
14
14
  - `react`: A fullstack React application with E2E type-safety. Inertia is used as a bridge between the frontend and the backend.
15
15
  - `vue`: A fullstack Vue application with E2E type-safety. Inertia is used as a bridge between the frontend and the backend.
16
+ - `api`: Type-safe REST API with dual authentication (monorepo structure).
16
17
 
17
18
  ## Usage
18
19
 
@@ -43,23 +44,26 @@ This argument is optional and the command will prompt you to enter the directory
43
44
 
44
45
  ### `--kit` (Default: Triggers prompt for selection)
45
46
 
46
- If you want to use your own starter kit hosted on Github, Gitlab, or Bitbucket, you can use the `--kit` flag to define the repo URL.
47
+ If you want to use your own starter kit hosted on GitHub, GitLab, or Bitbucket, you can use the `--kit` flag to define the repo URL. You can also use the starter kit aliases (`hypermedia`, `react`, `vue`, `api`) to quickly select an official starter kit.
47
48
 
48
49
  ```sh
50
+ # Use an official starter kit by alias
51
+ npm init adonisjs@next -- --kit="hypermedia"
52
+
49
53
  # Download from GitHub
50
54
  npm init adonisjs@next -- --kit="github:github_user/repo"
51
55
 
52
- # Github is the default provider, so if not specified, it will be assumed as github
56
+ # GitHub is the default provider, so if not specified, it will be assumed as github
53
57
  npm init adonisjs@next -- --kit="github_user/repo"
54
58
 
55
59
  # Download from GitLab
56
60
  npm init adonisjs@next -- --kit="gitlab:user/repo"
57
61
 
58
- # Download from BitBucket
62
+ # Download from Bitbucket
59
63
  npm init adonisjs@next -- --kit="bitbucket:user/repo"
60
64
  ```
61
65
 
62
- You can also pass specify the branch or tag name as follows:
66
+ You can also specify the branch or tag name as follows:
63
67
 
64
68
  ```sh
65
69
  # Branch name
@@ -87,10 +91,29 @@ npm init adonisjs@next -- --pkg="yarn"
87
91
 
88
92
  ### Other options
89
93
 
90
- | Option | Description |
91
- | ------------ | --------------------------------------- |
92
- | `--git-init` | Initialize git repository. |
93
- | `--verbose` | Enable verbose mode to display all logs |
94
+ | Option | Description |
95
+ | ------------------- | ----------------------------------------------- |
96
+ | `--git-init` | Initialize git repository. |
97
+ | `--skip-migrations` | Skip running database migrations automatically. |
98
+ | `--verbose` | Enable verbose mode to display all logs. |
99
+
100
+ ## Custom starter kits
101
+
102
+ You can create your own starter kits and use them with `create-adonisjs`. To support advanced features like monorepo structures, you can add a `create-adonisjs.json` file at the root of your starter kit:
103
+
104
+ ```json
105
+ {
106
+ "workspaces": true,
107
+ "backendSource": "apps/backend"
108
+ }
109
+ ```
110
+
111
+ **Configuration options:**
112
+
113
+ - `workspaces` (boolean): Set to `true` if your starter kit is a monorepo.
114
+ - `backendSource` (string): The relative path to the backend source directory in a monorepo. This is where the AdonisJS application resides.
115
+
116
+ The `create-adonisjs.json` file will be automatically deleted after the starter kit is processed.
94
117
 
95
118
  ## Debugging errors
96
119
 
@@ -114,8 +137,8 @@ In order to ensure that the AdonisJS community is welcoming to all, please revie
114
137
 
115
138
  create-adonisjs is open-sourced software licensed under the [MIT license](LICENSE.md).
116
139
 
117
- [gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/create-adonisjs/test.yml?style=for-the-badge
118
- [gh-workflow-url]: https://github.com/adonisjs/create-adonisjs/actions/workflows/test.yml 'Github action'
140
+ [gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/create-adonisjs/checks.yml?style=for-the-badge
141
+ [gh-workflow-url]: https://github.com/adonisjs/create-adonisjs/actions/workflows/checks.yml 'Github action'
119
142
  [npm-image]: https://img.shields.io/npm/v/create-adonisjs/latest.svg?style=for-the-badge&logo=npm
120
143
  [npm-url]: https://www.npmjs.com/package/create-adonisjs/v/latest 'npm'
121
144
  [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
package/build/bin/run.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  kernel
4
- } from "../chunk-VYPE5MQ2.js";
4
+ } from "../chunk-4GHTRYYP.js";
5
5
 
6
6
  // bin/run.ts
7
7
  kernel.handle(["create-adonisjs", ...process.argv.slice(2)]).catch(console.error);
@@ -0,0 +1,414 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // index.ts
13
+ import { HelpCommand, Kernel } from "@adonisjs/ace";
14
+
15
+ // commands/main.ts
16
+ import { cwd } from "process";
17
+ import { existsSync } from "fs";
18
+ import gradient from "gradient-string";
19
+ import { downloadTemplate } from "giget";
20
+ import { execa } from "execa";
21
+ import detectPackageManager from "which-pm-runs";
22
+ import { BaseCommand, args, flags } from "@adonisjs/ace";
23
+ import { basename, isAbsolute, join, relative } from "path";
24
+ import { copyFile, mkdir, readFile, unlink, writeFile } from "fs/promises";
25
+
26
+ // src/templates.ts
27
+ var templates = [
28
+ {
29
+ name: "Hypermedia app",
30
+ alias: "hypermedia",
31
+ hint: "A fullstack application using server-side templates",
32
+ source: "github:adonisjs/starter-kits/hypermedia"
33
+ },
34
+ {
35
+ name: "React app (using Inertia)",
36
+ alias: "react",
37
+ hint: "A fullstack React application with E2E type-safety",
38
+ source: "github:adonisjs/starter-kits/inertia-react"
39
+ },
40
+ {
41
+ name: "Vue app (using Inertia)",
42
+ alias: "vue",
43
+ hint: "A fullstack Vue application with E2E type-safety",
44
+ source: "github:adonisjs/starter-kits/inertia-vue"
45
+ },
46
+ {
47
+ name: "API (monorepo)",
48
+ alias: "api",
49
+ hint: "Type-safe REST API with dual authentication",
50
+ source: "github:adonisjs/starter-kits/api"
51
+ }
52
+ ];
53
+
54
+ // commands/main.ts
55
+ var CreateNewApp = class extends BaseCommand {
56
+ static commandName = "create-adonisjs";
57
+ static description = "Create a new AdonisJS application";
58
+ /**
59
+ * Inspects the downloaded starter kit by reading the create-adonisjs.json config file.
60
+ * This method determines if the starter kit is a monorepo and identifies the backend
61
+ * source directory location. The config file is deleted after being read.
62
+ *
63
+ * Sets the following properties:
64
+ * - `isMonorepo`: Whether the starter kit uses workspaces
65
+ * - `backendSourceDir`: The absolute path to the backend source code
66
+ *
67
+ * @example
68
+ * await this.#inspectStarterKit()
69
+ * if (this.isMonorepo) {
70
+ * console.log('Backend is in:', this.backendSourceDir)
71
+ * }
72
+ */
73
+ async #inspectStarterKit() {
74
+ const configFile = join(this.destination, "create-adonisjs.json");
75
+ try {
76
+ const config = await readFile(configFile, "utf-8");
77
+ const parsedConfig = JSON.parse(config);
78
+ await unlink(configFile);
79
+ this.isMonorepo = parsedConfig.workspaces ?? false;
80
+ this.backendSourceDir = parsedConfig.backendSource ? join(this.destination, parsedConfig.backendSource) : this.destination;
81
+ } catch {
82
+ this.backendSourceDir = this.destination;
83
+ }
84
+ }
85
+ /**
86
+ * Executes a bash command using execa with shared default options.
87
+ * Commands are run from the specified source directory with consistent
88
+ * settings for output display based on the verbose flag.
89
+ *
90
+ * @param sourceDir - The working directory to execute the command from
91
+ * @param file - The command or executable to run
92
+ * @param cliArgs - Array of command-line arguments to pass to the command
93
+ * @param options - Optional execa options to override defaults
94
+ *
95
+ * @example
96
+ * await this.#runBashCommand(this.destination, 'git', ['init'])
97
+ * await this.#runBashCommand(this.backendSourceDir, 'node', ['ace', 'generate:key'])
98
+ */
99
+ async #runBashCommand(sourceDir, file, cliArgs, options) {
100
+ await execa(file, cliArgs, {
101
+ cwd: sourceDir,
102
+ preferLocal: true,
103
+ windowsHide: false,
104
+ buffer: false,
105
+ stdio: this.verbose === true ? "inherit" : "ignore",
106
+ ...options
107
+ });
108
+ }
109
+ /**
110
+ * Prints the AdonisJS logo as ASCII art using a gradient effect.
111
+ * The banner is displayed at the start of the project creation process
112
+ * to provide visual branding.
113
+ *
114
+ * @example
115
+ * this.#printBannerArt()
116
+ */
117
+ #printBannerArt() {
118
+ const title = Buffer.from(
119
+ "ICAgICBfICAgICAgIF8gICAgICAgICAgICAgXyAgICAgICAgIF8gX19fXyAgCiAgICAvIFwgICBfX3wgfCBfX18gIF8gX18gKF8pX19fICAgIHwgLyBfX198IAogICAvIF8gXCAvIF9gIHwvIF8gXHwgJ18gXHwgLyBfX3xfICB8IFxfX18gXCAKICAvIF9fXyBcIChffCB8IChfKSB8IHwgfCB8IFxfXyBcIHxffCB8X19fKSB8CiAvXy8gICBcX1xfXyxffFxfX18vfF98IHxffF98X19fL1xfX18vfF9fX18vIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA=",
120
+ "base64"
121
+ ).toString();
122
+ this.logger.log("");
123
+ this.logger.log(`${gradient.mind.multiline(title)}`);
124
+ this.logger.log("");
125
+ }
126
+ /**
127
+ * Displays a success message with next steps after the project has been created.
128
+ * Shows instructions for navigating to the project, starting the dev server,
129
+ * and provides a link to the Discord community.
130
+ *
131
+ * @example
132
+ * this.#printSuccessMessage()
133
+ */
134
+ #printSuccessMessage() {
135
+ this.logger.log("");
136
+ this.ui.instructions().heading("Your AdonisJS project has been created successfully!").add(this.colors.cyan("cd " + relative(cwd(), this.destination))).add(this.colors.cyan(`${this.packageManager} run dev`)).add(this.colors.cyan("Open http://localhost:3333")).add("").add(`Have any questions?`).add(`Join our Discord server - ${this.colors.yellow("https://discord.gg/vDcEjq6")}`).render();
137
+ }
138
+ /**
139
+ * Prompts the user to specify a destination directory if one was not provided
140
+ * as a command argument. Converts relative paths to absolute paths using the
141
+ * current working directory.
142
+ *
143
+ * Sets the `destination` property to the absolute path where the project will be created.
144
+ *
145
+ * @example
146
+ * await this.#promptForDestination()
147
+ * console.log('Creating project at:', this.destination)
148
+ */
149
+ async #promptForDestination() {
150
+ if (!this.destination) {
151
+ this.destination = await this.prompt.ask("Where should we create your new project?", {
152
+ default: "./adonisjs-app"
153
+ });
154
+ }
155
+ this.destination = isAbsolute(this.destination) ? this.destination : join(cwd(), this.destination);
156
+ }
157
+ /**
158
+ * Prompts the user to select a starter kit if one was not provided via the --kit flag.
159
+ * Handles both interactive selection and CLI flag aliases. When multiple templates
160
+ * share the same alias (e.g., "inertia" for different frontend frameworks), prompts
161
+ * the user to choose between them.
162
+ *
163
+ * Sets the `kit` property to the Git repository URL of the selected template.
164
+ *
165
+ * @example
166
+ * await this.#promptForStarterKit()
167
+ * console.log('Using starter kit:', this.kit)
168
+ */
169
+ async #promptForStarterKit() {
170
+ if (!this.kit) {
171
+ const template = await this.prompt.choice(
172
+ "Select the kind of app you want to create?",
173
+ templates
174
+ );
175
+ this.kit = templates.find((t) => t.name === template).source;
176
+ } else {
177
+ const matchingTemplate = templates.find((t) => t.alias === this.kit);
178
+ if (matchingTemplate) {
179
+ this.kit = matchingTemplate.source;
180
+ }
181
+ }
182
+ }
183
+ /**
184
+ * Updates the package.json name property to match the destination directory name.
185
+ * This ensures the package name reflects the actual project directory rather than
186
+ * the starter kit's default name.
187
+ *
188
+ * @param sourceDir - The directory containing the package.json file to update
189
+ *
190
+ * @example
191
+ * await this.#replacePackageJsonName(this.backendSourceDir)
192
+ */
193
+ async #replacePackageJsonName(sourceDir) {
194
+ const pkgJsonPath = join(sourceDir, "package.json");
195
+ const pkgJson = await readFile(pkgJsonPath, "utf-8").then(JSON.parse);
196
+ pkgJson.name = basename(sourceDir);
197
+ await writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
198
+ }
199
+ /**
200
+ * Removes the README.md file from the destination directory.
201
+ * This allows users to start with a clean slate for their project documentation.
202
+ * Silently ignores errors if the file doesn't exist.
203
+ *
204
+ * @example
205
+ * await this.#removeReadmeFile()
206
+ */
207
+ async #removeReadmeFile() {
208
+ try {
209
+ await unlink(join(this.destination, "README.md"));
210
+ } catch {
211
+ }
212
+ }
213
+ /**
214
+ * Removes existing package manager lock files (package-lock.json, yarn.lock, pnpm-lock.yaml)
215
+ * from both the destination directory and backend source directory (if different).
216
+ * This ensures a fresh lock file will be generated based on the user's chosen package manager.
217
+ * Silently ignores errors if files don't exist.
218
+ *
219
+ * @example
220
+ * await this.#removeLockFile()
221
+ */
222
+ async #removeLockFile() {
223
+ const filesToRemove = [
224
+ unlink(join(this.destination, "package-lock.json")),
225
+ unlink(join(this.destination, "yarn.lock")),
226
+ unlink(join(this.destination, "pnpm-lock.yaml"))
227
+ ];
228
+ if (this.backendSourceDir !== this.destination) {
229
+ filesToRemove.push(
230
+ ...[
231
+ unlink(join(this.backendSourceDir, "package-lock.json")),
232
+ unlink(join(this.backendSourceDir, "yarn.lock")),
233
+ unlink(join(this.backendSourceDir, "pnpm-lock.yaml"))
234
+ ]
235
+ );
236
+ }
237
+ await Promise.allSettled(filesToRemove);
238
+ }
239
+ /**
240
+ * Copies the .env.example file to .env in the backend source directory if it exists.
241
+ * This provides the user with a properly configured environment file based on the
242
+ * starter kit's defaults.
243
+ *
244
+ * @example
245
+ * await this.#copyEnvExampleFile()
246
+ */
247
+ async #copyEnvExampleFile() {
248
+ const envPath = join(this.backendSourceDir, ".env");
249
+ const envExamplePath = join(this.backendSourceDir, ".env.example");
250
+ if (existsSync(envExamplePath)) {
251
+ await copyFile(envExamplePath, envPath);
252
+ }
253
+ }
254
+ /**
255
+ * Generates a fresh application encryption key by running the `node ace generate:key` command.
256
+ * This creates a secure random key for encrypting cookies and other sensitive data.
257
+ *
258
+ * @example
259
+ * await this.#generateFreshAppKey()
260
+ */
261
+ async #generateFreshAppKey() {
262
+ await this.#runBashCommand(this.backendSourceDir, "node", ["ace", "generate:key"]);
263
+ }
264
+ /**
265
+ * Creates the tmp directory and runs database migrations by executing `node ace migration:run`.
266
+ * This sets up the initial database schema for the new application.
267
+ *
268
+ * @example
269
+ * await this.#migrateDatabase()
270
+ */
271
+ async #migrateDatabase() {
272
+ await mkdir(join(this.backendSourceDir, "tmp"));
273
+ await this.#runBashCommand(this.backendSourceDir, "node", ["ace", "migration:run"]);
274
+ }
275
+ /**
276
+ * Main entry point for the create-adonisjs command.
277
+ * Orchestrates the entire project creation workflow including:
278
+ * - Displaying the AdonisJS banner
279
+ * - Prompting for destination and starter kit selection
280
+ * - Downloading and configuring the starter kit
281
+ * - Installing dependencies
282
+ * - Preparing the application (env files, app key, etc.)
283
+ * - Running database migrations
284
+ * - Displaying success message with next steps
285
+ *
286
+ * @example
287
+ * const command = new CreateNewApp()
288
+ * await command.run()
289
+ */
290
+ async run() {
291
+ this.packageManager = this.packageManager || detectPackageManager()?.name || "npm";
292
+ this.#printBannerArt();
293
+ await this.#promptForDestination();
294
+ await this.#promptForStarterKit();
295
+ const tasks = this.ui.tasks({ verbose: this.verbose === true });
296
+ tasks.add("Download starter kit", async (task) => {
297
+ task.update(`Downloading "${this.kit}"`);
298
+ await downloadTemplate(this.kit, {
299
+ dir: this.destination,
300
+ auth: this.token,
301
+ registry: false
302
+ });
303
+ await this.#inspectStarterKit();
304
+ await this.#removeLockFile();
305
+ return `Downloaded "${this.kit}"`;
306
+ }).addIf(this.gitInit === true, "Initialize git repository", async () => {
307
+ await this.#runBashCommand(this.destination, "git", ["init"]);
308
+ return "Initialized git repository";
309
+ }).add("Install packages", async (task) => {
310
+ const spinner = this.logger.await(`installing dependencies (${this.packageManager})`, {
311
+ silent: this.verbose
312
+ });
313
+ spinner.tap((line) => task.update(line));
314
+ spinner.start();
315
+ try {
316
+ await this.#runBashCommand(
317
+ this.isMonorepo ? this.destination : this.backendSourceDir,
318
+ this.packageManager,
319
+ ["install"]
320
+ );
321
+ return `Packages installed using "${this.packageManager}"`;
322
+ } finally {
323
+ spinner.stop();
324
+ }
325
+ }).add("Prepare application", async (task) => {
326
+ try {
327
+ await this.#replacePackageJsonName(
328
+ this.isMonorepo ? this.destination : this.backendSourceDir
329
+ );
330
+ await this.#removeReadmeFile();
331
+ await this.#copyEnvExampleFile();
332
+ await this.#generateFreshAppKey();
333
+ return "Application ready";
334
+ } catch (error) {
335
+ if (this.verbose) {
336
+ this.logger.fatal(error);
337
+ return task.error("Unable to prepare application");
338
+ }
339
+ return task.error(error);
340
+ }
341
+ }).addIf(!this.skipMigrations, "Migrate database", async (task) => {
342
+ try {
343
+ await this.#migrateDatabase();
344
+ return "Database migrated";
345
+ } catch (error) {
346
+ if (this.verbose) {
347
+ this.logger.fatal(error);
348
+ return task.error("Unable to migrate database");
349
+ }
350
+ return task.error(error);
351
+ }
352
+ });
353
+ await tasks.run();
354
+ if (tasks.getState() === "succeeded") {
355
+ this.#printSuccessMessage();
356
+ } else {
357
+ this.exitCode = 1;
358
+ }
359
+ }
360
+ };
361
+ __decorateClass([
362
+ args.string({ description: "Destination directory", required: false })
363
+ ], CreateNewApp.prototype, "destination", 2);
364
+ __decorateClass([
365
+ flags.string({
366
+ description: "Define path to a custom git repository to download the starter kit",
367
+ alias: "K"
368
+ })
369
+ ], CreateNewApp.prototype, "kit", 2);
370
+ __decorateClass([
371
+ flags.string({
372
+ description: "Auth token to download private repositories",
373
+ alias: "t"
374
+ })
375
+ ], CreateNewApp.prototype, "token", 2);
376
+ __decorateClass([
377
+ flags.boolean({
378
+ description: "Init git repository"
379
+ })
380
+ ], CreateNewApp.prototype, "gitInit", 2);
381
+ __decorateClass([
382
+ flags.boolean({
383
+ description: "Skip running database migrations"
384
+ })
385
+ ], CreateNewApp.prototype, "skipMigrations", 2);
386
+ __decorateClass([
387
+ flags.string({
388
+ description: "Define the package manager to install dependencies",
389
+ flagName: "pkg"
390
+ })
391
+ ], CreateNewApp.prototype, "packageManager", 2);
392
+ __decorateClass([
393
+ flags.boolean({
394
+ description: "Execute tasks in verbose mode",
395
+ alias: "v"
396
+ })
397
+ ], CreateNewApp.prototype, "verbose", 2);
398
+
399
+ // index.ts
400
+ Kernel.defaultCommand = CreateNewApp;
401
+ var kernel = Kernel.create();
402
+ kernel.defineFlag("help", {
403
+ type: "boolean",
404
+ description: HelpCommand.description
405
+ });
406
+ kernel.on("help", async (command, $kernel, parsed) => {
407
+ parsed.args.unshift(command.commandName);
408
+ await new HelpCommand($kernel, parsed, kernel.ui, kernel.prompt).exec();
409
+ return $kernel.shortcircuit();
410
+ });
411
+
412
+ export {
413
+ kernel
414
+ };
@@ -27,6 +27,10 @@ export declare class CreateNewApp extends BaseCommand {
27
27
  * Init git repository. Do not init when flag is not mentioned.
28
28
  */
29
29
  gitInit?: boolean;
30
+ /**
31
+ * Skip running database migrations. Defaults to false
32
+ */
33
+ skipMigrations?: boolean;
30
34
  /**
31
35
  * Package manager to use. Detect package manager when flag is not
32
36
  * mentioned.
@@ -37,7 +41,31 @@ export declare class CreateNewApp extends BaseCommand {
37
41
  */
38
42
  verbose?: boolean;
39
43
  /**
40
- * Main method
44
+ * Whether the starter kit is a monorepo workspace
45
+ * This property is set after the starter kit has been cloned and inspected
46
+ */
47
+ isMonorepo: boolean;
48
+ /**
49
+ * The directory containing the backend source code
50
+ * For monorepos, this points to the backend workspace directory.
51
+ * For single packages, this is the same as the destination directory.
52
+ * This property is set after the starter kit has been cloned and inspected
53
+ */
54
+ backendSourceDir: string;
55
+ /**
56
+ * Main entry point for the create-adonisjs command.
57
+ * Orchestrates the entire project creation workflow including:
58
+ * - Displaying the AdonisJS banner
59
+ * - Prompting for destination and starter kit selection
60
+ * - Downloading and configuring the starter kit
61
+ * - Installing dependencies
62
+ * - Preparing the application (env files, app key, etc.)
63
+ * - Running database migrations
64
+ * - Displaying success message with next steps
65
+ *
66
+ * @example
67
+ * const command = new CreateNewApp()
68
+ * await command.run()
41
69
  */
42
70
  run(): Promise<void>;
43
71
  }
package/build/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  kernel
3
- } from "./chunk-VYPE5MQ2.js";
3
+ } from "./chunk-4GHTRYYP.js";
4
4
  export {
5
5
  kernel
6
6
  };
@@ -1,5 +1,14 @@
1
1
  /**
2
- * List of first party templates
2
+ * List of first party templates available for creating new AdonisJS projects.
3
+ * Each template represents a different project starter kit with its own configuration
4
+ * and dependencies.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const hyperMediaTemplate = templates.find(t => t.alias === 'hypermedia')
9
+ * console.log(hyperMediaTemplate.source)
10
+ * // Output: 'github:adonisjs/starter-kits/hypermedia'
11
+ * ```
3
12
  */
4
13
  export declare const templates: {
5
14
  name: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-adonisjs",
3
3
  "description": "Scaffold new AdonisJS applications using starter kits",
4
- "version": "3.0.0-next.1",
4
+ "version": "3.0.0",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "scripts": {
23
23
  "pretest": "npm run lint",
24
- "test": "cross-env NODE_DEBUG=create-adonisjs c8 npm run quick:test",
24
+ "test": "cross-env NODE_DEBUG=create-adonisjs npm run quick:test",
25
25
  "lint": "eslint",
26
26
  "format": "prettier --write .",
27
27
  "typecheck": "tsc --noEmit",
@@ -37,30 +37,30 @@
37
37
  "quick:test": "node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts"
38
38
  },
39
39
  "devDependencies": {
40
- "@adonisjs/eslint-config": "^3.0.0-next.2",
40
+ "@adonisjs/eslint-config": "^3.0.0",
41
41
  "@adonisjs/prettier-config": "^1.4.5",
42
- "@adonisjs/tsconfig": "^2.0.0-next.0",
43
- "@japa/assert": "^4.1.1",
44
- "@japa/file-system": "^2.3.2",
45
- "@japa/runner": "^4.4.0",
46
- "@poppinss/ts-exec": "^1.4.1",
47
- "@release-it/conventional-changelog": "^10.0.1",
42
+ "@adonisjs/tsconfig": "^2.0.0",
43
+ "@japa/assert": "^4.2.0",
44
+ "@japa/file-system": "^3.0.0",
45
+ "@japa/runner": "^5.3.0",
46
+ "@poppinss/ts-exec": "^1.4.4",
47
+ "@release-it/conventional-changelog": "^10.0.5",
48
48
  "@types/gradient-string": "^1.1.6",
49
- "@types/node": "^24.5.2",
49
+ "@types/node": "^25.3.0",
50
50
  "@types/which-pm-runs": "^1.0.2",
51
- "c8": "^10.1.3",
52
- "cross-env": "^10.0.0",
51
+ "c8": "^11.0.0",
52
+ "cross-env": "^10.1.0",
53
53
  "del-cli": "^7.0.0",
54
- "eslint": "^9.36.0",
55
- "prettier": "^3.6.2",
56
- "release-it": "19.0.4",
57
- "tsup": "^8.5.0",
58
- "typescript": "^5.9.2"
54
+ "eslint": "^10.0.2",
55
+ "prettier": "^3.8.1",
56
+ "release-it": "19.2.4",
57
+ "tsup": "^8.5.1",
58
+ "typescript": "^5.9.3"
59
59
  },
60
60
  "dependencies": {
61
- "@adonisjs/ace": "^14.0.1-next.2",
62
- "execa": "^9.6.0",
63
- "giget": "^2.0.0",
61
+ "@adonisjs/ace": "^14.0.1",
62
+ "execa": "^9.6.1",
63
+ "giget": "^3.1.2",
64
64
  "gradient-string": "^3.0.0",
65
65
  "which-pm-runs": "^1.1.0"
66
66
  },
@@ -1,263 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __decorateClass = (decorators, target, key, kind) => {
4
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
- if (decorator = decorators[i])
7
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
- if (kind && result) __defProp(target, key, result);
9
- return result;
10
- };
11
-
12
- // index.ts
13
- import { HelpCommand, Kernel } from "@adonisjs/ace";
14
-
15
- // commands/main.ts
16
- import { cwd } from "process";
17
- import { existsSync } from "fs";
18
- import gradient from "gradient-string";
19
- import { downloadTemplate } from "giget";
20
- import { execa } from "execa";
21
- import detectPackageManager from "which-pm-runs";
22
- import { BaseCommand, args, flags } from "@adonisjs/ace";
23
- import { basename, isAbsolute, join, relative } from "path";
24
- import { copyFile, readFile, unlink, writeFile } from "fs/promises";
25
-
26
- // src/templates.ts
27
- var templates = [
28
- {
29
- name: "Hypermedia app",
30
- alias: "hypermedia",
31
- hint: "A fullstack application using server-side templates",
32
- source: "github:adonisjs/starter-kits/hypermedia"
33
- },
34
- {
35
- name: "React app (using Inertia)",
36
- alias: "inertia",
37
- hint: "A fullstack React application with E2E type-safety",
38
- source: "github:adonisjs/starter-kits/inertia-react"
39
- },
40
- {
41
- name: "Vue app (using Inertia)",
42
- alias: "inertia",
43
- hint: "A fullstack Vue application with E2E type-safety",
44
- source: "github:adonisjs/starter-kits/inertia-vue"
45
- }
46
- ];
47
-
48
- // commands/main.ts
49
- var CreateNewApp = class extends BaseCommand {
50
- static commandName = "create-adonisjs";
51
- static description = "Create a new AdonisJS application";
52
- /**
53
- * Runs bash command using execa with shared defaults
54
- */
55
- async #runBashCommand(file, cliArgs, options) {
56
- await execa(file, cliArgs, {
57
- cwd: this.destination,
58
- preferLocal: true,
59
- windowsHide: false,
60
- buffer: false,
61
- stdio: this.verbose === true ? "inherit" : "ignore",
62
- ...options
63
- });
64
- }
65
- /**
66
- * Prints AdonisJS as ASCII art
67
- */
68
- #printBannerArt() {
69
- const title = Buffer.from(
70
- "ICAgICBfICAgICAgIF8gICAgICAgICAgICAgXyAgICAgICAgIF8gX19fXyAgCiAgICAvIFwgICBfX3wgfCBfX18gIF8gX18gKF8pX19fICAgIHwgLyBfX198IAogICAvIF8gXCAvIF9gIHwvIF8gXHwgJ18gXHwgLyBfX3xfICB8IFxfX18gXCAKICAvIF9fXyBcIChffCB8IChfKSB8IHwgfCB8IFxfXyBcIHxffCB8X19fKSB8CiAvXy8gICBcX1xfXyxffFxfX18vfF98IHxffF98X19fL1xfX18vfF9fX18vIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA=",
71
- "base64"
72
- ).toString();
73
- this.logger.log("");
74
- this.logger.log(`${gradient.mind.multiline(title)}`);
75
- this.logger.log("");
76
- }
77
- /**
78
- * Print the success message
79
- */
80
- #printSuccessMessage() {
81
- this.logger.log("");
82
- this.ui.instructions().heading("Your AdonisJS project has been created successfully!").add(this.colors.cyan("cd " + relative(cwd(), this.destination))).add(this.colors.cyan(`${this.packageManager} run dev`)).add(this.colors.cyan("Open http://localhost:3333")).add("").add(`Have any questions?`).add(`Join our Discord server - ${this.colors.yellow("https://discord.gg/vDcEjq6")}`).render();
83
- }
84
- /**
85
- * Prompt for the destination directory
86
- */
87
- async #promptForDestination() {
88
- if (!this.destination) {
89
- this.destination = await this.prompt.ask("Where should we create your new project?", {
90
- default: "./adonisjs-app"
91
- });
92
- }
93
- this.destination = isAbsolute(this.destination) ? this.destination : join(cwd(), this.destination);
94
- }
95
- /**
96
- * Prompt to configure a starter kit
97
- */
98
- async #promptForStarterKit() {
99
- if (!this.kit) {
100
- const template = await this.prompt.choice(
101
- "Select the kind of app you want to create?",
102
- templates
103
- );
104
- this.kit = templates.find((t) => t.name === template).source;
105
- } else {
106
- const matchingTemplatesFromAlias = templates.filter((t) => t.alias === this.kit);
107
- if (matchingTemplatesFromAlias.length > 1) {
108
- const template = await this.prompt.choice(
109
- "Which frontend framework do you want to use?",
110
- matchingTemplatesFromAlias
111
- );
112
- this.kit = matchingTemplatesFromAlias.find((t) => t.name === template).source;
113
- } else if (matchingTemplatesFromAlias.length === 1) {
114
- this.kit = matchingTemplatesFromAlias[0].source;
115
- }
116
- }
117
- }
118
- /**
119
- * Replace the package.json name with the destination directory name.
120
- * Errors are ignored.
121
- */
122
- async #replacePackageJsonName() {
123
- const pkgJsonPath = join(this.destination, "package.json");
124
- const pkgJson = await readFile(pkgJsonPath, "utf-8").then(JSON.parse);
125
- pkgJson.name = basename(this.destination);
126
- await writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
127
- }
128
- /**
129
- * Optionally removes readme file. Errors are ignored
130
- */
131
- async #removeReadmeFile() {
132
- await unlink(join(this.destination, "README.md"));
133
- }
134
- /**
135
- * Optionally remove existing lock file. Errors are ignored
136
- */
137
- async #removeLockFile() {
138
- await Promise.allSettled([
139
- unlink(join(this.destination, "package-lock.json")),
140
- unlink(join(this.destination, "yarn.lock")),
141
- unlink(join(this.destination, "pnpm-lock.yaml"))
142
- ]);
143
- }
144
- /**
145
- * If starter template has a `.env.example` file, then copy it to `.env`
146
- */
147
- async #copyEnvExampleFile() {
148
- const envPath = join(this.destination, ".env");
149
- const envExamplePath = join(this.destination, ".env.example");
150
- if (existsSync(envExamplePath)) {
151
- await copyFile(envExamplePath, envPath);
152
- }
153
- }
154
- /**
155
- * Generate a fresh app key. Errors are ignored
156
- */
157
- async #generateFreshAppKey() {
158
- await this.#runBashCommand("node", ["ace", "generate:key"]);
159
- }
160
- /**
161
- * Main method
162
- */
163
- async run() {
164
- this.packageManager = this.packageManager || detectPackageManager()?.name || "npm";
165
- this.#printBannerArt();
166
- await this.#promptForDestination();
167
- await this.#promptForStarterKit();
168
- const tasks = this.ui.tasks({ verbose: this.verbose === true });
169
- tasks.add("Download starter kit", async (task) => {
170
- task.update(`Downloading "${this.kit}"`);
171
- await downloadTemplate(this.kit, {
172
- dir: this.destination,
173
- auth: this.token,
174
- registry: false
175
- });
176
- await this.#removeLockFile();
177
- return `Downloaded "${this.kit}"`;
178
- }).addIf(this.gitInit === true, "Initialize git repository", async () => {
179
- await this.#runBashCommand("git", ["init"]);
180
- return "Initialized git repository";
181
- }).add("Install packages", async (task) => {
182
- const spinner = this.logger.await(`installing dependencies (${this.packageManager})`, {
183
- silent: this.verbose
184
- });
185
- spinner.tap((line) => task.update(line));
186
- spinner.start();
187
- try {
188
- await this.#runBashCommand(this.packageManager, ["install"]);
189
- return `Packages installed using "${this.packageManager}"`;
190
- } finally {
191
- spinner.stop();
192
- }
193
- }).add("Prepare application", async () => {
194
- try {
195
- await this.#replacePackageJsonName();
196
- await this.#removeReadmeFile();
197
- await this.#copyEnvExampleFile();
198
- await this.#generateFreshAppKey();
199
- return "Application ready";
200
- } catch (error) {
201
- if (this.verbose) {
202
- this.logger.fatal(error);
203
- }
204
- return "Unable to prepare application";
205
- }
206
- });
207
- await tasks.run();
208
- if (tasks.getState() === "succeeded") {
209
- this.#printSuccessMessage();
210
- } else {
211
- this.exitCode = 1;
212
- }
213
- }
214
- };
215
- __decorateClass([
216
- args.string({ description: "Destination directory", required: false })
217
- ], CreateNewApp.prototype, "destination", 2);
218
- __decorateClass([
219
- flags.string({
220
- description: "Define path to a custom git repository to download the starter kit",
221
- alias: "K"
222
- })
223
- ], CreateNewApp.prototype, "kit", 2);
224
- __decorateClass([
225
- flags.string({
226
- description: "Auth token to download private repositories",
227
- alias: "t"
228
- })
229
- ], CreateNewApp.prototype, "token", 2);
230
- __decorateClass([
231
- flags.boolean({
232
- description: "Init git repository"
233
- })
234
- ], CreateNewApp.prototype, "gitInit", 2);
235
- __decorateClass([
236
- flags.string({
237
- description: "Define the package manager to install dependencies",
238
- flagName: "pkg"
239
- })
240
- ], CreateNewApp.prototype, "packageManager", 2);
241
- __decorateClass([
242
- flags.boolean({
243
- description: "Execute tasks in verbose mode",
244
- alias: "v"
245
- })
246
- ], CreateNewApp.prototype, "verbose", 2);
247
-
248
- // index.ts
249
- Kernel.defaultCommand = CreateNewApp;
250
- var kernel = Kernel.create();
251
- kernel.defineFlag("help", {
252
- type: "boolean",
253
- description: HelpCommand.description
254
- });
255
- kernel.on("help", async (command, $kernel, parsed) => {
256
- parsed.args.unshift(command.commandName);
257
- await new HelpCommand($kernel, parsed, kernel.ui, kernel.prompt).exec();
258
- return $kernel.shortcircuit();
259
- });
260
-
261
- export {
262
- kernel
263
- };