directus-template-cli 0.7.0-beta.4 → 0.7.0-beta.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.
- package/bin/dev.js +6 -0
- package/bin/run.js +5 -0
- package/dist/commands/apply.d.ts +17 -17
- package/dist/commands/apply.js +163 -173
- package/dist/commands/base.d.ts +15 -0
- package/dist/commands/base.js +45 -0
- package/dist/commands/extract.d.ts +16 -7
- package/dist/commands/extract.js +80 -73
- package/dist/commands/init.d.ts +20 -15
- package/dist/commands/init.js +189 -126
- package/dist/flags/common.d.ts +8 -7
- package/dist/flags/common.js +13 -11
- package/dist/index.js +1 -5
- package/dist/lib/constants.d.ts +3 -5
- package/dist/lib/constants.js +8 -13
- package/dist/lib/extract/extract-access.js +11 -15
- package/dist/lib/extract/extract-assets.js +20 -25
- package/dist/lib/extract/extract-collections.js +12 -16
- package/dist/lib/extract/extract-content.js +14 -19
- package/dist/lib/extract/extract-dashboards.js +22 -28
- package/dist/lib/extract/extract-extensions.js +12 -16
- package/dist/lib/extract/extract-fields.js +13 -17
- package/dist/lib/extract/extract-files.js +15 -19
- package/dist/lib/extract/extract-flows.js +22 -28
- package/dist/lib/extract/extract-folders.js +15 -19
- package/dist/lib/extract/extract-permissions.js +12 -16
- package/dist/lib/extract/extract-policies.js +12 -16
- package/dist/lib/extract/extract-presets.js +12 -16
- package/dist/lib/extract/extract-relations.js +14 -18
- package/dist/lib/extract/extract-roles.js +15 -19
- package/dist/lib/extract/extract-schema.js +17 -21
- package/dist/lib/extract/extract-settings.js +12 -16
- package/dist/lib/extract/extract-translations.js +12 -16
- package/dist/lib/extract/extract-users.js +15 -19
- package/dist/lib/extract/index.js +47 -51
- package/dist/lib/init/config.d.ts +1 -1
- package/dist/lib/init/config.js +3 -6
- package/dist/lib/init/index.d.ts +5 -9
- package/dist/lib/init/index.js +105 -85
- package/dist/lib/init/types.js +1 -2
- package/dist/lib/load/apply-flags.js +17 -23
- package/dist/lib/load/index.d.ts +1 -12
- package/dist/lib/load/index.js +40 -44
- package/dist/lib/load/load-access.js +15 -20
- package/dist/lib/load/load-collections.js +27 -32
- package/dist/lib/load/load-dashboards.js +19 -25
- package/dist/lib/load/load-data.js +43 -49
- package/dist/lib/load/load-extensions.js +30 -38
- package/dist/lib/load/load-files.js +20 -24
- package/dist/lib/load/load-flows.js +23 -29
- package/dist/lib/load/load-folders.js +16 -20
- package/dist/lib/load/load-permissions.js +13 -17
- package/dist/lib/load/load-policies.js +14 -18
- package/dist/lib/load/load-presets.js +14 -18
- package/dist/lib/load/load-relations.d.ts +2 -0
- package/dist/lib/load/load-relations.js +16 -18
- package/dist/lib/load/load-roles.js +19 -23
- package/dist/lib/load/load-settings.js +18 -21
- package/dist/lib/load/load-translations.js +14 -18
- package/dist/lib/load/load-users.js +21 -25
- package/dist/lib/load/update-required-fields.js +13 -17
- package/dist/lib/sdk.d.ts +1 -2
- package/dist/lib/sdk.js +27 -27
- package/dist/lib/types/extension.js +1 -2
- package/dist/lib/types.d.ts +18 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/utils/animated-bunny.js +9 -14
- package/dist/lib/utils/auth.d.ts +8 -6
- package/dist/lib/utils/auth.js +48 -39
- package/dist/lib/utils/catch-error.js +8 -11
- package/dist/lib/utils/check-template.js +4 -8
- package/dist/lib/utils/chunk-array.js +1 -5
- package/dist/lib/utils/ensure-dir.js +7 -12
- package/dist/lib/utils/filter-fields.js +1 -4
- package/dist/lib/utils/get-role-ids.d.ts +1 -1
- package/dist/lib/utils/get-role-ids.js +7 -12
- package/dist/lib/utils/get-template.js +33 -37
- package/dist/lib/utils/logger.js +11 -13
- package/dist/lib/utils/open-url.js +5 -8
- package/dist/lib/utils/parse-github-url.d.ts +10 -5
- package/dist/lib/utils/parse-github-url.js +80 -45
- package/dist/lib/utils/path.js +6 -10
- package/dist/lib/utils/protected-domains.js +1 -4
- package/dist/lib/utils/read-file.js +8 -12
- package/dist/lib/utils/read-templates.js +9 -15
- package/dist/lib/utils/sanitize-flags.d.ts +3 -0
- package/dist/lib/utils/sanitize-flags.js +4 -0
- package/dist/lib/utils/system-fields.js +19 -22
- package/dist/lib/utils/template-config.d.ts +16 -0
- package/dist/lib/utils/template-config.js +34 -0
- package/dist/lib/utils/template-defaults.d.ts +1 -1
- package/dist/lib/utils/template-defaults.js +5 -14
- package/dist/lib/utils/transform-github-url.js +1 -5
- package/dist/lib/utils/validate-url.js +3 -6
- package/dist/lib/utils/wait.js +1 -5
- package/dist/lib/utils/write-to-file.js +8 -11
- package/dist/services/docker.js +68 -21
- package/dist/services/github.d.ts +1 -1
- package/dist/services/github.js +53 -22
- package/dist/services/posthog.d.ts +37 -0
- package/dist/services/posthog.js +104 -0
- package/oclif.manifest.json +32 -13
- package/package.json +38 -33
- package/bin/dev +0 -17
- package/bin/run +0 -5
package/dist/commands/extract.js
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import { text, password, select, intro } 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 } 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
|
|
@@ -31,29 +44,29 @@ class ExtractCommand extends core_1.Command {
|
|
|
31
44
|
*/
|
|
32
45
|
async extractTemplate(templateName, directory, flags) {
|
|
33
46
|
try {
|
|
34
|
-
if (!
|
|
35
|
-
|
|
47
|
+
if (!fs.existsSync(directory)) {
|
|
48
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
36
49
|
}
|
|
37
|
-
const packageJSONContent =
|
|
38
|
-
const readmeContent =
|
|
39
|
-
const packageJSONPath =
|
|
40
|
-
const readmePath =
|
|
41
|
-
|
|
42
|
-
|
|
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);
|
|
43
56
|
}
|
|
44
57
|
catch (error) {
|
|
45
|
-
(
|
|
58
|
+
catchError(error, {
|
|
46
59
|
context: { function: 'extractTemplate' },
|
|
47
60
|
fatal: true,
|
|
48
61
|
logToFile: true,
|
|
49
62
|
});
|
|
50
63
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
+
ux.stdout(SEPARATOR);
|
|
69
|
+
ux.stdout('Template extracted successfully.');
|
|
57
70
|
this.exit(0);
|
|
58
71
|
}
|
|
59
72
|
/**
|
|
@@ -62,37 +75,46 @@ class ExtractCommand extends core_1.Command {
|
|
|
62
75
|
* @returns {Promise<void>} - Returns nothing
|
|
63
76
|
*/
|
|
64
77
|
async runInteractive(flags) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
await animatedBunny('Let\'s extract a template!');
|
|
79
|
+
intro(`${chalk.bgHex(DIRECTUS_PURPLE).white.bold('Directus Template CLI')} - Extract Template`);
|
|
80
|
+
const templateName = await text({
|
|
81
|
+
message: 'What is the name of the template you would like to extract?',
|
|
82
|
+
placeholder: 'My Template',
|
|
83
|
+
});
|
|
84
|
+
const directory = await text({
|
|
85
|
+
placeholder: `templates/${slugify(templateName)}`,
|
|
86
|
+
defaultValue: `templates/${slugify(templateName)}`,
|
|
87
|
+
message: "What directory would you like to extract the template to? If it doesn't exist, it will be created.",
|
|
88
|
+
});
|
|
89
|
+
ux.stdout(`You selected ${ux.colorize(DIRECTUS_PINK, directory)}`);
|
|
90
|
+
ux.stdout(SEPARATOR);
|
|
70
91
|
// Get Directus URL
|
|
71
|
-
const directusUrl = await
|
|
92
|
+
const directusUrl = await getDirectusUrl();
|
|
72
93
|
flags.directusUrl = directusUrl;
|
|
73
94
|
// Prompt for login method
|
|
74
|
-
const loginMethod = await
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
type: 'list',
|
|
84
|
-
},
|
|
85
|
-
]);
|
|
86
|
-
if (loginMethod.loginMethod === 'token') {
|
|
87
|
-
const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
|
|
95
|
+
const loginMethod = await select({
|
|
96
|
+
options: [
|
|
97
|
+
{ label: 'Directus Access Token', value: 'token' },
|
|
98
|
+
{ label: 'Email and Password', value: 'email' },
|
|
99
|
+
],
|
|
100
|
+
message: 'How do you want to log in?',
|
|
101
|
+
});
|
|
102
|
+
if (loginMethod === 'token') {
|
|
103
|
+
const directusToken = await getDirectusToken(directusUrl);
|
|
88
104
|
flags.directusToken = directusToken;
|
|
89
105
|
}
|
|
90
106
|
else {
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
});
|
|
114
|
+
flags.userPassword = userPassword;
|
|
93
115
|
}
|
|
94
|
-
|
|
95
|
-
await
|
|
116
|
+
ux.stdout(SEPARATOR);
|
|
117
|
+
await initializeDirectusApi(flags);
|
|
96
118
|
await this.extractTemplate(templateName, directory, flags);
|
|
97
119
|
}
|
|
98
120
|
/**
|
|
@@ -103,7 +125,7 @@ class ExtractCommand extends core_1.Command {
|
|
|
103
125
|
async runProgrammatic(flags) {
|
|
104
126
|
this.validateProgrammaticFlags(flags);
|
|
105
127
|
const { templateLocation, templateName } = flags;
|
|
106
|
-
await
|
|
128
|
+
await initializeDirectusApi(flags);
|
|
107
129
|
await this.extractTemplate(templateName, templateLocation, flags);
|
|
108
130
|
}
|
|
109
131
|
/**
|
|
@@ -113,27 +135,12 @@ class ExtractCommand extends core_1.Command {
|
|
|
113
135
|
* @returns {void}
|
|
114
136
|
*/
|
|
115
137
|
validateProgrammaticFlags(flags) {
|
|
116
|
-
|
|
138
|
+
validateAuthFlags(flags);
|
|
117
139
|
if (!flags.templateLocation) {
|
|
118
|
-
|
|
140
|
+
ux.error('Template location is required for programmatic mode.');
|
|
119
141
|
}
|
|
120
142
|
if (!flags.templateName) {
|
|
121
|
-
|
|
143
|
+
ux.error('Template name is required for programmatic mode.');
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
|
-
ExtractCommand.description = 'Extract a template from a Directus instance.';
|
|
126
|
-
ExtractCommand.examples = [
|
|
127
|
-
'$ directus-template-cli extract',
|
|
128
|
-
'$ directus-template-cli extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055"',
|
|
129
|
-
];
|
|
130
|
-
ExtractCommand.flags = {
|
|
131
|
-
directusToken: customFlags.directusToken,
|
|
132
|
-
directusUrl: customFlags.directusUrl,
|
|
133
|
-
programmatic: customFlags.programmatic,
|
|
134
|
-
templateLocation: customFlags.templateLocation,
|
|
135
|
-
templateName: customFlags.templateName,
|
|
136
|
-
userEmail: customFlags.userEmail,
|
|
137
|
-
userPassword: customFlags.userPassword,
|
|
138
|
-
};
|
|
139
|
-
exports.default = ExtractCommand;
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
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 {
|
|
3
14
|
static args: {
|
|
4
|
-
directory: import("@oclif/core/
|
|
15
|
+
directory: import("@oclif/core/interfaces").Arg<string, {
|
|
5
16
|
exists?: boolean;
|
|
6
17
|
}>;
|
|
7
18
|
};
|
|
8
19
|
static description: string;
|
|
9
20
|
static examples: string[];
|
|
10
21
|
static flags: {
|
|
11
|
-
frontend: import("@oclif/core/
|
|
12
|
-
gitInit: import("@oclif/core/
|
|
13
|
-
installDeps: import("@oclif/core/
|
|
14
|
-
overrideDir: import("@oclif/core/
|
|
15
|
-
|
|
16
|
-
|
|
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>;
|
|
17
28
|
};
|
|
18
29
|
private targetDir;
|
|
19
30
|
/**
|
|
@@ -28,10 +39,4 @@ export default class InitCommand extends Command {
|
|
|
28
39
|
* @returns void
|
|
29
40
|
*/
|
|
30
41
|
private runInteractive;
|
|
31
|
-
/**
|
|
32
|
-
* Programmatic mode: relies on flags only, with checks for template existence and valid frontend.
|
|
33
|
-
* @param flags - The flags passed to the command.
|
|
34
|
-
* @returns void
|
|
35
|
-
*/
|
|
36
|
-
private runProgrammatic;
|
|
37
42
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,19 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 = '.';
|
|
17
58
|
/**
|
|
18
59
|
* Entrypoint for the command.
|
|
19
60
|
* @returns Promise that resolves when the command is complete.
|
|
@@ -23,11 +64,8 @@ class InitCommand extends core_1.Command {
|
|
|
23
64
|
const typedFlags = flags;
|
|
24
65
|
const typedArgs = args;
|
|
25
66
|
// Set the target directory and create it if it doesn't exist
|
|
26
|
-
this.targetDir =
|
|
27
|
-
|
|
28
|
-
// fs.mkdirSync(this.targetDir, {recursive: true})
|
|
29
|
-
// }
|
|
30
|
-
await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags, typedArgs));
|
|
67
|
+
this.targetDir = path.resolve(args.directory);
|
|
68
|
+
await this.runInteractive(typedFlags, typedArgs);
|
|
31
69
|
}
|
|
32
70
|
/**
|
|
33
71
|
* Interactive mode: prompts the user for each piece of info, with added template checks.
|
|
@@ -36,143 +74,168 @@ class InitCommand extends core_1.Command {
|
|
|
36
74
|
* @returns void
|
|
37
75
|
*/
|
|
38
76
|
async runInteractive(flags, args) {
|
|
39
|
-
|
|
40
|
-
|
|
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`);
|
|
41
80
|
// Create GitHub service
|
|
42
|
-
const github =
|
|
81
|
+
const github = createGitHub();
|
|
43
82
|
// If no dir is provided, ask for it
|
|
44
83
|
if (!args.directory || args.directory === '.') {
|
|
45
|
-
|
|
84
|
+
const dirResponse = await text({
|
|
46
85
|
message: 'Enter the directory to create the project in:',
|
|
47
86
|
placeholder: './my-directus-project',
|
|
48
|
-
})
|
|
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
|
+
}
|
|
49
105
|
}
|
|
50
106
|
// 1. Fetch available templates
|
|
51
107
|
const availableTemplates = await github.getTemplates();
|
|
52
108
|
// 2. Prompt for template if not provided
|
|
53
109
|
let { template } = flags;
|
|
54
110
|
if (!template) {
|
|
55
|
-
|
|
111
|
+
const templateResponse = await select({
|
|
56
112
|
message: 'Which Directus backend template would you like to use?',
|
|
57
113
|
options: availableTemplates.map(template => ({
|
|
58
114
|
label: template,
|
|
59
115
|
value: template,
|
|
60
116
|
})),
|
|
61
|
-
})
|
|
117
|
+
});
|
|
118
|
+
if (isCancel(templateResponse)) {
|
|
119
|
+
cancel('Project creation cancelled.');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
template = templateResponse;
|
|
62
123
|
}
|
|
63
124
|
// 3. Validate that the template exists, fetch subdirectories
|
|
64
125
|
let directories = await github.getTemplateDirectories(template);
|
|
65
|
-
|
|
126
|
+
const isDirectUrl = template?.startsWith('http');
|
|
127
|
+
while (!isDirectUrl && directories.length === 0) {
|
|
66
128
|
this.log(`Template "${template}" doesn't seem to exist in directus-labs/directus-starters.`);
|
|
67
|
-
|
|
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;
|
|
68
137
|
directories = await github.getTemplateDirectories(template);
|
|
69
138
|
}
|
|
70
139
|
flags.template = template;
|
|
71
|
-
//
|
|
72
|
-
const
|
|
73
|
-
if (potentialFrontends.length === 0) {
|
|
74
|
-
this.error(`No frontends found for template "${template}". Exiting.`);
|
|
75
|
-
}
|
|
76
|
-
// 4. If user hasn't specified a valid flags.frontend, ask from the list
|
|
140
|
+
// Download the template to a temporary directory to read its configuration
|
|
141
|
+
const tempDir = path.join(os.tmpdir(), `directus-template-${Date.now()}`);
|
|
77
142
|
let chosenFrontend = flags.frontend;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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;
|
|
86
169
|
}
|
|
87
|
-
|
|
88
|
-
|
|
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({
|
|
89
177
|
initialValue: true,
|
|
90
178
|
message: 'Would you like to install project dependencies automatically?',
|
|
91
|
-
})
|
|
92
|
-
|
|
179
|
+
});
|
|
180
|
+
if (isCancel(installDepsResponse)) {
|
|
181
|
+
cancel('Project creation cancelled.');
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
const installDeps = installDepsResponse;
|
|
185
|
+
const initGitResponse = await confirm({
|
|
93
186
|
initialValue: true,
|
|
94
187
|
message: 'Initialize a new Git repository?',
|
|
95
|
-
}).then(ans => ans);
|
|
96
|
-
await (0, init_1.init)(this.targetDir, {
|
|
97
|
-
frontend: chosenFrontend,
|
|
98
|
-
gitInit: initGit,
|
|
99
|
-
installDeps,
|
|
100
|
-
template,
|
|
101
188
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* Programmatic mode: relies on flags only, with checks for template existence and valid frontend.
|
|
106
|
-
* @param flags - The flags passed to the command.
|
|
107
|
-
* @returns void
|
|
108
|
-
*/
|
|
109
|
-
async runProgrammatic(flags) {
|
|
110
|
-
const github = (0, github_1.createGitHub)();
|
|
111
|
-
if (!flags.template) {
|
|
112
|
-
core_1.ux.error('Missing --template parameter for programmatic mode.');
|
|
189
|
+
if (isCancel(initGitResponse)) {
|
|
190
|
+
cancel('Project creation cancelled.');
|
|
191
|
+
process.exit(0);
|
|
113
192
|
}
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
});
|
|
116
209
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await (0, init_1.init)(this.targetDir, {
|
|
128
|
-
frontend,
|
|
129
|
-
installDeps: true,
|
|
130
|
-
template,
|
|
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
|
+
},
|
|
131
220
|
});
|
|
132
|
-
|
|
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);
|
|
133
240
|
}
|
|
134
241
|
}
|
|
135
|
-
InitCommand.args = {
|
|
136
|
-
directory: core_1.Args.directory({
|
|
137
|
-
default: '.',
|
|
138
|
-
description: 'Directory to create the project in',
|
|
139
|
-
required: false,
|
|
140
|
-
}),
|
|
141
|
-
};
|
|
142
|
-
InitCommand.description = 'Initialize a new Directus + Frontend monorepo using official or community starters.';
|
|
143
|
-
InitCommand.examples = [
|
|
144
|
-
'$ directus-template-cli init',
|
|
145
|
-
'$ directus-template-cli init my-project',
|
|
146
|
-
'$ directus-template-cli init --frontend=nextjs --template=simple-cms --programmatic',
|
|
147
|
-
'$ directus-template-cli init my-project --frontend=nextjs --template=simple-cms --programmatic',
|
|
148
|
-
];
|
|
149
|
-
InitCommand.flags = {
|
|
150
|
-
frontend: core_1.Flags.string({
|
|
151
|
-
description: 'Frontend framework to use (e.g., nextjs, nuxt, astro)',
|
|
152
|
-
}),
|
|
153
|
-
gitInit: core_1.Flags.boolean({
|
|
154
|
-
aliases: ['git-init'],
|
|
155
|
-
allowNo: true,
|
|
156
|
-
default: true,
|
|
157
|
-
description: 'Initialize a new Git repository',
|
|
158
|
-
}),
|
|
159
|
-
installDeps: core_1.Flags.boolean({
|
|
160
|
-
aliases: ['install-deps'],
|
|
161
|
-
allowNo: true,
|
|
162
|
-
default: true,
|
|
163
|
-
description: 'Install dependencies automatically',
|
|
164
|
-
}),
|
|
165
|
-
overrideDir: core_1.Flags.boolean({
|
|
166
|
-
default: false,
|
|
167
|
-
description: 'Override the default directory',
|
|
168
|
-
}),
|
|
169
|
-
programmatic: core_1.Flags.boolean({
|
|
170
|
-
char: 'p',
|
|
171
|
-
default: false,
|
|
172
|
-
description: 'Run in programmatic mode (non-interactive)',
|
|
173
|
-
}),
|
|
174
|
-
template: core_1.Flags.string({
|
|
175
|
-
description: 'Template name (e.g., simple-cms) or GitHub URL (e.g., https://github.com/directus-labs/starters/tree/main/simple-cms)',
|
|
176
|
-
}),
|
|
177
|
-
};
|
|
178
|
-
exports.default = InitCommand;
|
package/dist/flags/common.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const directusToken: import("@oclif/core/
|
|
2
|
-
export declare const directusUrl: import("@oclif/core/
|
|
3
|
-
export declare const userEmail: import("@oclif/core/
|
|
4
|
-
export declare const userPassword: import("@oclif/core/
|
|
5
|
-
export declare const programmatic: import("@oclif/core/
|
|
6
|
-
export declare const templateLocation: import("@oclif/core/
|
|
7
|
-
export declare const templateName: import("@oclif/core/
|
|
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>;
|