directus-template-cli 0.7.0-beta.1 → 0.7.0-beta.2

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.
@@ -24,6 +24,7 @@ export default class InitCommand extends Command {
24
24
  /**
25
25
  * Interactive mode: prompts the user for each piece of info, with added template checks.
26
26
  * @param flags - The flags passed to the command.
27
+ * @param args - The arguments passed to the command.
27
28
  * @returns void
28
29
  */
29
30
  private runInteractive;
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const prompts_1 = require("@clack/prompts");
4
5
  const core_1 = require("@oclif/core");
5
- const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
7
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
8
+ const constants_1 = require("../lib/constants");
7
9
  const init_1 = require("../lib/init");
10
+ const animated_bunny_1 = require("../lib/utils/animated-bunny");
8
11
  const github_1 = require("../services/github");
9
12
  class InitCommand extends core_1.Command {
10
13
  constructor() {
@@ -18,37 +21,44 @@ class InitCommand extends core_1.Command {
18
21
  async run() {
19
22
  const { args, flags } = await this.parse(InitCommand);
20
23
  const typedFlags = flags;
24
+ const typedArgs = args;
21
25
  // Set the target directory and create it if it doesn't exist
22
26
  this.targetDir = node_path_1.default.resolve(args.directory);
23
27
  // if (!fs.existsSync(this.targetDir)) {
24
28
  // fs.mkdirSync(this.targetDir, {recursive: true})
25
29
  // }
26
- await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags));
30
+ await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags, typedArgs));
27
31
  }
28
32
  /**
29
33
  * Interactive mode: prompts the user for each piece of info, with added template checks.
30
34
  * @param flags - The flags passed to the command.
35
+ * @param args - The arguments passed to the command.
31
36
  * @returns void
32
37
  */
33
- async runInteractive(flags) {
34
- core_1.ux.styledHeader('Directus Template CLI - Init');
38
+ async runInteractive(flags, args) {
39
+ await (0, animated_bunny_1.animatedBunny)('Let\'s create a new Directus project!');
40
+ (0, prompts_1.intro)(`${chalk_1.default.bgHex(constants_1.DIRECTUS_PURPLE).white.bold('Directus Template CLI 🐰')} - Create Project`);
35
41
  // Create GitHub service
36
42
  const github = (0, github_1.createGitHub)();
43
+ // If no dir is provided, ask for it
44
+ if (!args.directory || args.directory === '.') {
45
+ this.targetDir = await (0, prompts_1.text)({
46
+ message: 'Enter the directory to create the project in:',
47
+ placeholder: './my-directus-project',
48
+ }).then(ans => ans);
49
+ }
37
50
  // 1. Fetch available templates
38
51
  const availableTemplates = await github.getTemplates();
39
52
  // 2. Prompt for template if not provided
40
53
  let { template } = flags;
41
54
  if (!template) {
42
- template = await inquirer_1.default
43
- .prompt([
44
- {
45
- choices: availableTemplates,
46
- message: 'Which Directus starters template would you like to use?',
47
- name: 'template',
48
- type: 'list',
49
- },
50
- ])
51
- .then(ans => ans.template);
55
+ template = await (0, prompts_1.select)({
56
+ message: 'Which Directus backend template would you like to use?',
57
+ options: availableTemplates.map(template => ({
58
+ label: template,
59
+ value: template,
60
+ })),
61
+ }).then(ans => ans);
52
62
  }
53
63
  // 3. Validate that the template exists, fetch subdirectories
54
64
  let directories = await github.getTemplateDirectories(template);
@@ -66,55 +76,30 @@ class InitCommand extends core_1.Command {
66
76
  // 4. If user hasn't specified a valid flags.frontend, ask from the list
67
77
  let chosenFrontend = flags.frontend;
68
78
  if (!chosenFrontend || !potentialFrontends.includes(chosenFrontend)) {
69
- chosenFrontend = await inquirer_1.default
70
- .prompt([
71
- {
72
- choices: potentialFrontends,
73
- message: 'Which frontend framework do you want to use?',
74
- name: 'chosenFrontend',
75
- type: 'list',
76
- },
77
- ])
78
- .then(ans => ans.chosenFrontend);
79
+ chosenFrontend = await (0, prompts_1.select)({
80
+ message: 'Which frontend framework do you want to use?',
81
+ options: potentialFrontends.map(frontend => ({
82
+ label: frontend,
83
+ value: frontend,
84
+ })),
85
+ }).then(ans => ans);
79
86
  }
80
87
  flags.frontend = chosenFrontend;
81
- // 5. Continue with the rest of the interactive flow:
82
- // if (!this.checkDockerInstalled()) {
83
- // const {installDocker} = await inquirer.prompt<{ installDocker: boolean }>([
84
- // {
85
- // default: false,
86
- // message: 'Docker is not installed. Do you want to install Docker?',
87
- // name: 'installDocker',
88
- // type: 'confirm',
89
- // },
90
- // ])
91
- // if (installDocker) {
92
- // ux.log('Please follow Docker\'s official instructions to install Docker, then re-run the init command.')
93
- // this.exit(0)
94
- // }
95
- // }
96
- const { installDeps } = await inquirer_1.default.prompt([
97
- {
98
- default: true,
99
- message: 'Would you like to install project dependencies automatically?',
100
- name: 'installDeps',
101
- type: 'confirm',
102
- },
103
- ]);
104
- const { initGit } = await inquirer_1.default.prompt([
105
- {
106
- default: true,
107
- message: 'Initialize a new Git repository?',
108
- name: 'initGit',
109
- type: 'confirm',
110
- },
111
- ]);
88
+ const installDeps = await (0, prompts_1.confirm)({
89
+ initialValue: true,
90
+ message: 'Would you like to install project dependencies automatically?',
91
+ }).then(ans => ans);
92
+ const initGit = await (0, prompts_1.confirm)({
93
+ initialValue: true,
94
+ message: 'Initialize a new Git repository?',
95
+ }).then(ans => ans);
112
96
  await (0, init_1.init)(this.targetDir, {
113
97
  frontend: chosenFrontend,
114
98
  gitInit: initGit,
115
99
  installDeps,
116
100
  template,
117
101
  });
102
+ core_1.ux.exit(0);
118
103
  }
119
104
  /**
120
105
  * Programmatic mode: relies on flags only, with checks for template existence and valid frontend.
@@ -144,6 +129,7 @@ class InitCommand extends core_1.Command {
144
129
  installDeps: true,
145
130
  template,
146
131
  });
132
+ core_1.ux.exit(0);
147
133
  }
148
134
  }
149
135
  InitCommand.args = {
@@ -1,3 +1,4 @@
1
+ import { type DownloadTemplateResult } from 'giget';
1
2
  interface InitFlags {
2
3
  frontend?: string;
3
4
  gitInit?: boolean;
@@ -5,5 +6,9 @@ interface InitFlags {
5
6
  overrideDir?: boolean;
6
7
  template?: string;
7
8
  }
8
- export declare function init(dir: string, flags: InitFlags): Promise<void>;
9
+ export declare function init(dir: string, flags: InitFlags): Promise<{
10
+ directusDir: string;
11
+ frontendDir: string;
12
+ template: DownloadTemplateResult;
13
+ }>;
9
14
  export {};
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.init = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const prompts_1 = require("@clack/prompts");
5
6
  const core_1 = require("@oclif/core");
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
8
  const giget_1 = require("giget");
7
9
  const glob_1 = require("glob");
8
10
  const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
@@ -74,7 +76,9 @@ async function init(dir, flags) {
74
76
  const healthCheckUrl = `${config_1.DIRECTUS_CONFIG.url}:${config_1.DIRECTUS_CONFIG.port}${config_1.DOCKER_CONFIG.healthCheckEndpoint}`;
75
77
  await dockerService.waitForHealthy(healthCheckUrl);
76
78
  const templatePath = node_path_1.default.join(directusDir, 'template');
77
- core_1.ux.log(`Attempting to apply template from: ${templatePath}`);
79
+ // const s = spinner()
80
+ // s.start(`Attempting to apply template from: ${templatePath}`)
81
+ // ux.log(`Attempting to apply template from: ${templatePath}`)
78
82
  await apply_1.default.run([
79
83
  '--directusUrl=http://localhost:8055',
80
84
  '-p',
@@ -82,6 +86,7 @@ async function init(dir, flags) {
82
86
  '--userPassword=d1r3ctu5',
83
87
  '--templateLocation=' + templatePath,
84
88
  ]);
89
+ // s.stop('Template applied!')
85
90
  }
86
91
  catch (error) {
87
92
  core_1.ux.error('Failed to start Directus containers or apply template');
@@ -90,32 +95,41 @@ async function init(dir, flags) {
90
95
  }
91
96
  // Install dependencies for frontend if it exists
92
97
  if (flags.installDeps && node_fs_1.default.existsSync(frontendDir)) {
93
- core_1.ux.action.start('Installing dependencies');
98
+ const s = (0, prompts_1.spinner)();
99
+ s.start('Installing dependencies');
100
+ // ux.action.start('Installing dependencies')
94
101
  try {
95
102
  const packageManager = await (0, nypm_1.detectPackageManager)(frontendDir);
96
103
  await (0, nypm_1.installDependencies)({
97
104
  cwd: frontendDir,
98
105
  packageManager,
106
+ silent: true,
99
107
  });
100
108
  }
101
109
  catch (error) {
102
110
  core_1.ux.warn('Failed to install dependencies');
103
111
  throw error;
104
112
  }
105
- core_1.ux.action.stop();
113
+ // ux.action.stop()
114
+ s.stop('Dependencies installed!');
106
115
  }
107
116
  // Initialize Git repo
108
117
  if (flags.gitInit) {
109
- core_1.ux.action.start('Initializing git repository');
118
+ const s = (0, prompts_1.spinner)();
119
+ s.start('Initializing git repository');
120
+ // ux.action.start('Initializing git repository')
110
121
  await initGit(dir);
111
- core_1.ux.action.stop();
122
+ // ux.action.stop()
123
+ s.stop('Git repository initialized!');
112
124
  }
113
125
  // Finishing up
114
- core_1.ux.log(`🚀 Directus initialized with template – ${flags.template}`);
115
- core_1.ux.log('You\'ll find the following directories in your project:');
116
- core_1.ux.log(' directus');
117
- core_1.ux.log(`• ${flags.frontend}`);
118
- core_1.ux.exit(0);
126
+ const relativeDir = node_path_1.default.relative(process.cwd(), dir);
127
+ const nextSteps = `- Directus is running on http://localhost:8055 \n- Navigate to your project directory using ${chalk_1.default.cyan(`cd ${relativeDir}`)} and start developing! \n- Review the \`./README.md\` file for next steps.`;
128
+ (0, prompts_1.note)(nextSteps, 'Next Steps');
129
+ // ux.log('You\'ll find the following directories in your project:')
130
+ // ux.log('• directus')
131
+ // ux.log(`• ${flags.frontend}`)
132
+ (0, prompts_1.outro)(`Problems? Join the community on Discord at ${chalk_1.default.underline(chalk_1.default.cyan('https://directus.chat'))}`);
119
133
  }
120
134
  catch (error) {
121
135
  (0, catch_error_1.default)(error, {
@@ -124,6 +138,11 @@ async function init(dir, flags) {
124
138
  logToFile: true,
125
139
  });
126
140
  }
141
+ return {
142
+ directusDir,
143
+ frontendDir,
144
+ template,
145
+ };
127
146
  }
128
147
  exports.init = init;
129
148
  /**
@@ -133,10 +152,10 @@ exports.init = init;
133
152
  */
134
153
  async function initGit(targetDir) {
135
154
  try {
136
- core_1.ux.action.start('Initializing git repository');
155
+ // ux.action.start('Initializing git repository')
137
156
  const { execa } = await Promise.resolve().then(() => tslib_1.__importStar(require('execa')));
138
157
  await execa('git', ['init'], { cwd: targetDir });
139
- core_1.ux.action.stop();
158
+ // ux.action.stop()
140
159
  }
141
160
  catch (error) {
142
161
  (0, catch_error_1.default)(error, {
@@ -0,0 +1,2 @@
1
+ export declare const RANDOM_SAYINGS: string[];
2
+ export declare function animatedBunny(customMessage?: string): Promise<void>;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.animatedBunny = exports.RANDOM_SAYINGS = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
+ const log_update_1 = tslib_1.__importDefault(require("log-update"));
7
+ const constants_1 = require("../constants");
8
+ exports.RANDOM_SAYINGS = [
9
+ 'One does not simply write backends...',
10
+ 'I don\'t always test my code, but when I do, I use production.',
11
+ 'A wild Directus appears!',
12
+ 'Error 418: I\'m a teapot. Just kidding, I\'m Directus and I\'ve got your backend covered.',
13
+ 'I\'ll fix it later. Narrator: They didn\'t fix it later.',
14
+ ];
15
+ async function animatedBunny(customMessage) {
16
+ const saying = customMessage || exports.RANDOM_SAYINGS[Math.floor(Math.random() * exports.RANDOM_SAYINGS.length)];
17
+ let typedSaying = '';
18
+ let blinkState = true;
19
+ let charIndex = 0;
20
+ let isCleanedUp = false;
21
+ const cleanup = () => {
22
+ if (isCleanedUp)
23
+ return;
24
+ isCleanedUp = true;
25
+ clearInterval(animation);
26
+ clearInterval(typing);
27
+ log_update_1.default.done();
28
+ };
29
+ // Ensure cleanup on process exit
30
+ process.on('exit', cleanup);
31
+ process.on('SIGINT', () => {
32
+ cleanup();
33
+ process.exit(0);
34
+ });
35
+ const updateFrame = () => {
36
+ if (isCleanedUp)
37
+ return;
38
+ const eyes = blinkState ? '• •' : '- -';
39
+ const frame = `
40
+ (\\(\\
41
+ ( ${eyes}) ${chalk_1.default.dim('.')}${chalk_1.default.hex(constants_1.DIRECTUS_PINK).visible(`"${typedSaying}"`)}
42
+ o_(")(")
43
+ `;
44
+ (0, log_update_1.default)(frame);
45
+ };
46
+ const animation = setInterval(() => {
47
+ blinkState = !blinkState;
48
+ updateFrame();
49
+ }, 500);
50
+ const typing = setInterval(() => {
51
+ if (charIndex < saying.length) {
52
+ typedSaying += saying[charIndex];
53
+ charIndex++;
54
+ updateFrame();
55
+ }
56
+ else {
57
+ clearInterval(typing);
58
+ }
59
+ }, 10);
60
+ try {
61
+ // Run the animation for the duration of typing plus 1 second
62
+ await new Promise(resolve => setTimeout(resolve, saying.length * 10 + 1000));
63
+ }
64
+ finally {
65
+ cleanup();
66
+ // Remove the event listeners
67
+ process.removeListener('exit', cleanup);
68
+ process.removeListener('SIGINT', cleanup);
69
+ }
70
+ }
71
+ exports.animatedBunny = animatedBunny;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createDocker = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const core_1 = require("@oclif/core");
5
+ const prompts_1 = require("@clack/prompts");
6
6
  const execa_1 = require("execa");
7
7
  const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
8
8
  const wait_1 = require("../lib/utils/wait");
@@ -43,12 +43,14 @@ async function checkDocker() {
43
43
  */
44
44
  function startContainers(cwd) {
45
45
  try {
46
- core_1.ux.action.start('Starting Docker containers');
46
+ // ux.action.start('Starting Docker containers')
47
+ const s = (0, prompts_1.spinner)();
48
+ s.start('Starting Docker containers');
47
49
  return (0, execa_1.execa)('docker-compose', ['up', '-d'], {
48
50
  cwd,
49
51
  // stdio: 'inherit',
50
52
  }).then(() => {
51
- core_1.ux.action.stop();
53
+ s.stop('Docker containers running!');
52
54
  });
53
55
  }
54
56
  catch (error) {
@@ -88,7 +90,8 @@ function stopContainers(cwd) {
88
90
  */
89
91
  function createWaitForHealthy(config) {
90
92
  async function waitForHealthy(healthCheckUrl) {
91
- core_1.ux.action.start('Waiting for service to be healthy');
93
+ const s = (0, prompts_1.spinner)();
94
+ s.start('Waiting for Directus to be ready.');
92
95
  try {
93
96
  await (0, wait_1.waitFor)(async () => {
94
97
  try {
@@ -103,11 +106,11 @@ function createWaitForHealthy(config) {
103
106
  interval: config.interval,
104
107
  maxAttempts: config.maxAttempts,
105
108
  });
106
- core_1.ux.action.stop();
109
+ s.stop('Directus is ready!');
107
110
  return true;
108
111
  }
109
112
  catch (error) {
110
- core_1.ux.action.stop('failed');
113
+ s.stop('');
111
114
  (0, catch_error_1.default)(error, {
112
115
  context: { function: 'waitForHealthy', url: healthCheckUrl },
113
116
  fatal: true,
@@ -359,5 +359,5 @@
359
359
  ]
360
360
  }
361
361
  },
362
- "version": "0.7.0-beta.1"
362
+ "version": "0.7.0-beta.2"
363
363
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus-template-cli",
3
- "version": "0.7.0-beta.1",
3
+ "version": "0.7.0-beta.2",
4
4
  "description": "CLI Utility for applying templates to a Directus instance.",
5
5
  "author": "bryantgillespie @bryantgillespie",
6
6
  "bin": {
@@ -17,12 +17,14 @@
17
17
  "/oclif.manifest.json"
18
18
  ],
19
19
  "dependencies": {
20
+ "@clack/prompts": "^0.10.0",
20
21
  "@directus/sdk": "^17.0.1",
21
22
  "@oclif/core": "^3.18.1",
22
23
  "@oclif/plugin-help": "^6.0.12",
23
24
  "@oclif/plugin-plugins": "^4.1.22",
24
25
  "@octokit/rest": "^21.1.1",
25
26
  "bottleneck": "^2.19.5",
27
+ "chalk": "^5.4.1",
26
28
  "defu": "^6.1.4",
27
29
  "dotenv": "^16.4.1",
28
30
  "execa": "^9.5.2",
@@ -30,7 +32,9 @@
30
32
  "giget": "^1.2.1",
31
33
  "glob": "^11.0.1",
32
34
  "inquirer": "^8.2.5",
35
+ "log-update": "^6.1.0",
33
36
  "nypm": "^0.5.2",
37
+ "picocolors": "^1.1.1",
34
38
  "pkg-types": "^1.3.1",
35
39
  "slugify": "^1.6.6"
36
40
  },