@directus/extensions-sdk 9.14.2 → 9.15.1

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 (53) hide show
  1. package/dist/cjs/cli/commands/build.d.ts +6 -8
  2. package/dist/cjs/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cjs/cli/commands/build.js +242 -71
  4. package/dist/cjs/cli/commands/create.d.ts.map +1 -1
  5. package/dist/cjs/cli/commands/create.js +27 -33
  6. package/dist/cjs/cli/run.js +5 -5
  7. package/dist/cjs/cli/types.d.ts +9 -0
  8. package/dist/cjs/cli/types.d.ts.map +1 -1
  9. package/dist/cjs/cli/utils/load-config.d.ts +2 -1
  10. package/dist/cjs/cli/utils/load-config.d.ts.map +1 -1
  11. package/dist/cjs/cli/utils/logger.d.ts +2 -1
  12. package/dist/cjs/cli/utils/logger.d.ts.map +1 -1
  13. package/dist/cjs/cli/utils/logger.js +11 -1
  14. package/dist/cjs/cli/utils/to-object.d.ts +2 -0
  15. package/dist/cjs/cli/utils/to-object.d.ts.map +1 -0
  16. package/dist/cjs/cli/utils/to-object.js +17 -0
  17. package/dist/cjs/index.d.ts +1 -1
  18. package/dist/cjs/index.d.ts.map +1 -1
  19. package/dist/cjs/index.js +3 -1
  20. package/dist/esm/cli/commands/build.d.ts +6 -8
  21. package/dist/esm/cli/commands/build.d.ts.map +1 -1
  22. package/dist/esm/cli/commands/build.js +242 -71
  23. package/dist/esm/cli/commands/create.d.ts.map +1 -1
  24. package/dist/esm/cli/commands/create.js +25 -31
  25. package/dist/esm/cli/run.js +5 -5
  26. package/dist/esm/cli/types.d.ts +9 -0
  27. package/dist/esm/cli/types.d.ts.map +1 -1
  28. package/dist/esm/cli/utils/load-config.d.ts +2 -1
  29. package/dist/esm/cli/utils/load-config.d.ts.map +1 -1
  30. package/dist/esm/cli/utils/logger.d.ts +2 -1
  31. package/dist/esm/cli/utils/logger.d.ts.map +1 -1
  32. package/dist/esm/cli/utils/logger.js +9 -1
  33. package/dist/esm/cli/utils/to-object.d.ts +2 -0
  34. package/dist/esm/cli/utils/to-object.d.ts.map +1 -0
  35. package/dist/esm/cli/utils/to-object.js +14 -0
  36. package/dist/esm/index.d.ts +1 -1
  37. package/dist/esm/index.d.ts.map +1 -1
  38. package/dist/esm/index.js +1 -1
  39. package/package.json +3 -3
  40. package/src/cli/commands/build.ts +358 -93
  41. package/src/cli/commands/create.ts +33 -30
  42. package/src/cli/run.ts +5 -5
  43. package/src/cli/types.ts +8 -0
  44. package/src/cli/utils/load-config.ts +2 -1
  45. package/src/cli/utils/logger.ts +11 -1
  46. package/src/cli/utils/to-object.ts +16 -0
  47. package/src/index.ts +2 -0
  48. package/templates/common/typescript/tsconfig.json +2 -1
  49. package/templates/operation/javascript/src/api.js +6 -0
  50. package/templates/operation/javascript/src/app.js +23 -0
  51. package/templates/operation/typescript/src/api.ts +12 -0
  52. package/templates/operation/typescript/src/app.ts +25 -0
  53. package/templates/operation/typescript/src/shims.d.ts +5 -0
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-console */
2
+ import readline from 'readline';
2
3
  import chalk from 'chalk';
3
- export default function log(message, type) {
4
+ export function log(message, type) {
4
5
  if (type === 'info') {
5
6
  console.log(`${chalk.bold.gray('[Info]')} ${message}`);
6
7
  }
@@ -14,3 +15,10 @@ export default function log(message, type) {
14
15
  console.log(message);
15
16
  }
16
17
  }
18
+ export function clear() {
19
+ const repeatCount = process.stdout.rows - 2;
20
+ const blank = repeatCount > 0 ? '\n'.repeat(repeatCount) : '';
21
+ console.log(blank);
22
+ readline.cursorTo(process.stdout, 0, 0);
23
+ readline.clearScreenDown(process.stdout);
24
+ }
@@ -0,0 +1,2 @@
1
+ export default function toObject(val: string): Record<string, string> | null;
2
+ //# sourceMappingURL=to-object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-object.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/to-object.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAe3E"}
@@ -0,0 +1,14 @@
1
+ export default function toObject(val) {
2
+ const arr = val.split(',');
3
+ const obj = {};
4
+ for (const v of arr) {
5
+ const sub = v.match(/^([^:]+):(.+)$/);
6
+ if (sub) {
7
+ obj[sub[1]] = sub[2];
8
+ }
9
+ else {
10
+ return null;
11
+ }
12
+ }
13
+ return obj;
14
+ }
@@ -1,3 +1,3 @@
1
- export { defineInterface, defineDisplay, defineLayout, defineModule, definePanel, defineHook, defineEndpoint, getFieldsFromTemplate, getRelationType, } from '@directus/shared/utils';
1
+ export { defineInterface, defineDisplay, defineLayout, defineModule, definePanel, defineHook, defineEndpoint, defineOperationApp, defineOperationApi, getFieldsFromTemplate, getRelationType, } from '@directus/shared/utils';
2
2
  export { useStores, useApi, useExtensions, useSync, useCollection, useItems, useLayout, useFilterFields, } from '@directus/shared/composables';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,eAAe,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,SAAS,EACT,MAAM,EACN,aAAa,EACb,OAAO,EACP,aAAa,EACb,QAAQ,EACR,SAAS,EACT,eAAe,GACf,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,SAAS,EACT,MAAM,EACN,aAAa,EACb,OAAO,EACP,aAAa,EACb,QAAQ,EACR,SAAS,EACT,eAAe,GACf,MAAM,8BAA8B,CAAC"}
package/dist/esm/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { defineInterface, defineDisplay, defineLayout, defineModule, definePanel, defineHook, defineEndpoint, getFieldsFromTemplate, getRelationType, } from '@directus/shared/utils';
1
+ export { defineInterface, defineDisplay, defineLayout, defineModule, definePanel, defineHook, defineEndpoint, defineOperationApp, defineOperationApi, getFieldsFromTemplate, getRelationType, } from '@directus/shared/utils';
2
2
  export { useStores, useApi, useExtensions, useSync, useCollection, useItems, useLayout, useFilterFields, } from '@directus/shared/composables';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/extensions-sdk",
3
- "version": "9.14.2",
3
+ "version": "9.15.1",
4
4
  "description": "A toolkit to develop extensions to extend Directus.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "repository": "directus/directus",
@@ -25,7 +25,7 @@
25
25
  "author": "Nicola Krumschmidt",
26
26
  "gitHead": "24621f3934dc77eb23441331040ed13c676ceffd",
27
27
  "dependencies": {
28
- "@directus/shared": "9.14.2",
28
+ "@directus/shared": "9.15.1",
29
29
  "@rollup/plugin-commonjs": "^21.0.1",
30
30
  "@rollup/plugin-json": "^4.1.0",
31
31
  "@rollup/plugin-node-resolve": "^13.1.3",
@@ -53,6 +53,6 @@
53
53
  "build:esm": "tsc --project ./tsconfig.json --module ES2015 --outDir ./dist/esm",
54
54
  "build:cjs": "tsc --project ./tsconfig.json --module CommonJS --outDir ./dist/cjs",
55
55
  "cleanup": "rimraf ./dist",
56
- "dev": "npm run build -w --preserveWatchOutput --incremental"
56
+ "dev": "pnpm build -- -w --preserveWatchOutput --incremental"
57
57
  }
58
58
  }
@@ -18,69 +18,183 @@ import typescript from 'rollup-plugin-typescript2';
18
18
  import { terser } from 'rollup-plugin-terser';
19
19
  import styles from 'rollup-plugin-styles';
20
20
  import vue from 'rollup-plugin-vue';
21
- import { EXTENSION_PKG_KEY, EXTENSION_TYPES, APP_SHARED_DEPS, API_SHARED_DEPS } from '@directus/shared/constants';
22
- import { isAppExtension, isExtension, validateExtensionManifest } from '@directus/shared/utils';
23
- import { ExtensionManifestRaw, ExtensionType } from '@directus/shared/types';
24
- import log from '../utils/logger';
21
+ import {
22
+ EXTENSION_PKG_KEY,
23
+ EXTENSION_TYPES,
24
+ APP_SHARED_DEPS,
25
+ API_SHARED_DEPS,
26
+ APP_EXTENSION_TYPES,
27
+ HYBRID_EXTENSION_TYPES,
28
+ } from '@directus/shared/constants';
29
+ import { isIn, isTypeIn, validateExtensionManifest } from '@directus/shared/utils';
30
+ import { ApiExtensionType, AppExtensionType, ExtensionManifestRaw } from '@directus/shared/types';
31
+ import { log, clear } from '../utils/logger';
25
32
  import { getLanguageFromPath, isLanguage } from '../utils/languages';
26
- import { Language } from '../types';
33
+ import { Language, RollupConfig, RollupMode } from '../types';
27
34
  import loadConfig from '../utils/load-config';
35
+ import toObject from '../utils/to-object';
28
36
 
29
37
  type BuildOptions = {
30
- type: string;
31
- input: string;
32
- output: string;
33
- language: string;
34
- force: boolean;
35
- watch: boolean;
36
- minify: boolean;
37
- sourcemap: boolean;
38
+ type?: string;
39
+ input?: string;
40
+ output?: string;
41
+ watch?: boolean;
42
+ minify?: boolean;
43
+ sourcemap?: boolean;
38
44
  };
39
45
 
40
46
  export default async function build(options: BuildOptions): Promise<void> {
41
- const packagePath = path.resolve('package.json');
42
- let extensionManifest: ExtensionManifestRaw = {};
47
+ const watch = options.watch ?? false;
48
+ const sourcemap = options.sourcemap ?? false;
49
+ const minify = options.minify ?? false;
50
+
51
+ if (!options.type && !options.input && !options.output) {
52
+ const packagePath = path.resolve('package.json');
53
+ let extensionManifest: ExtensionManifestRaw = {};
54
+
55
+ if (!(await fse.pathExists(packagePath))) {
56
+ log(`Current directory is not a valid package.`, 'error');
57
+ process.exit(1);
58
+ } else {
59
+ extensionManifest = await fse.readJSON(packagePath);
60
+
61
+ if (!validateExtensionManifest(extensionManifest)) {
62
+ log(`Current directory is not a valid Directus extension.`, 'error');
63
+ process.exit(1);
64
+ }
65
+ }
66
+
67
+ const extensionOptions = extensionManifest[EXTENSION_PKG_KEY];
68
+
69
+ if (!isTypeIn(extensionOptions, EXTENSION_TYPES)) {
70
+ log(
71
+ `Extension type ${chalk.bold(
72
+ extensionOptions.type
73
+ )} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(
74
+ ', '
75
+ )}.`,
76
+ 'error'
77
+ );
78
+ process.exit(1);
79
+ }
43
80
 
44
- if (!(await fse.pathExists(packagePath))) {
45
- log(`Current directory is not a package.`, !options.force ? 'error' : 'warn');
46
- if (!options.force) process.exit(1);
81
+ if (isTypeIn(extensionOptions, HYBRID_EXTENSION_TYPES)) {
82
+ await buildHybridExtension({
83
+ inputApp: extensionOptions.source.app,
84
+ inputApi: extensionOptions.source.api,
85
+ outputApp: extensionOptions.path.app,
86
+ outputApi: extensionOptions.path.api,
87
+ watch,
88
+ sourcemap,
89
+ minify,
90
+ });
91
+ } else {
92
+ await buildAppOrApiExtension({
93
+ type: extensionOptions.type,
94
+ input: extensionOptions.source,
95
+ output: extensionOptions.path,
96
+ watch,
97
+ sourcemap,
98
+ minify,
99
+ });
100
+ }
47
101
  } else {
48
- extensionManifest = await fse.readJSON(packagePath);
102
+ const type = options.type;
103
+ const input = options.input;
104
+ const output = options.output;
49
105
 
50
- if (!validateExtensionManifest(extensionManifest)) {
51
- log(`Current directory is not a Directus extension.`, !options.force ? 'error' : 'warn');
52
- if (!options.force) process.exit(1);
106
+ if (!type) {
107
+ log(`Extension type has to be specified using the ${chalk.blue('[-t, --type <type>]')} option.`, 'error');
108
+ process.exit(1);
109
+ } else if (!isIn(type, EXTENSION_TYPES)) {
110
+ log(
111
+ `Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) =>
112
+ chalk.bold.magenta(t)
113
+ ).join(', ')}.`,
114
+ 'error'
115
+ );
116
+ process.exit(1);
53
117
  }
54
- }
55
118
 
56
- const extensionOptions = extensionManifest[EXTENSION_PKG_KEY];
119
+ if (!input) {
120
+ log(`Extension entrypoint has to be specified using the ${chalk.blue('[-i, --input <file>]')} option.`, 'error');
121
+ process.exit(1);
122
+ }
123
+ if (!output) {
124
+ log(
125
+ `Extension output file has to be specified using the ${chalk.blue('[-o, --output <file>]')} option.`,
126
+ 'error'
127
+ );
128
+ process.exit(1);
129
+ }
57
130
 
58
- const type = options.type || extensionOptions?.type;
131
+ if (isIn(type, HYBRID_EXTENSION_TYPES)) {
132
+ const inputObject = toObject(input);
133
+ const outputObject = toObject(output);
59
134
 
60
- if (!type || !isExtension(type)) {
61
- log(
62
- `Extension type ${chalk.bold(type)} does not exist. Available extension types: ${EXTENSION_TYPES.map((t) =>
63
- chalk.bold.magenta(t)
64
- ).join(', ')}.`,
65
- 'error'
66
- );
67
- process.exit(1);
68
- }
135
+ if (!inputObject || !inputObject.app || !inputObject.api) {
136
+ log(
137
+ `Input option needs to be of the format ${chalk.blue('[-i app:<app-entrypoint>,api:<api-entrypoint>]')}.`,
138
+ 'error'
139
+ );
140
+ process.exit(1);
141
+ }
142
+ if (!outputObject || !outputObject.app || !outputObject.api) {
143
+ log(
144
+ `Output option needs to be of the format ${chalk.blue('[-o app:<app-output-file>,api:<api-output-file>]')}.`,
145
+ 'error'
146
+ );
147
+ process.exit(1);
148
+ }
69
149
 
70
- const input = options.input || (extensionOptions as { source: string })?.source;
71
- const output = options.output || (extensionOptions as { path: string })?.path;
150
+ await buildHybridExtension({
151
+ inputApp: inputObject.app,
152
+ inputApi: inputObject.api,
153
+ outputApp: outputObject.app,
154
+ outputApi: outputObject.api,
155
+ watch,
156
+ sourcemap,
157
+ minify,
158
+ });
159
+ } else {
160
+ await buildAppOrApiExtension({
161
+ type,
162
+ input,
163
+ output,
164
+ watch,
165
+ sourcemap,
166
+ minify,
167
+ });
168
+ }
169
+ }
170
+ }
72
171
 
73
- if (!input || !(await fse.pathExists(input)) || !(await fse.stat(input)).isFile()) {
172
+ async function buildAppOrApiExtension({
173
+ type,
174
+ input,
175
+ output,
176
+ watch,
177
+ sourcemap,
178
+ minify,
179
+ }: {
180
+ type: AppExtensionType | ApiExtensionType;
181
+ input: string;
182
+ output: string;
183
+ watch: boolean;
184
+ sourcemap: boolean;
185
+ minify: boolean;
186
+ }) {
187
+ if (!(await fse.pathExists(input)) || !(await fse.stat(input)).isFile()) {
74
188
  log(`Entrypoint ${chalk.bold(input)} does not exist.`, 'error');
75
189
  process.exit(1);
76
190
  }
77
191
 
78
- if (!output) {
79
- log(`Output file must be a valid path.`, 'error');
192
+ if (output.length === 0) {
193
+ log(`Output file ${chalk.bold(output)} must be a valid path.`, 'error');
80
194
  process.exit(1);
81
195
  }
82
196
 
83
- const language = options.language || getLanguageFromPath(input);
197
+ const language = getLanguageFromPath(input);
84
198
 
85
199
  if (!isLanguage(language)) {
86
200
  log(`Language ${chalk.bold(language)} is not supported.`, 'error');
@@ -88,61 +202,199 @@ export default async function build(options: BuildOptions): Promise<void> {
88
202
  }
89
203
 
90
204
  const config = await loadConfig();
205
+ const plugins = config.plugins ?? [];
206
+
207
+ const mode = isIn(type, APP_EXTENSION_TYPES) ? 'browser' : 'node';
208
+
209
+ const rollupOptions = getRollupOptions({ mode, input, language, sourcemap, minify, plugins });
210
+ const rollupOutputOptions = getRollupOutputOptions({ mode, output, sourcemap });
211
+
212
+ if (watch) {
213
+ await watchExtension({ rollupOptions, rollupOutputOptions });
214
+ } else {
215
+ await buildExtension({ rollupOptions, rollupOutputOptions });
216
+ }
217
+ }
218
+
219
+ async function buildHybridExtension({
220
+ inputApp,
221
+ inputApi,
222
+ outputApp,
223
+ outputApi,
224
+ watch,
225
+ sourcemap,
226
+ minify,
227
+ }: {
228
+ inputApp: string;
229
+ inputApi: string;
230
+ outputApp: string;
231
+ outputApi: string;
232
+ watch: boolean;
233
+ sourcemap: boolean;
234
+ minify: boolean;
235
+ }) {
236
+ if (!(await fse.pathExists(inputApp)) || !(await fse.stat(inputApp)).isFile()) {
237
+ log(`App entrypoint ${chalk.bold(inputApp)} does not exist.`, 'error');
238
+ process.exit(1);
239
+ }
240
+ if (!(await fse.pathExists(inputApi)) || !(await fse.stat(inputApi)).isFile()) {
241
+ log(`API entrypoint ${chalk.bold(inputApi)} does not exist.`, 'error');
242
+ process.exit(1);
243
+ }
91
244
 
92
- const spinner = ora('Building Directus extension...').start();
245
+ if (outputApp.length === 0) {
246
+ log(`App output file ${chalk.bold(outputApp)} must be a valid path.`, 'error');
247
+ process.exit(1);
248
+ }
249
+ if (outputApi.length === 0) {
250
+ log(`API output file ${chalk.bold(outputApi)} must be a valid path.`, 'error');
251
+ process.exit(1);
252
+ }
93
253
 
94
- const rollupOptions = getRollupOptions(type, language, input, config.plugins, options);
95
- const rollupOutputOptions = getRollupOutputOptions(type, output, options);
254
+ const languageApp = getLanguageFromPath(inputApp);
255
+ const languageApi = getLanguageFromPath(inputApi);
96
256
 
97
- if (options.watch) {
257
+ if (!isLanguage(languageApp)) {
258
+ log(`App language ${chalk.bold(languageApp)} is not supported.`, 'error');
259
+ process.exit(1);
260
+ }
261
+ if (!isLanguage(languageApi)) {
262
+ log(`API language ${chalk.bold(languageApp)} is not supported.`, 'error');
263
+ process.exit(1);
264
+ }
265
+
266
+ const config = await loadConfig();
267
+ const plugins = config.plugins ?? [];
268
+
269
+ const rollupOptionsApp = getRollupOptions({
270
+ mode: 'browser',
271
+ input: inputApp,
272
+ language: languageApp,
273
+ sourcemap,
274
+ minify,
275
+ plugins,
276
+ });
277
+ const rollupOptionsApi = getRollupOptions({
278
+ mode: 'node',
279
+ input: inputApi,
280
+ language: languageApi,
281
+ sourcemap,
282
+ minify,
283
+ plugins,
284
+ });
285
+ const rollupOutputOptionsApp = getRollupOutputOptions({ mode: 'browser', output: outputApp, sourcemap });
286
+ const rollupOutputOptionsApi = getRollupOutputOptions({ mode: 'node', output: outputApi, sourcemap });
287
+
288
+ const rollupOptionsAll = [
289
+ { rollupOptions: rollupOptionsApp, rollupOutputOptions: rollupOutputOptionsApp },
290
+ { rollupOptions: rollupOptionsApi, rollupOutputOptions: rollupOutputOptionsApi },
291
+ ];
292
+
293
+ if (watch) {
294
+ await watchExtension(rollupOptionsAll);
295
+ } else {
296
+ await buildExtension(rollupOptionsAll);
297
+ }
298
+ }
299
+
300
+ async function buildExtension(config: RollupConfig | RollupConfig[]) {
301
+ const configs = Array.isArray(config) ? config : [config];
302
+
303
+ const spinner = ora(chalk.bold('Building Directus extension...')).start();
304
+
305
+ const result = await Promise.all(
306
+ configs.map(async (c) => {
307
+ try {
308
+ const bundle = await rollup(c.rollupOptions);
309
+
310
+ await bundle.write(c.rollupOutputOptions);
311
+ await bundle.close();
312
+ } catch (error) {
313
+ return formatRollupError(error as RollupError);
314
+ }
315
+
316
+ return null;
317
+ })
318
+ );
319
+
320
+ const resultErrors = result.filter((r) => r !== null);
321
+
322
+ if (resultErrors.length > 0) {
323
+ spinner.fail(chalk.bold('Failed'));
324
+
325
+ log(resultErrors.join('\n\n'));
326
+
327
+ process.exit(1);
328
+ } else {
329
+ spinner.succeed(chalk.bold('Done'));
330
+ }
331
+ }
332
+
333
+ async function watchExtension(config: RollupConfig | RollupConfig[]) {
334
+ const configs = Array.isArray(config) ? config : [config];
335
+
336
+ const spinner = ora(chalk.bold('Building Directus extension...'));
337
+
338
+ let buildCount = 0;
339
+
340
+ for (const c of configs) {
98
341
  const watcher = rollupWatch({
99
- ...rollupOptions,
100
- output: rollupOutputOptions,
342
+ ...c.rollupOptions,
343
+ output: c.rollupOutputOptions,
101
344
  });
102
345
 
103
346
  watcher.on('event', async (event) => {
104
347
  switch (event.code) {
105
- case 'ERROR': {
106
- spinner.fail(chalk.bold('Failed'));
107
- handleRollupError(event.error);
108
- spinner.start(chalk.bold('Watching files for changes...'));
348
+ case 'BUNDLE_START':
349
+ if (buildCount === 0) {
350
+ clear();
351
+ spinner.start();
352
+ }
353
+
354
+ buildCount++;
109
355
  break;
110
- }
111
356
  case 'BUNDLE_END':
112
357
  await event.result.close();
113
- spinner.succeed(chalk.bold('Done'));
114
- spinner.start(chalk.bold('Watching files for changes...'));
358
+
359
+ buildCount--;
360
+
361
+ if (buildCount === 0) {
362
+ spinner.succeed(chalk.bold('Done'));
363
+ log(chalk.bold.green('Watching files for changes...'));
364
+ }
115
365
  break;
116
- case 'BUNDLE_START':
117
- spinner.text = 'Building Directus extension...';
366
+ case 'ERROR': {
367
+ buildCount--;
368
+
369
+ spinner.fail(chalk.bold('Failed'));
370
+ log(formatRollupError(event.error));
371
+
372
+ if (buildCount > 0) {
373
+ spinner.start();
374
+ }
118
375
  break;
376
+ }
119
377
  }
120
378
  });
121
- } else {
122
- try {
123
- const bundle = await rollup(rollupOptions);
124
-
125
- await bundle.write(rollupOutputOptions);
126
-
127
- await bundle.close();
128
-
129
- spinner.succeed(chalk.bold('Done'));
130
- } catch (error) {
131
- spinner.fail(chalk.bold('Failed'));
132
- handleRollupError(error as RollupError);
133
- process.exitCode = 1;
134
- }
135
379
  }
136
380
  }
137
381
 
138
- function getRollupOptions(
139
- type: ExtensionType,
140
- language: Language,
141
- input: string,
142
- plugins: Plugin[] = [],
143
- options: BuildOptions
144
- ): RollupOptions {
145
- if (isAppExtension(type)) {
382
+ function getRollupOptions({
383
+ mode,
384
+ input,
385
+ language,
386
+ sourcemap,
387
+ minify,
388
+ plugins,
389
+ }: {
390
+ mode: RollupMode;
391
+ input: string;
392
+ language: Language;
393
+ sourcemap: boolean;
394
+ minify: boolean;
395
+ plugins: Plugin[];
396
+ }): RollupOptions {
397
+ if (mode === 'browser') {
146
398
  return {
147
399
  input,
148
400
  external: APP_SHARED_DEPS,
@@ -152,7 +404,7 @@ function getRollupOptions(
152
404
  styles(),
153
405
  ...plugins,
154
406
  nodeResolve({ browser: true }),
155
- commonjs({ esmExternals: true, sourceMap: options.sourcemap }),
407
+ commonjs({ esmExternals: true, sourceMap: sourcemap }),
156
408
  json(),
157
409
  replace({
158
410
  values: {
@@ -160,7 +412,7 @@ function getRollupOptions(
160
412
  },
161
413
  preventAssignment: true,
162
414
  }),
163
- options.minify ? terser() : null,
415
+ minify ? terser() : null,
164
416
  ],
165
417
  };
166
418
  } else {
@@ -171,7 +423,7 @@ function getRollupOptions(
171
423
  language === 'typescript' ? typescript({ check: false }) : null,
172
424
  ...plugins,
173
425
  nodeResolve(),
174
- commonjs({ sourceMap: options.sourcemap }),
426
+ commonjs({ sourceMap: sourcemap }),
175
427
  json(),
176
428
  replace({
177
429
  values: {
@@ -179,19 +431,27 @@ function getRollupOptions(
179
431
  },
180
432
  preventAssignment: true,
181
433
  }),
182
- options.minify ? terser() : null,
434
+ minify ? terser() : null,
183
435
  ],
184
436
  };
185
437
  }
186
438
  }
187
439
 
188
- function getRollupOutputOptions(type: ExtensionType, output: string, options: BuildOptions): RollupOutputOptions {
189
- if (isAppExtension(type)) {
440
+ function getRollupOutputOptions({
441
+ mode,
442
+ output,
443
+ sourcemap,
444
+ }: {
445
+ mode: RollupMode;
446
+ output: string;
447
+ sourcemap: boolean;
448
+ }): RollupOutputOptions {
449
+ if (mode === 'browser') {
190
450
  return {
191
451
  file: output,
192
452
  format: 'es',
193
453
  inlineDynamicImports: true,
194
- sourcemap: options.sourcemap,
454
+ sourcemap,
195
455
  };
196
456
  } else {
197
457
  return {
@@ -199,30 +459,35 @@ function getRollupOutputOptions(type: ExtensionType, output: string, options: Bu
199
459
  format: 'cjs',
200
460
  exports: 'default',
201
461
  inlineDynamicImports: true,
202
- sourcemap: options.sourcemap,
462
+ sourcemap,
203
463
  };
204
464
  }
205
465
  }
206
466
 
207
- function handleRollupError(error: RollupError): void {
208
- const pluginPrefix = error.plugin ? `(plugin ${error.plugin}) ` : '';
209
- log('\n' + chalk.red.bold(`${pluginPrefix}${error.name}: ${error.message}`));
467
+ function formatRollupError(error: RollupError): string {
468
+ let message = '';
469
+
470
+ message += `${chalk.bold.red(`[${error.name}]`)} ${error.message}${
471
+ error.plugin ? ` (plugin ${error.plugin})` : ''
472
+ }\n`;
210
473
 
211
474
  if (error.url) {
212
- log(chalk.cyan(error.url), 'error');
475
+ message += '\n' + chalk.green(error.url);
213
476
  }
214
477
 
215
478
  if (error.loc) {
216
- log(`${(error.loc.file || error.id)!} (${error.loc.line}:${error.loc.column})`);
479
+ message += '\n' + chalk.green(`${error.loc.file ?? error.id}:${error.loc.line}:${error.loc.column}`);
217
480
  } else if (error.id) {
218
- log(error.id);
481
+ message += '\n' + chalk.green(error.id);
219
482
  }
220
483
 
221
484
  if (error.frame) {
222
- log(chalk.dim(error.frame));
485
+ message += '\n' + chalk.dim(error.frame);
223
486
  }
224
487
 
225
488
  if (error.stack) {
226
- log(chalk.dim(error.stack));
489
+ message += '\n' + chalk.dim(error.stack);
227
490
  }
491
+
492
+ return message;
228
493
  }