directus-template-cli 0.7.8 → 0.8.0

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 (55) hide show
  1. package/README.md +134 -37
  2. package/dist/commands/apply.d.ts +5 -0
  3. package/dist/commands/apply.js +32 -68
  4. package/dist/commands/extract.d.ts +30 -0
  5. package/dist/commands/extract.js +14 -5
  6. package/dist/commands/init.d.ts +4 -0
  7. package/dist/commands/init.js +90 -87
  8. package/dist/lib/extract/expand-deep-plan.d.ts +2 -0
  9. package/dist/lib/extract/expand-deep-plan.js +54 -0
  10. package/dist/lib/extract/expand-schema-plan.d.ts +2 -0
  11. package/dist/lib/extract/expand-schema-plan.js +55 -0
  12. package/dist/lib/extract/extract-assets.js +19 -6
  13. package/dist/lib/extract/extract-collections.d.ts +2 -1
  14. package/dist/lib/extract/extract-collections.js +5 -2
  15. package/dist/lib/extract/extract-content.d.ts +2 -1
  16. package/dist/lib/extract/extract-content.js +108 -13
  17. package/dist/lib/extract/extract-fields.d.ts +2 -1
  18. package/dist/lib/extract/extract-fields.js +5 -5
  19. package/dist/lib/extract/extract-relations.d.ts +2 -1
  20. package/dist/lib/extract/extract-relations.js +6 -4
  21. package/dist/lib/extract/index.d.ts +2 -1
  22. package/dist/lib/extract/index.js +67 -29
  23. package/dist/lib/init/index.d.ts +15 -0
  24. package/dist/lib/init/index.js +29 -13
  25. package/dist/lib/load/apply-flags.d.ts +5 -2
  26. package/dist/lib/load/apply-flags.js +0 -50
  27. package/dist/lib/load/finalize-collections.d.ts +2 -0
  28. package/dist/lib/load/finalize-collections.js +28 -0
  29. package/dist/lib/load/finalize-fields.d.ts +2 -0
  30. package/dist/lib/load/finalize-fields.js +25 -0
  31. package/dist/lib/load/index.js +38 -18
  32. package/dist/lib/load/load-collections.d.ts +2 -1
  33. package/dist/lib/load/load-collections.js +17 -30
  34. package/dist/lib/load/load-data.d.ts +3 -1
  35. package/dist/lib/load/load-data.js +47 -34
  36. package/dist/lib/load/load-relations.d.ts +2 -1
  37. package/dist/lib/load/load-relations.js +17 -7
  38. package/dist/lib/template-plan/collections.d.ts +4 -0
  39. package/dist/lib/template-plan/collections.js +26 -0
  40. package/dist/lib/template-plan/flags.d.ts +18 -0
  41. package/dist/lib/template-plan/flags.js +61 -0
  42. package/dist/lib/template-plan/index.d.ts +16 -0
  43. package/dist/lib/template-plan/index.js +77 -0
  44. package/dist/lib/template-plan/junctions.d.ts +10 -0
  45. package/dist/lib/template-plan/junctions.js +19 -0
  46. package/dist/lib/template-plan/metadata-plan.d.ts +2 -0
  47. package/dist/lib/template-plan/metadata-plan.js +36 -0
  48. package/dist/lib/template-plan/metadata.d.ts +5 -0
  49. package/dist/lib/template-plan/metadata.js +42 -0
  50. package/dist/lib/template-plan/types.d.ts +34 -0
  51. package/dist/lib/template-plan/types.js +1 -0
  52. package/oclif.manifest.json +173 -16
  53. package/package.json +1 -1
  54. package/dist/lib/load/update-required-fields.d.ts +0 -1
  55. package/dist/lib/load/update-required-fields.js +0 -20
@@ -2,12 +2,13 @@ import { readFields, readRelations } from '@directus/sdk';
2
2
  import { ux } from '@oclif/core';
3
3
  import { DIRECTUS_PINK } from '../constants.js';
4
4
  import { api } from '../sdk.js';
5
+ import { includesRelation } from '../template-plan/index.js';
5
6
  import catchError from '../utils/catch-error.js';
6
7
  import writeToFile from '../utils/write-to-file.js';
7
8
  /**
8
9
  * Extract relations from the Directus instance
9
10
  */
10
- export default async function extractRelations(dir) {
11
+ export default async function extractRelations(dir, plan) {
11
12
  ux.action.start(ux.colorize(DIRECTUS_PINK, 'Extracting relations'));
12
13
  try {
13
14
  const response = await api.client.request(readRelations());
@@ -16,9 +17,10 @@ export default async function extractRelations(dir) {
16
17
  const customFields = fields.filter((i) => !i.meta?.system);
17
18
  const relations = response
18
19
  // Filter out relations where the collection starts with 'directus_' && the field is not within the customFields array
19
- .filter((i) => !i.collection.startsWith('directus_', 0)
20
- || customFields.some((f) => f.collection === i.collection && f.field === i.field))
21
- .map(i => {
20
+ .filter((i) => !i.collection.startsWith('directus_', 0) ||
21
+ customFields.some((f) => f.collection === i.collection && f.field === i.field))
22
+ .filter((i) => includesRelation(i.collection, i.related_collection, plan))
23
+ .map((i) => {
22
24
  delete i.meta.id;
23
25
  return i;
24
26
  });
@@ -1 +1,2 @@
1
- export default function extract(dir: string): Promise<{}>;
1
+ import { type TemplatePlan } from '../template-plan/index.js';
2
+ export default function extract(dir: string, plan?: TemplatePlan): Promise<{}>;
@@ -1,5 +1,9 @@
1
1
  import { ux } from '@oclif/core';
2
- import fs from 'node:fs';
2
+ import fs from 'node:fs/promises';
3
+ import { buildTemplatePlan, writeTemplateMetadata, } from '../template-plan/index.js';
4
+ import catchError from '../utils/catch-error.js';
5
+ import { expandDeepPlan } from './expand-deep-plan.js';
6
+ import { expandSchemaPlan } from './expand-schema-plan.js';
3
7
  import extractAccess from './extract-access.js';
4
8
  import { downloadAllFiles } from './extract-assets.js';
5
9
  import extractCollections from './extract-collections.js';
@@ -19,34 +23,68 @@ import extractSchema from './extract-schema.js';
19
23
  import extractSettings from './extract-settings.js';
20
24
  import extractTranslations from './extract-translations.js';
21
25
  import extractUsers from './extract-users.js';
22
- export default async function extract(dir) {
23
- // Get the destination directory for the actual files
26
+ export default async function extract(dir, plan = buildTemplatePlan()) {
24
27
  const destination = `${dir}/src`;
25
- // Check if directory exists, if not, then create it.
26
- if (!fs.existsSync(destination)) {
27
- ux.stdout(`Attempting to create directory at: ${destination}`);
28
- fs.mkdirSync(destination, { recursive: true });
29
- }
30
- await extractSchema(destination);
31
- await extractCollections(destination);
32
- await extractFields(destination);
33
- await extractRelations(destination);
34
- await extractFolders(destination);
35
- await extractFiles(destination);
36
- await extractUsers(destination);
37
- await extractRoles(destination);
38
- await extractPermissions(destination);
39
- await extractPolicies(destination);
40
- await extractAccess(destination);
41
- await extractPresets(destination);
42
- await extractTranslations(destination);
43
- await extractFlows(destination);
44
- await extractOperations(destination);
45
- await extractDashboards(destination);
46
- await extractPanels(destination);
47
- await extractSettings(destination);
48
- await extractExtensions(destination);
49
- await extractContent(destination);
50
- await downloadAllFiles(destination);
28
+ const schemaPlan = await expandSchemaPlan(plan);
29
+ const effectivePlan = await expandDeepPlan(schemaPlan);
30
+ try {
31
+ await fs.mkdir(destination, { recursive: true });
32
+ }
33
+ catch (error) {
34
+ catchError(error, { context: { destination }, fatal: true });
35
+ }
36
+ if (effectivePlan.components.schema) {
37
+ await extractSchema(destination);
38
+ await extractCollections(destination, effectivePlan);
39
+ await extractFields(destination, effectivePlan);
40
+ await extractRelations(destination, effectivePlan);
41
+ }
42
+ if (effectivePlan.components.files) {
43
+ await extractFolders(destination);
44
+ await extractFiles(destination);
45
+ await downloadAllFiles(destination);
46
+ }
47
+ if (effectivePlan.components.users || effectivePlan.components.permissions) {
48
+ await extractRoles(destination);
49
+ await extractPermissions(destination);
50
+ await extractPolicies(destination);
51
+ if (effectivePlan.components.users) {
52
+ await extractUsers(destination);
53
+ }
54
+ await extractAccess(destination);
55
+ }
56
+ if (effectivePlan.components.settings) {
57
+ await extractPresets(destination);
58
+ await extractTranslations(destination);
59
+ await extractSettings(destination);
60
+ }
61
+ if (effectivePlan.components.flows) {
62
+ await extractFlows(destination);
63
+ await extractOperations(destination);
64
+ }
65
+ if (effectivePlan.components.dashboards) {
66
+ await extractDashboards(destination);
67
+ await extractPanels(destination);
68
+ }
69
+ if (effectivePlan.components.extensions) {
70
+ await extractExtensions(destination);
71
+ }
72
+ const warnings = [];
73
+ if (effectivePlan.components.content) {
74
+ const contentWarnings = await extractContent(destination, effectivePlan);
75
+ warnings.push(...contentWarnings);
76
+ }
77
+ for (const warning of warnings) {
78
+ ux.warn(`Excluded relation: ${warning.collection}.${warning.field} -> ${warning.relatedCollection} (${warning.count} records)`);
79
+ }
80
+ try {
81
+ await writeTemplateMetadata(destination, effectivePlan, warnings);
82
+ }
83
+ catch (error) {
84
+ catchError(error, {
85
+ context: { function: 'writeTemplateMetadata' },
86
+ fatal: true,
87
+ });
88
+ }
51
89
  return {};
52
90
  }
@@ -1,4 +1,5 @@
1
1
  import { type DownloadTemplateResult } from 'giget';
2
+ import { type PackageManager } from 'nypm';
2
3
  import type { InitFlags } from '../../commands/init.js';
3
4
  export declare function init({ dir, flags }: {
4
5
  dir: string;
@@ -8,3 +9,17 @@ export declare function init({ dir, flags }: {
8
9
  frontendDir: string;
9
10
  template: DownloadTemplateResult;
10
11
  }>;
12
+ type DependencyInstaller = (options: {
13
+ cwd: string;
14
+ packageManager?: PackageManager;
15
+ silent: boolean;
16
+ }) => Promise<unknown>;
17
+ type InstallFrontendDependenciesOptions = {
18
+ frontendDir: string;
19
+ install?: DependencyInstaller;
20
+ onSkip?: () => void;
21
+ packageManager: null | PackageManager;
22
+ warn?: (message: string) => void;
23
+ };
24
+ export declare function installFrontendDependencies({ frontendDir, install, onSkip, packageManager, warn, }: InstallFrontendDependenciesOptions): Promise<boolean>;
25
+ export {};
@@ -133,21 +133,18 @@ export async function init({ dir, flags }) {
133
133
  // Install dependencies if requested
134
134
  if (flags.installDeps) {
135
135
  const s = spinner();
136
+ let dependenciesInstalled = true;
136
137
  s.start('Installing dependencies');
137
- try {
138
- if (fs.existsSync(frontendDir)) {
139
- await installDependencies({
140
- cwd: frontendDir,
141
- packageManager,
142
- silent: true,
143
- });
144
- }
138
+ if (fs.existsSync(frontendDir)) {
139
+ dependenciesInstalled = await installFrontendDependencies({
140
+ frontendDir,
141
+ onSkip: () => s.stop('Dependency installation skipped'),
142
+ packageManager,
143
+ });
145
144
  }
146
- catch (error) {
147
- ux.warn('Failed to install dependencies');
148
- throw error;
145
+ if (dependenciesInstalled) {
146
+ s.stop('Dependencies installed!');
149
147
  }
150
- s.stop('Dependencies installed!');
151
148
  }
152
149
  // Initialize Git repo
153
150
  if (flags.gitInit) {
@@ -165,7 +162,7 @@ export async function init({ dir, flags }) {
165
162
  : `- Complete the onboarding form at ${pinkText(directusInfo.url || 'http://localhost:8055')} to create your admin account. \n`;
166
163
  const frontendText = flags.frontend ? `- To start the frontend, run ${pinkText(`cd ${flags.frontend}`)} and then ${pinkText(`${packageManager?.name} run dev`)}. \n` : '';
167
164
  const projectText = `- Navigate to your project directory using ${pinkText(`cd ${relativeDir}`)}. \n`;
168
- const readmeText = '- Review the \`./README.md\` file for more information and next steps.';
165
+ const readmeText = '- Review the `./README.md` file for more information and next steps.';
169
166
  const nextSteps = `${directusText}${directusLoginText}${projectText}${frontendText}${readmeText}`;
170
167
  note(nextSteps, 'Next Steps');
171
168
  clackLog.warn(BSL_LICENSE_HEADLINE);
@@ -203,3 +200,22 @@ async function initGit(targetDir) {
203
200
  });
204
201
  }
205
202
  }
203
+ export async function installFrontendDependencies({ frontendDir, install = installDependencies, onSkip, packageManager, warn = ux.warn, }) {
204
+ try {
205
+ await install({
206
+ cwd: frontendDir,
207
+ packageManager: packageManager ?? undefined,
208
+ silent: true,
209
+ });
210
+ return true;
211
+ }
212
+ catch {
213
+ onSkip?.();
214
+ warn('Failed to install dependencies');
215
+ if (packageManager?.name === 'pnpm') {
216
+ warn('This starter uses pnpm. From the frontend directory, try running: corepack enable && pnpm install');
217
+ }
218
+ warn('You can install dependencies manually and continue using the generated project.');
219
+ return false;
220
+ }
221
+ }
@@ -1,16 +1,21 @@
1
1
  export interface ApplyFlags {
2
+ allowBrokenRelations?: boolean;
3
+ collections?: string;
2
4
  content: boolean;
3
5
  dashboards: boolean;
4
6
  directusToken: string;
5
7
  directusUrl: string;
6
8
  disableTelemetry?: boolean;
9
+ excludeCollections?: string;
7
10
  extensions: boolean;
8
11
  files: boolean;
9
12
  flows: boolean;
13
+ noAssets?: boolean;
10
14
  noExit?: boolean;
11
15
  partial: boolean;
12
16
  permissions: boolean;
13
17
  programmatic: boolean;
18
+ relationStrategy?: 'deep' | 'empty' | 'preserve';
14
19
  schema: boolean;
15
20
  settings: boolean;
16
21
  templateLocation: string;
@@ -19,6 +24,4 @@ export interface ApplyFlags {
19
24
  userPassword: string;
20
25
  users?: boolean;
21
26
  }
22
- export declare const loadFlags: readonly ["content", "dashboards", "extensions", "files", "flows", "permissions", "schema", "settings", "users"];
23
27
  export declare function validateProgrammaticFlags(flags: ApplyFlags): ApplyFlags;
24
- export declare function validateInteractiveFlags(flags: ApplyFlags): ApplyFlags;
@@ -1,16 +1,4 @@
1
1
  import { ux } from '@oclif/core';
2
- import catchError from '../utils/catch-error.js';
3
- export const loadFlags = [
4
- 'content',
5
- 'dashboards',
6
- 'extensions',
7
- 'files',
8
- 'flows',
9
- 'permissions',
10
- 'schema',
11
- 'settings',
12
- 'users',
13
- ];
14
2
  export function validateProgrammaticFlags(flags) {
15
3
  const { directusToken, directusUrl, templateLocation, userEmail, userPassword } = flags;
16
4
  if (!directusUrl)
@@ -19,43 +7,5 @@ export function validateProgrammaticFlags(flags) {
19
7
  ux.error('Either Directus token or email and password are required for programmatic mode.');
20
8
  if (!templateLocation)
21
9
  ux.error('Template location is required for programmatic mode.');
22
- return flags.partial ? handlePartialFlags(flags) : setAllFlagsTrue(flags);
23
- }
24
- export function validateInteractiveFlags(flags) {
25
- return flags.partial ? handlePartialFlags(flags) : setAllFlagsTrue(flags);
26
- }
27
- function handlePartialFlags(flags) {
28
- const enabledFlags = loadFlags.filter(flag => flags[flag] === true);
29
- const disabledFlags = loadFlags.filter(flag => flags[flag] === false);
30
- if (enabledFlags.length > 0) {
31
- for (const flag of loadFlags)
32
- flags[flag] = enabledFlags.includes(flag);
33
- }
34
- else if (disabledFlags.length > 0) {
35
- for (const flag of loadFlags)
36
- flags[flag] = !disabledFlags.includes(flag);
37
- }
38
- else {
39
- setAllFlagsTrue(flags);
40
- }
41
- handleDependencies(flags);
42
- if (!loadFlags.some(flag => flags[flag])) {
43
- catchError(new Error('When using --partial, at least one component must be loaded.'), { fatal: true });
44
- }
45
- return flags;
46
- }
47
- function handleDependencies(flags) {
48
- if (flags.content && (!flags.schema || !flags.files)) {
49
- flags.schema = flags.files = true;
50
- ux.warn('Content loading requires schema and files. Enabling schema and files flags.');
51
- }
52
- if (flags.users && !flags.permissions) {
53
- flags.permissions = true;
54
- ux.warn('User loading requires permissions. Enabling permissions flag.');
55
- }
56
- }
57
- function setAllFlagsTrue(flags) {
58
- for (const flag of loadFlags)
59
- flags[flag] = true;
60
10
  return flags;
61
11
  }
@@ -0,0 +1,2 @@
1
+ import { type TemplatePlan } from '../template-plan/index.js';
2
+ export default function finalizeCollections(dir: string, plan?: TemplatePlan): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import { updateCollection } from '@directus/sdk';
2
+ import { ux } from '@oclif/core';
3
+ import { DIRECTUS_PINK } from '../constants.js';
4
+ import { api } from '../sdk.js';
5
+ import { includesSchemaCollection } from '../template-plan/index.js';
6
+ import catchError from '../utils/catch-error.js';
7
+ import readFile from '../utils/read-file.js';
8
+ export default async function finalizeCollections(dir, plan) {
9
+ const collections = readFile('collections', dir)
10
+ .filter((collection) => includesSchemaCollection(collection.collection, plan))
11
+ .filter((collection) => !collection.collection.startsWith('directus_'));
12
+ ux.action.start(ux.colorize(DIRECTUS_PINK, `Finalizing metadata for ${collections.length} collections`));
13
+ const collectionNames = new Set(collections.map((collection) => collection.collection));
14
+ for await (const collection of collections) {
15
+ const meta = { ...collection.meta };
16
+ if (meta.group && !collectionNames.has(meta.group)) {
17
+ ux.warn(`Skipping missing group "${meta.group}" for collection "${collection.collection}"`);
18
+ delete meta.group;
19
+ }
20
+ try {
21
+ await api.client.request(updateCollection(collection.collection, { meta }));
22
+ }
23
+ catch (error) {
24
+ catchError(error, { context: { collection: collection.collection, group: meta.group } });
25
+ }
26
+ }
27
+ ux.action.stop();
28
+ }
@@ -0,0 +1,2 @@
1
+ import { type TemplatePlan } from '../template-plan/index.js';
2
+ export default function finalizeFields(dir: string, plan?: TemplatePlan): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import { updateField } from '@directus/sdk';
2
+ import { ux } from '@oclif/core';
3
+ import { DIRECTUS_PINK } from '../constants.js';
4
+ import { api } from '../sdk.js';
5
+ import { includesSchemaCollection } from '../template-plan/index.js';
6
+ import catchError from '../utils/catch-error.js';
7
+ import readFile from '../utils/read-file.js';
8
+ export default async function finalizeFields(dir, plan) {
9
+ const fields = readFile('fields', dir)
10
+ .filter((field) => includesSchemaCollection(field.collection, plan))
11
+ .filter((field) => field.schema);
12
+ ux.action.start(ux.colorize(DIRECTUS_PINK, `Finalizing metadata for ${fields.length} fields`));
13
+ for await (const field of fields) {
14
+ try {
15
+ await api.client.request(updateField(field.collection, field.field, {
16
+ meta: field.meta ? { ...field.meta } : undefined,
17
+ schema: field.schema ? { ...field.schema } : undefined,
18
+ }));
19
+ }
20
+ catch (error) {
21
+ catchError(error, { context: { collection: field.collection, field: field.field } });
22
+ }
23
+ }
24
+ ux.action.stop();
25
+ }
@@ -1,5 +1,8 @@
1
1
  import { ux } from '@oclif/core';
2
+ import { applyMetadataToPlan, buildTemplatePlan, readTemplateMetadata } from '../template-plan/index.js';
2
3
  import checkTemplate from '../utils/check-template.js';
4
+ import finalizeCollections from './finalize-collections.js';
5
+ import finalizeFields from './finalize-fields.js';
3
6
  import loadAccess from './load-access.js';
4
7
  import loadCollections from './load-collections.js';
5
8
  import loadDashboards from './load-dashboards.js';
@@ -16,48 +19,65 @@ import loadRoles from './load-roles.js';
16
19
  import loadSettings from './load-settings.js';
17
20
  import loadTranslations from './load-translations.js';
18
21
  import loadUsers from './load-users.js';
19
- import updateRequiredFields from './update-required-fields.js';
20
22
  export default async function apply(dir, flags) {
21
23
  const source = `${dir}/src`;
22
- const isTemplateOk = await checkTemplate(source);
23
- if (!isTemplateOk) {
24
- ux.error('The template is missing the collections, fields, or relations files. Older templates are not supported in v0.4 of directus-template-cli. Try using v0.3 to load older templates npx directus-template-cli@0.3 apply or extract the template using latest version before applying. Exiting...');
24
+ const metadata = readTemplateMetadata(source);
25
+ const requestedPlan = buildTemplatePlan(flags);
26
+ const effectivePlan = applyMetadataToPlan(requestedPlan, metadata);
27
+ const { components } = effectivePlan;
28
+ if (!metadata) {
29
+ ux.warn('No template-meta.json found. Treating as a full template — relation integrity check skipped.');
25
30
  }
26
- if (flags.schema) {
27
- await loadCollections(source);
28
- await loadRelations(source);
31
+ else if (metadata.partial) {
32
+ ux.warn('Template metadata indicates this is a partial template.');
29
33
  }
30
- if (flags.permissions || flags.users) {
34
+ const brokenRelationWarnings = metadata?.warnings?.filter((warning) => warning.type === 'excluded_relation') || [];
35
+ if (components.content && brokenRelationWarnings.length > 0 && !effectivePlan.allowBrokenRelations) {
36
+ ux.error('This partial template contains excluded relation references. Re-run with --allow-broken-relations to apply anyway.');
37
+ }
38
+ if (!metadata || components.schema) {
39
+ const isTemplateOk = await checkTemplate(source);
40
+ if (!isTemplateOk) {
41
+ ux.error('The template is missing the collections, fields, or relations files. Older templates are not supported in v0.4 of directus-template-cli. Try using v0.3 to load older templates npx directus-template-cli@0.3 apply or extract the template using latest version before applying. Exiting...');
42
+ }
43
+ }
44
+ if (components.schema) {
45
+ await loadCollections(source, effectivePlan);
46
+ await loadRelations(source, effectivePlan);
47
+ await finalizeCollections(source, effectivePlan);
48
+ }
49
+ if (components.permissions || components.users) {
31
50
  await loadRoles(source);
32
51
  await loadPolicies(source);
33
52
  await loadPermissions(source);
34
- if (flags.users) {
53
+ if (components.users) {
35
54
  await loadUsers(source);
36
55
  }
37
56
  await loadAccess(source);
38
57
  }
39
- if (flags.files) {
58
+ if (components.files) {
40
59
  await loadFolders(source);
41
60
  await loadFiles(source);
42
61
  }
43
- if (flags.content) {
44
- await loadData(source);
62
+ if (components.content) {
63
+ await loadData(source, effectivePlan);
45
64
  }
46
- if (flags.schema) {
47
- await updateRequiredFields(source);
65
+ if (components.schema) {
66
+ // Finalize fields after data loading because skeleton records rely on relaxed constraints.
67
+ await finalizeFields(source, effectivePlan);
48
68
  }
49
- if (flags.dashboards) {
69
+ if (components.dashboards) {
50
70
  await loadDashboards(source);
51
71
  }
52
- if (flags.flows) {
72
+ if (components.flows) {
53
73
  await loadFlows(source);
54
74
  }
55
- if (flags.settings) {
75
+ if (components.settings) {
56
76
  await loadSettings(source);
57
77
  await loadTranslations(source);
58
78
  await loadPresets(source);
59
79
  }
60
- if (flags.extensions) {
80
+ if (components.extensions) {
61
81
  await loadExtensions(source);
62
82
  }
63
83
  return {};
@@ -1,6 +1,7 @@
1
+ import { type TemplatePlan } from '../template-plan/index.js';
1
2
  /**
2
3
  * Load collections into the Directus instance
3
4
  * @param dir - The directory to read the collections and fields from
4
5
  * @returns {Promise<void>} - Returns nothing
5
6
  */
6
- export default function loadCollections(dir: string): Promise<void>;
7
+ export default function loadCollections(dir: string, plan?: TemplatePlan): Promise<void>;
@@ -1,7 +1,8 @@
1
- import { createCollection, createField, readCollections, readFields, updateCollection, } from '@directus/sdk';
1
+ import { createCollection, createField, readCollections, readFields } from '@directus/sdk';
2
2
  import { ux } from '@oclif/core';
3
3
  import { DIRECTUS_PINK } from '../constants.js';
4
4
  import { api } from '../sdk.js';
5
+ import { includesSchemaCollection } from '../template-plan/index.js';
5
6
  import catchError from '../utils/catch-error.js';
6
7
  import readFile from '../utils/read-file.js';
7
8
  /**
@@ -9,12 +10,11 @@ import readFile from '../utils/read-file.js';
9
10
  * @param dir - The directory to read the collections and fields from
10
11
  * @returns {Promise<void>} - Returns nothing
11
12
  */
12
- export default async function loadCollections(dir) {
13
- const collectionsToAdd = readFile('collections', dir);
14
- const fieldsToAdd = readFile('fields', dir);
13
+ export default async function loadCollections(dir, plan) {
14
+ const collectionsToAdd = readFile('collections', dir).filter((collection) => includesSchemaCollection(collection.collection, plan));
15
+ const fieldsToAdd = readFile('fields', dir).filter((field) => includesSchemaCollection(field.collection, plan));
15
16
  ux.action.start(ux.colorize(DIRECTUS_PINK, `Loading ${collectionsToAdd.length} collections and ${fieldsToAdd.length} fields`));
16
17
  await processCollections(collectionsToAdd, fieldsToAdd);
17
- await updateCollections(collectionsToAdd);
18
18
  await addCustomFieldsOnSystemCollections(fieldsToAdd);
19
19
  ux.action.stop();
20
20
  }
@@ -24,10 +24,12 @@ async function processCollections(collectionsToAdd, fieldsToAdd) {
24
24
  for await (const collection of collectionsToAdd) {
25
25
  try {
26
26
  const existingCollection = existingCollections.find((c) => c.collection === collection.collection);
27
- await (existingCollection ? addNewFieldsToExistingCollection(collection.collection, fieldsToAdd, existingFields) : addNewCollectionWithFields(collection, fieldsToAdd));
27
+ await (existingCollection
28
+ ? addNewFieldsToExistingCollection(collection.collection, fieldsToAdd, existingFields)
29
+ : addNewCollectionWithFields(collection, fieldsToAdd));
28
30
  }
29
31
  catch (error) {
30
- catchError(error);
32
+ catchError(error, { context: { collection: collection.collection } });
31
33
  }
32
34
  }
33
35
  }
@@ -44,8 +46,9 @@ const removeRequiredorIsNullable = (field) => {
44
46
  return field;
45
47
  };
46
48
  async function addNewCollectionWithFields(collection, allFields) {
47
- const collectionFields = allFields.filter(field => field.collection === collection.collection)
48
- .map(field => removeRequiredorIsNullable(field));
49
+ const collectionFields = allFields
50
+ .filter((field) => field.collection === collection.collection)
51
+ .map((field) => removeRequiredorIsNullable(field));
49
52
  const collectionWithoutGroup = {
50
53
  ...collection,
51
54
  fields: collectionFields,
@@ -55,8 +58,9 @@ async function addNewCollectionWithFields(collection, allFields) {
55
58
  await api.client.request(createCollection(collectionWithoutGroup));
56
59
  }
57
60
  async function addNewFieldsToExistingCollection(collectionName, fieldsToAdd, existingFields) {
58
- const collectionFieldsToAdd = fieldsToAdd.filter(field => field.collection === collectionName)
59
- .map(field => removeRequiredorIsNullable(field));
61
+ const collectionFieldsToAdd = fieldsToAdd
62
+ .filter((field) => field.collection === collectionName)
63
+ .map((field) => removeRequiredorIsNullable(field));
60
64
  const existingCollectionFields = existingFields.filter((field) => field.collection === collectionName);
61
65
  for await (const field of collectionFieldsToAdd) {
62
66
  if (!existingCollectionFields.some((existingField) => existingField.field === field.field)) {
@@ -65,28 +69,11 @@ async function addNewFieldsToExistingCollection(collectionName, fieldsToAdd, exi
65
69
  await api.client.request(createField(collectionName, field));
66
70
  }
67
71
  catch (error) {
68
- catchError(error);
72
+ catchError(error, { context: { collection: collectionName, field: field.field } });
69
73
  }
70
74
  }
71
75
  }
72
76
  }
73
- async function updateCollections(collections) {
74
- for await (const collection of collections) {
75
- try {
76
- if (collection.meta.group) {
77
- const pl = {
78
- meta: {
79
- group: collection.meta.group,
80
- },
81
- };
82
- await api.client.request(updateCollection(collection.collection, pl));
83
- }
84
- }
85
- catch (error) {
86
- catchError(error);
87
- }
88
- }
89
- }
90
77
  async function addCustomFieldsOnSystemCollections(fields) {
91
78
  const customFields = fields.filter((field) => field.collection.startsWith('directus_'));
92
79
  const existingFields = await api.client.request(readFields());
@@ -99,7 +86,7 @@ async function addCustomFieldsOnSystemCollections(fields) {
99
86
  }
100
87
  }
101
88
  catch (error) {
102
- catchError(error);
89
+ catchError(error, { context: { collection: field.collection, field: field.field } });
103
90
  }
104
91
  }
105
92
  }
@@ -1 +1,3 @@
1
- export default function loadData(dir: string): Promise<void>;
1
+ import { type TemplatePlan } from '../template-plan/index.js';
2
+ export default function loadData(dir: string, plan?: TemplatePlan): Promise<void>;
3
+ export declare function getUserCollections(dir: string, plan?: TemplatePlan): any[];