directus-template-cli 0.7.4 → 0.7.6

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.
@@ -1,6 +1,7 @@
1
- import { confirm, intro, select, text, isCancel, cancel, log as clackLog } from '@clack/prompts';
2
- import { Args, Flags, ux } from '@oclif/core';
1
+ import { cancel, log as clackLog, confirm, intro, isCancel, select, text } from '@clack/prompts';
2
+ import { Args, Flags } from '@oclif/core';
3
3
  import chalk from 'chalk';
4
+ import { downloadTemplate } from 'giget';
4
5
  import fs from 'node:fs';
5
6
  import os from 'node:os';
6
7
  import path from 'pathe';
@@ -8,12 +9,11 @@ import { disableTelemetry } from '../flags/common.js';
8
9
  import { DIRECTUS_PURPLE } from '../lib/constants.js';
9
10
  import { init } from '../lib/init/index.js';
10
11
  import { animatedBunny } from '../lib/utils/animated-bunny.js';
11
- import { createGitHub } from '../services/github.js';
12
- import { readTemplateConfig } from '../lib/utils/template-config.js';
13
12
  import { createGigetString, parseGitHubUrl } from '../lib/utils/parse-github-url.js';
14
- import { downloadTemplate } from 'giget';
13
+ import { readTemplateConfig } from '../lib/utils/template-config.js';
14
+ import { createGitHub } from '../services/github.js';
15
+ import { shutdown, track } from '../services/posthog.js';
15
16
  import { BaseCommand } from './base.js';
16
- import { track, shutdown } from '../services/posthog.js';
17
17
  export default class InitCommand extends BaseCommand {
18
18
  static args = {
19
19
  directory: Args.directory({
@@ -30,6 +30,7 @@ export default class InitCommand extends BaseCommand {
30
30
  '$ directus-template-cli init my-project --frontend=nextjs --template=simple-cms',
31
31
  ];
32
32
  static flags = {
33
+ disableTelemetry,
33
34
  frontend: Flags.string({
34
35
  description: 'Frontend framework to use (e.g., nextjs, nuxt, astro)',
35
36
  }),
@@ -54,7 +55,6 @@ export default class InitCommand extends BaseCommand {
54
55
  template: Flags.string({
55
56
  description: 'Template name (e.g., simple-cms) or GitHub URL (e.g., https://github.com/directus-labs/starters/tree/main/simple-cms)',
56
57
  }),
57
- disableTelemetry: disableTelemetry,
58
58
  };
59
59
  targetDir = '.';
60
60
  /**
@@ -79,6 +79,15 @@ export default class InitCommand extends BaseCommand {
79
79
  // Show animated intro
80
80
  await animatedBunny('Let\'s create a new Directus project!');
81
81
  intro(`${chalk.bgHex(DIRECTUS_PURPLE).white.bold('Directus Template CLI')} - Create Project`);
82
+ // Check Docker availability before proceeding
83
+ const { createDocker } = await import('../services/docker.js');
84
+ const { DOCKER_CONFIG } = await import('../lib/init/config.js');
85
+ const dockerService = createDocker(DOCKER_CONFIG);
86
+ const dockerStatus = await dockerService.checkDocker();
87
+ if (!dockerStatus.installed || !dockerStatus.running) {
88
+ cancel(dockerStatus.message || 'Docker is required to initialize a Directus project.');
89
+ process.exit(1);
90
+ }
82
91
  // Create GitHub service
83
92
  const github = createGitHub();
84
93
  // If no dir is provided, ask for it
@@ -100,8 +109,8 @@ export default class InitCommand extends BaseCommand {
100
109
  }
101
110
  if (fs.existsSync(this.targetDir) && !flags.overwriteDir) {
102
111
  const overwriteDirResponse = await confirm({
103
- message: 'Directory already exists. Would you like to overwrite it?',
104
112
  initialValue: false,
113
+ message: 'Directory already exists. Would you like to overwrite it?',
105
114
  });
106
115
  if (isCancel(overwriteDirResponse) || overwriteDirResponse === false) {
107
116
  cancel('Project creation cancelled.');
@@ -120,9 +129,9 @@ export default class InitCommand extends BaseCommand {
120
129
  const templateResponse = await select({
121
130
  message: 'Which Directus backend template would you like to use?',
122
131
  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
132
  hint: tmpl.description, // Show the description as a hint
133
+ label: tmpl.name, // Display the friendly name
134
+ value: tmpl.id, // The value submitted will be the ID (directory name)
126
135
  })),
127
136
  });
128
137
  if (isCancel(templateResponse)) {
@@ -164,13 +173,12 @@ export default class InitCommand extends BaseCommand {
164
173
  if (templateInfo?.frontendOptions.length > 0 && (!chosenFrontend || !templateInfo.frontendOptions.find(f => f.id === chosenFrontend))) {
165
174
  const frontendResponse = await select({
166
175
  message: 'Which frontend framework do you want to use?',
167
- options: [
168
- ...templateInfo.frontendOptions.map(frontend => ({
169
- label: frontend.name,
170
- value: frontend.id,
171
- })),
172
- // { label: 'No frontend', value: '' },
173
- ],
176
+ options: templateInfo.frontendOptions.map(frontend => ({
177
+ label: frontend.name,
178
+ value: frontend.id,
179
+ }))
180
+ // { label: 'No frontend', value: '' },
181
+ ,
174
182
  });
175
183
  if (isCancel(frontendResponse)) {
176
184
  cancel('Project creation cancelled.');
@@ -183,7 +191,7 @@ export default class InitCommand extends BaseCommand {
183
191
  finally {
184
192
  // Clean up temporary directory
185
193
  if (fs.existsSync(tempDir)) {
186
- fs.rmSync(tempDir, { recursive: true, force: true });
194
+ fs.rmSync(tempDir, { force: true, recursive: true });
187
195
  }
188
196
  }
189
197
  const installDepsResponse = await confirm({
@@ -207,17 +215,17 @@ export default class InitCommand extends BaseCommand {
207
215
  // Track the command start unless telemetry is disabled
208
216
  if (!flags.disableTelemetry) {
209
217
  await track({
210
- lifecycle: 'start',
211
- distinctId: this.userConfig.distinctId,
212
218
  command: 'init',
219
+ config: this.config,
220
+ distinctId: this.userConfig.distinctId,
213
221
  flags: {
214
222
  frontend: chosenFrontend,
215
223
  gitInit: initGit,
216
224
  installDeps,
217
225
  template,
218
226
  },
227
+ lifecycle: 'start',
219
228
  runId: this.runId,
220
- config: this.config,
221
229
  });
222
230
  }
223
231
  // Initialize the project
@@ -227,28 +235,28 @@ export default class InitCommand extends BaseCommand {
227
235
  frontend: chosenFrontend,
228
236
  gitInit: initGit,
229
237
  installDeps,
230
- template,
231
238
  overwriteDir: flags.overwriteDir,
239
+ template,
232
240
  },
233
241
  });
234
242
  // Track the command completion unless telemetry is disabled
235
243
  if (!flags.disableTelemetry) {
236
244
  await track({
237
245
  command: 'init',
238
- lifecycle: 'complete',
246
+ config: this.config,
239
247
  distinctId: this.userConfig.distinctId,
240
248
  flags: {
241
249
  frontend: chosenFrontend,
242
250
  gitInit: initGit,
243
251
  installDeps,
244
- template,
245
252
  overwriteDir: flags.overwriteDir,
253
+ template,
246
254
  },
255
+ lifecycle: 'complete',
247
256
  runId: this.runId,
248
- config: this.config,
249
257
  });
250
258
  await shutdown();
251
259
  }
252
- ux.exit(0);
260
+ process.exit(0);
253
261
  }
254
262
  }
@@ -1,19 +1,19 @@
1
- import { note, outro, spinner, log as clackLog } from '@clack/prompts';
1
+ import { log as clackLog, note, outro, spinner } from '@clack/prompts';
2
2
  import { ux } from '@oclif/core';
3
+ import dotenv from 'dotenv';
3
4
  import { execa } from 'execa';
4
5
  import { downloadTemplate } from 'giget';
5
6
  import { glob } from 'glob';
6
7
  import fs from 'node:fs';
7
8
  import { detectPackageManager, installDependencies } from 'nypm';
8
9
  import path from 'pathe';
9
- import dotenv from 'dotenv';
10
10
  import ApplyCommand from '../../commands/apply.js';
11
11
  import { createDocker } from '../../services/docker.js';
12
+ import { BSL_LICENSE_CTA, BSL_LICENSE_HEADLINE, BSL_LICENSE_TEXT, pinkText } from '../constants.js';
12
13
  import catchError from '../utils/catch-error.js';
13
14
  import { createGigetString, parseGitHubUrl } from '../utils/parse-github-url.js';
14
15
  import { readTemplateConfig } from '../utils/template-config.js';
15
16
  import { DOCKER_CONFIG } from './config.js';
16
- import { BSL_LICENSE_TEXT, BSL_LICENSE_HEADLINE, BSL_LICENSE_CTA, pinkText } from '../constants.js';
17
17
  export async function init({ dir, flags }) {
18
18
  // Check target directory
19
19
  const shouldForce = flags.overwriteDir;
@@ -36,15 +36,13 @@ export async function init({ dir, flags }) {
36
36
  });
37
37
  // For direct URLs, we need to check if there's a directus directory
38
38
  // If not, assume the entire repo is a directus template
39
- if (isDirectUrl) {
40
- if (!fs.existsSync(directusDir)) {
41
- // Move all files to directus directory
42
- fs.mkdirSync(directusDir, { recursive: true });
43
- const files = fs.readdirSync(dir);
44
- for (const file of files) {
45
- if (file !== 'directus') {
46
- fs.renameSync(path.join(dir, file), path.join(directusDir, file));
47
- }
39
+ if (isDirectUrl && !fs.existsSync(directusDir)) {
40
+ // Move all files to directus directory
41
+ fs.mkdirSync(directusDir, { recursive: true });
42
+ const files = fs.readdirSync(dir);
43
+ for (const file of files) {
44
+ if (file !== 'directus') {
45
+ fs.renameSync(path.join(dir, file), path.join(directusDir, file));
48
46
  }
49
47
  }
50
48
  }
@@ -120,6 +118,7 @@ export async function init({ dir, flags }) {
120
118
  await ApplyCommand.run([
121
119
  `--directusUrl=${directusInfo.url || 'http://localhost:8055'}`,
122
120
  '-p',
121
+ '--noExit',
123
122
  `--userEmail=${directusInfo.email}`,
124
123
  `--userPassword=${directusInfo.password}`,
125
124
  `--templateLocation=${templatePath}`,
@@ -161,7 +160,9 @@ export async function init({ dir, flags }) {
161
160
  const relativeDir = path.relative(process.cwd(), dir);
162
161
  const directusUrl = directusInfo.url ?? 'http://localhost:8055';
163
162
  const directusText = `- Directus is running on ${directusUrl}. \n`;
164
- const directusLoginText = `- You can login with the email: ${pinkText(directusInfo.email)} and password: ${pinkText(directusInfo.password)}. \n`;
163
+ const directusLoginText = directusInfo.email && directusInfo.password
164
+ ? `- You can login with the email: ${pinkText(directusInfo.email)} and password: ${pinkText(directusInfo.password)}. \n`
165
+ : `- Complete the onboarding form at ${pinkText(directusInfo.url || 'http://localhost:8055')} to create your admin account. \n`;
165
166
  const frontendText = flags.frontend ? `- To start the frontend, run ${pinkText(`cd ${flags.frontend}`)} and then ${pinkText(`${packageManager?.name} run dev`)}. \n` : '';
166
167
  const projectText = `- Navigate to your project directory using ${pinkText(`cd ${relativeDir}`)}. \n`;
167
168
  const readmeText = '- Review the \`./README.md\` file for more information and next steps.';
@@ -3,9 +3,11 @@ export interface ApplyFlags {
3
3
  dashboards: boolean;
4
4
  directusToken: string;
5
5
  directusUrl: string;
6
+ disableTelemetry?: boolean;
6
7
  extensions: boolean;
7
8
  files: boolean;
8
9
  flows: boolean;
10
+ noExit?: boolean;
9
11
  partial: boolean;
10
12
  permissions: boolean;
11
13
  programmatic: boolean;
@@ -16,7 +18,6 @@ export interface ApplyFlags {
16
18
  userEmail: string;
17
19
  userPassword: string;
18
20
  users?: boolean;
19
- disableTelemetry?: boolean;
20
21
  }
21
22
  export declare const loadFlags: readonly ["content", "dashboards", "extensions", "files", "flows", "permissions", "schema", "settings", "users"];
22
23
  export declare function validateProgrammaticFlags(flags: ApplyFlags): ApplyFlags;
@@ -36,11 +36,9 @@ const removeRequiredorIsNullable = (field) => {
36
36
  field.meta.required = false;
37
37
  }
38
38
  if (field.schema?.is_nullable === false) {
39
- // eslint-disable-next-line camelcase
40
39
  field.schema.is_nullable = true;
41
40
  }
42
41
  if (field.schema?.is_unique === true) {
43
- // eslint-disable-next-line camelcase
44
42
  field.schema.is_unique = false;
45
43
  }
46
44
  return field;
@@ -20,12 +20,10 @@ const customDefu = createDefu((obj, key, value) => {
20
20
  });
21
21
  function mergeArrays(key, current, incoming) {
22
22
  const mergeKeys = {
23
- /* eslint-disable camelcase */
24
23
  basemaps: ['key'],
25
24
  custom_aspect_ratios: ['key'],
26
25
  module_bar: ['id', 'type'],
27
26
  storage_asset_presets: ['key'],
28
- /* eslint-enable camelcase */
29
27
  };
30
28
  const keys = mergeKeys[key];
31
29
  if (!keys)
package/dist/lib/sdk.d.ts CHANGED
@@ -14,7 +14,7 @@ export declare class DirectusError extends Error {
14
14
  parseErrors(): Promise<void>;
15
15
  }
16
16
  declare class Api {
17
- client: (RestClient<Schema> & AuthenticationClient<Schema>) | undefined;
17
+ client: (AuthenticationClient<Schema> & RestClient<Schema>) | undefined;
18
18
  private authData;
19
19
  private limiter;
20
20
  constructor();
@@ -11,10 +11,10 @@ export interface ExtensionSchema {
11
11
  name: string;
12
12
  type: ExtensionType;
13
13
  }>;
14
- entrypoint: {
14
+ entrypoint: string | {
15
15
  api: string;
16
16
  app: string;
17
- } | string;
17
+ };
18
18
  host: string;
19
19
  local: boolean;
20
20
  name: string;
@@ -3,16 +3,16 @@ export interface DirectusTemplateFrontend {
3
3
  path: string;
4
4
  }
5
5
  export interface DirectusTemplateConfig {
6
- name: string;
7
6
  description: string;
8
- template: string | null;
9
7
  frontends: {
10
8
  [key: string]: DirectusTemplateFrontend;
11
9
  };
10
+ name: string;
11
+ template: null | string;
12
12
  }
13
13
  export interface TemplatePackageJson {
14
- name: string;
15
- version: string;
16
14
  description: string;
17
15
  'directus:template'?: DirectusTemplateConfig;
16
+ name: string;
17
+ version: string;
18
18
  }
@@ -50,8 +50,8 @@ export async function animatedBunny(customMessage) {
50
50
  }
51
51
  }, 25);
52
52
  try {
53
- // Run the animation for the duration of typing plus 1 second
54
- await new Promise(resolve => setTimeout(resolve, saying.length * 25 + 1000));
53
+ // Run the animation for the duration of typing plus 500ms
54
+ await new Promise(resolve => setTimeout(resolve, saying.length * 25 + 500));
55
55
  }
56
56
  finally {
57
57
  cleanup();
@@ -23,12 +23,12 @@ export declare function getDirectusEmailAndPassword(): Promise<{
23
23
  * Initialize the Directus API with the provided flags and log in the user
24
24
  * @param flags - The validated ApplyFlags
25
25
  * @returns {Promise<void>} - Returns nothing
26
- */
26
+ */
27
27
  export declare function initializeDirectusApi(flags: AuthFlags): Promise<void>;
28
28
  /**
29
- * Validate the authentication flags
30
- * @param flags - The AuthFlags
31
- * @returns {void} - Errors if the flags are invalid
32
- */
29
+ * Validate the authentication flags
30
+ * @param flags - The AuthFlags
31
+ * @returns {void} - Errors if the flags are invalid
32
+ */
33
33
  export declare function validateAuthFlags(flags: AuthFlags): void;
34
34
  export {};
@@ -1,22 +1,23 @@
1
+ import { isCancel, log, password, text } from '@clack/prompts';
1
2
  import { readMe } from '@directus/sdk';
2
- import { text, log, isCancel, password } from '@clack/prompts';
3
3
  import { ux } from '@oclif/core';
4
+ import process from 'node:process';
5
+ import { DEFAULT_DIRECTUS_URL } from '../../lib/constants.js';
4
6
  import { api } from '../sdk.js';
5
7
  import catchError from './catch-error.js';
6
8
  import validateUrl from './validate-url.js';
7
- import { DEFAULT_DIRECTUS_URL } from '../../lib/constants.js';
8
9
  /**
9
10
  * Get the Directus URL from the user
10
11
  * @returns The Directus URL
11
12
  */
12
13
  export async function getDirectusUrl() {
13
14
  const directusUrl = await text({
14
- placeholder: DEFAULT_DIRECTUS_URL,
15
15
  message: 'What is your Directus URL?',
16
+ placeholder: DEFAULT_DIRECTUS_URL,
16
17
  });
17
18
  if (isCancel(directusUrl)) {
18
19
  log.info('Exiting...');
19
- ux.exit(0);
20
+ process.exit(0);
20
21
  }
21
22
  if (!directusUrl) {
22
23
  ux.warn(`No URL provided, using default: ${DEFAULT_DIRECTUS_URL}`);
@@ -37,12 +38,12 @@ export async function getDirectusUrl() {
37
38
  */
38
39
  export async function getDirectusToken(directusUrl) {
39
40
  const directusToken = await text({
40
- placeholder: 'admin-token-here',
41
41
  message: 'What is your Directus Admin Token?',
42
+ placeholder: 'admin-token-here',
42
43
  });
43
44
  if (isCancel(directusToken)) {
44
45
  log.info('Exiting...');
45
- ux.exit(0);
46
+ process.exit(0);
46
47
  }
47
48
  // Validate token by fetching the user
48
49
  try {
@@ -72,7 +73,7 @@ export async function getDirectusEmailAndPassword() {
72
73
  });
73
74
  if (isCancel(userEmail)) {
74
75
  log.info('Exiting...');
75
- ux.exit(0);
76
+ process.exit(0);
76
77
  }
77
78
  const userPassword = await password({
78
79
  message: 'What is your password?',
@@ -84,7 +85,7 @@ export async function getDirectusEmailAndPassword() {
84
85
  });
85
86
  if (isCancel(userPassword)) {
86
87
  log.info('Exiting...');
87
- ux.exit(0);
88
+ process.exit(0);
88
89
  }
89
90
  return { userEmail, userPassword };
90
91
  }
@@ -92,7 +93,7 @@ export async function getDirectusEmailAndPassword() {
92
93
  * Initialize the Directus API with the provided flags and log in the user
93
94
  * @param flags - The validated ApplyFlags
94
95
  * @returns {Promise<void>} - Returns nothing
95
- */
96
+ */
96
97
  export async function initializeDirectusApi(flags) {
97
98
  api.initialize(flags.directusUrl);
98
99
  try {
@@ -112,10 +113,10 @@ export async function initializeDirectusApi(flags) {
112
113
  }
113
114
  }
114
115
  /**
115
- * Validate the authentication flags
116
- * @param flags - The AuthFlags
117
- * @returns {void} - Errors if the flags are invalid
118
- */
116
+ * Validate the authentication flags
117
+ * @param flags - The AuthFlags
118
+ * @returns {void} - Errors if the flags are invalid
119
+ */
119
120
  export function validateAuthFlags(flags) {
120
121
  if (!flags.directusUrl) {
121
122
  ux.error('Directus URL is required.');
@@ -1,8 +1,8 @@
1
1
  import { ux } from '@oclif/core';
2
+ import { getExecutionContext } from '../../services/execution-context.js';
3
+ import { captureException } from '../../services/posthog.js';
2
4
  import { DirectusError } from '../sdk.js';
3
5
  import { logger } from '../utils/logger.js';
4
- import { captureException } from '../../services/posthog.js';
5
- import { getExecutionContext } from '../../services/execution-context.js';
6
6
  /**
7
7
  * Handles errors by formatting them and optionally logging to console and file.
8
8
  * @param error - The error to be handled.
@@ -11,7 +11,7 @@ import { getExecutionContext } from '../../services/execution-context.js';
11
11
  */
12
12
  export default function catchError(error, options = {}) {
13
13
  const { context = {}, fatal = false, logToFile = true } = options;
14
- const { distinctId, disableTelemetry } = getExecutionContext();
14
+ const { disableTelemetry, distinctId } = getExecutionContext();
15
15
  let errorMessage;
16
16
  if (error instanceof DirectusError) {
17
17
  errorMessage = error.message;
@@ -24,7 +24,7 @@ export default function catchError(error, options = {}) {
24
24
  }
25
25
  // Capture exception before logging/exiting
26
26
  if (!disableTelemetry && distinctId) {
27
- captureException({ error, distinctId, properties: { context } });
27
+ captureException({ distinctId, error, properties: { context } });
28
28
  }
29
29
  // Format the error message with context if provided
30
30
  const formattedMessage = [
@@ -30,7 +30,7 @@ class Logger {
30
30
  this.writeToFile(`Log started at ${timestamp}\n`);
31
31
  }
32
32
  sanitize(obj) {
33
- const sensitiveFields = new Set(['password', 'token', 'secret', 'key', 'authorization', 'email', 'access_token', 'refresh_token']);
33
+ const sensitiveFields = new Set(['access_token', 'authorization', 'email', 'key', 'password', 'refresh_token', 'secret', 'token']);
34
34
  return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
35
35
  if (sensitiveFields.has(key.toLowerCase())) {
36
36
  return [key, '********'];
@@ -25,7 +25,7 @@ function cleanGitHubUrl(url) {
25
25
  parsed.hash = '';
26
26
  return parsed.toString();
27
27
  }
28
- catch (error) {
28
+ catch {
29
29
  // If URL parsing fails, just remove .git suffix
30
30
  return url.replace(/\.git$/, '');
31
31
  }
@@ -80,7 +80,7 @@ export function parseGitHubUrl(url) {
80
80
  // Ensure path is undefined if empty string
81
81
  if (path === '')
82
82
  path = undefined;
83
- return { owner, repo, path, ref };
83
+ return { owner, path, ref, repo };
84
84
  }
85
85
  catch (error) {
86
86
  throw new Error(`Invalid GitHub URL: ${url}. Error: ${error.message}`);
@@ -92,7 +92,7 @@ export function parseGitHubUrl(url) {
92
92
  const [owner, repo, ...rest] = parts;
93
93
  const path = rest.length > 0 ? rest.join('/') : undefined;
94
94
  // Assume default branch for simple paths unless we add ref detection here too
95
- return { owner, repo, path, ref: DEFAULT_BRANCH };
95
+ return { owner, path, ref: DEFAULT_BRANCH, repo };
96
96
  }
97
97
  // Handle simple template names using DEFAULT_REPO
98
98
  return {
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
- import path from 'pathe';
3
2
  import { cwd } from 'node:process';
3
+ import path from 'pathe';
4
4
  /**
5
5
  * Resolves a given path to an absolute path and checks if it exists.
6
6
  * @param inputPath The path to resolve.
@@ -2,6 +2,6 @@ interface Template {
2
2
  directoryPath: string;
3
3
  templateName: string;
4
4
  }
5
- export declare function readTemplate(directoryPath: string): Promise<Template | null>;
5
+ export declare function readTemplate(directoryPath: string): Promise<null | Template>;
6
6
  export declare function readAllTemplates(directoryPath: string): Promise<Template[]>;
7
7
  export {};
@@ -1,4 +1,2 @@
1
- const SENSITIVE_FLAGS = ['userEmail', 'userPassword', 'directusToken'];
2
- export const sanitizeFlags = (flags) => {
3
- return Object.fromEntries(Object.entries(flags).filter(([key]) => !SENSITIVE_FLAGS.includes(key)));
4
- };
1
+ const SENSITIVE_FLAGS = new Set(['directusToken', 'userEmail', 'userPassword']);
2
+ export const sanitizeFlags = (flags) => Object.fromEntries(Object.entries(flags).filter(([key]) => !SENSITIVE_FLAGS.has(key)));
@@ -13,4 +13,4 @@ export interface TemplateInfo {
13
13
  * @returns Template configuration and frontend options
14
14
  * @throws Error if package.json is missing or invalid
15
15
  */
16
- export declare function readTemplateConfig(dir: string): TemplateInfo | null;
16
+ export declare function readTemplateConfig(dir: string): null | TemplateInfo;
@@ -28,7 +28,7 @@ export function readTemplateConfig(dir) {
28
28
  frontendOptions,
29
29
  };
30
30
  }
31
- catch (error) {
31
+ catch {
32
32
  return null;
33
33
  }
34
34
  }
@@ -1,7 +1,7 @@
1
- import { spinner, log } from '@clack/prompts';
1
+ import { log, spinner } from '@clack/prompts';
2
+ import { ux } from '@oclif/core';
2
3
  import { execa } from 'execa';
3
4
  import net from 'node:net';
4
- import { ux } from '@oclif/core';
5
5
  import catchError from '../lib/utils/catch-error.js';
6
6
  import { waitFor } from '../lib/utils/wait.js';
7
7
  /**
@@ -41,11 +41,11 @@ async function checkPort(port) {
41
41
  */
42
42
  async function checkRequiredPorts() {
43
43
  const portsToCheck = [
44
- { port: 8055, name: 'Directus API' },
45
- { port: 5432, name: 'PostgreSQL' },
44
+ { name: 'Directus API', port: 8055 },
45
+ { name: 'PostgreSQL', port: 5432 },
46
46
  ];
47
47
  let hasConflicts = false;
48
- for (const { port, name } of portsToCheck) {
48
+ for (const { name, port } of portsToCheck) {
49
49
  const status = await checkPort(port);
50
50
  if (status.inUse) {
51
51
  hasConflicts = true;
@@ -63,27 +63,32 @@ async function checkRequiredPorts() {
63
63
  * @returns {Promise<DockerCheckResult>} Docker installation and running status
64
64
  */
65
65
  async function checkDocker() {
66
+ // First check if Docker is installed
66
67
  try {
67
- // Check if Docker is installed
68
- const versionResult = await execa('docker', ['--version']);
69
- const isInstalled = versionResult.exitCode === 0;
70
- if (!isInstalled) {
71
- return { installed: false, message: 'Docker is not installed. Please install Docker at https://docs.docker.com/get-started/get-docker/', running: false };
72
- }
73
- // Check if Docker daemon is running
74
- const statusResult = await execa('docker', ['info']);
75
- const isRunning = statusResult.exitCode === 0;
68
+ await execa('docker', ['--version']);
69
+ }
70
+ catch {
71
+ // Docker is not installed
72
+ return {
73
+ installed: false,
74
+ message: 'Docker is not installed. Please install Docker at https://docs.docker.com/get-started/get-docker/',
75
+ running: false,
76
+ };
77
+ }
78
+ // Docker is installed, now check if it's running
79
+ try {
80
+ await execa('docker', ['info']);
76
81
  return {
77
82
  installed: true,
78
83
  message: 'Docker is installed and running.',
79
- running: isRunning,
84
+ running: true,
80
85
  };
81
86
  }
82
87
  catch {
83
- // If any command fails, Docker is either not installed or not running
88
+ // Docker is installed but not running
84
89
  return {
85
- installed: false,
86
- message: 'Docker is not running. Please start Docker and try again.',
90
+ installed: true,
91
+ message: 'Docker is installed but not running. Please start Docker before running the init command.',
87
92
  running: false,
88
93
  };
89
94
  }
@@ -129,7 +134,7 @@ async function checkImagesExist(imageNames) {
129
134
  // This catch block might be redundant due to allSettled, but good for safety
130
135
  log.error('Error checking for Docker images.');
131
136
  catchError(error, {
132
- context: { imageNames, function: 'checkImagesExist' },
137
+ context: { function: 'checkImagesExist', imageNames },
133
138
  fatal: false,
134
139
  logToFile: true,
135
140
  });
@@ -2,8 +2,8 @@
2
2
  * Defines the structure for the execution context, holding telemetry information.
3
3
  */
4
4
  export interface ExecutionContext {
5
- distinctId?: string;
6
5
  disableTelemetry?: boolean;
6
+ distinctId?: string;
7
7
  }
8
8
  /**
9
9
  * Sets the global execution context.
@@ -6,9 +6,9 @@ interface GitHubUrlParts {
6
6
  repo: string;
7
7
  }
8
8
  export interface TemplateInfo {
9
+ description?: string;
9
10
  id: string;
10
11
  name: string;
11
- description?: string;
12
12
  }
13
13
  export interface GitHubService {
14
14
  getTemplateDirectories(template: string, customUrl?: string): Promise<string[]>;