directus-template-cli 0.7.0-beta.8 → 0.7.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
@@ -1,17 +1,17 @@
1
1
  # Directus Template CLI
2
2
 
3
- A streamlined CLI tool for managing Directus templates - making it easy to apply and extract template configurations across instances.
3
+ A streamlined CLI tool for creating new Directus projects and managing Directus templates - making it easy to apply and extract template configurations across instances.
4
4
 
5
- ⚠️ **Beta Release Notice**: This tool is currently in beta and best suited for:
5
+ This tool is best suited for:
6
6
  - Proof of Concept (POC) projects
7
7
  - Demo environments
8
8
  - New project setups
9
9
 
10
- We strongly recommend against using this tool in existing production environments or as a critical part of your CI/CD pipeline without thorough testing. Always create backups before applying templates.
10
+ ⚠️ We strongly recommend against using this tool in existing production environments or as a critical part of your CI/CD pipeline without thorough testing. Always create backups before applying templates.
11
11
 
12
12
  **Important Notes:**
13
13
  - **Primary Purpose**: Built to deploy templates created by the Directus Core Team. While community templates are supported, the unlimited possible configurations make comprehensive support challenging.
14
- - **Database Compatibility**: PostgreSQL and SQLite are recommended. MySQL users may encounter known issues.
14
+ - **Database Compatibility**: PostgreSQL is recommended. Applying templates that are extracted and applied between different databases (Extract from SQLite -> Apply to Postgres) can caused issues and is not recommended. MySQL users may encounter known issues.
15
15
  - **Performance**: Remote operations (extract/apply) are rate-limited to 10 requests/second using bottleneck. Processing time varies based on your instance size (collections, items, assets).
16
16
  - **Version Compatibility**:
17
17
  - v0.5.0+: Compatible with Directus 11 and up
@@ -19,6 +19,105 @@ We strongly recommend against using this tool in existing production environment
19
19
 
20
20
  Using the @latest tag ensures you're receiving the latest version of the packaged templates with the CLI. You can review [the specific versions on NPM](https://www.npmjs.com/package/directus-template-cli) and use @{version} syntax to apply the templates included with that version.
21
21
 
22
+ ## Initializing a New Project
23
+
24
+ The CLI can initialize a new Directus project with an optional frontend framework using official or community templates.
25
+
26
+ 1. Run the following command and follow the interactive prompts:
27
+
28
+ ```
29
+ npx directus-template-cli@latest init
30
+ ```
31
+
32
+ You'll be guided through:
33
+ - Selecting a directory for your new project
34
+ - Choosing a Directus backend template
35
+ - Selecting a frontend framework (if available for the template)
36
+ - Setting up Git and installing dependencies
37
+
38
+ ### Command Options
39
+
40
+ You can also provide arguments and flags:
41
+
42
+ ```
43
+ npx directus-template-cli@latest init my-project
44
+ ```
45
+
46
+ The first argument (`my-project` in the example above) specifies the directory where the project will be created. If not provided, you'll be prompted to enter a directory during the interactive process.
47
+
48
+ ```
49
+ npx directus-template-cli@latest init --frontend=nextjs --template=cms
50
+ npx directus-template-cli@latest init my-project --frontend=nextjs --template=cms
51
+ npx directus-template-cli@latest init --template=https://github.com/directus-labs/starters/tree/main/cms
52
+ ```
53
+
54
+
55
+ Available flags:
56
+
57
+ - `--frontend`: Frontend framework to use (e.g., nextjs, nuxt, astro)
58
+ - `--gitInit`: Initialize a new Git repository (defaults to true, use --no-gitInit to disable)
59
+ - `--installDeps`: Install dependencies automatically (defaults to true, use --no-installDeps to disable)
60
+ - `--overwriteDir`: Override the default directory if it already exists (defaults to false)
61
+ - `--template`: Template name (e.g., simple-cms) or GitHub URL (e.g., https://github.com/directus-labs/starters/tree/main/cms)
62
+ - `--disableTelemetry`: Disable telemetry collection
63
+
64
+ You can use any public GitHub repository URL for the `--template` parameter, pointing to the specific directory containing the template. This is especially useful for using community-maintained templates or your own custom templates hosted on GitHub.
65
+
66
+ ### Creating Custom Templates
67
+
68
+ You can create your own custom templates for use with the `init` command. A template is defined by a `package.json` file with a `directus:template` property that specifies the template configuration.
69
+
70
+ NOTE: the `init` command will NOT work without this step of the process.
71
+
72
+ Here's an example of a template configuration:
73
+
74
+ ```json
75
+ {
76
+ "name": "directus-cms-starter",
77
+ "version": "1.0.0",
78
+ "description": "A starter template for Directus CMS projects",
79
+ "directus:template": {
80
+ "name": "CMS",
81
+ "description": "A ready-to-use CMS with block builder, visual editing, and integration with your favorite framework.",
82
+ "template": "./directus/template",
83
+ "frontends": {
84
+ "nextjs": {
85
+ "name": "Next.js",
86
+ "path": "./nextjs"
87
+ },
88
+ "nuxt": {
89
+ "name": "Nuxt",
90
+ "path": "./nuxt"
91
+ },
92
+ "astro": {
93
+ "name": "Astro",
94
+ "path": "./astro"
95
+ },
96
+ "svelte": {
97
+ "name": "Svelte",
98
+ "path": "./sveltekit"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ The `directus:template` property contains:
106
+
107
+ - `name`: Display name for the template in the CLI
108
+ - `description`: A brief description of the template's purpose and features
109
+ - `template`: Path to the Directus template directory (containing schema, permissions, etc.) - this should point to a template extracted using the `extract` command
110
+ - `frontends`: Object defining available frontend frameworks for this template
111
+ - Each key is a frontend identifier used with the `--frontend` flag
112
+ - Each frontend has a `name` (display name) and `path` (directory containing the frontend code)
113
+
114
+ When you use this template with the `init` command, it will:
115
+ 1. Copy the Directus template files from the specified template directory
116
+ 2. Copy the selected frontend code based on your choice or the `--frontend` flag
117
+ 3. Set up the project structure with both backend and frontend integrated
118
+
119
+ > **Note**: The template directory (`./directus/template` in the example above) should contain a valid Directus template created using the `extract` command. The directory structure should match what is created by the CLI when extracting a template, with subdirectories for schema, permissions, content, etc.
120
+
22
121
  ## Applying a Template
23
122
 
24
123
  🚧 Make backups of your project/database before applying templates.
@@ -59,7 +158,7 @@ npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055"
59
158
 
60
159
  ```
61
160
 
62
- Available flags for programmatic mode:
161
+ Available flags:
63
162
 
64
163
  - `--directusUrl`: URL of the Directus instance to apply the template to (required)
65
164
  - `--directusToken`: Token to use for the Directus instance (required if not using email/password)
@@ -77,6 +176,7 @@ Available flags for programmatic mode:
77
176
  - `--schema`: Load Schema
78
177
  - `--settings`: Load Settings
79
178
  - `--users`: Load Users
179
+ - `--disableTelemetry`: Disable telemetry collection
80
180
 
81
181
  When using `--partial`, you can also use `--no` flags to exclude specific components from being applied. For example:
82
182
 
@@ -130,7 +230,7 @@ You can also pass flags as environment variables. This can be useful for CI/CD p
130
230
  - `TARGET_DIRECTUS_PASSWORD`: Equivalent to `--userPassword`
131
231
  - `TEMPLATE_LOCATION`: Equivalent to `--templateLocation`
132
232
  - `TEMPLATE_TYPE`: Equivalent to `--templateType`
133
- -
233
+
134
234
 
135
235
  ### Existing Data
136
236
 
@@ -142,7 +242,7 @@ In most of the system collections (collections,roles, permissions, etc.), if an
142
242
 
143
243
  Exceptions:
144
244
 
145
- - directus_settings: The CLI attempts to merge the template's project settings with the existing settings in the target instance. Using the existing settings as a base and updating them with the values from the template. This should prevent overwriting branding, themes, and other customizations.
245
+ - `directus_settings`: The CLI attempts to merge the template's project settings with the existing settings in the target instance. Using the existing settings as a base and updating them with the values from the template. This should prevent overwriting branding, themes, and other customizations.
146
246
 
147
247
  **Your Collections:**
148
248
 
@@ -181,7 +281,7 @@ Using email/password:
181
281
  npx directus-template-cli@latest extract -p --templateName="My Template" --templateLocation="./my-template" --userEmail="admin@example.com" --userPassword="admin" --directusUrl="http://localhost:8055"
182
282
  ```
183
283
 
184
- Available flags for programmatic mode:
284
+ Available flags:
185
285
 
186
286
  - `--directusUrl`: URL of the Directus instance to extract the template from (required)
187
287
  - `--directusToken`: Token to use for the Directus instance (required if not using email/password)
@@ -189,6 +289,7 @@ Available flags for programmatic mode:
189
289
  - `--userPassword`: Password for Directus authentication (required if not using token)
190
290
  - `--templateLocation`: Directory to extract the template to (required)
191
291
  - `--templateName`: Name of the template (required)
292
+ - `--disableTelemetry`: Disable telemetry collection
192
293
 
193
294
  #### Using Environment Variables
194
295
 
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class ApplyCommand extends Command {
1
+ import { BaseCommand } from './base.js';
2
+ export default class ApplyCommand extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
@@ -20,6 +20,7 @@ export default class ApplyCommand extends Command {
20
20
  userEmail: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
21
21
  userPassword: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
22
22
  users: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
+ disableTelemetry: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
24
  };
24
25
  /**
25
26
  * MAIN
@@ -1,18 +1,21 @@
1
- import { Command, Flags, ux } from '@oclif/core';
2
- import { text, select, password, log, intro } from '@clack/prompts';
1
+ import { Flags, ux } from '@oclif/core';
2
+ import { text, select, log, intro } from '@clack/prompts';
3
3
  import * as path from 'pathe';
4
4
  import { animatedBunny } from '../lib/utils/animated-bunny.js';
5
5
  import * as customFlags from '../flags/common.js';
6
6
  import { DIRECTUS_PINK, DIRECTUS_PURPLE, SEPARATOR } from '../lib/constants.js';
7
7
  import { validateInteractiveFlags, validateProgrammaticFlags } from '../lib/load/apply-flags.js';
8
8
  import apply from '../lib/load/index.js';
9
- import { getDirectusToken, getDirectusUrl, initializeDirectusApi } from '../lib/utils/auth.js';
9
+ import { getDirectusToken, getDirectusUrl, initializeDirectusApi, getDirectusEmailAndPassword } from '../lib/utils/auth.js';
10
10
  import catchError from '../lib/utils/catch-error.js';
11
11
  import { getCommunityTemplates, getGithubTemplate, getInteractiveLocalTemplate, getLocalTemplate } from '../lib/utils/get-template.js';
12
12
  import { logger } from '../lib/utils/logger.js';
13
13
  import openUrl from '../lib/utils/open-url.js';
14
14
  import chalk from 'chalk';
15
- export default class ApplyCommand extends Command {
15
+ import { BaseCommand } from './base.js';
16
+ import { track, shutdown } from '../services/posthog.js';
17
+ import { BSL_LICENSE_HEADLINE, BSL_LICENSE_TEXT, BSL_LICENSE_CTA } from '../lib/constants.js';
18
+ export default class ApplyCommand extends BaseCommand {
16
19
  static description = 'Apply a template to a blank Directus instance.';
17
20
  static examples = [
18
21
  '$ directus-template-cli apply',
@@ -85,6 +88,7 @@ export default class ApplyCommand extends Command {
85
88
  default: undefined,
86
89
  description: 'Load users',
87
90
  }),
91
+ disableTelemetry: customFlags.disableTelemetry,
88
92
  };
89
93
  /**
90
94
  * MAIN
@@ -164,21 +168,52 @@ export default class ApplyCommand extends Command {
164
168
  validatedFlags.directusToken = directusToken;
165
169
  }
166
170
  else {
167
- const userEmail = await text({
168
- message: 'What is your email?',
169
- });
171
+ const { userEmail, userPassword } = await getDirectusEmailAndPassword();
170
172
  validatedFlags.userEmail = userEmail;
171
- const userPassword = await password({
172
- message: 'What is your password?',
173
- });
174
173
  validatedFlags.userPassword = userPassword;
175
174
  }
176
175
  await initializeDirectusApi(validatedFlags);
177
176
  if (template) {
178
177
  // /* TODO: Replace with custom styledHeader function */ ux.styledHeader(ux.colorize(DIRECTUS_PURPLE, `Applying template - ${template.templateName} to ${directusUrl}`))
178
+ // Track start just before applying
179
+ if (!validatedFlags.disableTelemetry) {
180
+ await track({
181
+ command: 'apply',
182
+ lifecycle: 'start',
183
+ distinctId: this.userConfig.distinctId,
184
+ flags: {
185
+ programmatic: false,
186
+ templateName: template.templateName,
187
+ templateType,
188
+ // Include other relevant flags from validatedFlags if needed
189
+ ...validatedFlags,
190
+ },
191
+ runId: this.runId,
192
+ config: this.config,
193
+ });
194
+ }
179
195
  await apply(template.directoryPath, validatedFlags);
180
196
  ux.action.stop();
197
+ // Track completion before final messages/exit
198
+ if (!validatedFlags.disableTelemetry) {
199
+ await track({
200
+ command: 'apply',
201
+ lifecycle: 'complete',
202
+ distinctId: this.userConfig.distinctId,
203
+ flags: {
204
+ templateName: template.templateName,
205
+ templateType,
206
+ ...validatedFlags,
207
+ },
208
+ runId: this.runId,
209
+ config: this.config,
210
+ });
211
+ await shutdown();
212
+ }
181
213
  ux.stdout(SEPARATOR);
214
+ log.warn(BSL_LICENSE_HEADLINE);
215
+ log.info(BSL_LICENSE_TEXT);
216
+ log.info(BSL_LICENSE_CTA);
182
217
  ux.stdout('Template applied successfully.');
183
218
  ux.exit(0);
184
219
  }
@@ -216,10 +251,45 @@ export default class ApplyCommand extends Command {
216
251
  const logMessage = `Applying template - ${template.templateName} to ${validatedFlags.directusUrl}`;
217
252
  // /* TODO: Replace with custom styledHeader function */ ux.styledHeader(logMessage)
218
253
  logger.log('info', logMessage);
254
+ // Track start just before applying
255
+ if (!validatedFlags.disableTelemetry) {
256
+ await track({
257
+ command: 'apply',
258
+ lifecycle: 'start',
259
+ distinctId: this.userConfig.distinctId,
260
+ flags: {
261
+ programmatic: true,
262
+ templateName: template.templateName,
263
+ // Include other relevant flags from validatedFlags
264
+ ...validatedFlags,
265
+ },
266
+ runId: this.runId,
267
+ config: this.config,
268
+ });
269
+ }
219
270
  await apply(template.directoryPath, validatedFlags);
220
271
  ux.action.stop();
272
+ // Track completion before final messages/exit
273
+ if (!validatedFlags.disableTelemetry) {
274
+ await track({
275
+ command: 'apply',
276
+ lifecycle: 'complete',
277
+ distinctId: this.userConfig.distinctId,
278
+ flags: {
279
+ templateName: template.templateName,
280
+ // Include other relevant flags from validatedFlags
281
+ ...validatedFlags,
282
+ },
283
+ runId: this.runId,
284
+ config: this.config,
285
+ });
286
+ await shutdown();
287
+ }
221
288
  ux.stdout(SEPARATOR);
222
289
  ux.stdout('Template applied successfully.');
290
+ log.warn(BSL_LICENSE_HEADLINE);
291
+ log.info(BSL_LICENSE_TEXT);
292
+ log.info(BSL_LICENSE_CTA);
223
293
  // ux.exit(0)
224
294
  }
225
295
  /**
@@ -6,6 +6,7 @@ export declare abstract class BaseCommand extends Command {
6
6
  runId: string;
7
7
  userConfig: UserConfig;
8
8
  constructor(argv: string[], config: Config);
9
+ init(): Promise<void>;
9
10
  private loadUserConfig;
10
11
  /**
11
12
  * Save the current user configuration to disk
@@ -2,6 +2,7 @@ import { Command } from '@oclif/core';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import fs from 'node:fs';
4
4
  import path from 'pathe';
5
+ import { setExecutionContext } from '../services/execution-context.js';
5
6
  export class BaseCommand extends Command {
6
7
  runId;
7
8
  userConfig = {};
@@ -10,6 +11,18 @@ export class BaseCommand extends Command {
10
11
  this.runId = randomUUID();
11
12
  this.loadUserConfig();
12
13
  }
14
+ async init() {
15
+ await super.init();
16
+ const { flags } = await this.parse({
17
+ flags: this.ctor.flags,
18
+ args: this.ctor.args,
19
+ strict: false,
20
+ });
21
+ setExecutionContext({
22
+ distinctId: this.userConfig.distinctId ?? undefined,
23
+ disableTelemetry: flags?.disableTelemetry ?? false,
24
+ });
25
+ }
13
26
  loadUserConfig() {
14
27
  try {
15
28
  const configPath = path.join(this.config.configDir, 'config.json');
@@ -1,4 +1,4 @@
1
- import { Command } from '@oclif/core';
1
+ import { BaseCommand } from './base.js';
2
2
  export interface ExtractFlags {
3
3
  directusToken: string;
4
4
  directusUrl: string;
@@ -7,8 +7,9 @@ export interface ExtractFlags {
7
7
  templateName: string;
8
8
  userEmail: string;
9
9
  userPassword: string;
10
+ disableTelemetry?: boolean;
10
11
  }
11
- export default class ExtractCommand extends Command {
12
+ export default class ExtractCommand extends BaseCommand {
12
13
  static description: string;
13
14
  static examples: string[];
14
15
  static flags: {
@@ -19,6 +20,7 @@ export default class ExtractCommand extends Command {
19
20
  templateName: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
20
21
  userEmail: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
21
22
  userPassword: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
23
+ disableTelemetry: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
24
  };
23
25
  /**
24
26
  * Main run method for the ExtractCommand
@@ -1,17 +1,19 @@
1
- import { text, password, select, intro } from '@clack/prompts';
2
- import { Command, ux } from '@oclif/core';
1
+ import { text, select, intro, log } from '@clack/prompts';
2
+ import { ux } from '@oclif/core';
3
3
  import slugify from '@sindresorhus/slugify';
4
4
  import chalk from 'chalk';
5
5
  import fs from 'node:fs';
6
6
  import path from 'pathe';
7
7
  import * as customFlags from '../flags/common.js';
8
- import { DIRECTUS_PINK, DIRECTUS_PURPLE, SEPARATOR } from '../lib/constants.js';
8
+ import { DIRECTUS_PINK, DIRECTUS_PURPLE, SEPARATOR, BSL_LICENSE_TEXT, BSL_LICENSE_CTA, BSL_LICENSE_HEADLINE } from '../lib/constants.js';
9
9
  import { animatedBunny } from '../lib/utils/animated-bunny.js';
10
+ import { BaseCommand } from './base.js';
11
+ import { track, shutdown } from '../services/posthog.js';
10
12
  import extract from '../lib/extract/index.js';
11
- import { getDirectusToken, getDirectusUrl, initializeDirectusApi, validateAuthFlags } from '../lib/utils/auth.js';
13
+ import { getDirectusToken, getDirectusUrl, initializeDirectusApi, validateAuthFlags, getDirectusEmailAndPassword } from '../lib/utils/auth.js';
12
14
  import catchError from '../lib/utils/catch-error.js';
13
15
  import { generatePackageJsonContent, generateReadmeContent, } from '../lib/utils/template-defaults.js';
14
- export default class ExtractCommand extends Command {
16
+ export default class ExtractCommand extends BaseCommand {
15
17
  static description = 'Extract a template from a Directus instance.';
16
18
  static examples = [
17
19
  '$ directus-template-cli extract',
@@ -25,6 +27,7 @@ export default class ExtractCommand extends Command {
25
27
  templateName: customFlags.templateName,
26
28
  userEmail: customFlags.userEmail,
27
29
  userPassword: customFlags.userPassword,
30
+ disableTelemetry: customFlags.disableTelemetry,
28
31
  };
29
32
  /**
30
33
  * Main run method for the ExtractCommand
@@ -43,6 +46,22 @@ export default class ExtractCommand extends Command {
43
46
  * @returns {Promise<void>} - Returns nothing
44
47
  */
45
48
  async extractTemplate(templateName, directory, flags) {
49
+ // Track start of extraction attempt
50
+ if (!flags.disableTelemetry) {
51
+ await track({
52
+ command: 'extract',
53
+ lifecycle: 'start',
54
+ distinctId: this.userConfig.distinctId,
55
+ flags: {
56
+ templateName,
57
+ templateLocation: directory,
58
+ directusUrl: flags.directusUrl,
59
+ programmatic: flags.programmatic,
60
+ },
61
+ runId: this.runId,
62
+ config: this.config,
63
+ });
64
+ }
46
65
  try {
47
66
  if (!fs.existsSync(directory)) {
48
67
  fs.mkdirSync(directory, { recursive: true });
@@ -65,6 +84,26 @@ export default class ExtractCommand extends Command {
65
84
  ux.action.start(`Extracting template - ${ux.colorize(DIRECTUS_PINK, templateName)} from ${ux.colorize(DIRECTUS_PINK, flags.directusUrl)} to ${ux.colorize(DIRECTUS_PINK, directory)}`);
66
85
  await extract(directory);
67
86
  ux.action.stop();
87
+ // Track completion before final messages/exit
88
+ if (!flags.disableTelemetry) {
89
+ await track({
90
+ command: 'extract',
91
+ lifecycle: 'complete',
92
+ distinctId: this.userConfig.distinctId,
93
+ flags: {
94
+ templateName,
95
+ templateLocation: directory,
96
+ directusUrl: flags.directusUrl,
97
+ programmatic: flags.programmatic,
98
+ },
99
+ runId: this.runId,
100
+ config: this.config,
101
+ });
102
+ await shutdown();
103
+ }
104
+ log.warn(BSL_LICENSE_HEADLINE);
105
+ log.info(BSL_LICENSE_TEXT);
106
+ log.info(BSL_LICENSE_CTA);
68
107
  ux.stdout(SEPARATOR);
69
108
  ux.stdout('Template extracted successfully.');
70
109
  this.exit(0);
@@ -104,13 +143,8 @@ export default class ExtractCommand extends Command {
104
143
  flags.directusToken = directusToken;
105
144
  }
106
145
  else {
107
- const email = await text({
108
- message: 'What is your email?',
109
- });
110
- flags.userEmail = email;
111
- const userPassword = await password({
112
- message: 'What is our password?',
113
- });
146
+ const { userEmail, userPassword } = await getDirectusEmailAndPassword();
147
+ flags.userEmail = userEmail;
114
148
  flags.userPassword = userPassword;
115
149
  }
116
150
  ux.stdout(SEPARATOR);
@@ -3,7 +3,7 @@ export interface InitFlags {
3
3
  frontend?: string;
4
4
  gitInit?: boolean;
5
5
  installDeps?: boolean;
6
- overrideDir?: boolean;
6
+ overwriteDir?: boolean;
7
7
  template?: string;
8
8
  disableTelemetry?: boolean;
9
9
  }
@@ -22,7 +22,7 @@ export default class InitCommand extends BaseCommand {
22
22
  frontend: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
23
23
  gitInit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
24
24
  installDeps: import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
- overrideDir: import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
+ overwriteDir: import("@oclif/core/interfaces").BooleanFlag<boolean>;
26
26
  template: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
27
27
  disableTelemetry: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
28
  };
@@ -1,4 +1,4 @@
1
- import { confirm, intro, select, text, isCancel, cancel } from '@clack/prompts';
1
+ import { confirm, intro, select, text, isCancel, cancel, log as clackLog } from '@clack/prompts';
2
2
  import { Args, Flags, ux } from '@oclif/core';
3
3
  import chalk from 'chalk';
4
4
  import fs from 'node:fs';
@@ -45,7 +45,9 @@ export default class InitCommand extends BaseCommand {
45
45
  default: true,
46
46
  description: 'Install dependencies automatically',
47
47
  }),
48
- overrideDir: Flags.boolean({
48
+ overwriteDir: Flags.boolean({
49
+ aliases: ['overwrite-dir'],
50
+ allowNo: true,
49
51
  default: false,
50
52
  description: 'Override the default directory',
51
53
  }),
@@ -81,7 +83,7 @@ export default class InitCommand extends BaseCommand {
81
83
  const github = createGitHub();
82
84
  // If no dir is provided, ask for it
83
85
  if (!args.directory || args.directory === '.') {
84
- const dirResponse = await text({
86
+ let dirResponse = await text({
85
87
  message: 'Enter the directory to create the project in:',
86
88
  placeholder: './my-directus-project',
87
89
  });
@@ -89,30 +91,38 @@ export default class InitCommand extends BaseCommand {
89
91
  cancel('Project creation cancelled.');
90
92
  process.exit(0);
91
93
  }
94
+ // If there's no response, set a default
95
+ if (!dirResponse) {
96
+ clackLog.warn('No directory provided, using default: ./my-directus-project');
97
+ dirResponse = './my-directus-project';
98
+ }
92
99
  this.targetDir = dirResponse;
93
100
  }
94
- if (fs.existsSync(this.targetDir) && !flags.overrideDir) {
95
- const overrideDirResponse = await confirm({
101
+ if (fs.existsSync(this.targetDir) && !flags.overwriteDir) {
102
+ const overwriteDirResponse = await confirm({
96
103
  message: 'Directory already exists. Would you like to overwrite it?',
104
+ initialValue: false,
97
105
  });
98
- if (isCancel(overrideDirResponse)) {
106
+ if (isCancel(overwriteDirResponse) || overwriteDirResponse === false) {
99
107
  cancel('Project creation cancelled.');
100
108
  process.exit(0);
101
109
  }
102
- if (overrideDirResponse) {
103
- flags.overrideDir = true;
110
+ if (overwriteDirResponse) {
111
+ flags.overwriteDir = true;
104
112
  }
105
113
  }
106
- // 1. Fetch available templates
114
+ // 1. Fetch available templates (now returns Array<{id: string, name: string, description?: string}>)
107
115
  const availableTemplates = await github.getTemplates();
108
116
  // 2. Prompt for template if not provided
109
- let { template } = flags;
117
+ let { template } = flags; // This will store the chosen template ID
118
+ let chosenTemplateObject;
110
119
  if (!template) {
111
120
  const templateResponse = await select({
112
121
  message: 'Which Directus backend template would you like to use?',
113
- options: availableTemplates.map(template => ({
114
- label: template,
115
- value: template,
122
+ options: availableTemplates.map(tmpl => ({
123
+ value: tmpl.id, // The value submitted will be the ID (directory name)
124
+ label: tmpl.name, // Display the friendly name
125
+ hint: tmpl.description, // Show the description as a hint
116
126
  })),
117
127
  });
118
128
  if (isCancel(templateResponse)) {
@@ -121,22 +131,25 @@ export default class InitCommand extends BaseCommand {
121
131
  }
122
132
  template = templateResponse;
123
133
  }
124
- // 3. Validate that the template exists, fetch subdirectories
125
- let directories = await github.getTemplateDirectories(template);
134
+ // Find the chosen template object for potential future use (e.g., display name later)
135
+ chosenTemplateObject = availableTemplates.find(t => t.id === template);
136
+ // 3. Validate that the template exists in the available list
126
137
  const isDirectUrl = template?.startsWith('http');
127
- while (!isDirectUrl && directories.length === 0) {
128
- this.log(`Template "${template}" doesn't seem to exist in directus-labs/directus-starters.`);
138
+ // Validate against the 'id' property of the template objects
139
+ while (!isDirectUrl && !availableTemplates.some(t => t.id === template)) {
140
+ // Keep the warning message simple or refer back to the list shown in the prompt
141
+ clackLog.warn(`Template ID "${template}" is not valid. Please choose from the list provided or enter a direct GitHub URL.`);
129
142
  const templateNameResponse = await text({
130
- message: 'Please enter a valid template name, or Ctrl+C to cancel:',
143
+ message: 'Please enter a valid template ID, a direct GitHub URL, or Ctrl+C to cancel:',
131
144
  });
132
145
  if (isCancel(templateNameResponse)) {
133
146
  cancel('Project creation cancelled.');
134
147
  process.exit(0);
135
148
  }
136
149
  template = templateNameResponse;
137
- directories = await github.getTemplateDirectories(template);
150
+ chosenTemplateObject = availableTemplates.find(t => t.id === template); // Update chosen object after re-entry
138
151
  }
139
- flags.template = template;
152
+ flags.template = template; // Ensure the flag stores the ID
140
153
  // Download the template to a temporary directory to read its configuration
141
154
  const tempDir = path.join(os.tmpdir(), `directus-template-${Date.now()}`);
142
155
  let chosenFrontend = flags.frontend;
@@ -215,7 +228,7 @@ export default class InitCommand extends BaseCommand {
215
228
  gitInit: initGit,
216
229
  installDeps,
217
230
  template,
218
- overrideDir: flags.overrideDir,
231
+ overwriteDir: flags.overwriteDir,
219
232
  },
220
233
  });
221
234
  // Track the command completion unless telemetry is disabled
@@ -229,7 +242,7 @@ export default class InitCommand extends BaseCommand {
229
242
  gitInit: initGit,
230
243
  installDeps,
231
244
  template,
232
- overrideDir: flags.overrideDir,
245
+ overwriteDir: flags.overwriteDir,
233
246
  },
234
247
  runId: this.runId,
235
248
  config: this.config,
@@ -1,6 +1,8 @@
1
1
  export declare const DIRECTUS_PURPLE = "#6644ff";
2
2
  export declare const DIRECTUS_PINK = "#FF99DD";
3
3
  export declare const SEPARATOR = "------------------";
4
+ export declare const pinkText: import("chalk").ChalkInstance;
5
+ export declare const purpleText: import("chalk").ChalkInstance;
4
6
  export declare const COMMUNITY_TEMPLATE_REPO: {
5
7
  string: string;
6
8
  url: string;
@@ -15,3 +17,9 @@ export declare const DEFAULT_REPO: {
15
17
  export declare const POSTHOG_PUBLIC_KEY = "phc_STopE6gj6LDIjYonVF7493kQJK8S4v0Xrl6YPr2z9br";
16
18
  export declare const POSTHOG_HOST = "https://us.i.posthog.com";
17
19
  export declare const DEFAULT_BRANCH = "main";
20
+ export declare const BSL_LICENSE_URL = "https://directus.io/bsl";
21
+ export declare const BSL_EMAIL = "licensing@directus.io";
22
+ export declare const BSL_LICENSE_HEADLINE = "You REQUIRE a license to use Directus if your organization has more than $5MM USD a year in revenue and/or funding.";
23
+ export declare const BSL_LICENSE_TEXT = "For all organizations with less than $5MM USD a year in revenue and funding, Directus is free for personal projects, hobby projects and in production. This second group does not require a license. Directus is licensed under BSL 1.1.";
24
+ export declare const BSL_LICENSE_CTA: string;
25
+ export declare const DEFAULT_DIRECTUS_URL = "http://localhost:8055";