directus-template-cli 0.6.0-beta.2 → 0.7.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +0 -14
  2. package/bin/dev.js +6 -0
  3. package/bin/run.js +5 -0
  4. package/dist/commands/apply.d.ts +17 -17
  5. package/dist/commands/apply.js +166 -174
  6. package/dist/commands/base.d.ts +15 -0
  7. package/dist/commands/base.js +45 -0
  8. package/dist/commands/extract.d.ts +16 -9
  9. package/dist/commands/extract.js +81 -100
  10. package/dist/commands/init.d.ts +42 -0
  11. package/dist/commands/init.js +241 -0
  12. package/dist/flags/common.d.ts +8 -7
  13. package/dist/flags/common.js +13 -11
  14. package/dist/index.js +1 -5
  15. package/dist/lib/constants.d.ts +18 -0
  16. package/dist/lib/constants.js +25 -6
  17. package/dist/lib/extract/extract-access.js +11 -15
  18. package/dist/lib/extract/extract-assets.js +20 -25
  19. package/dist/lib/extract/extract-collections.js +12 -16
  20. package/dist/lib/extract/extract-content.d.ts +1 -1
  21. package/dist/lib/extract/extract-content.js +17 -26
  22. package/dist/lib/extract/extract-dashboards.js +22 -28
  23. package/dist/lib/extract/extract-extensions.js +12 -16
  24. package/dist/lib/extract/extract-fields.js +12 -16
  25. package/dist/lib/extract/extract-files.js +15 -19
  26. package/dist/lib/extract/extract-flows.js +22 -28
  27. package/dist/lib/extract/extract-folders.js +15 -19
  28. package/dist/lib/extract/extract-permissions.js +12 -16
  29. package/dist/lib/extract/extract-policies.js +12 -16
  30. package/dist/lib/extract/extract-presets.js +12 -16
  31. package/dist/lib/extract/extract-relations.js +14 -18
  32. package/dist/lib/extract/extract-roles.js +15 -19
  33. package/dist/lib/extract/extract-schema.js +17 -21
  34. package/dist/lib/extract/extract-settings.js +12 -16
  35. package/dist/lib/extract/extract-translations.js +12 -16
  36. package/dist/lib/extract/extract-users.js +15 -19
  37. package/dist/lib/extract/index.d.ts +1 -6
  38. package/dist/lib/extract/index.js +47 -58
  39. package/dist/lib/init/config.d.ts +3 -0
  40. package/dist/lib/init/config.js +12 -0
  41. package/dist/lib/init/index.d.ts +10 -0
  42. package/dist/lib/init/index.js +192 -0
  43. package/dist/lib/init/types.d.ts +30 -0
  44. package/dist/lib/init/types.js +1 -0
  45. package/dist/lib/load/apply-flags.js +17 -23
  46. package/dist/lib/load/index.d.ts +1 -12
  47. package/dist/lib/load/index.js +40 -44
  48. package/dist/lib/load/load-access.js +15 -20
  49. package/dist/lib/load/load-collections.d.ts +2 -0
  50. package/dist/lib/load/load-collections.js +29 -32
  51. package/dist/lib/load/load-dashboards.js +19 -25
  52. package/dist/lib/load/load-data.js +43 -49
  53. package/dist/lib/load/load-extensions.js +30 -38
  54. package/dist/lib/load/load-files.js +20 -24
  55. package/dist/lib/load/load-flows.js +23 -29
  56. package/dist/lib/load/load-folders.js +16 -20
  57. package/dist/lib/load/load-permissions.js +13 -17
  58. package/dist/lib/load/load-policies.js +14 -18
  59. package/dist/lib/load/load-presets.js +14 -18
  60. package/dist/lib/load/load-relations.d.ts +2 -0
  61. package/dist/lib/load/load-relations.js +16 -18
  62. package/dist/lib/load/load-roles.js +19 -23
  63. package/dist/lib/load/load-settings.js +18 -21
  64. package/dist/lib/load/load-translations.js +14 -18
  65. package/dist/lib/load/load-users.js +21 -25
  66. package/dist/lib/load/update-required-fields.js +13 -17
  67. package/dist/lib/sdk.d.ts +1 -2
  68. package/dist/lib/sdk.js +27 -27
  69. package/dist/lib/types/extension.js +1 -2
  70. package/dist/lib/types.d.ts +18 -0
  71. package/dist/lib/types.js +1 -0
  72. package/dist/lib/utils/animated-bunny.d.ts +2 -0
  73. package/dist/lib/utils/animated-bunny.js +62 -0
  74. package/dist/lib/utils/auth.d.ts +8 -6
  75. package/dist/lib/utils/auth.js +48 -39
  76. package/dist/lib/utils/catch-error.js +8 -11
  77. package/dist/lib/utils/check-template.js +4 -8
  78. package/dist/lib/utils/chunk-array.js +1 -5
  79. package/dist/lib/utils/ensure-dir.d.ts +2 -0
  80. package/dist/lib/utils/ensure-dir.js +11 -0
  81. package/dist/lib/utils/filter-fields.js +1 -4
  82. package/dist/lib/utils/get-role-ids.d.ts +1 -1
  83. package/dist/lib/utils/get-role-ids.js +7 -12
  84. package/dist/lib/utils/get-template.js +33 -36
  85. package/dist/lib/utils/logger.js +11 -13
  86. package/dist/lib/utils/open-url.js +5 -8
  87. package/dist/lib/utils/parse-github-url.d.ts +19 -0
  88. package/dist/lib/utils/parse-github-url.js +89 -0
  89. package/dist/lib/utils/path.js +6 -10
  90. package/dist/lib/utils/protected-domains.js +1 -4
  91. package/dist/lib/utils/read-file.js +8 -12
  92. package/dist/lib/utils/read-templates.js +9 -15
  93. package/dist/lib/utils/sanitize-flags.d.ts +3 -0
  94. package/dist/lib/utils/sanitize-flags.js +4 -0
  95. package/dist/lib/utils/system-fields.js +19 -22
  96. package/dist/lib/utils/template-config.d.ts +16 -0
  97. package/dist/lib/utils/template-config.js +34 -0
  98. package/dist/lib/utils/template-defaults.d.ts +1 -1
  99. package/dist/lib/utils/template-defaults.js +5 -14
  100. package/dist/lib/utils/transform-github-url.js +1 -5
  101. package/dist/lib/utils/validate-url.js +3 -6
  102. package/dist/lib/utils/wait.d.ts +7 -0
  103. package/dist/lib/utils/wait.js +9 -0
  104. package/dist/lib/utils/write-to-file.js +8 -11
  105. package/dist/services/docker.d.ts +23 -0
  106. package/dist/services/docker.js +187 -0
  107. package/dist/services/github.d.ts +18 -0
  108. package/dist/services/github.js +88 -0
  109. package/dist/services/posthog.d.ts +37 -0
  110. package/dist/services/posthog.js +104 -0
  111. package/oclif.manifest.json +102 -23
  112. package/package.json +46 -29
  113. package/bin/dev +0 -17
  114. package/bin/run +0 -5
@@ -1,18 +1,31 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const core_1 = require("@oclif/core");
5
- const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
6
- const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
7
- const node_path_1 = tslib_1.__importDefault(require("node:path"));
8
- const slugify_1 = tslib_1.__importDefault(require("slugify"));
9
- const customFlags = tslib_1.__importStar(require("../flags/common"));
10
- const constants_1 = require("../lib/constants");
11
- const extract_1 = tslib_1.__importDefault(require("../lib/extract/"));
12
- const auth_1 = require("../lib/utils/auth");
13
- const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
14
- const template_defaults_1 = require("../lib/utils/template-defaults");
15
- class ExtractCommand extends core_1.Command {
1
+ import { text, password, select, intro, log } from '@clack/prompts';
2
+ import { Command, ux } from '@oclif/core';
3
+ import slugify from '@sindresorhus/slugify';
4
+ import chalk from 'chalk';
5
+ import fs from 'node:fs';
6
+ import path from 'pathe';
7
+ import * as customFlags from '../flags/common.js';
8
+ import { DIRECTUS_PINK, DIRECTUS_PURPLE, SEPARATOR, BSL_LICENSE_TEXT } from '../lib/constants.js';
9
+ import { animatedBunny } from '../lib/utils/animated-bunny.js';
10
+ import extract from '../lib/extract/index.js';
11
+ import { getDirectusToken, getDirectusUrl, initializeDirectusApi, validateAuthFlags } from '../lib/utils/auth.js';
12
+ import catchError from '../lib/utils/catch-error.js';
13
+ import { generatePackageJsonContent, generateReadmeContent, } from '../lib/utils/template-defaults.js';
14
+ export default class ExtractCommand extends Command {
15
+ static description = 'Extract a template from a Directus instance.';
16
+ static examples = [
17
+ '$ directus-template-cli extract',
18
+ '$ directus-template-cli extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055"',
19
+ ];
20
+ static flags = {
21
+ directusToken: customFlags.directusToken,
22
+ directusUrl: customFlags.directusUrl,
23
+ programmatic: customFlags.programmatic,
24
+ templateLocation: customFlags.templateLocation,
25
+ templateName: customFlags.templateName,
26
+ userEmail: customFlags.userEmail,
27
+ userPassword: customFlags.userPassword,
28
+ };
16
29
  /**
17
30
  * Main run method for the ExtractCommand
18
31
  * @returns {Promise<void>} - Returns nothing
@@ -30,37 +43,31 @@ class ExtractCommand extends core_1.Command {
30
43
  * @returns {Promise<void>} - Returns nothing
31
44
  */
32
45
  async extractTemplate(templateName, directory, flags) {
33
- var _a;
34
46
  try {
35
- if (!node_fs_1.default.existsSync(directory)) {
36
- node_fs_1.default.mkdirSync(directory, { recursive: true });
47
+ if (!fs.existsSync(directory)) {
48
+ fs.mkdirSync(directory, { recursive: true });
37
49
  }
38
- const packageJSONContent = (0, template_defaults_1.generatePackageJsonContent)(templateName);
39
- const readmeContent = (0, template_defaults_1.generateReadmeContent)(templateName);
40
- const packageJSONPath = node_path_1.default.join(directory, 'package.json');
41
- const readmePath = node_path_1.default.join(directory, 'README.md');
42
- node_fs_1.default.writeFileSync(packageJSONPath, packageJSONContent);
43
- node_fs_1.default.writeFileSync(readmePath, readmeContent);
50
+ const packageJSONContent = generatePackageJsonContent(templateName);
51
+ const readmeContent = generateReadmeContent(templateName);
52
+ const packageJSONPath = path.join(directory, 'package.json');
53
+ const readmePath = path.join(directory, 'README.md');
54
+ fs.writeFileSync(packageJSONPath, packageJSONContent);
55
+ fs.writeFileSync(readmePath, readmeContent);
44
56
  }
45
57
  catch (error) {
46
- (0, catch_error_1.default)(error, {
58
+ catchError(error, {
47
59
  context: { function: 'extractTemplate' },
48
60
  fatal: true,
49
61
  logToFile: true,
50
62
  });
51
63
  }
52
- core_1.ux.log(constants_1.SEPARATOR);
53
- const exclusionMessage = ((_a = flags.excludeCollections) === null || _a === void 0 ? void 0 : _a.length)
54
- ? ` (excluding ${flags.excludeCollections.join(', ')})`
55
- : '';
56
- core_1.ux.action.start(`Extracting template - ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, templateName)}${exclusionMessage} from ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, flags.directusUrl)} to ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, directory)}`);
57
- await (0, extract_1.default)(directory, {
58
- excludeCollections: flags.excludeCollections,
59
- skipFiles: flags.skipFiles,
60
- });
61
- core_1.ux.action.stop();
62
- core_1.ux.log(constants_1.SEPARATOR);
63
- core_1.ux.log('Template extracted successfully.');
64
+ ux.stdout(SEPARATOR);
65
+ ux.action.start(`Extracting template - ${ux.colorize(DIRECTUS_PINK, templateName)} from ${ux.colorize(DIRECTUS_PINK, flags.directusUrl)} to ${ux.colorize(DIRECTUS_PINK, directory)}`);
66
+ await extract(directory);
67
+ ux.action.stop();
68
+ log.warn(BSL_LICENSE_TEXT);
69
+ ux.stdout(SEPARATOR);
70
+ ux.stdout('Template extracted successfully.');
64
71
  this.exit(0);
65
72
  }
66
73
  /**
@@ -69,43 +76,46 @@ class ExtractCommand extends core_1.Command {
69
76
  * @returns {Promise<void>} - Returns nothing
70
77
  */
71
78
  async runInteractive(flags) {
72
- core_1.ux.styledHeader(core_1.ux.colorize(constants_1.DIRECTUS_PURPLE, 'Directus Template CLI - Extract'));
73
- const templateName = await core_1.ux.prompt('What is the name of the template you would like to extract?');
74
- const directory = await core_1.ux.prompt("What directory would you like to extract the template to? If it doesn't exist, it will be created.", { default: `templates/${(0, slugify_1.default)(templateName, { lower: true, strict: true })}` });
75
- core_1.ux.log(`You selected ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, directory)}`);
76
- const excludeCollectionsInput = await core_1.ux.prompt('Enter collection names to exclude (comma-separated) or press enter to skip', { required: false });
77
- if (excludeCollectionsInput) {
78
- flags.excludeCollections = excludeCollectionsInput.split(',').map(name => name.trim());
79
- }
80
- const skipFiles = await core_1.ux.confirm('Skip extracting files and assets? (y/N)');
81
- flags.skipFiles = skipFiles;
82
- core_1.ux.log(constants_1.SEPARATOR);
79
+ await animatedBunny('Let\'s extract a template!');
80
+ intro(`${chalk.bgHex(DIRECTUS_PURPLE).white.bold('Directus Template CLI')} - Extract Template`);
81
+ const templateName = await text({
82
+ message: 'What is the name of the template you would like to extract?',
83
+ placeholder: 'My Template',
84
+ });
85
+ const directory = await text({
86
+ placeholder: `templates/${slugify(templateName)}`,
87
+ defaultValue: `templates/${slugify(templateName)}`,
88
+ message: "What directory would you like to extract the template to? If it doesn't exist, it will be created.",
89
+ });
90
+ ux.stdout(`You selected ${ux.colorize(DIRECTUS_PINK, directory)}`);
91
+ ux.stdout(SEPARATOR);
83
92
  // Get Directus URL
84
- const directusUrl = await (0, auth_1.getDirectusUrl)();
93
+ const directusUrl = await getDirectusUrl();
85
94
  flags.directusUrl = directusUrl;
86
95
  // Prompt for login method
87
- const loginMethod = await inquirer_1.default.prompt([
88
- {
89
- choices: [
90
- { name: 'Directus Access Token', value: 'token' },
91
- { name: 'Email and Password', value: 'email' },
92
- ],
93
- default: 'token',
94
- message: 'How do you want to log in?',
95
- name: 'loginMethod',
96
- type: 'list',
97
- },
98
- ]);
99
- if (loginMethod.loginMethod === 'token') {
100
- const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
96
+ const loginMethod = await select({
97
+ options: [
98
+ { label: 'Directus Access Token', value: 'token' },
99
+ { label: 'Email and Password', value: 'email' },
100
+ ],
101
+ message: 'How do you want to log in?',
102
+ });
103
+ if (loginMethod === 'token') {
104
+ const directusToken = await getDirectusToken(directusUrl);
101
105
  flags.directusToken = directusToken;
102
106
  }
103
107
  else {
104
- flags.userEmail = await core_1.ux.prompt('What is your email?');
105
- flags.userPassword = await core_1.ux.prompt('What is your password?', { type: 'hide' });
108
+ const email = await text({
109
+ message: 'What is your email?',
110
+ });
111
+ flags.userEmail = email;
112
+ const userPassword = await password({
113
+ message: 'What is our password?',
114
+ });
115
+ flags.userPassword = userPassword;
106
116
  }
107
- core_1.ux.log(constants_1.SEPARATOR);
108
- await (0, auth_1.initializeDirectusApi)(flags);
117
+ ux.stdout(SEPARATOR);
118
+ await initializeDirectusApi(flags);
109
119
  await this.extractTemplate(templateName, directory, flags);
110
120
  }
111
121
  /**
@@ -116,7 +126,7 @@ class ExtractCommand extends core_1.Command {
116
126
  async runProgrammatic(flags) {
117
127
  this.validateProgrammaticFlags(flags);
118
128
  const { templateLocation, templateName } = flags;
119
- await (0, auth_1.initializeDirectusApi)(flags);
129
+ await initializeDirectusApi(flags);
120
130
  await this.extractTemplate(templateName, templateLocation, flags);
121
131
  }
122
132
  /**
@@ -126,41 +136,12 @@ class ExtractCommand extends core_1.Command {
126
136
  * @returns {void}
127
137
  */
128
138
  validateProgrammaticFlags(flags) {
129
- (0, auth_1.validateAuthFlags)(flags);
139
+ validateAuthFlags(flags);
130
140
  if (!flags.templateLocation) {
131
- core_1.ux.error('Template location is required for programmatic mode.');
141
+ ux.error('Template location is required for programmatic mode.');
132
142
  }
133
143
  if (!flags.templateName) {
134
- core_1.ux.error('Template name is required for programmatic mode.');
144
+ ux.error('Template name is required for programmatic mode.');
135
145
  }
136
146
  }
137
147
  }
138
- ExtractCommand.description = 'Extract a template from a Directus instance.';
139
- ExtractCommand.examples = [
140
- '$ directus-template-cli extract',
141
- '$ directus-template-cli extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055"',
142
- '$ directus-template-cli extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055" --excludeCollections=collection1,collection2',
143
- ];
144
- ExtractCommand.flags = {
145
- directusToken: customFlags.directusToken,
146
- directusUrl: customFlags.directusUrl,
147
- excludeCollections: core_1.Flags.string({
148
- char: 'e',
149
- delimiter: ',', // Will split on commas and return an array
150
- description: 'Comma-separated list of collection names to exclude from extraction',
151
- multiple: true,
152
- required: false,
153
- }),
154
- programmatic: customFlags.programmatic,
155
- skipFiles: core_1.Flags.boolean({
156
- char: 'f',
157
- default: false,
158
- description: 'Skip extracting files and assets',
159
- required: false,
160
- }),
161
- templateLocation: customFlags.templateLocation,
162
- templateName: customFlags.templateName,
163
- userEmail: customFlags.userEmail,
164
- userPassword: customFlags.userPassword,
165
- };
166
- exports.default = ExtractCommand;
@@ -0,0 +1,42 @@
1
+ import { BaseCommand } from './base.js';
2
+ export interface InitFlags {
3
+ frontend?: string;
4
+ gitInit?: boolean;
5
+ installDeps?: boolean;
6
+ overrideDir?: boolean;
7
+ template?: string;
8
+ disableTelemetry?: boolean;
9
+ }
10
+ export interface InitArgs {
11
+ directory: string;
12
+ }
13
+ export default class InitCommand extends BaseCommand {
14
+ static args: {
15
+ directory: import("@oclif/core/interfaces").Arg<string, {
16
+ exists?: boolean;
17
+ }>;
18
+ };
19
+ static description: string;
20
+ static examples: string[];
21
+ static flags: {
22
+ frontend: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
23
+ gitInit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
24
+ installDeps: import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
+ overrideDir: import("@oclif/core/interfaces").BooleanFlag<boolean>;
26
+ template: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
27
+ disableTelemetry: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
+ };
29
+ private targetDir;
30
+ /**
31
+ * Entrypoint for the command.
32
+ * @returns Promise that resolves when the command is complete.
33
+ */
34
+ run(): Promise<void>;
35
+ /**
36
+ * Interactive mode: prompts the user for each piece of info, with added template checks.
37
+ * @param flags - The flags passed to the command.
38
+ * @param args - The arguments passed to the command.
39
+ * @returns void
40
+ */
41
+ private runInteractive;
42
+ }
@@ -0,0 +1,241 @@
1
+ import { confirm, intro, select, text, isCancel, cancel } from '@clack/prompts';
2
+ import { Args, Flags, ux } from '@oclif/core';
3
+ import chalk from 'chalk';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'pathe';
7
+ import { disableTelemetry } from '../flags/common.js';
8
+ import { DIRECTUS_PURPLE } from '../lib/constants.js';
9
+ import { init } from '../lib/init/index.js';
10
+ 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
+ import { createGigetString, parseGitHubUrl } from '../lib/utils/parse-github-url.js';
14
+ import { downloadTemplate } from 'giget';
15
+ import { BaseCommand } from './base.js';
16
+ import { track, shutdown } from '../services/posthog.js';
17
+ export default class InitCommand extends BaseCommand {
18
+ static args = {
19
+ directory: Args.directory({
20
+ default: '.',
21
+ description: 'Directory to create the project in',
22
+ required: false,
23
+ }),
24
+ };
25
+ static description = 'Initialize a new Directus + Frontend monorepo using official or community starters.';
26
+ static examples = [
27
+ '$ directus-template-cli init',
28
+ '$ directus-template-cli init my-project',
29
+ '$ directus-template-cli init --frontend=nextjs --template=simple-cms',
30
+ '$ directus-template-cli init my-project --frontend=nextjs --template=simple-cms',
31
+ ];
32
+ static flags = {
33
+ frontend: Flags.string({
34
+ description: 'Frontend framework to use (e.g., nextjs, nuxt, astro)',
35
+ }),
36
+ gitInit: Flags.boolean({
37
+ aliases: ['git-init'],
38
+ allowNo: true,
39
+ default: true,
40
+ description: 'Initialize a new Git repository',
41
+ }),
42
+ installDeps: Flags.boolean({
43
+ aliases: ['install-deps'],
44
+ allowNo: true,
45
+ default: true,
46
+ description: 'Install dependencies automatically',
47
+ }),
48
+ overrideDir: Flags.boolean({
49
+ default: false,
50
+ description: 'Override the default directory',
51
+ }),
52
+ template: Flags.string({
53
+ description: 'Template name (e.g., simple-cms) or GitHub URL (e.g., https://github.com/directus-labs/starters/tree/main/simple-cms)',
54
+ }),
55
+ disableTelemetry: disableTelemetry,
56
+ };
57
+ targetDir = '.';
58
+ /**
59
+ * Entrypoint for the command.
60
+ * @returns Promise that resolves when the command is complete.
61
+ */
62
+ async run() {
63
+ const { args, flags } = await this.parse(InitCommand);
64
+ const typedFlags = flags;
65
+ const typedArgs = args;
66
+ // Set the target directory and create it if it doesn't exist
67
+ this.targetDir = path.resolve(args.directory);
68
+ await this.runInteractive(typedFlags, typedArgs);
69
+ }
70
+ /**
71
+ * Interactive mode: prompts the user for each piece of info, with added template checks.
72
+ * @param flags - The flags passed to the command.
73
+ * @param args - The arguments passed to the command.
74
+ * @returns void
75
+ */
76
+ async runInteractive(flags, args) {
77
+ // Show animated intro
78
+ await animatedBunny('Let\'s create a new Directus project!');
79
+ intro(`${chalk.bgHex(DIRECTUS_PURPLE).white.bold('Directus Template CLI')} - Create Project`);
80
+ // Create GitHub service
81
+ const github = createGitHub();
82
+ // If no dir is provided, ask for it
83
+ if (!args.directory || args.directory === '.') {
84
+ const dirResponse = await text({
85
+ message: 'Enter the directory to create the project in:',
86
+ placeholder: './my-directus-project',
87
+ });
88
+ if (isCancel(dirResponse)) {
89
+ cancel('Project creation cancelled.');
90
+ process.exit(0);
91
+ }
92
+ this.targetDir = dirResponse;
93
+ }
94
+ if (fs.existsSync(this.targetDir) && !flags.overrideDir) {
95
+ const overrideDirResponse = await confirm({
96
+ message: 'Directory already exists. Would you like to overwrite it?',
97
+ });
98
+ if (isCancel(overrideDirResponse)) {
99
+ cancel('Project creation cancelled.');
100
+ process.exit(0);
101
+ }
102
+ if (overrideDirResponse) {
103
+ flags.overrideDir = true;
104
+ }
105
+ }
106
+ // 1. Fetch available templates
107
+ const availableTemplates = await github.getTemplates();
108
+ // 2. Prompt for template if not provided
109
+ let { template } = flags;
110
+ if (!template) {
111
+ const templateResponse = await select({
112
+ message: 'Which Directus backend template would you like to use?',
113
+ options: availableTemplates.map(template => ({
114
+ label: template,
115
+ value: template,
116
+ })),
117
+ });
118
+ if (isCancel(templateResponse)) {
119
+ cancel('Project creation cancelled.');
120
+ process.exit(0);
121
+ }
122
+ template = templateResponse;
123
+ }
124
+ // 3. Validate that the template exists, fetch subdirectories
125
+ let directories = await github.getTemplateDirectories(template);
126
+ 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.`);
129
+ const templateNameResponse = await text({
130
+ message: 'Please enter a valid template name, or Ctrl+C to cancel:',
131
+ });
132
+ if (isCancel(templateNameResponse)) {
133
+ cancel('Project creation cancelled.');
134
+ process.exit(0);
135
+ }
136
+ template = templateNameResponse;
137
+ directories = await github.getTemplateDirectories(template);
138
+ }
139
+ flags.template = template;
140
+ // Download the template to a temporary directory to read its configuration
141
+ const tempDir = path.join(os.tmpdir(), `directus-template-${Date.now()}`);
142
+ let chosenFrontend = flags.frontend;
143
+ try {
144
+ await downloadTemplate(createGigetString(parseGitHubUrl(template)), {
145
+ dir: tempDir,
146
+ force: true,
147
+ });
148
+ // Read template configuration
149
+ const templateInfo = readTemplateConfig(tempDir);
150
+ // 4. If template has frontends and user hasn't specified a valid one, ask from the list
151
+ if (templateInfo?.frontendOptions.length > 0 && (!chosenFrontend || !templateInfo.frontendOptions.find(f => f.id === chosenFrontend))) {
152
+ const frontendResponse = await select({
153
+ message: 'Which frontend framework do you want to use?',
154
+ options: [
155
+ ...templateInfo.frontendOptions.map(frontend => ({
156
+ label: frontend.name,
157
+ value: frontend.id,
158
+ })),
159
+ // { label: 'No frontend', value: '' },
160
+ ],
161
+ });
162
+ if (isCancel(frontendResponse)) {
163
+ cancel('Project creation cancelled.');
164
+ process.exit(0);
165
+ }
166
+ chosenFrontend = frontendResponse;
167
+ }
168
+ flags.frontend = chosenFrontend;
169
+ }
170
+ finally {
171
+ // Clean up temporary directory
172
+ if (fs.existsSync(tempDir)) {
173
+ fs.rmSync(tempDir, { recursive: true, force: true });
174
+ }
175
+ }
176
+ const installDepsResponse = await confirm({
177
+ initialValue: true,
178
+ message: 'Would you like to install project dependencies automatically?',
179
+ });
180
+ if (isCancel(installDepsResponse)) {
181
+ cancel('Project creation cancelled.');
182
+ process.exit(0);
183
+ }
184
+ const installDeps = installDepsResponse;
185
+ const initGitResponse = await confirm({
186
+ initialValue: true,
187
+ message: 'Initialize a new Git repository?',
188
+ });
189
+ if (isCancel(initGitResponse)) {
190
+ cancel('Project creation cancelled.');
191
+ process.exit(0);
192
+ }
193
+ const initGit = initGitResponse;
194
+ // Track the command start unless telemetry is disabled
195
+ if (!flags.disableTelemetry) {
196
+ await track({
197
+ lifecycle: 'start',
198
+ distinctId: this.userConfig.distinctId,
199
+ command: 'init',
200
+ flags: {
201
+ frontend: chosenFrontend,
202
+ gitInit: initGit,
203
+ installDeps,
204
+ template,
205
+ },
206
+ runId: this.runId,
207
+ config: this.config,
208
+ });
209
+ }
210
+ // Initialize the project
211
+ await init({
212
+ dir: this.targetDir,
213
+ flags: {
214
+ frontend: chosenFrontend,
215
+ gitInit: initGit,
216
+ installDeps,
217
+ template,
218
+ overrideDir: flags.overrideDir,
219
+ },
220
+ });
221
+ // Track the command completion unless telemetry is disabled
222
+ if (!flags.disableTelemetry) {
223
+ await track({
224
+ command: 'init',
225
+ lifecycle: 'complete',
226
+ distinctId: this.userConfig.distinctId,
227
+ flags: {
228
+ frontend: chosenFrontend,
229
+ gitInit: initGit,
230
+ installDeps,
231
+ template,
232
+ overrideDir: flags.overrideDir,
233
+ },
234
+ runId: this.runId,
235
+ config: this.config,
236
+ });
237
+ await shutdown();
238
+ }
239
+ ux.exit(0);
240
+ }
241
+ }
@@ -1,7 +1,8 @@
1
- export declare const directusToken: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
2
- export declare const directusUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
3
- export declare const userEmail: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
4
- export declare const userPassword: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
5
- export declare const programmatic: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
6
- export declare const templateLocation: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- export declare const templateName: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
1
+ export declare const directusToken: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
2
+ export declare const directusUrl: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
3
+ export declare const userEmail: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
4
+ export declare const userPassword: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
5
+ export declare const programmatic: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ export declare const templateLocation: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ export declare const templateName: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ export declare const disableTelemetry: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,41 +1,43 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.templateName = exports.templateLocation = exports.programmatic = exports.userPassword = exports.userEmail = exports.directusUrl = exports.directusToken = void 0;
4
- const core_1 = require("@oclif/core");
5
- exports.directusToken = core_1.Flags.string({
1
+ import { Flags } from '@oclif/core';
2
+ export const directusToken = Flags.string({
6
3
  description: 'Token to use for the Directus instance',
7
4
  env: 'DIRECTUS_TOKEN',
8
5
  exclusive: ['userEmail', 'userPassword'],
9
6
  });
10
- exports.directusUrl = core_1.Flags.string({
7
+ export const directusUrl = Flags.string({
11
8
  description: 'URL of the Directus instance',
12
9
  env: 'DIRECTUS_URL',
13
10
  });
14
- exports.userEmail = core_1.Flags.string({
11
+ export const userEmail = Flags.string({
15
12
  dependsOn: ['userPassword'],
16
13
  description: 'Email for Directus authentication',
17
14
  env: 'DIRECTUS_EMAIL',
18
15
  exclusive: ['directusToken'],
19
16
  });
20
- exports.userPassword = core_1.Flags.string({
17
+ export const userPassword = Flags.string({
21
18
  dependsOn: ['userEmail'],
22
19
  description: 'Password for Directus authentication',
23
20
  env: 'DIRECTUS_PASSWORD',
24
21
  exclusive: ['directusToken'],
25
22
  });
26
- exports.programmatic = core_1.Flags.boolean({
23
+ export const programmatic = Flags.boolean({
27
24
  char: 'p',
28
25
  default: false,
29
26
  description: 'Run in programmatic mode (non-interactive) for use cases such as CI/CD pipelines.',
30
27
  summary: 'Run in programmatic mode',
31
28
  });
32
- exports.templateLocation = core_1.Flags.string({
29
+ export const templateLocation = Flags.string({
33
30
  dependsOn: ['programmatic'],
34
31
  description: 'Location of the template',
35
32
  env: 'TEMPLATE_LOCATION',
36
33
  });
37
- exports.templateName = core_1.Flags.string({
34
+ export const templateName = Flags.string({
38
35
  dependsOn: ['programmatic'],
39
36
  description: 'Name of the template',
40
37
  env: 'TEMPLATE_NAME',
41
38
  });
39
+ export const disableTelemetry = Flags.boolean({
40
+ default: false,
41
+ description: 'Disable telemetry',
42
+ env: 'DISABLE_TELEMETRY',
43
+ });
package/dist/index.js CHANGED
@@ -1,5 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.run = void 0;
4
- var core_1 = require("@oclif/core");
5
- Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
1
+ export { run } from '@oclif/core';
@@ -1,3 +1,21 @@
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;
6
+ export declare const COMMUNITY_TEMPLATE_REPO: {
7
+ string: string;
8
+ url: string;
9
+ };
10
+ export declare const DEFAULT_REPO: {
11
+ owner: string;
12
+ path: string;
13
+ ref: string;
14
+ repo: string;
15
+ url: string;
16
+ };
17
+ export declare const POSTHOG_PUBLIC_KEY = "phc_STopE6gj6LDIjYonVF7493kQJK8S4v0Xrl6YPr2z9br";
18
+ export declare const POSTHOG_HOST = "https://us.i.posthog.com";
19
+ export declare const DEFAULT_BRANCH = "main";
20
+ export declare const BSL_LICENSE_URL = "https://directus.io/bsl";
21
+ export declare const BSL_LICENSE_TEXT: string;
@@ -1,6 +1,25 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SEPARATOR = exports.DIRECTUS_PINK = exports.DIRECTUS_PURPLE = void 0;
4
- exports.DIRECTUS_PURPLE = '#6644ff';
5
- exports.DIRECTUS_PINK = '#FF99DD';
6
- exports.SEPARATOR = '------------------';
1
+ import chalk from 'chalk';
2
+ import terminalLink from 'terminal-link';
3
+ export const DIRECTUS_PURPLE = '#6644ff';
4
+ export const DIRECTUS_PINK = '#FF99DD';
5
+ export const SEPARATOR = '------------------';
6
+ export const pinkText = chalk.hex(DIRECTUS_PINK);
7
+ export const purpleText = chalk.hex(DIRECTUS_PURPLE);
8
+ export const COMMUNITY_TEMPLATE_REPO = {
9
+ string: 'github:directus-labs/directus-templates',
10
+ url: 'https://github.com/directus-labs/directus-templates',
11
+ };
12
+ export const DEFAULT_REPO = {
13
+ owner: 'directus-labs',
14
+ path: '',
15
+ ref: 'main',
16
+ repo: 'starters',
17
+ url: 'https://github.com/directus-labs/starters',
18
+ };
19
+ export const POSTHOG_PUBLIC_KEY = 'phc_STopE6gj6LDIjYonVF7493kQJK8S4v0Xrl6YPr2z9br';
20
+ export const POSTHOG_HOST = 'https://us.i.posthog.com';
21
+ export const DEFAULT_BRANCH = 'main';
22
+ export const BSL_LICENSE_URL = 'https://directus.io/bsl';
23
+ const BSL_LINK = terminalLink(BSL_LICENSE_URL, BSL_LICENSE_URL);
24
+ const BSL_MAILTO = terminalLink('sales-demo-with-evil-sales@directus.io', 'mailto:sales-demo-with-evil-sales@directus.io');
25
+ export const BSL_LICENSE_TEXT = `You REQUIRE a license to use Directus if your organisation has more than $5MM USD a year in revenue and/or funding.\nFor 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. \nDirectus is licensed under BSL1.1. Visit ${pinkText(BSL_LINK)} for more information or reach out to us at ${pinkText(BSL_MAILTO)}.`;