@directus/extensions-sdk 9.20.3 → 9.21.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 (155) hide show
  1. package/dist/cjs/cli/commands/add.d.ts +2 -0
  2. package/dist/cjs/cli/commands/add.js +242 -0
  3. package/dist/cjs/cli/commands/build.d.ts +1 -1
  4. package/dist/cjs/cli/commands/build.js +169 -43
  5. package/dist/cjs/cli/commands/create.d.ts +2 -2
  6. package/dist/cjs/cli/commands/create.js +66 -47
  7. package/dist/cjs/cli/commands/helpers/copy-template.d.ts +4 -0
  8. package/dist/cjs/cli/commands/helpers/copy-template.js +53 -0
  9. package/dist/cjs/cli/commands/helpers/generate-bundle-entrypoint.d.ts +3 -0
  10. package/dist/cjs/cli/commands/helpers/generate-bundle-entrypoint.js +24 -0
  11. package/dist/cjs/cli/commands/helpers/get-extension-dev-deps.d.ts +4 -0
  12. package/dist/cjs/cli/commands/helpers/get-extension-dev-deps.js +27 -0
  13. package/dist/cjs/cli/commands/helpers/load-config.d.ts +3 -0
  14. package/dist/cjs/cli/{utils → commands/helpers}/load-config.js +0 -0
  15. package/dist/cjs/cli/commands/helpers/validate-cli-options.d.ts +4 -0
  16. package/dist/cjs/cli/commands/helpers/validate-cli-options.js +39 -0
  17. package/dist/cjs/cli/run.js +3 -1
  18. package/dist/cjs/cli/types.d.ts +5 -5
  19. package/dist/cjs/cli/utils/detect-json-indent.d.ts +2 -0
  20. package/dist/cjs/cli/utils/detect-json-indent.js +14 -0
  21. package/dist/cjs/cli/utils/get-package-manager-agent.d.ts +5 -0
  22. package/dist/cjs/cli/utils/get-package-manager-agent.js +15 -0
  23. package/dist/cjs/cli/utils/get-package-manager-agent.test.d.ts +2 -0
  24. package/dist/cjs/cli/utils/get-package-manager-agent.test.js +24 -0
  25. package/dist/cjs/cli/utils/get-package-manager.d.ts +5 -0
  26. package/dist/cjs/cli/utils/get-package-manager.js +20 -0
  27. package/dist/cjs/cli/utils/get-package-manager.test.d.ts +2 -0
  28. package/dist/cjs/cli/utils/get-package-manager.test.js +42 -0
  29. package/dist/cjs/cli/utils/get-sdk-version.d.ts +2 -0
  30. package/dist/cjs/cli/utils/get-sdk-version.js +7 -0
  31. package/dist/cjs/cli/utils/get-template-path.d.ts +2 -0
  32. package/dist/cjs/cli/utils/get-template-path.js +10 -0
  33. package/dist/cjs/cli/utils/try-parse-json.d.ts +3 -0
  34. package/dist/cjs/cli/utils/try-parse-json.js +11 -0
  35. package/dist/esm/cli/commands/add.d.ts +2 -0
  36. package/dist/esm/cli/commands/add.js +236 -0
  37. package/dist/esm/cli/commands/build.d.ts +1 -1
  38. package/dist/esm/cli/commands/build.js +167 -41
  39. package/dist/esm/cli/commands/create.d.ts +2 -2
  40. package/dist/esm/cli/commands/create.js +66 -47
  41. package/dist/esm/cli/commands/helpers/copy-template.d.ts +4 -0
  42. package/dist/esm/cli/commands/helpers/copy-template.js +47 -0
  43. package/dist/esm/cli/commands/helpers/generate-bundle-entrypoint.d.ts +3 -0
  44. package/dist/esm/cli/commands/helpers/generate-bundle-entrypoint.js +18 -0
  45. package/dist/esm/cli/commands/helpers/get-extension-dev-deps.d.ts +4 -0
  46. package/dist/esm/cli/commands/helpers/get-extension-dev-deps.js +21 -0
  47. package/dist/esm/cli/commands/helpers/load-config.d.ts +3 -0
  48. package/dist/esm/cli/{utils → commands/helpers}/load-config.js +0 -0
  49. package/dist/esm/cli/commands/helpers/validate-cli-options.d.ts +4 -0
  50. package/dist/esm/cli/commands/helpers/validate-cli-options.js +34 -0
  51. package/dist/esm/cli/run.js +3 -1
  52. package/dist/esm/cli/types.d.ts +5 -5
  53. package/dist/esm/cli/utils/detect-json-indent.d.ts +2 -0
  54. package/dist/esm/cli/utils/detect-json-indent.js +11 -0
  55. package/dist/esm/cli/utils/get-package-manager-agent.d.ts +5 -0
  56. package/dist/esm/cli/utils/get-package-manager-agent.js +12 -0
  57. package/dist/esm/cli/utils/get-package-manager-agent.test.d.ts +2 -0
  58. package/dist/esm/cli/utils/get-package-manager-agent.test.js +19 -0
  59. package/dist/esm/cli/utils/get-package-manager.d.ts +5 -0
  60. package/dist/esm/cli/utils/get-package-manager.js +14 -0
  61. package/dist/esm/cli/utils/get-package-manager.test.d.ts +2 -0
  62. package/dist/esm/cli/utils/get-package-manager.test.js +37 -0
  63. package/dist/esm/cli/utils/get-sdk-version.d.ts +2 -0
  64. package/dist/esm/cli/utils/get-sdk-version.js +4 -0
  65. package/dist/esm/cli/utils/get-template-path.d.ts +2 -0
  66. package/dist/esm/cli/utils/get-template-path.js +4 -0
  67. package/dist/esm/cli/utils/try-parse-json.d.ts +3 -0
  68. package/dist/esm/cli/utils/try-parse-json.js +8 -0
  69. package/package.json +51 -21
  70. package/templates/common/{javascript → common/config}/_gitignore +0 -0
  71. package/templates/common/typescript/{tsconfig.json → config/tsconfig.json} +0 -0
  72. package/templates/display/javascript/{src → source}/display.vue +0 -0
  73. package/templates/display/javascript/{src → source}/index.js +0 -0
  74. package/templates/display/typescript/{src → source}/display.vue +0 -0
  75. package/templates/display/typescript/{src → source}/index.ts +0 -0
  76. package/templates/display/typescript/{src → source}/shims.d.ts +0 -0
  77. package/templates/endpoint/javascript/{src → source}/index.js +0 -0
  78. package/templates/endpoint/typescript/{src → source}/index.ts +0 -0
  79. package/templates/hook/javascript/{src → source}/index.js +0 -0
  80. package/templates/hook/typescript/{src → source}/index.ts +0 -0
  81. package/templates/interface/javascript/{src → source}/index.js +0 -0
  82. package/templates/interface/javascript/{src → source}/interface.vue +0 -0
  83. package/templates/interface/typescript/{src → source}/index.ts +0 -0
  84. package/templates/interface/typescript/{src → source}/interface.vue +0 -0
  85. package/templates/interface/typescript/{src → source}/shims.d.ts +0 -0
  86. package/templates/layout/javascript/{src → source}/index.js +0 -0
  87. package/templates/layout/javascript/{src → source}/layout.vue +0 -0
  88. package/templates/layout/typescript/{src → source}/index.ts +0 -0
  89. package/templates/layout/typescript/{src → source}/layout.vue +0 -0
  90. package/templates/layout/typescript/{src → source}/shims.d.ts +0 -0
  91. package/templates/module/javascript/{src → source}/index.js +0 -0
  92. package/templates/module/javascript/{src → source}/module.vue +0 -0
  93. package/templates/module/typescript/{src → source}/index.ts +0 -0
  94. package/templates/module/typescript/{src → source}/module.vue +0 -0
  95. package/templates/module/typescript/{src → source}/shims.d.ts +0 -0
  96. package/templates/operation/javascript/{src → source}/api.js +0 -0
  97. package/templates/operation/javascript/{src → source}/app.js +0 -0
  98. package/templates/operation/typescript/{src → source}/api.ts +0 -0
  99. package/templates/operation/typescript/{src → source}/app.ts +0 -0
  100. package/templates/operation/typescript/{src → source}/shims.d.ts +0 -0
  101. package/templates/panel/javascript/{src → source}/index.js +0 -0
  102. package/templates/panel/javascript/{src → source}/panel.vue +0 -0
  103. package/templates/panel/typescript/{src → source}/index.ts +0 -0
  104. package/templates/panel/typescript/{src → source}/panel.vue +0 -0
  105. package/templates/panel/typescript/{src → source}/shims.d.ts +0 -0
  106. package/.editorconfig +0 -12
  107. package/cli.d.ts +0 -1
  108. package/dist/cjs/cli/commands/build.d.ts.map +0 -1
  109. package/dist/cjs/cli/commands/create.d.ts.map +0 -1
  110. package/dist/cjs/cli/index.d.ts.map +0 -1
  111. package/dist/cjs/cli/run.d.ts.map +0 -1
  112. package/dist/cjs/cli/types.d.ts.map +0 -1
  113. package/dist/cjs/cli/utils/get-package-version.d.ts.map +0 -1
  114. package/dist/cjs/cli/utils/languages.d.ts.map +0 -1
  115. package/dist/cjs/cli/utils/load-config.d.ts +0 -3
  116. package/dist/cjs/cli/utils/load-config.d.ts.map +0 -1
  117. package/dist/cjs/cli/utils/logger.d.ts.map +0 -1
  118. package/dist/cjs/cli/utils/rename-map.d.ts +0 -2
  119. package/dist/cjs/cli/utils/rename-map.d.ts.map +0 -1
  120. package/dist/cjs/cli/utils/rename-map.js +0 -23
  121. package/dist/cjs/cli/utils/to-object.d.ts +0 -2
  122. package/dist/cjs/cli/utils/to-object.d.ts.map +0 -1
  123. package/dist/cjs/cli/utils/to-object.js +0 -17
  124. package/dist/cjs/index.d.ts.map +0 -1
  125. package/dist/esm/cli/commands/build.d.ts.map +0 -1
  126. package/dist/esm/cli/commands/create.d.ts.map +0 -1
  127. package/dist/esm/cli/index.d.ts.map +0 -1
  128. package/dist/esm/cli/run.d.ts.map +0 -1
  129. package/dist/esm/cli/types.d.ts.map +0 -1
  130. package/dist/esm/cli/utils/get-package-version.d.ts.map +0 -1
  131. package/dist/esm/cli/utils/languages.d.ts.map +0 -1
  132. package/dist/esm/cli/utils/load-config.d.ts +0 -3
  133. package/dist/esm/cli/utils/load-config.d.ts.map +0 -1
  134. package/dist/esm/cli/utils/logger.d.ts.map +0 -1
  135. package/dist/esm/cli/utils/rename-map.d.ts +0 -2
  136. package/dist/esm/cli/utils/rename-map.d.ts.map +0 -1
  137. package/dist/esm/cli/utils/rename-map.js +0 -17
  138. package/dist/esm/cli/utils/to-object.d.ts +0 -2
  139. package/dist/esm/cli/utils/to-object.d.ts.map +0 -1
  140. package/dist/esm/cli/utils/to-object.js +0 -14
  141. package/dist/esm/index.d.ts.map +0 -1
  142. package/src/cli/commands/build.ts +0 -493
  143. package/src/cli/commands/create.ts +0 -135
  144. package/src/cli/index.ts +0 -4
  145. package/src/cli/run.ts +0 -32
  146. package/src/cli/types.ts +0 -12
  147. package/src/cli/utils/get-package-version.ts +0 -9
  148. package/src/cli/utils/languages.ts +0 -26
  149. package/src/cli/utils/load-config.ts +0 -21
  150. package/src/cli/utils/logger.ts +0 -25
  151. package/src/cli/utils/rename-map.ts +0 -20
  152. package/src/cli/utils/to-object.ts +0 -16
  153. package/src/index.ts +0 -23
  154. package/templates/common/typescript/_gitignore +0 -3
  155. package/tsconfig.json +0 -30
@@ -0,0 +1,236 @@
1
+ import path from 'path';
2
+ import chalk from 'chalk';
3
+ import fse from 'fs-extra';
4
+ import inquirer from 'inquirer';
5
+ import { log } from '../utils/logger';
6
+ import { isIn, isTypeIn, validateExtensionManifest } from '@directus/shared/utils';
7
+ import { pathToRelativeUrl } from '@directus/shared/utils/node';
8
+ import { EXTENSION_LANGUAGES, EXTENSION_NAME_REGEX, EXTENSION_PKG_KEY, EXTENSION_TYPES, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
9
+ import { getLanguageFromPath, isLanguage, languageToShort } from '../utils/languages';
10
+ import getExtensionDevDeps from './helpers/get-extension-dev-deps';
11
+ import execa from 'execa';
12
+ import ora from 'ora';
13
+ import copyTemplate from './helpers/copy-template';
14
+ import detectJsonIndent from '../utils/detect-json-indent';
15
+ import getPackageManager from '../utils/get-package-manager';
16
+ export default async function add() {
17
+ var _a, _b;
18
+ const extensionPath = process.cwd();
19
+ const packagePath = path.resolve('package.json');
20
+ if (!(await fse.pathExists(packagePath))) {
21
+ log(`Current directory is not a valid package.`, 'error');
22
+ process.exit(1);
23
+ }
24
+ const extensionManifestFile = await fse.readFile(packagePath, 'utf8');
25
+ const extensionManifest = JSON.parse(extensionManifestFile);
26
+ const indent = detectJsonIndent(extensionManifestFile);
27
+ if (!validateExtensionManifest(extensionManifest)) {
28
+ log(`Current directory is not a valid Directus extension.`, 'error');
29
+ process.exit(1);
30
+ }
31
+ const extensionOptions = extensionManifest[EXTENSION_PKG_KEY];
32
+ if (extensionOptions.type === 'pack') {
33
+ log(`Adding entries to extensions with type ${chalk.bold('pack')} is not currently supported.`, 'error');
34
+ process.exit(1);
35
+ }
36
+ const sourceExists = await fse.pathExists(path.resolve('src'));
37
+ if (extensionOptions.type === 'bundle') {
38
+ const { type, name, language, alternativeSource } = await inquirer.prompt([
39
+ {
40
+ type: 'list',
41
+ name: 'type',
42
+ message: 'Choose the extension type',
43
+ choices: EXTENSION_TYPES,
44
+ },
45
+ {
46
+ type: 'input',
47
+ name: 'name',
48
+ message: 'Choose a name for the entry',
49
+ validate: (name) => (name.length === 0 ? 'Entry name can not be empty.' : true),
50
+ },
51
+ {
52
+ type: 'list',
53
+ name: 'language',
54
+ message: 'Choose the language to use',
55
+ choices: EXTENSION_LANGUAGES,
56
+ },
57
+ {
58
+ type: 'input',
59
+ name: 'alternativeSource',
60
+ message: 'Specify the path to the extension source',
61
+ when: !sourceExists && extensionOptions.entries.length > 0,
62
+ },
63
+ ]);
64
+ const spinner = ora(chalk.bold('Modifying Directus extension...')).start();
65
+ const source = alternativeSource !== null && alternativeSource !== void 0 ? alternativeSource : 'src';
66
+ const sourcePath = path.resolve(source, name);
67
+ await fse.ensureDir(sourcePath);
68
+ await copyTemplate(type, extensionPath, sourcePath, language);
69
+ const newEntries = [
70
+ ...extensionOptions.entries,
71
+ isIn(type, HYBRID_EXTENSION_TYPES)
72
+ ? {
73
+ type,
74
+ name,
75
+ source: {
76
+ app: `${pathToRelativeUrl(source)}/${name}/app.${languageToShort(language)}`,
77
+ api: `${pathToRelativeUrl(source)}/${name}/api.${languageToShort(language)}`,
78
+ },
79
+ }
80
+ : {
81
+ type,
82
+ name,
83
+ source: `${pathToRelativeUrl(source)}/${name}/index.${languageToShort(language)}`,
84
+ },
85
+ ];
86
+ const newExtensionOptions = { ...extensionOptions, entries: newEntries };
87
+ const newExtensionManifest = {
88
+ ...extensionManifest,
89
+ [EXTENSION_PKG_KEY]: newExtensionOptions,
90
+ devDependencies: await getExtensionDevDeps(newEntries.map((entry) => entry.type), getLanguageFromEntries(newEntries)),
91
+ };
92
+ await fse.writeJSON(packagePath, newExtensionManifest, { spaces: indent !== null && indent !== void 0 ? indent : '\t' });
93
+ const packageManager = getPackageManager();
94
+ await execa(packageManager, ['install'], { cwd: extensionPath });
95
+ spinner.succeed(chalk.bold('Done'));
96
+ }
97
+ else {
98
+ const { proceed } = await inquirer.prompt([
99
+ {
100
+ type: 'confirm',
101
+ name: 'proceed',
102
+ message: 'This will convert your extension to a bundle. Do you want to proceed?',
103
+ },
104
+ ]);
105
+ if (!proceed) {
106
+ log(`Extension has not been modified.`, 'info');
107
+ process.exit(1);
108
+ }
109
+ const oldName = (_b = (_a = extensionManifest.name.match(EXTENSION_NAME_REGEX)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : extensionManifest.name;
110
+ const { type, name, language, convertName, extensionName, alternativeSource } = await inquirer.prompt([
111
+ {
112
+ type: 'list',
113
+ name: 'type',
114
+ message: 'Choose the extension type',
115
+ choices: EXTENSION_TYPES,
116
+ },
117
+ {
118
+ type: 'input',
119
+ name: 'name',
120
+ message: 'Choose a name for the entry',
121
+ validate: (name) => (name.length === 0 ? 'Entry name can not be empty.' : true),
122
+ },
123
+ {
124
+ type: 'list',
125
+ name: 'language',
126
+ message: 'Choose the language to use',
127
+ choices: EXTENSION_LANGUAGES,
128
+ },
129
+ {
130
+ type: 'input',
131
+ name: 'convertName',
132
+ message: 'Choose a name for the extension that is converted to an entry',
133
+ default: oldName,
134
+ validate: (name) => (name.length === 0 ? 'Entry name can not be empty.' : true),
135
+ },
136
+ {
137
+ type: 'input',
138
+ name: 'extensionName',
139
+ message: 'Choose a name for the extension',
140
+ default: ({ convertName }) => (convertName !== oldName ? oldName : null),
141
+ validate: (name) => (name.length === 0 ? 'Extension name can not be empty.' : true),
142
+ },
143
+ {
144
+ type: 'input',
145
+ name: 'alternativeSource',
146
+ message: 'Specify the path to the extension source',
147
+ when: !sourceExists,
148
+ },
149
+ ]);
150
+ const spinner = ora(chalk.bold('Modifying Directus extension...')).start();
151
+ const source = alternativeSource !== null && alternativeSource !== void 0 ? alternativeSource : 'src';
152
+ const convertSourcePath = path.resolve(source, convertName);
153
+ const entrySourcePath = path.resolve(source, name);
154
+ const convertFiles = await fse.readdir(source);
155
+ await Promise.all(convertFiles.map((file) => fse.move(path.resolve(source, file), path.join(convertSourcePath, file))));
156
+ await fse.ensureDir(entrySourcePath);
157
+ await copyTemplate(type, extensionPath, entrySourcePath, language);
158
+ const toConvertSourceUrl = (entrypoint) => path.posix.join(pathToRelativeUrl(source), convertName, path.posix.relative(source, entrypoint));
159
+ const entries = [
160
+ isTypeIn(extensionOptions, HYBRID_EXTENSION_TYPES)
161
+ ? {
162
+ type: extensionOptions.type,
163
+ name: convertName,
164
+ source: {
165
+ app: toConvertSourceUrl(extensionOptions.source.app),
166
+ api: toConvertSourceUrl(extensionOptions.source.api),
167
+ },
168
+ }
169
+ : {
170
+ type: extensionOptions.type,
171
+ name: convertName,
172
+ source: toConvertSourceUrl(extensionOptions.source),
173
+ },
174
+ isIn(type, HYBRID_EXTENSION_TYPES)
175
+ ? {
176
+ type,
177
+ name,
178
+ source: {
179
+ app: `${pathToRelativeUrl(source)}/${name}/app.${languageToShort(language)}`,
180
+ api: `${pathToRelativeUrl(source)}/${name}/api.${languageToShort(language)}`,
181
+ },
182
+ }
183
+ : {
184
+ type,
185
+ name,
186
+ source: `${pathToRelativeUrl(source)}/${name}/index.${languageToShort(language)}`,
187
+ },
188
+ ];
189
+ const newExtensionOptions = {
190
+ type: 'bundle',
191
+ path: { app: 'dist/app.js', api: 'dist/api.js' },
192
+ entries,
193
+ host: extensionOptions.host,
194
+ hidden: extensionOptions.hidden,
195
+ };
196
+ const newExtensionManifest = {
197
+ ...extensionManifest,
198
+ name: EXTENSION_NAME_REGEX.test(extensionName) ? extensionName : `directus-extension-${extensionName}`,
199
+ keywords: ['directus', 'directus-extension', `directus-custom-bundle`],
200
+ [EXTENSION_PKG_KEY]: newExtensionOptions,
201
+ devDependencies: await getExtensionDevDeps(entries.map((entry) => entry.type), getLanguageFromEntries(entries)),
202
+ };
203
+ await fse.writeJSON(packagePath, newExtensionManifest, { spaces: indent !== null && indent !== void 0 ? indent : '\t' });
204
+ const packageManager = getPackageManager();
205
+ await execa(packageManager, ['install'], { cwd: extensionPath });
206
+ spinner.succeed(chalk.bold('Done'));
207
+ }
208
+ }
209
+ function getLanguageFromEntries(entries) {
210
+ const languages = new Set();
211
+ for (const entry of entries) {
212
+ if (isTypeIn(entry, HYBRID_EXTENSION_TYPES)) {
213
+ const languageApp = getLanguageFromPath(entry.source.app);
214
+ const languageApi = getLanguageFromPath(entry.source.api);
215
+ if (!isLanguage(languageApp)) {
216
+ log(`App language ${chalk.bold(languageApp)} is not supported.`, 'error');
217
+ process.exit(1);
218
+ }
219
+ if (!isLanguage(languageApi)) {
220
+ log(`API language ${chalk.bold(languageApi)} is not supported.`, 'error');
221
+ process.exit(1);
222
+ }
223
+ languages.add(languageApp);
224
+ languages.add(languageApi);
225
+ }
226
+ else {
227
+ const language = getLanguageFromPath(entry.source);
228
+ if (!isLanguage(language)) {
229
+ log(`Language ${chalk.bold(language)} is not supported.`, 'error');
230
+ process.exit(1);
231
+ }
232
+ languages.add(language);
233
+ }
234
+ }
235
+ return Array.from(languages);
236
+ }
@@ -1,4 +1,4 @@
1
- declare type BuildOptions = {
1
+ type BuildOptions = {
2
2
  type?: string;
3
3
  input?: string;
4
4
  output?: string;
@@ -1,22 +1,25 @@
1
- import path from 'path';
1
+ import { API_SHARED_DEPS, APP_EXTENSION_TYPES, APP_SHARED_DEPS, EXTENSION_PACKAGE_TYPES, EXTENSION_PKG_KEY, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
2
+ import { isIn, isTypeIn, validateExtensionManifest } from '@directus/shared/utils';
3
+ import commonjs from '@rollup/plugin-commonjs';
4
+ import json from '@rollup/plugin-json';
5
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
6
+ import replace from '@rollup/plugin-replace';
7
+ import terser from '@rollup/plugin-terser';
8
+ import virtual from '@rollup/plugin-virtual';
2
9
  import chalk from 'chalk';
3
10
  import fse from 'fs-extra';
4
11
  import ora from 'ora';
12
+ import path from 'path';
5
13
  import { rollup, watch as rollupWatch, } from 'rollup';
6
- import { nodeResolve } from '@rollup/plugin-node-resolve';
7
- import commonjs from '@rollup/plugin-commonjs';
8
- import json from '@rollup/plugin-json';
9
- import replace from '@rollup/plugin-replace';
10
- import typescript from 'rollup-plugin-typescript2';
11
- import { terser } from 'rollup-plugin-terser';
12
14
  import styles from 'rollup-plugin-styles';
15
+ import typescript from 'rollup-plugin-typescript2';
13
16
  import vue from 'rollup-plugin-vue';
14
- import { EXTENSION_PKG_KEY, EXTENSION_TYPES, APP_SHARED_DEPS, API_SHARED_DEPS, APP_EXTENSION_TYPES, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
15
- import { isIn, isTypeIn, validateExtensionManifest } from '@directus/shared/utils';
16
- import { log, clear } from '../utils/logger';
17
17
  import { getLanguageFromPath, isLanguage } from '../utils/languages';
18
- import loadConfig from '../utils/load-config';
19
- import toObject from '../utils/to-object';
18
+ import { clear, log } from '../utils/logger';
19
+ import tryParseJson from '../utils/try-parse-json';
20
+ import generateBundleEntrypoint from './helpers/generate-bundle-entrypoint';
21
+ import loadConfig from './helpers/load-config';
22
+ import { validateBundleEntriesOption, validateSplitEntrypointOption } from './helpers/validate-cli-options';
20
23
  export default async function build(options) {
21
24
  var _a, _b, _c;
22
25
  const watch = (_a = options.watch) !== null && _a !== void 0 ? _a : false;
@@ -24,24 +27,31 @@ export default async function build(options) {
24
27
  const minify = (_c = options.minify) !== null && _c !== void 0 ? _c : false;
25
28
  if (!options.type && !options.input && !options.output) {
26
29
  const packagePath = path.resolve('package.json');
27
- let extensionManifest = {};
28
30
  if (!(await fse.pathExists(packagePath))) {
29
31
  log(`Current directory is not a valid package.`, 'error');
30
32
  process.exit(1);
31
33
  }
32
- else {
33
- extensionManifest = await fse.readJSON(packagePath);
34
- if (!validateExtensionManifest(extensionManifest)) {
35
- log(`Current directory is not a valid Directus extension.`, 'error');
36
- process.exit(1);
37
- }
34
+ const extensionManifest = await fse.readJSON(packagePath);
35
+ if (!validateExtensionManifest(extensionManifest)) {
36
+ log(`Current directory is not a valid Directus extension.`, 'error');
37
+ process.exit(1);
38
38
  }
39
39
  const extensionOptions = extensionManifest[EXTENSION_PKG_KEY];
40
- if (!isTypeIn(extensionOptions, EXTENSION_TYPES)) {
41
- log(`Extension type ${chalk.bold(extensionOptions.type)} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
40
+ if (extensionOptions.type === 'pack') {
41
+ log(`Building extension type ${chalk.bold('pack')} is not currently supported.`, 'error');
42
42
  process.exit(1);
43
43
  }
44
- if (isTypeIn(extensionOptions, HYBRID_EXTENSION_TYPES)) {
44
+ if (extensionOptions.type === 'bundle') {
45
+ await buildBundleExtension({
46
+ entries: extensionOptions.entries,
47
+ outputApp: extensionOptions.path.app,
48
+ outputApi: extensionOptions.path.api,
49
+ watch,
50
+ sourcemap,
51
+ minify,
52
+ });
53
+ }
54
+ else if (isTypeIn(extensionOptions, HYBRID_EXTENSION_TYPES)) {
45
55
  await buildHybridExtension({
46
56
  inputApp: extensionOptions.source.app,
47
57
  inputApi: extensionOptions.source.api,
@@ -71,8 +81,12 @@ export default async function build(options) {
71
81
  log(`Extension type has to be specified using the ${chalk.blue('[-t, --type <type>]')} option.`, 'error');
72
82
  process.exit(1);
73
83
  }
74
- else if (!isIn(type, EXTENSION_TYPES)) {
75
- log(`Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
84
+ if (!isIn(type, EXTENSION_PACKAGE_TYPES)) {
85
+ log(`Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_PACKAGE_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
86
+ process.exit(1);
87
+ }
88
+ if (type === 'pack') {
89
+ log(`Building extension type ${chalk.bold('pack')} is not currently supported.`, 'error');
76
90
  process.exit(1);
77
91
  }
78
92
  if (!input) {
@@ -83,22 +97,42 @@ export default async function build(options) {
83
97
  log(`Extension output file has to be specified using the ${chalk.blue('[-o, --output <file>]')} option.`, 'error');
84
98
  process.exit(1);
85
99
  }
86
- if (isIn(type, HYBRID_EXTENSION_TYPES)) {
87
- const inputObject = toObject(input);
88
- const outputObject = toObject(output);
89
- if (!inputObject || !inputObject.app || !inputObject.api) {
90
- log(`Input option needs to be of the format ${chalk.blue('[-i app:<app-entrypoint>,api:<api-entrypoint>]')}.`, 'error');
100
+ if (type === 'bundle') {
101
+ const entries = tryParseJson(input);
102
+ const splitOutput = tryParseJson(output);
103
+ if (!validateBundleEntriesOption(entries)) {
104
+ log(`Input option needs to be of the format ${chalk.blue(`[-i '[{"type":"<extension-type>","name":"<extension-name>","source":<entrypoint>}]']`)}.`, 'error');
105
+ process.exit(1);
106
+ }
107
+ if (!validateSplitEntrypointOption(splitOutput)) {
108
+ log(`Output option needs to be of the format ${chalk.blue(`[-o '{"app":"<app-entrypoint>","api":"<api-entrypoint>"}']`)}.`, 'error');
109
+ process.exit(1);
110
+ }
111
+ await buildBundleExtension({
112
+ entries,
113
+ outputApp: splitOutput.app,
114
+ outputApi: splitOutput.api,
115
+ watch,
116
+ sourcemap,
117
+ minify,
118
+ });
119
+ }
120
+ else if (isIn(type, HYBRID_EXTENSION_TYPES)) {
121
+ const splitInput = tryParseJson(input);
122
+ const splitOutput = tryParseJson(output);
123
+ if (!validateSplitEntrypointOption(splitInput)) {
124
+ log(`Input option needs to be of the format ${chalk.blue(`[-i '{"app":"<app-entrypoint>","api":"<api-entrypoint>"}']`)}.`, 'error');
91
125
  process.exit(1);
92
126
  }
93
- if (!outputObject || !outputObject.app || !outputObject.api) {
94
- log(`Output option needs to be of the format ${chalk.blue('[-o app:<app-output-file>,api:<api-output-file>]')}.`, 'error');
127
+ if (!validateSplitEntrypointOption(splitOutput)) {
128
+ log(`Output option needs to be of the format ${chalk.blue(`[-o '{"app":"<app-entrypoint>","api":"<api-entrypoint>"}']`)}.`, 'error');
95
129
  process.exit(1);
96
130
  }
97
131
  await buildHybridExtension({
98
- inputApp: inputObject.app,
99
- inputApi: inputObject.api,
100
- outputApp: outputObject.app,
101
- outputApi: outputObject.api,
132
+ inputApp: splitInput.app,
133
+ inputApi: splitInput.api,
134
+ outputApp: splitOutput.app,
135
+ outputApi: splitOutput.api,
102
136
  watch,
103
137
  sourcemap,
104
138
  minify,
@@ -168,7 +202,7 @@ async function buildHybridExtension({ inputApp, inputApi, outputApp, outputApi,
168
202
  process.exit(1);
169
203
  }
170
204
  if (!isLanguage(languageApi)) {
171
- log(`API language ${chalk.bold(languageApp)} is not supported.`, 'error');
205
+ log(`API language ${chalk.bold(languageApi)} is not supported.`, 'error');
172
206
  process.exit(1);
173
207
  }
174
208
  const config = await loadConfig();
@@ -202,6 +236,95 @@ async function buildHybridExtension({ inputApp, inputApi, outputApp, outputApi,
202
236
  await buildExtension(rollupOptionsAll);
203
237
  }
204
238
  }
239
+ async function buildBundleExtension({ entries, outputApp, outputApi, watch, sourcemap, minify, }) {
240
+ var _a;
241
+ if (outputApp.length === 0) {
242
+ log(`App output file can not be empty.`, 'error');
243
+ process.exit(1);
244
+ }
245
+ if (outputApi.length === 0) {
246
+ log(`API output file can not be empty.`, 'error');
247
+ process.exit(1);
248
+ }
249
+ const languagesApp = new Set();
250
+ const languagesApi = new Set();
251
+ for (const entry of entries) {
252
+ if (isTypeIn(entry, HYBRID_EXTENSION_TYPES)) {
253
+ const inputApp = entry.source.app;
254
+ const inputApi = entry.source.api;
255
+ if (!(await fse.pathExists(inputApp)) || !(await fse.stat(inputApp)).isFile()) {
256
+ log(`App entrypoint ${chalk.bold(inputApp)} does not exist.`, 'error');
257
+ process.exit(1);
258
+ }
259
+ if (!(await fse.pathExists(inputApi)) || !(await fse.stat(inputApi)).isFile()) {
260
+ log(`API entrypoint ${chalk.bold(inputApi)} does not exist.`, 'error');
261
+ process.exit(1);
262
+ }
263
+ const languageApp = getLanguageFromPath(inputApp);
264
+ const languageApi = getLanguageFromPath(inputApi);
265
+ if (!isLanguage(languageApp)) {
266
+ log(`App language ${chalk.bold(languageApp)} is not supported.`, 'error');
267
+ process.exit(1);
268
+ }
269
+ if (!isLanguage(languageApi)) {
270
+ log(`API language ${chalk.bold(languageApi)} is not supported.`, 'error');
271
+ process.exit(1);
272
+ }
273
+ languagesApp.add(languageApp);
274
+ languagesApi.add(languageApi);
275
+ }
276
+ else {
277
+ const input = entry.source;
278
+ if (!(await fse.pathExists(input)) || !(await fse.stat(input)).isFile()) {
279
+ log(`Entrypoint ${chalk.bold(input)} does not exist.`, 'error');
280
+ process.exit(1);
281
+ }
282
+ const language = getLanguageFromPath(input);
283
+ if (!isLanguage(language)) {
284
+ log(`Language ${chalk.bold(language)} is not supported.`, 'error');
285
+ process.exit(1);
286
+ }
287
+ if (isIn(entry.type, APP_EXTENSION_TYPES)) {
288
+ languagesApp.add(language);
289
+ }
290
+ else {
291
+ languagesApi.add(language);
292
+ }
293
+ }
294
+ }
295
+ const config = await loadConfig();
296
+ const plugins = (_a = config.plugins) !== null && _a !== void 0 ? _a : [];
297
+ const entrypointApp = generateBundleEntrypoint('app', entries);
298
+ const entrypointApi = generateBundleEntrypoint('api', entries);
299
+ const rollupOptionsApp = getRollupOptions({
300
+ mode: 'browser',
301
+ input: { entry: entrypointApp },
302
+ language: Array.from(languagesApp),
303
+ sourcemap,
304
+ minify,
305
+ plugins,
306
+ });
307
+ const rollupOptionsApi = getRollupOptions({
308
+ mode: 'node',
309
+ input: { entry: entrypointApi },
310
+ language: Array.from(languagesApi),
311
+ sourcemap,
312
+ minify,
313
+ plugins,
314
+ });
315
+ const rollupOutputOptionsApp = getRollupOutputOptions({ mode: 'browser', output: outputApp, sourcemap });
316
+ const rollupOutputOptionsApi = getRollupOutputOptions({ mode: 'node', output: outputApi, sourcemap });
317
+ const rollupOptionsAll = [
318
+ { rollupOptions: rollupOptionsApp, rollupOutputOptions: rollupOutputOptionsApp },
319
+ { rollupOptions: rollupOptionsApi, rollupOutputOptions: rollupOutputOptionsApi },
320
+ ];
321
+ if (watch) {
322
+ await watchExtension(rollupOptionsAll);
323
+ }
324
+ else {
325
+ await buildExtension(rollupOptionsAll);
326
+ }
327
+ }
205
328
  async function buildExtension(config) {
206
329
  const configs = Array.isArray(config) ? config : [config];
207
330
  const spinner = ora(chalk.bold('Building Directus extension...')).start();
@@ -266,13 +389,15 @@ async function watchExtension(config) {
266
389
  }
267
390
  }
268
391
  function getRollupOptions({ mode, input, language, sourcemap, minify, plugins, }) {
392
+ const languages = Array.isArray(language) ? language : [language];
269
393
  if (mode === 'browser') {
270
394
  return {
271
- input,
395
+ input: typeof input !== 'string' ? 'entry' : input,
272
396
  external: APP_SHARED_DEPS,
273
397
  plugins: [
398
+ typeof input !== 'string' ? virtual(input) : null,
274
399
  vue({ preprocessStyles: true }),
275
- language === 'typescript' ? typescript({ check: false }) : null,
400
+ languages.includes('typescript') ? typescript({ check: false }) : null,
276
401
  styles(),
277
402
  ...plugins,
278
403
  nodeResolve({ browser: true }),
@@ -290,10 +415,11 @@ function getRollupOptions({ mode, input, language, sourcemap, minify, plugins, }
290
415
  }
291
416
  else {
292
417
  return {
293
- input,
418
+ input: typeof input !== 'string' ? 'entry' : input,
294
419
  external: API_SHARED_DEPS,
295
420
  plugins: [
296
- language === 'typescript' ? typescript({ check: false }) : null,
421
+ typeof input !== 'string' ? virtual(input) : null,
422
+ languages.includes('typescript') ? typescript({ check: false }) : null,
297
423
  ...plugins,
298
424
  nodeResolve(),
299
425
  commonjs({ sourceMap: sourcemap }),
@@ -322,7 +448,7 @@ function getRollupOutputOptions({ mode, output, sourcemap, }) {
322
448
  return {
323
449
  file: output,
324
450
  format: 'cjs',
325
- exports: 'default',
451
+ exports: 'auto',
326
452
  inlineDynamicImports: true,
327
453
  sourcemap,
328
454
  };
@@ -1,5 +1,5 @@
1
- declare type CreateOptions = {
2
- language: string;
1
+ type CreateOptions = {
2
+ language?: string;
3
3
  };
4
4
  export default function create(type: string, name: string, options: CreateOptions): Promise<void>;
5
5
  export {};