directus-template-cli 0.2.0 → 0.3.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.
Files changed (42) hide show
  1. package/README.md +27 -3
  2. package/dist/commands/apply.d.ts +1 -1
  3. package/dist/commands/apply.js +55 -46
  4. package/dist/commands/extract.d.ts +7 -0
  5. package/dist/commands/extract.js +54 -0
  6. package/dist/lib/extract/extract-assets.d.ts +3 -0
  7. package/dist/lib/extract/extract-assets.js +49 -0
  8. package/dist/lib/extract/extract-content.d.ts +3 -0
  9. package/dist/lib/extract/extract-content.js +33 -0
  10. package/dist/lib/extract/extract-files.d.ts +4 -0
  11. package/dist/lib/extract/extract-files.js +48 -0
  12. package/dist/lib/extract/extract-folders.d.ts +4 -0
  13. package/dist/lib/extract/extract-folders.js +26 -0
  14. package/dist/lib/extract/extract-from-endpoint.d.ts +4 -0
  15. package/dist/lib/extract/extract-from-endpoint.js +23 -0
  16. package/dist/lib/extract/extract-roles.d.ts +4 -0
  17. package/dist/lib/extract/extract-roles.js +36 -0
  18. package/dist/lib/extract/extract-schema.d.ts +1 -0
  19. package/dist/lib/extract/extract-schema.js +29 -0
  20. package/dist/lib/extract/extract-users.d.ts +4 -0
  21. package/dist/lib/extract/extract-users.js +49 -0
  22. package/dist/lib/extract/index.d.ts +1 -0
  23. package/dist/lib/extract/index.js +55 -0
  24. package/dist/lib/extract/public-permissions.d.ts +1 -0
  25. package/dist/lib/extract/public-permissions.js +21 -0
  26. package/dist/lib/load/index.js +31 -28
  27. package/dist/lib/load/load-folders.js +12 -4
  28. package/dist/lib/load/load-presets.js +19 -8
  29. package/dist/lib/load/load-public-permissions.d.ts +2 -1
  30. package/dist/lib/load/load-public-permissions.js +11 -5
  31. package/dist/lib/utils/auth.d.ts +2 -0
  32. package/dist/lib/utils/auth.js +34 -0
  33. package/dist/lib/utils/filter-fields.d.ts +1 -0
  34. package/dist/lib/utils/filter-fields.js +22 -0
  35. package/dist/lib/utils/read-templates.d.ts +2 -1
  36. package/dist/lib/utils/read-templates.js +25 -20
  37. package/dist/lib/utils/template-defaults.d.ts +2 -0
  38. package/dist/lib/utils/template-defaults.js +37 -0
  39. package/dist/lib/utils/write-to-file.d.ts +1 -1
  40. package/dist/lib/utils/write-to-file.js +13 -9
  41. package/oclif.manifest.json +15 -1
  42. package/package.json +3 -2
package/README.md CHANGED
@@ -1,19 +1,43 @@
1
1
  # Directus Template CLI
2
2
 
3
- A CLI tool to apply Directus "templates" to a blank Directus instance.
3
+ A CLI tool to make applying or extracting Directus "templates" a little easier...well a lot easier.
4
4
 
5
- Note: This is a pre-release. It is recommended for use on POC or demo projects only.
5
+ **Notes:**
6
+
7
+ - This is a pre-release. It is recommended for use on POC or demo projects only.
8
+ - ⚠️ Known issues with using MySQL currently, please use ONLY PostgreSQL or SQLite for your database provider.
9
+ - Templates are applied / extracted on an all or nothing basis – meaning that all the schema, content, and system settings are extracted or applied. We'd love to support more granular operations in the future. (PRs welcome 🙏)
6
10
 
7
11
  ## Usage
8
12
 
13
+ ### Applying a Template
14
+
15
+ **To avoid potential conflicts and bad outcomes, templates can only be applied to a blank instance currently.**
16
+
9
17
  1. Create a Directus instance on [Directus Cloud](https://directus.cloud) or using self-hosted version.
10
18
  2. Login and create a Static Access Token for the admin user.
11
19
  3. Copy the static token and your Directus URL.
12
20
  4. Run the following command on the terminal and follow the prompts.
21
+
13
22
  ```
14
23
  $ npx directus-template-cli apply
15
24
  ```
16
25
 
26
+ You can choose from our templates bundled with the CLI or you can also choose a template from a local directory.
27
+
28
+ ### Extracting a Template
29
+
30
+ The CLI can also extract a template from a Directus instance so that it can be applied to other instances.
31
+
32
+ 1. Make sure you remove any sensitive data from the Directus instance you don't want to include in the template.
33
+ 2. Login and create a Static Access Token for the admin user.
34
+ 3. Copy the static token and your Directus URL.
35
+ 4. Run the following command on the terminal and follow the prompts.
36
+
37
+ ```
38
+ $ npx directus-template-cli extract
39
+ ```
40
+
17
41
  ## License
18
42
 
19
- This template is licensed under the [MIT License](https://opensource.org/licenses/MIT).
43
+ This tool is licensed under the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,4 +1,4 @@
1
- import { Command } from '@oclif/core';
1
+ import { Command } from "@oclif/core";
2
2
  export default class ApplyCommand extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
@@ -4,50 +4,61 @@ const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
5
  const core_2 = require("@oclif/core");
6
6
  const inquirer = tslib_1.__importStar(require("inquirer"));
7
- const read_templates_1 = tslib_1.__importDefault(require("../lib/utils/read-templates"));
8
- const validate_url_1 = tslib_1.__importDefault(require("../lib/utils/validate-url"));
7
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
9
8
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
9
+ const read_templates_1 = require("../lib/utils/read-templates");
10
10
  const api_1 = require("../lib/api");
11
11
  const load_1 = tslib_1.__importDefault(require("../lib/load/"));
12
- const separator = '------------------';
12
+ const auth_1 = require("../lib/utils/auth");
13
+ const separator = "------------------";
13
14
  async function getTemplate() {
14
- const TEMPLATE_DIR = node_path_1.default.join(__dirname, '..', '..', 'templates');
15
- const templates = await (0, read_templates_1.default)(TEMPLATE_DIR);
16
- const templateChoices = templates.map((template) => {
15
+ const TEMPLATE_DIR = node_path_1.default.join(__dirname, "..", "..", "templates");
16
+ const templates = await (0, read_templates_1.readAllTemplates)(TEMPLATE_DIR);
17
+ const officialTemplateChoices = templates.map((template) => {
17
18
  return { name: template.templateName, value: template };
18
19
  });
19
- const template = await inquirer.prompt([{
20
- name: 'template',
21
- message: 'Select a template.',
22
- type: 'list',
23
- choices: templateChoices,
24
- }]);
25
- return template;
26
- }
27
- async function getDirectusUrl() {
28
- const directusUrl = await core_2.ux.prompt('What is your Directus URL?');
29
- // Validate URL
30
- if (!(0, validate_url_1.default)(directusUrl)) {
31
- core_2.ux.warn('Invalid URL');
32
- return getDirectusUrl();
33
- }
34
- return directusUrl;
35
- }
36
- async function getDirectusToken(directusUrl) {
37
- const directusToken = await core_2.ux.prompt('What is your Directus Admin Token?');
38
- // Validate token
39
- try {
40
- await api_1.api.get('/users/me', {
41
- headers: {
42
- Authorization: `Bearer ${directusToken}`,
20
+ const templateType = await inquirer.prompt([
21
+ {
22
+ name: "templateType",
23
+ message: "What type of template would you like to apply?",
24
+ type: "list",
25
+ choices: [
26
+ {
27
+ name: "Official templates",
28
+ value: "official",
29
+ },
30
+ {
31
+ name: "From a local directory",
32
+ value: "local",
33
+ },
34
+ // {
35
+ // name: "From a git repository",
36
+ // value: "git",
37
+ // },
38
+ ],
39
+ },
40
+ ]);
41
+ let template;
42
+ if (templateType.templateType === "official") {
43
+ template = await inquirer.prompt([
44
+ {
45
+ name: "template",
46
+ message: "Select a template.",
47
+ type: "list",
48
+ choices: officialTemplateChoices,
43
49
  },
44
- });
45
- return directusToken;
50
+ ]);
46
51
  }
47
- catch {
48
- core_2.ux.warn('Invalid token');
49
- return getDirectusToken(directusUrl);
52
+ if (templateType.templateType === "local") {
53
+ const localTemplateDir = await core_2.ux.prompt("What is the local template directory?");
54
+ if (!node_fs_1.default.existsSync(localTemplateDir)) {
55
+ core_2.ux.error("Directory does not exist.");
56
+ }
57
+ else {
58
+ template = { template: await (0, read_templates_1.readTemplate)(localTemplateDir) };
59
+ }
50
60
  }
61
+ return template;
51
62
  }
52
63
  class ApplyCommand extends core_1.Command {
53
64
  async run() {
@@ -55,32 +66,30 @@ class ApplyCommand extends core_1.Command {
55
66
  const chosenTemplate = await getTemplate();
56
67
  this.log(`You selected ${chosenTemplate.template.templateName}`);
57
68
  this.log(separator);
58
- const directusUrl = await getDirectusUrl();
69
+ const directusUrl = await (0, auth_1.getDirectusUrl)();
59
70
  api_1.api.setBaseUrl(directusUrl);
60
- const directusToken = await getDirectusToken(directusUrl);
71
+ const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
61
72
  api_1.api.setAuthToken(directusToken);
62
73
  this.log(separator);
63
74
  // Check if Directus instance is empty, if not, throw error
64
- const { data } = await api_1.api.get('/collections');
75
+ const { data } = await api_1.api.get("/collections");
65
76
  // Look for collections that don't start with directus_
66
77
  const collections = data.data.filter((collection) => {
67
- return !collection.collection.startsWith('directus_');
78
+ return !collection.collection.startsWith("directus_");
68
79
  });
69
80
  if (collections.length > 0) {
70
- core_2.ux.error('Directus instance is not empty. Please use a blank instance. Copying a template into an existing instance is not supported at this time.');
81
+ core_2.ux.error("Directus instance is not empty. Please use a blank instance. Copying a template into an existing instance is not supported at this time.");
71
82
  }
72
83
  // Run load script
73
84
  core_2.ux.action.start(`Applying template - ${chosenTemplate.template.templateName}`);
74
85
  await (0, load_1.default)(chosenTemplate.template.directoryPath, this);
75
86
  core_2.ux.action.stop();
76
87
  this.log(separator);
77
- this.log('Template applied successfully.');
78
- this.exit;
88
+ this.log("Template applied successfully.");
89
+ this.exit(0);
79
90
  }
80
91
  }
81
92
  exports.default = ApplyCommand;
82
- ApplyCommand.description = 'Apply a template to a blank Directus instance.';
83
- ApplyCommand.examples = [
84
- '$ directus-template-cli apply',
85
- ];
93
+ ApplyCommand.description = "Apply a template to a blank Directus instance.";
94
+ ApplyCommand.examples = ["$ directus-template-cli apply"];
86
95
  ApplyCommand.flags = {};
@@ -0,0 +1,7 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class ExtractCommand extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {};
6
+ run(): Promise<void>;
7
+ }
@@ -0,0 +1,54 @@
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 core_2 = require("@oclif/core");
6
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
7
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
8
+ const api_1 = require("../lib/api");
9
+ const extract_1 = tslib_1.__importDefault(require("../lib/extract/"));
10
+ const auth_1 = require("../lib/utils/auth");
11
+ const template_defaults_1 = require("../lib/utils/template-defaults");
12
+ const separator = "------------------";
13
+ class ExtractCommand extends core_1.Command {
14
+ async run() {
15
+ const { flags } = await this.parse(ExtractCommand);
16
+ const templateName = await core_2.ux.prompt("What is the name of the template?.");
17
+ const directory = await core_2.ux.prompt("What directory would you like to extract the template to? If it doesn't exist, it will be created.");
18
+ this.log(`You selected ${directory}`);
19
+ try {
20
+ // Check if directory exists, if not, then create it.
21
+ if (!node_fs_1.default.existsSync(directory)) {
22
+ node_fs_1.default.mkdirSync(directory, { recursive: true });
23
+ }
24
+ // Create package.json and README.md
25
+ const packageJSONContent = (0, template_defaults_1.generatePackageJsonContent)(templateName);
26
+ const readmeContent = (0, template_defaults_1.generateReadmeContent)(templateName);
27
+ // Write the content to the specified directory
28
+ const packageJSONPath = node_path_1.default.join(directory, "package.json");
29
+ const readmePath = node_path_1.default.join(directory, "README.md");
30
+ node_fs_1.default.writeFileSync(packageJSONPath, packageJSONContent);
31
+ node_fs_1.default.writeFileSync(readmePath, readmeContent);
32
+ }
33
+ catch (error) {
34
+ console.error(`Failed to create directory or write files: ${error.message}`);
35
+ }
36
+ this.log(separator);
37
+ const directusUrl = await (0, auth_1.getDirectusUrl)();
38
+ api_1.api.setBaseUrl(directusUrl);
39
+ const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
40
+ api_1.api.setAuthToken(directusToken);
41
+ this.log(separator);
42
+ // Run the extract script
43
+ core_2.ux.action.start(`Extracting template - from ${directusUrl} to ${directory}`);
44
+ await (0, extract_1.default)(directory, this);
45
+ core_2.ux.action.stop();
46
+ this.log(separator);
47
+ this.log("Template extracted successfully.");
48
+ this.exit(0);
49
+ }
50
+ }
51
+ exports.default = ExtractCommand;
52
+ ExtractCommand.description = "Extract a template from a Directus instance.";
53
+ ExtractCommand.examples = ["$ directus-template-cli extract"];
54
+ ExtractCommand.flags = {};
@@ -0,0 +1,3 @@
1
+ export declare function getAssetList(): Promise<any>;
2
+ export declare function downloadFile(file: any, dir: string): Promise<unknown>;
3
+ export declare function downloadAllFiles(dir: string): Promise<void>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.downloadAllFiles = exports.downloadFile = exports.getAssetList = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
6
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
7
+ const api_1 = require("../api");
8
+ async function getAssetList() {
9
+ const { data } = await api_1.api.get("/files", {
10
+ params: {
11
+ limit: "-1",
12
+ },
13
+ });
14
+ return data.data;
15
+ }
16
+ exports.getAssetList = getAssetList;
17
+ async function downloadFile(file, dir) {
18
+ const response = await api_1.api.get(`assets/${file.id}`, {
19
+ responseType: "stream",
20
+ });
21
+ // Create assets folder if it doesn't exist
22
+ const fullPath = node_path_1.default.join(dir, "assets");
23
+ if (node_path_1.default && !node_fs_1.default.existsSync(fullPath)) {
24
+ node_fs_1.default.mkdirSync(fullPath, { recursive: true });
25
+ }
26
+ const writePath = node_path_1.default.resolve(dir, "assets", file.filename_disk);
27
+ const writer = node_fs_1.default.createWriteStream(writePath);
28
+ response.data.pipe(writer);
29
+ return new Promise((resolve, reject) => {
30
+ writer.on("finish", () => {
31
+ // console.log(`Wrote ${file.filename_disk}`);
32
+ resolve(null);
33
+ });
34
+ writer.on("error", reject);
35
+ });
36
+ }
37
+ exports.downloadFile = downloadFile;
38
+ async function downloadAllFiles(dir) {
39
+ const fileList = await getAssetList();
40
+ for (const file of fileList) {
41
+ try {
42
+ await downloadFile(file, dir);
43
+ }
44
+ catch (error) {
45
+ console.log(`Error downloading ${file.filename_disk}`, error);
46
+ }
47
+ }
48
+ }
49
+ exports.downloadAllFiles = downloadAllFiles;
@@ -0,0 +1,3 @@
1
+ export declare function getCollections(): Promise<any>;
2
+ export declare function getDataFromCollection(collection: string, dir: string): Promise<void>;
3
+ export declare function extractContent(dir: string): Promise<void>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractContent = exports.getDataFromCollection = exports.getCollections = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const api_1 = require("../api");
6
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
7
+ async function getCollections() {
8
+ const { data } = await api_1.api.get("/collections");
9
+ const collections = data.data
10
+ .filter((item) => !item.collection.startsWith("directus_", 0))
11
+ .filter((item) => item.schema != null)
12
+ .map((i) => i.collection);
13
+ return collections;
14
+ }
15
+ exports.getCollections = getCollections;
16
+ async function getDataFromCollection(collection, dir) {
17
+ try {
18
+ const { data } = await api_1.api.get(`items/${collection}`); // ADD limit = -1
19
+ (0, write_to_file_1.default)(`${collection}`, data.data, `${dir}/content/`);
20
+ }
21
+ catch {
22
+ console.log(`error getting items from ${collection}`);
23
+ // Errors are thrown for 'folder' collections
24
+ }
25
+ }
26
+ exports.getDataFromCollection = getDataFromCollection;
27
+ async function extractContent(dir) {
28
+ const collections = await getCollections();
29
+ for (const collection of collections) {
30
+ await getDataFromCollection(collection, dir);
31
+ }
32
+ }
33
+ exports.extractContent = extractContent;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Extract files from the API
3
+ */
4
+ export default function extractFiles(dir: string): Promise<void>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ const filter_fields_1 = tslib_1.__importDefault(require("../utils/filter-fields"));
7
+ const systemFields = [
8
+ "id",
9
+ "storage",
10
+ "filename_disk",
11
+ "filename_download",
12
+ "title",
13
+ "type",
14
+ "folder",
15
+ "uploaded_by",
16
+ "uploaded_on",
17
+ "modified_by",
18
+ "modified_on",
19
+ "charset",
20
+ "filesize",
21
+ "width",
22
+ "height",
23
+ "duration",
24
+ "embed",
25
+ "description",
26
+ "location",
27
+ "tags",
28
+ "metadata",
29
+ ];
30
+ /**
31
+ * Extract files from the API
32
+ */
33
+ async function extractFiles(dir) {
34
+ try {
35
+ const { data } = await api_1.api.get("/files", {
36
+ params: {
37
+ limit: "-1",
38
+ },
39
+ });
40
+ const filteredData = (0, filter_fields_1.default)(data.data, systemFields);
41
+ // Use the dynamic dir parameter
42
+ await (0, write_to_file_1.default)("files", filteredData, dir);
43
+ }
44
+ catch (error) {
45
+ console.log("Error extracting Files:", error.response.data.errors);
46
+ }
47
+ }
48
+ exports.default = extractFiles;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Extract folders from the API
3
+ */
4
+ export default function extractFolders(dir: string): Promise<void>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ const filter_fields_1 = tslib_1.__importDefault(require("../utils/filter-fields"));
7
+ const systemFields = ["id", "name", "parent"];
8
+ /**
9
+ * Extract folders from the API
10
+ */
11
+ async function extractFolders(dir) {
12
+ try {
13
+ const { data } = await api_1.api.get("/folders", {
14
+ params: {
15
+ limit: "-1",
16
+ },
17
+ });
18
+ const filteredData = (0, filter_fields_1.default)(data.data, systemFields);
19
+ // Use the dynamic dir parameter
20
+ await (0, write_to_file_1.default)("folders", filteredData, dir);
21
+ }
22
+ catch (error) {
23
+ console.log("Error extracting Folders:", error.response.data.errors);
24
+ }
25
+ }
26
+ exports.default = extractFolders;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * [default query an endpoint and write the result to file]
3
+ */
4
+ export default function extractFromEndpoint(path: string, dir: string): Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ /**
7
+ * [default query an endpoint and write the result to file]
8
+ */
9
+ async function extractFromEndpoint(path, dir) {
10
+ try {
11
+ const { data } = await api_1.api.get(`/${path}`, {
12
+ params: {
13
+ limit: "-1",
14
+ },
15
+ });
16
+ // Use the dynamic dir parameter
17
+ await (0, write_to_file_1.default)(`${path}`, data.data, dir);
18
+ }
19
+ catch (error) {
20
+ console.log(`Error querying endpoint ${path}:`, error.response.data.errors);
21
+ }
22
+ }
23
+ exports.default = extractFromEndpoint;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Extract roles from the API
3
+ */
4
+ export default function extractRoles(dir: string): Promise<void>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ const filter_fields_1 = tslib_1.__importDefault(require("../utils/filter-fields"));
7
+ const systemFields = [
8
+ "id",
9
+ "name",
10
+ "description",
11
+ "icon",
12
+ "enforce_tfa",
13
+ "external_id",
14
+ "ip_whitelist",
15
+ "app_access",
16
+ "admin_access",
17
+ ];
18
+ /**
19
+ * Extract roles from the API
20
+ */
21
+ async function extractRoles(dir) {
22
+ try {
23
+ const { data } = await api_1.api.get("/roles", {
24
+ params: {
25
+ limit: "-1",
26
+ },
27
+ });
28
+ const filteredData = (0, filter_fields_1.default)(data.data, systemFields);
29
+ // Use the dynamic dir parameter
30
+ await (0, write_to_file_1.default)("roles", filteredData, dir);
31
+ }
32
+ catch (error) {
33
+ console.log("Error extracting Roles:", error.response.data.errors);
34
+ }
35
+ }
36
+ exports.default = extractRoles;
@@ -0,0 +1 @@
1
+ export default function extractSchema(dir: string): Promise<void>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
5
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
+ const api_1 = require("../api");
7
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
8
+ async function extractSchema(dir) {
9
+ const schemaDir = node_path_1.default.join(dir, "schema");
10
+ // Check if directory for schema exists, if not, then create it.
11
+ if (!node_fs_1.default.existsSync(schemaDir)) {
12
+ console.log(`Attempting to create directory at: ${schemaDir}`);
13
+ node_fs_1.default.mkdirSync(schemaDir, { recursive: true });
14
+ }
15
+ // Get the schema
16
+ try {
17
+ const { data } = await api_1.api.get("/schema/snapshot", {
18
+ params: {
19
+ limit: "-1",
20
+ },
21
+ });
22
+ // Write the schema to the specified directory
23
+ await (0, write_to_file_1.default)("schema/snapshot", data.data, dir);
24
+ }
25
+ catch (error) {
26
+ console.log("Error fetching schema snapshot:", error);
27
+ }
28
+ }
29
+ exports.default = extractSchema;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Extract users from the API
3
+ */
4
+ export default function extractUsers(dir: string): Promise<void>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ const filter_fields_1 = tslib_1.__importDefault(require("../utils/filter-fields"));
7
+ const systemFields = [
8
+ "id",
9
+ "status",
10
+ "first_name",
11
+ "last_name",
12
+ "email",
13
+ "password",
14
+ "token",
15
+ "last_access",
16
+ "last_page",
17
+ "external_identifier",
18
+ "tfa_secret",
19
+ "auth_data",
20
+ "provider",
21
+ "theme",
22
+ "role",
23
+ "language",
24
+ "avatar",
25
+ "title",
26
+ "description",
27
+ "location",
28
+ "tags",
29
+ "email_notifications",
30
+ ];
31
+ /**
32
+ * Extract users from the API
33
+ */
34
+ async function extractUsers(dir) {
35
+ try {
36
+ const { data } = await api_1.api.get("/users", {
37
+ params: {
38
+ limit: "-1",
39
+ },
40
+ });
41
+ const filteredData = (0, filter_fields_1.default)(data.data, systemFields);
42
+ // Use the dynamic dir parameter
43
+ await (0, write_to_file_1.default)("users", filteredData, dir);
44
+ }
45
+ catch (error) {
46
+ console.log("Error extracting Users:", error.response.data.errors);
47
+ }
48
+ }
49
+ exports.default = extractUsers;
@@ -0,0 +1 @@
1
+ export default function extract(dir: string, cli: any): Promise<{}>;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
5
+ const extract_assets_1 = require("./extract-assets");
6
+ const extract_schema_1 = tslib_1.__importDefault(require("./extract-schema"));
7
+ const extract_from_endpoint_1 = tslib_1.__importDefault(require("./extract-from-endpoint"));
8
+ const public_permissions_1 = tslib_1.__importDefault(require("./public-permissions"));
9
+ const extract_content_1 = require("./extract-content");
10
+ const extract_folders_1 = tslib_1.__importDefault(require("./extract-folders"));
11
+ const extract_users_1 = tslib_1.__importDefault(require("./extract-users"));
12
+ const extract_roles_1 = tslib_1.__importDefault(require("./extract-roles"));
13
+ const extract_files_1 = tslib_1.__importDefault(require("./extract-files"));
14
+ const endpoints = [
15
+ // "folders",
16
+ // "fields",
17
+ // "users",
18
+ // "roles",
19
+ // "files",
20
+ "operations",
21
+ "permissions",
22
+ "collections",
23
+ "flows",
24
+ "dashboards",
25
+ "panels",
26
+ "presets",
27
+ "settings",
28
+ ];
29
+ async function extract(dir, cli) {
30
+ // Get the destination directory for the actual files
31
+ const destination = dir + "/src";
32
+ // Check if directory exists, if not, then create it.
33
+ if (!node_fs_1.default.existsSync(destination)) {
34
+ console.log(`Attempting to create directory at: ${destination}`);
35
+ node_fs_1.default.mkdirSync(destination, { recursive: true });
36
+ }
37
+ // Extract the schema
38
+ await (0, extract_schema_1.default)(destination);
39
+ await (0, extract_folders_1.default)(destination);
40
+ await (0, extract_users_1.default)(destination);
41
+ await (0, extract_roles_1.default)(destination);
42
+ await (0, extract_files_1.default)(destination);
43
+ // Iterate through the endpoints
44
+ for (const endpoint of endpoints) {
45
+ await (0, extract_from_endpoint_1.default)(endpoint, destination);
46
+ }
47
+ // Extract public permissions
48
+ await (0, public_permissions_1.default)(destination);
49
+ // Extract content
50
+ await (0, extract_content_1.extractContent)(destination);
51
+ // Extract assets
52
+ await (0, extract_assets_1.downloadAllFiles)(destination);
53
+ return {};
54
+ }
55
+ exports.default = extract;
@@ -0,0 +1 @@
1
+ export default function extractPublicPermissions(dir: string): Promise<void>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const api_1 = require("../api");
5
+ const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
6
+ async function extractPublicPermissions(dir) {
7
+ try {
8
+ const { data } = await api_1.api.get("permissions", {
9
+ params: {
10
+ limit: "-1",
11
+ "filter[role][_null]": true,
12
+ },
13
+ });
14
+ // Write the public permissions to the specified directory
15
+ await (0, write_to_file_1.default)("public-permissions", data.data, dir);
16
+ }
17
+ catch (error) {
18
+ console.log("Error fetching public permissions:", error);
19
+ }
20
+ }
21
+ exports.default = extractPublicPermissions;
@@ -7,43 +7,46 @@ const load_schema_1 = tslib_1.__importDefault(require("./load-schema"));
7
7
  const load_roles_1 = tslib_1.__importDefault(require("./load-roles"));
8
8
  const load_dashboards_1 = tslib_1.__importDefault(require("./load-dashboards"));
9
9
  const load_files_1 = tslib_1.__importDefault(require("./load-files"));
10
+ const load_folders_1 = tslib_1.__importDefault(require("./load-folders"));
10
11
  const load_users_1 = tslib_1.__importDefault(require("./load-users"));
11
12
  const load_flows_1 = tslib_1.__importDefault(require("./load-flows"));
12
13
  const load_operations_1 = tslib_1.__importDefault(require("./load-operations"));
13
14
  const load_data_1 = tslib_1.__importDefault(require("./load-data"));
14
15
  const load_presets_1 = tslib_1.__importDefault(require("./load-presets"));
15
16
  const load_settings_1 = tslib_1.__importDefault(require("./load-settings"));
16
- const load_public_permissions_1 = tslib_1.__importDefault(require("./load-public-permissions"));
17
+ const load_public_permissions_1 = require("./load-public-permissions");
17
18
  async function apply(dir, cli) {
18
19
  // Get the source directory for the actual files
19
- const source = dir + '/src';
20
+ const source = dir + "/src";
20
21
  // Load the template files into the destination
21
- await (0, load_schema_1.default)(source + '/schema');
22
- cli.log('Loaded Schema');
23
- await (0, load_roles_1.default)((0, read_file_1.default)('roles', source));
24
- cli.log('Loaded Roles');
25
- await (0, load_to_destination_1.default)('folders', (0, read_file_1.default)('folders', source));
26
- cli.log('Loaded Folders');
27
- await (0, load_dashboards_1.default)((0, read_file_1.default)('dashboards', source));
28
- cli.log('Loaded Dashboards');
29
- await (0, load_to_destination_1.default)('panels', (0, read_file_1.default)('panels', source)); // Comes after dashboards
30
- cli.log('Loaded Panels');
31
- await (0, load_files_1.default)((0, read_file_1.default)('files', source), source); // Comes after folders
32
- cli.log('Loaded Files');
33
- await (0, load_users_1.default)((0, read_file_1.default)('users', source)); // Comes after roles, files
34
- cli.log('Loaded Users');
35
- await (0, load_flows_1.default)((0, read_file_1.default)('flows', source));
36
- cli.log('Loaded Flows');
37
- await (0, load_operations_1.default)((0, read_file_1.default)('operations', source)); // Comes after flows
38
- cli.log('Loaded Operations');
39
- await (0, load_data_1.default)((0, read_file_1.default)('collections', source), source);
40
- cli.log('Loaded Data');
41
- await (0, load_presets_1.default)((0, read_file_1.default)('presets', source));
42
- cli.log('Loaded Presets');
43
- await (0, load_settings_1.default)((0, read_file_1.default)('settings', source));
44
- cli.log('Loaded Settings');
45
- await (0, load_public_permissions_1.default)((0, read_file_1.default)('public-permissions', source));
46
- cli.log('Loaded Public Permissions');
22
+ await (0, load_schema_1.default)(source + "/schema");
23
+ cli.log("Loaded Schema");
24
+ await (0, load_roles_1.default)((0, read_file_1.default)("roles", source));
25
+ cli.log("Loaded Roles");
26
+ await (0, load_files_1.default)((0, read_file_1.default)("files", source), source); // Comes after folders
27
+ cli.log("Loaded Files");
28
+ await (0, load_users_1.default)((0, read_file_1.default)("users", source)); // Comes after roles, files
29
+ cli.log("Loaded Users");
30
+ await (0, load_folders_1.default)(source);
31
+ cli.log("Loaded Folders");
32
+ await (0, load_dashboards_1.default)((0, read_file_1.default)("dashboards", source));
33
+ cli.log("Loaded Dashboards");
34
+ await (0, load_to_destination_1.default)("panels", (0, read_file_1.default)("panels", source)); // Comes after dashboards
35
+ cli.log("Loaded Panels");
36
+ await (0, load_flows_1.default)((0, read_file_1.default)("flows", source));
37
+ cli.log("Loaded Flows");
38
+ await (0, load_operations_1.default)((0, read_file_1.default)("operations", source)); // Comes after flows
39
+ cli.log("Loaded Operations");
40
+ await (0, load_data_1.default)((0, read_file_1.default)("collections", source), source);
41
+ cli.log("Loaded Data");
42
+ await (0, load_presets_1.default)((0, read_file_1.default)("presets", source));
43
+ cli.log("Loaded Presets");
44
+ await (0, load_settings_1.default)((0, read_file_1.default)("settings", source));
45
+ cli.log("Loaded Settings");
46
+ await (0, load_public_permissions_1.loadPublicPermissions)((0, read_file_1.default)("public-permissions", source));
47
+ cli.log("Loaded Public Permissions");
48
+ await (0, load_public_permissions_1.loadPermissions)((0, read_file_1.default)("permissions", source));
49
+ cli.log("Loaded Permissions");
47
50
  return {};
48
51
  }
49
52
  exports.default = apply;
@@ -5,12 +5,20 @@ const api_1 = require("../api");
5
5
  const read_file_1 = tslib_1.__importDefault(require("../utils/read-file"));
6
6
  async function loadFolders(dir) {
7
7
  try {
8
- const folders = await (0, read_file_1.default)('folders', dir);
9
- const { data } = await api_1.api.post('/folders', folders);
10
- console.log('Folder creation', data);
8
+ const folders = await (0, read_file_1.default)("folders", dir);
9
+ const folderSkeleton = folders.map((folder) => {
10
+ return { id: folder.id, name: folder.name };
11
+ });
12
+ // Create the folders
13
+ const { data } = await api_1.api.post("/folders", folderSkeleton);
14
+ // Loop through the folders and update them with relationships
15
+ folders.forEach(async (folder) => {
16
+ const { id, ...rest } = folder;
17
+ await api_1.api.patch(`/folders/${id}`, rest);
18
+ });
11
19
  }
12
20
  catch (error) {
13
- console.log('Error loading Folders', error.response.data.errors);
21
+ console.log("Error loading Folders", error.response.data.errors);
14
22
  }
15
23
  }
16
24
  exports.default = loadFolders;
@@ -2,16 +2,27 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const api_1 = require("../api");
4
4
  exports.default = async (presets) => {
5
- const cleanPresets = presets.map(preset => {
5
+ await deleteAllPresets();
6
+ const cleanPresets = presets.map((preset) => {
6
7
  preset.user = null;
7
8
  return preset;
8
9
  });
9
- for (const preset of cleanPresets) {
10
- try {
11
- await api_1.api.post('presets', preset);
12
- }
13
- catch (error) {
14
- console.log('Error uploading preset', error.response.data.errors);
15
- }
10
+ try {
11
+ await api_1.api.post("presets", cleanPresets);
12
+ }
13
+ catch (error) {
14
+ console.log("Error uploading preset", error.response.data.errors);
15
+ }
16
+ };
17
+ const deleteAllPresets = async () => {
18
+ try {
19
+ const { data: presets } = await api_1.api.get("presets");
20
+ const ids = presets.data.map((preset) => preset.id);
21
+ await api_1.api.delete("presets", {
22
+ data: ids,
23
+ });
24
+ }
25
+ catch (error) {
26
+ console.log("Error removing existing presets", error.response.data.errors);
16
27
  }
17
28
  };
@@ -1 +1,2 @@
1
- export default function loadPublicPermissions(roles: any): Promise<void>;
1
+ export declare function loadPublicPermissions(roles: any): Promise<void>;
2
+ export declare function loadPermissions(roles: any): Promise<void>;
@@ -1,19 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadPermissions = exports.loadPublicPermissions = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  const api_1 = require("../api");
5
6
  const load_to_destination_1 = tslib_1.__importDefault(require("../utils/load-to-destination"));
6
7
  async function removeallPublicPermissions() {
7
- const { data } = await api_1.api.get('permissions?filter[role][_null]=true&limit=-1');
8
- const ids = data.data.map(i => i.id);
8
+ const { data } = await api_1.api.get("permissions?filter[role][_null]=true&limit=-1");
9
+ console.log("Removing all public permissions", data.data);
10
+ const ids = data.data.map((i) => i.id);
9
11
  if (!ids)
10
12
  return;
11
- await api_1.api.delete('permissions', {
13
+ await api_1.api.delete("permissions", {
12
14
  data: ids,
13
15
  });
14
16
  }
15
17
  async function loadPublicPermissions(roles) {
16
18
  await removeallPublicPermissions();
17
- await (0, load_to_destination_1.default)('permissions', roles);
19
+ await (0, load_to_destination_1.default)("permissions", roles);
18
20
  }
19
- exports.default = loadPublicPermissions;
21
+ exports.loadPublicPermissions = loadPublicPermissions;
22
+ async function loadPermissions(roles) {
23
+ await (0, load_to_destination_1.default)("permissions", roles);
24
+ }
25
+ exports.loadPermissions = loadPermissions;
@@ -0,0 +1,2 @@
1
+ export declare function getDirectusUrl(): any;
2
+ export declare function getDirectusToken(directusUrl: string): any;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDirectusToken = exports.getDirectusUrl = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const api_1 = require("../api");
7
+ const validate_url_1 = tslib_1.__importDefault(require("./validate-url"));
8
+ async function getDirectusUrl() {
9
+ const directusUrl = await core_1.ux.prompt("What is your Directus URL?");
10
+ // Validate URL
11
+ if (!(0, validate_url_1.default)(directusUrl)) {
12
+ core_1.ux.warn("Invalid URL");
13
+ return getDirectusUrl();
14
+ }
15
+ return directusUrl;
16
+ }
17
+ exports.getDirectusUrl = getDirectusUrl;
18
+ async function getDirectusToken(directusUrl) {
19
+ const directusToken = await core_1.ux.prompt("What is your Directus Admin Token?");
20
+ // Validate token
21
+ try {
22
+ await api_1.api.get("/users/me", {
23
+ headers: {
24
+ Authorization: `Bearer ${directusToken}`,
25
+ },
26
+ });
27
+ return directusToken;
28
+ }
29
+ catch {
30
+ core_1.ux.warn("Invalid token");
31
+ return getDirectusToken(directusUrl);
32
+ }
33
+ }
34
+ exports.getDirectusToken = getDirectusToken;
@@ -0,0 +1 @@
1
+ export default function filterFields(dataArray: any, systemFields: any): any;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Utility function to filter out non-system fields
4
+ function filterFields(dataArray, systemFields) {
5
+ return dataArray.map((item) => {
6
+ for (const key of Object.keys(item)) {
7
+ if (!systemFields.includes(key)) {
8
+ const value = item[key];
9
+ if (Array.isArray(value)) {
10
+ const isArrayOfIntegers = value.every((v) => Number.isInteger(v));
11
+ const isArrayOfUUIDs = value.every((v) => typeof v === "string" &&
12
+ /[\dA-Fa-f]{8}(?:-[\dA-Fa-f]{4}){3}-[\dA-Fa-f]{12}/.test(v));
13
+ if (isArrayOfIntegers || isArrayOfUUIDs) {
14
+ item[key] = null; // or item[key] = [];
15
+ }
16
+ }
17
+ }
18
+ }
19
+ return item;
20
+ });
21
+ }
22
+ exports.default = filterFields;
@@ -2,5 +2,6 @@ interface Template {
2
2
  directoryPath: string;
3
3
  templateName: string;
4
4
  }
5
- export default function readTemplates(directoryPath: string): Promise<Template[]>;
5
+ export declare function readTemplate(directoryPath: string): Promise<Template | null>;
6
+ export declare function readAllTemplates(directoryPath: string): Promise<Template[]>;
6
7
  export {};
@@ -1,36 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readAllTemplates = exports.readTemplate = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
5
6
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
- async function readTemplates(directoryPath) {
7
+ async function readTemplate(directoryPath) {
8
+ const packageFilePath = node_path_1.default.join(directoryPath, "package.json");
9
+ try {
10
+ const packageData = await node_fs_1.default.promises.readFile(packageFilePath, "utf-8");
11
+ const packageJson = JSON.parse(packageData);
12
+ if (packageJson.templateName) {
13
+ return {
14
+ directoryPath,
15
+ templateName: packageJson.templateName,
16
+ };
17
+ }
18
+ return null;
19
+ }
20
+ catch (error) {
21
+ console.error(`Failed to read package.json file in directory ${directoryPath}: ${error}`);
22
+ return null;
23
+ }
24
+ }
25
+ exports.readTemplate = readTemplate;
26
+ async function readAllTemplates(directoryPath) {
7
27
  const templates = [];
8
- // Read the contents of the directory
9
28
  const files = await node_fs_1.default.promises.readdir(directoryPath);
10
- // Loop through each file in the directory
11
29
  for (const file of files) {
12
- // Check if the file is a directory
13
30
  const filePath = node_path_1.default.join(directoryPath, file);
14
31
  const stats = await node_fs_1.default.promises.stat(filePath);
15
32
  if (stats.isDirectory()) {
16
- // Read the contents of the package.json file in the directory
17
- const packageFilePath = node_path_1.default.join(filePath, 'package.json');
18
- try {
19
- const packageData = await node_fs_1.default.promises.readFile(packageFilePath, 'utf-8');
20
- const packageJson = JSON.parse(packageData);
21
- if (packageJson.templateName) {
22
- const template = {
23
- directoryPath: filePath,
24
- templateName: packageJson.templateName,
25
- };
26
- templates.push(template);
27
- }
28
- }
29
- catch (error) {
30
- console.error(`Failed to read package.json file in directory ${filePath}: ${error}`);
33
+ const template = await readTemplate(filePath);
34
+ if (template) {
35
+ templates.push(template);
31
36
  }
32
37
  }
33
38
  }
34
39
  return templates;
35
40
  }
36
- exports.default = readTemplates;
41
+ exports.readAllTemplates = readAllTemplates;
@@ -0,0 +1,2 @@
1
+ export declare const generatePackageJsonContent: (templateName: string) => string;
2
+ export declare const generateReadmeContent: (templateName: string) => string;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateReadmeContent = exports.generatePackageJsonContent = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const slugify_1 = tslib_1.__importDefault(require("slugify"));
6
+ const generatePackageJsonContent = (templateName) => {
7
+ const slugifiedName = (0, slugify_1.default)(templateName, {
8
+ lower: true,
9
+ strict: true, // Remove special characters
10
+ });
11
+ const packageName = `directus-template-${slugifiedName}`;
12
+ return JSON.stringify({
13
+ name: packageName,
14
+ templateName: templateName,
15
+ version: "1.0.0",
16
+ description: "",
17
+ author: "",
18
+ license: "MIT",
19
+ files: ["src"],
20
+ }, null, 2);
21
+ };
22
+ exports.generatePackageJsonContent = generatePackageJsonContent;
23
+ const generateReadmeContent = (templateName) => {
24
+ return `# ${templateName} Template
25
+
26
+ This is a template for [Directus](https://directus.io/) - an open-source headless CMS and API. Use the template-cli to load / apply this template to a blank instance.
27
+
28
+ ## Why
29
+
30
+ ## What
31
+
32
+ ## License
33
+
34
+ This template is licensed under the [MIT License](https://opensource.org/licenses/MIT).
35
+ `;
36
+ };
37
+ exports.generateReadmeContent = generateReadmeContent;
@@ -1,2 +1,2 @@
1
- declare const _default: (fileName: string, data: any) => Promise<void>;
1
+ declare const _default: (fileName: string, data: any, dir: string) => Promise<void>;
2
2
  export default _default;
@@ -3,20 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
5
  const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
6
- const dir = node_path_1.default.join(__dirname, '..', 'source');
7
- exports.default = async (fileName, data) => {
8
- const folders = fileName.split('/');
6
+ exports.default = async (fileName, data, dir) => {
7
+ const folders = fileName.split("/");
9
8
  const endFileName = folders.pop();
10
- const folderPath = folders.join('/');
9
+ const folderPath = folders.join("/");
10
+ // Generate the full path where you want to write the file
11
11
  const fullPath = node_path_1.default.join(dir, folderPath);
12
- if (node_path_1.default && !node_fs_1.default.existsSync(fullPath)) {
13
- node_fs_1.default.mkdirSync(fullPath);
12
+ // Check if the directory exists. Create if it doesn't.
13
+ if (!node_fs_1.default.existsSync(fullPath)) {
14
+ node_fs_1.default.mkdirSync(fullPath, { recursive: true });
14
15
  }
16
+ // Construct the full file path
17
+ const fullFilePath = node_path_1.default.join(fullPath, `${endFileName}.json`);
15
18
  try {
16
- await node_fs_1.default.promises.writeFile(`${dir}/${fileName}.json`, JSON.stringify(data, null, 2));
17
- console.log(`Wrote ${fileName}`);
19
+ // Write the file
20
+ await node_fs_1.default.promises.writeFile(fullFilePath, JSON.stringify(data, null, 2));
21
+ // console.log(`Wrote ${fullFilePath}`);
18
22
  }
19
23
  catch (error) {
20
- console.log('error writing to file', error);
24
+ console.log("Error writing to file", error.data.errors);
21
25
  }
22
26
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.0",
2
+ "version": "0.3.0-beta.2",
3
3
  "commands": {
4
4
  "apply": {
5
5
  "id": "apply",
@@ -14,6 +14,20 @@
14
14
  ],
15
15
  "flags": {},
16
16
  "args": {}
17
+ },
18
+ "extract": {
19
+ "id": "extract",
20
+ "description": "Extract a template from a Directus instance.",
21
+ "strict": true,
22
+ "pluginName": "directus-template-cli",
23
+ "pluginAlias": "directus-template-cli",
24
+ "pluginType": "core",
25
+ "aliases": [],
26
+ "examples": [
27
+ "$ directus-template-cli extract"
28
+ ],
29
+ "flags": {},
30
+ "args": {}
17
31
  }
18
32
  }
19
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus-template-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0-beta.2",
4
4
  "description": "CLI Utility for applying templates to a Directus instance.",
5
5
  "author": "bryantgillespie @bryantgillespie",
6
6
  "bin": {
@@ -26,7 +26,8 @@
26
26
  "dotenv": "^16.0.3",
27
27
  "formdata-node": "^5.0.0",
28
28
  "generate-password": "^1.7.0",
29
- "inquirer": "^8.2.5"
29
+ "inquirer": "^8.2.5",
30
+ "slugify": "^1.6.6"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@oclif/test": "^2.3.16",