@directus/extensions-sdk 9.22.1 → 9.22.4

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.
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const inquirer_1 = __importDefault(require("inquirer"));
10
10
  const logger_1 = require("../utils/logger");
11
+ const types_1 = require("@directus/shared/types");
11
12
  const utils_1 = require("@directus/shared/utils");
12
13
  const node_1 = require("@directus/shared/utils/node");
13
14
  const constants_1 = require("@directus/shared/constants");
@@ -26,18 +27,18 @@ async function add() {
26
27
  (0, logger_1.log)(`Current directory is not a valid package.`, 'error');
27
28
  process.exit(1);
28
29
  }
29
- const extensionManifestFile = await fs_extra_1.default.readFile(packagePath, 'utf8');
30
- const extensionManifest = JSON.parse(extensionManifestFile);
31
- const indent = (0, detect_json_indent_1.default)(extensionManifestFile);
32
- if (!(0, utils_1.validateExtensionManifest)(extensionManifest)) {
30
+ let extensionManifest;
31
+ let indent = null;
32
+ try {
33
+ const extensionManifestFile = await fs_extra_1.default.readFile(packagePath, 'utf8');
34
+ extensionManifest = types_1.ExtensionManifest.parse(JSON.parse(extensionManifestFile));
35
+ indent = (0, detect_json_indent_1.default)(extensionManifestFile);
36
+ }
37
+ catch (e) {
33
38
  (0, logger_1.log)(`Current directory is not a valid Directus extension.`, 'error');
34
39
  process.exit(1);
35
40
  }
36
41
  const extensionOptions = extensionManifest[constants_1.EXTENSION_PKG_KEY];
37
- if (extensionOptions.type === 'pack') {
38
- (0, logger_1.log)(`Adding entries to extensions with type ${chalk_1.default.bold('pack')} is not currently supported.`, 'error');
39
- process.exit(1);
40
- }
41
42
  const sourceExists = await fs_extra_1.default.pathExists(path_1.default.resolve('src'));
42
43
  if (extensionOptions.type === 'bundle') {
43
44
  const { type, name, language, alternativeSource } = await inquirer_1.default.prompt([
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const constants_1 = require("@directus/shared/constants");
7
+ const types_1 = require("@directus/shared/types");
7
8
  const utils_1 = require("@directus/shared/utils");
8
9
  const plugin_commonjs_1 = __importDefault(require("@rollup/plugin-commonjs"));
9
10
  const plugin_json_1 = __importDefault(require("@rollup/plugin-json"));
@@ -36,16 +37,15 @@ async function build(options) {
36
37
  (0, logger_1.log)(`Current directory is not a valid package.`, 'error');
37
38
  process.exit(1);
38
39
  }
39
- const extensionManifest = await fs_extra_1.default.readJSON(packagePath);
40
- if (!(0, utils_1.validateExtensionManifest)(extensionManifest)) {
40
+ let extensionManifest;
41
+ try {
42
+ extensionManifest = types_1.ExtensionManifest.parse(await fs_extra_1.default.readJSON(packagePath));
43
+ }
44
+ catch (err) {
41
45
  (0, logger_1.log)(`Current directory is not a valid Directus extension.`, 'error');
42
46
  process.exit(1);
43
47
  }
44
48
  const extensionOptions = extensionManifest[constants_1.EXTENSION_PKG_KEY];
45
- if (extensionOptions.type === 'pack') {
46
- (0, logger_1.log)(`Building extension type ${chalk_1.default.bold('pack')} is not currently supported.`, 'error');
47
- process.exit(1);
48
- }
49
49
  if (extensionOptions.type === 'bundle') {
50
50
  await buildBundleExtension({
51
51
  entries: extensionOptions.entries,
@@ -86,12 +86,8 @@ async function build(options) {
86
86
  (0, logger_1.log)(`Extension type has to be specified using the ${chalk_1.default.blue('[-t, --type <type>]')} option.`, 'error');
87
87
  process.exit(1);
88
88
  }
89
- if (!(0, utils_1.isIn)(type, constants_1.EXTENSION_PACKAGE_TYPES)) {
90
- (0, logger_1.log)(`Extension type ${chalk_1.default.bold(type)} is not supported. Available extension types: ${constants_1.EXTENSION_PACKAGE_TYPES.map((t) => chalk_1.default.bold.magenta(t)).join(', ')}.`, 'error');
91
- process.exit(1);
92
- }
93
- if (type === 'pack') {
94
- (0, logger_1.log)(`Building extension type ${chalk_1.default.bold('pack')} is not currently supported.`, 'error');
89
+ if (!(0, utils_1.isIn)(type, constants_1.EXTENSION_TYPES)) {
90
+ (0, logger_1.log)(`Extension type ${chalk_1.default.bold(type)} is not supported. Available extension types: ${constants_1.EXTENSION_TYPES.map((t) => chalk_1.default.bold.magenta(t)).join(', ')}.`, 'error');
95
91
  process.exit(1);
96
92
  }
97
93
  if (!input) {
@@ -103,9 +99,9 @@ async function build(options) {
103
99
  process.exit(1);
104
100
  }
105
101
  if (type === 'bundle') {
106
- const entries = (0, try_parse_json_1.default)(input);
102
+ const entries = types_1.ExtensionOptionsBundleEntries.safeParse((0, try_parse_json_1.default)(input));
107
103
  const splitOutput = (0, try_parse_json_1.default)(output);
108
- if (!(0, validate_cli_options_1.validateBundleEntriesOption)(entries)) {
104
+ if (entries.success === false) {
109
105
  (0, logger_1.log)(`Input option needs to be of the format ${chalk_1.default.blue(`[-i '[{"type":"<extension-type>","name":"<extension-name>","source":<entrypoint>}]']`)}.`, 'error');
110
106
  process.exit(1);
111
107
  }
@@ -114,7 +110,7 @@ async function build(options) {
114
110
  process.exit(1);
115
111
  }
116
112
  await buildBundleExtension({
117
- entries,
113
+ entries: entries.data,
118
114
  outputApp: splitOutput.app,
119
115
  outputApi: splitOutput.api,
120
116
  watch,
@@ -20,8 +20,8 @@ async function create(type, name, options) {
20
20
  var _a;
21
21
  const targetDir = name.substring(name.lastIndexOf('/') + 1);
22
22
  const targetPath = path_1.default.resolve(targetDir);
23
- if (!(0, utils_1.isIn)(type, constants_1.EXTENSION_PACKAGE_TYPES)) {
24
- (0, logger_1.log)(`Extension type ${chalk_1.default.bold(type)} is not supported. Available extension types: ${constants_1.EXTENSION_PACKAGE_TYPES.map((t) => chalk_1.default.bold.magenta(t)).join(', ')}.`, 'error');
23
+ if (!(0, utils_1.isIn)(type, constants_1.EXTENSION_TYPES)) {
24
+ (0, logger_1.log)(`Extension type ${chalk_1.default.bold(type)} is not supported. Available extension types: ${constants_1.EXTENSION_TYPES.map((t) => chalk_1.default.bold.magenta(t)).join(', ')}.`, 'error');
25
25
  process.exit(1);
26
26
  }
27
27
  if (targetDir.length === 0) {
@@ -40,7 +40,7 @@ async function create(type, name, options) {
40
40
  process.exit(1);
41
41
  }
42
42
  }
43
- if ((0, utils_1.isIn)(type, constants_1.PACKAGE_EXTENSION_TYPES)) {
43
+ if ((0, utils_1.isIn)(type, constants_1.BUNDLE_EXTENSION_TYPES)) {
44
44
  await createPackageExtension({ type, name, targetDir, targetPath });
45
45
  }
46
46
  else {
@@ -54,7 +54,7 @@ async function createPackageExtension({ type, name, targetDir, targetPath, }) {
54
54
  await fs_extra_1.default.ensureDir(targetPath);
55
55
  await (0, copy_template_1.default)(type, targetPath);
56
56
  const host = `^${(0, get_sdk_version_1.default)()}`;
57
- const options = type === 'bundle' ? { type, path: { app: 'dist/app.js', api: 'dist/api.js' }, entries: [], host } : { type, host };
57
+ const options = { type, path: { app: 'dist/app.js', api: 'dist/api.js' }, entries: [], host };
58
58
  const packageManifest = getPackageManifest(name, options, await (0, get_extension_dev_deps_1.default)(type));
59
59
  await fs_extra_1.default.writeJSON(path_1.default.join(targetPath, 'package.json'), packageManifest, { spaces: '\t' });
60
60
  const packageManager = (0, get_package_manager_1.default)();
@@ -92,17 +92,24 @@ async function createLocalExtension({ type, name, targetDir, targetPath, languag
92
92
  (0, logger_1.log)(getDoneMessage(type, targetDir, targetPath, packageManager));
93
93
  }
94
94
  function getPackageManifest(name, options, deps) {
95
- return {
95
+ const packageManifest = {
96
96
  name: constants_1.EXTENSION_NAME_REGEX.test(name) ? name : `directus-extension-${name}`,
97
+ description: 'Please enter a description for your extension',
98
+ icon: 'extension',
97
99
  version: '1.0.0',
98
100
  keywords: ['directus', 'directus-extension', `directus-custom-${options.type}`],
99
101
  [constants_1.EXTENSION_PKG_KEY]: options,
100
102
  scripts: {
101
103
  build: 'directus-extension build',
102
104
  dev: 'directus-extension build -w --no-minify',
105
+ link: 'directus-extension link',
103
106
  },
104
107
  devDependencies: deps,
105
108
  };
109
+ if (options.type === 'bundle') {
110
+ packageManifest.scripts['add'] = 'directus-extension add';
111
+ }
112
+ return packageManifest;
106
113
  }
107
114
  function getDoneMessage(type, targetDir, targetPath, packageManager) {
108
115
  return `
@@ -1,4 +1,4 @@
1
- import { ExtensionPackageType } from '@directus/shared/types';
1
+ import { ExtensionType } from '@directus/shared/types';
2
2
  import { Language } from '../../types';
3
- export default function copyTemplate(type: ExtensionPackageType, extensionPath: string, sourcePath?: string, language?: Language): Promise<void>;
3
+ export default function copyTemplate(type: ExtensionType, extensionPath: string, sourcePath?: string, language?: Language): Promise<void>;
4
4
  //# sourceMappingURL=copy-template.d.ts.map
@@ -8,7 +8,7 @@ const constants_1 = require("@directus/shared/constants");
8
8
  const utils_1 = require("@directus/shared/utils");
9
9
  const node_1 = require("@directus/shared/utils/node");
10
10
  function generateBundleEntrypoint(mode, entries) {
11
- const types = mode === 'app' ? constants_1.APP_OR_HYBRID_EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES;
11
+ const types = [...(mode === 'app' ? constants_1.APP_EXTENSION_TYPES : constants_1.API_EXTENSION_TYPES), ...constants_1.HYBRID_EXTENSION_TYPES];
12
12
  const entriesForTypes = entries.filter((entry) => (0, utils_1.isIn)(entry.type, types));
13
13
  const imports = entriesForTypes.map((entry, i) => `import e${i} from './${(0, node_1.pathToRelativeUrl)(path_1.default.resolve((0, utils_1.isTypeIn)(entry, constants_1.HYBRID_EXTENSION_TYPES)
14
14
  ? mode === 'app'
@@ -1,4 +1,4 @@
1
- import { ExtensionPackageType } from '@directus/shared/types';
1
+ import { ExtensionType } from '@directus/shared/types';
2
2
  import { Language } from '../../types';
3
- export default function getExtensionDevDeps(type: ExtensionPackageType | ExtensionPackageType[], language?: Language | Language[]): Promise<Record<string, string>>;
3
+ export default function getExtensionDevDeps(type: ExtensionType | ExtensionType[], language?: Language | Language[]): Promise<Record<string, string>>;
4
4
  //# sourceMappingURL=get-extension-dev-deps.d.ts.map
@@ -14,12 +14,12 @@ async function getExtensionDevDeps(type, language = []) {
14
14
  '@directus/extensions-sdk': (0, get_sdk_version_1.default)(),
15
15
  };
16
16
  if (languages.includes('typescript')) {
17
- if (types.some((type) => (0, utils_1.isIn)(type, constants_1.API_OR_HYBRID_EXTENSION_TYPES))) {
17
+ if (types.some((type) => (0, utils_1.isIn)(type, [...constants_1.API_EXTENSION_TYPES, ...constants_1.HYBRID_EXTENSION_TYPES]))) {
18
18
  deps['@types/node'] = `^${await (0, get_package_version_1.default)('@types/node')}`;
19
19
  }
20
20
  deps['typescript'] = `^${await (0, get_package_version_1.default)('typescript')}`;
21
21
  }
22
- if (types.some((type) => (0, utils_1.isIn)(type, constants_1.APP_OR_HYBRID_EXTENSION_TYPES))) {
22
+ if (types.some((type) => (0, utils_1.isIn)(type, [...constants_1.APP_EXTENSION_TYPES, ...constants_1.HYBRID_EXTENSION_TYPES]))) {
23
23
  deps['vue'] = `^${await (0, get_package_version_1.default)('vue')}`;
24
24
  }
25
25
  return deps;
@@ -1,4 +1,3 @@
1
- import { ExtensionOptionsBundleEntry, JsonValue, SplitEntrypoint } from '@directus/shared/types';
1
+ import { JsonValue, SplitEntrypoint } from '@directus/shared/types';
2
2
  export declare function validateSplitEntrypointOption(option: JsonValue | undefined): option is SplitEntrypoint;
3
- export declare function validateBundleEntriesOption(option: JsonValue | undefined): option is ExtensionOptionsBundleEntry[];
4
3
  //# sourceMappingURL=validate-cli-options.d.ts.map
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateBundleEntriesOption = exports.validateSplitEntrypointOption = void 0;
4
- const utils_1 = require("@directus/shared/utils");
3
+ exports.validateSplitEntrypointOption = void 0;
5
4
  function validateNonPrimitive(value) {
6
5
  if (value === undefined ||
7
6
  value === null ||
@@ -22,18 +21,3 @@ function validateSplitEntrypointOption(option) {
22
21
  return true;
23
22
  }
24
23
  exports.validateSplitEntrypointOption = validateSplitEntrypointOption;
25
- function validateBundleEntriesOption(option) {
26
- if (!validateNonPrimitive(option) || !Array.isArray(option)) {
27
- return false;
28
- }
29
- if (!option.every((entry) => {
30
- if (!validateNonPrimitive(entry) || Array.isArray(entry)) {
31
- return false;
32
- }
33
- return (0, utils_1.validateExtensionOptionsBundleEntry)(entry);
34
- })) {
35
- return false;
36
- }
37
- return true;
38
- }
39
- exports.validateBundleEntriesOption = validateBundleEntriesOption;
@@ -0,0 +1,2 @@
1
+ export default function link(extensionsPath: string): Promise<void>;
2
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = __importDefault(require("path"));
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const logger_1 = require("../utils/logger");
9
+ const types_1 = require("@directus/shared/types");
10
+ async function link(extensionsPath) {
11
+ var _a;
12
+ const extensionPath = process.cwd();
13
+ const absoluteExtensionsPath = path_1.default.resolve(extensionsPath);
14
+ if (!fs_extra_1.default.existsSync(absoluteExtensionsPath)) {
15
+ (0, logger_1.log)(`Extensions folder does not exist at ${absoluteExtensionsPath}`, 'error');
16
+ return;
17
+ }
18
+ const packagePath = path_1.default.resolve('package.json');
19
+ if (!(await fs_extra_1.default.pathExists(packagePath))) {
20
+ (0, logger_1.log)(`Current directory is not a valid package.`, 'error');
21
+ return;
22
+ }
23
+ let manifestFile;
24
+ try {
25
+ manifestFile = await fs_extra_1.default.readJSON(packagePath);
26
+ }
27
+ catch (err) {
28
+ (0, logger_1.log)(`Current directory is not a valid Directus extension.`, 'error');
29
+ return;
30
+ }
31
+ const extensionManifest = types_1.ExtensionManifest.parse(manifestFile);
32
+ const extensionName = extensionManifest.name;
33
+ if (!extensionName) {
34
+ (0, logger_1.log)(`Extension name not found in package.json`, 'error');
35
+ return;
36
+ }
37
+ const type = (_a = extensionManifest['directus:extension']) === null || _a === void 0 ? void 0 : _a.type;
38
+ if (!type) {
39
+ (0, logger_1.log)(`Extension type not found in package.json`, 'error');
40
+ return;
41
+ }
42
+ const extensionTarget = path_1.default.join(absoluteExtensionsPath, extensionName);
43
+ try {
44
+ fs_extra_1.default.ensureSymlinkSync(extensionPath, extensionTarget);
45
+ }
46
+ catch (error) {
47
+ (0, logger_1.log)(error.message, 'error');
48
+ (0, logger_1.log)(`Try running this command with administrator privileges`, 'info');
49
+ return;
50
+ }
51
+ (0, logger_1.log)(`Linked ${extensionName} to ${extensionTarget}`);
52
+ return;
53
+ }
54
+ exports.default = link;
@@ -7,6 +7,7 @@ const commander_1 = require("commander");
7
7
  const create_1 = __importDefault(require("./commands/create"));
8
8
  const add_1 = __importDefault(require("./commands/add"));
9
9
  const build_1 = __importDefault(require("./commands/build"));
10
+ const link_1 = __importDefault(require("./commands/link"));
10
11
  const pkg = require('../../../package.json');
11
12
  const program = new commander_1.Command();
12
13
  program.name('directus-extension').usage('[command] [options]');
@@ -30,4 +31,9 @@ program
30
31
  .option('--no-minify', 'disable minification')
31
32
  .option('--sourcemap', 'include source maps in output')
32
33
  .action(build_1.default);
34
+ program
35
+ .command('link')
36
+ .description('Creates a symlink to the extension in the Directus extensions folder')
37
+ .argument('<path>', 'path to the extension folder of directus')
38
+ .action(link_1.default);
33
39
  program.parse(process.argv);
@@ -3,7 +3,8 @@ import chalk from 'chalk';
3
3
  import fse from 'fs-extra';
4
4
  import inquirer from 'inquirer';
5
5
  import { log } from '../utils/logger';
6
- import { isIn, isTypeIn, validateExtensionManifest } from '@directus/shared/utils';
6
+ import { ExtensionManifest, } from '@directus/shared/types';
7
+ import { isIn, isTypeIn } from '@directus/shared/utils';
7
8
  import { pathToRelativeUrl } from '@directus/shared/utils/node';
8
9
  import { EXTENSION_LANGUAGES, EXTENSION_NAME_REGEX, EXTENSION_PKG_KEY, EXTENSION_TYPES, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
9
10
  import { getLanguageFromPath, isLanguage, languageToShort } from '../utils/languages';
@@ -21,18 +22,18 @@ export default async function add() {
21
22
  log(`Current directory is not a valid package.`, 'error');
22
23
  process.exit(1);
23
24
  }
24
- const extensionManifestFile = await fse.readFile(packagePath, 'utf8');
25
- const extensionManifest = JSON.parse(extensionManifestFile);
26
- const indent = detectJsonIndent(extensionManifestFile);
27
- if (!validateExtensionManifest(extensionManifest)) {
25
+ let extensionManifest;
26
+ let indent = null;
27
+ try {
28
+ const extensionManifestFile = await fse.readFile(packagePath, 'utf8');
29
+ extensionManifest = ExtensionManifest.parse(JSON.parse(extensionManifestFile));
30
+ indent = detectJsonIndent(extensionManifestFile);
31
+ }
32
+ catch (e) {
28
33
  log(`Current directory is not a valid Directus extension.`, 'error');
29
34
  process.exit(1);
30
35
  }
31
36
  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
37
  const sourceExists = await fse.pathExists(path.resolve('src'));
37
38
  if (extensionOptions.type === 'bundle') {
38
39
  const { type, name, language, alternativeSource } = await inquirer.prompt([
@@ -1,5 +1,6 @@
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';
1
+ import { API_SHARED_DEPS, APP_EXTENSION_TYPES, APP_SHARED_DEPS, EXTENSION_PKG_KEY, EXTENSION_TYPES, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
2
+ import { ExtensionManifest, ExtensionOptionsBundleEntries, } from '@directus/shared/types';
3
+ import { isIn, isTypeIn } from '@directus/shared/utils';
3
4
  import commonjs from '@rollup/plugin-commonjs';
4
5
  import json from '@rollup/plugin-json';
5
6
  import { nodeResolve } from '@rollup/plugin-node-resolve';
@@ -19,7 +20,7 @@ import { clear, log } from '../utils/logger';
19
20
  import tryParseJson from '../utils/try-parse-json';
20
21
  import generateBundleEntrypoint from './helpers/generate-bundle-entrypoint';
21
22
  import loadConfig from './helpers/load-config';
22
- import { validateBundleEntriesOption, validateSplitEntrypointOption } from './helpers/validate-cli-options';
23
+ import { validateSplitEntrypointOption } from './helpers/validate-cli-options';
23
24
  export default async function build(options) {
24
25
  var _a, _b, _c;
25
26
  const watch = (_a = options.watch) !== null && _a !== void 0 ? _a : false;
@@ -31,16 +32,15 @@ export default async function build(options) {
31
32
  log(`Current directory is not a valid package.`, 'error');
32
33
  process.exit(1);
33
34
  }
34
- const extensionManifest = await fse.readJSON(packagePath);
35
- if (!validateExtensionManifest(extensionManifest)) {
35
+ let extensionManifest;
36
+ try {
37
+ extensionManifest = ExtensionManifest.parse(await fse.readJSON(packagePath));
38
+ }
39
+ catch (err) {
36
40
  log(`Current directory is not a valid Directus extension.`, 'error');
37
41
  process.exit(1);
38
42
  }
39
43
  const extensionOptions = extensionManifest[EXTENSION_PKG_KEY];
40
- if (extensionOptions.type === 'pack') {
41
- log(`Building extension type ${chalk.bold('pack')} is not currently supported.`, 'error');
42
- process.exit(1);
43
- }
44
44
  if (extensionOptions.type === 'bundle') {
45
45
  await buildBundleExtension({
46
46
  entries: extensionOptions.entries,
@@ -81,12 +81,8 @@ export default async function build(options) {
81
81
  log(`Extension type has to be specified using the ${chalk.blue('[-t, --type <type>]')} option.`, 'error');
82
82
  process.exit(1);
83
83
  }
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');
84
+ if (!isIn(type, EXTENSION_TYPES)) {
85
+ log(`Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
90
86
  process.exit(1);
91
87
  }
92
88
  if (!input) {
@@ -98,9 +94,9 @@ export default async function build(options) {
98
94
  process.exit(1);
99
95
  }
100
96
  if (type === 'bundle') {
101
- const entries = tryParseJson(input);
97
+ const entries = ExtensionOptionsBundleEntries.safeParse(tryParseJson(input));
102
98
  const splitOutput = tryParseJson(output);
103
- if (!validateBundleEntriesOption(entries)) {
99
+ if (entries.success === false) {
104
100
  log(`Input option needs to be of the format ${chalk.blue(`[-i '[{"type":"<extension-type>","name":"<extension-name>","source":<entrypoint>}]']`)}.`, 'error');
105
101
  process.exit(1);
106
102
  }
@@ -109,7 +105,7 @@ export default async function build(options) {
109
105
  process.exit(1);
110
106
  }
111
107
  await buildBundleExtension({
112
- entries,
108
+ entries: entries.data,
113
109
  outputApp: splitOutput.app,
114
110
  outputApi: splitOutput.api,
115
111
  watch,
@@ -3,7 +3,7 @@ import chalk from 'chalk';
3
3
  import fse from 'fs-extra';
4
4
  import execa from 'execa';
5
5
  import ora from 'ora';
6
- import { EXTENSION_PKG_KEY, EXTENSION_LANGUAGES, HYBRID_EXTENSION_TYPES, EXTENSION_NAME_REGEX, EXTENSION_PACKAGE_TYPES, PACKAGE_EXTENSION_TYPES, } from '@directus/shared/constants';
6
+ import { EXTENSION_PKG_KEY, EXTENSION_LANGUAGES, HYBRID_EXTENSION_TYPES, EXTENSION_NAME_REGEX, EXTENSION_TYPES, BUNDLE_EXTENSION_TYPES, } from '@directus/shared/constants';
7
7
  import { isIn } from '@directus/shared/utils';
8
8
  import { log } from '../utils/logger';
9
9
  import { isLanguage, languageToShort } from '../utils/languages';
@@ -15,8 +15,8 @@ export default async function create(type, name, options) {
15
15
  var _a;
16
16
  const targetDir = name.substring(name.lastIndexOf('/') + 1);
17
17
  const targetPath = path.resolve(targetDir);
18
- if (!isIn(type, EXTENSION_PACKAGE_TYPES)) {
19
- log(`Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_PACKAGE_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
18
+ if (!isIn(type, EXTENSION_TYPES)) {
19
+ log(`Extension type ${chalk.bold(type)} is not supported. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.`, 'error');
20
20
  process.exit(1);
21
21
  }
22
22
  if (targetDir.length === 0) {
@@ -35,7 +35,7 @@ export default async function create(type, name, options) {
35
35
  process.exit(1);
36
36
  }
37
37
  }
38
- if (isIn(type, PACKAGE_EXTENSION_TYPES)) {
38
+ if (isIn(type, BUNDLE_EXTENSION_TYPES)) {
39
39
  await createPackageExtension({ type, name, targetDir, targetPath });
40
40
  }
41
41
  else {
@@ -48,7 +48,7 @@ async function createPackageExtension({ type, name, targetDir, targetPath, }) {
48
48
  await fse.ensureDir(targetPath);
49
49
  await copyTemplate(type, targetPath);
50
50
  const host = `^${getSdkVersion()}`;
51
- const options = type === 'bundle' ? { type, path: { app: 'dist/app.js', api: 'dist/api.js' }, entries: [], host } : { type, host };
51
+ const options = { type, path: { app: 'dist/app.js', api: 'dist/api.js' }, entries: [], host };
52
52
  const packageManifest = getPackageManifest(name, options, await getExtensionDevDeps(type));
53
53
  await fse.writeJSON(path.join(targetPath, 'package.json'), packageManifest, { spaces: '\t' });
54
54
  const packageManager = getPackageManager();
@@ -86,17 +86,24 @@ async function createLocalExtension({ type, name, targetDir, targetPath, languag
86
86
  log(getDoneMessage(type, targetDir, targetPath, packageManager));
87
87
  }
88
88
  function getPackageManifest(name, options, deps) {
89
- return {
89
+ const packageManifest = {
90
90
  name: EXTENSION_NAME_REGEX.test(name) ? name : `directus-extension-${name}`,
91
+ description: 'Please enter a description for your extension',
92
+ icon: 'extension',
91
93
  version: '1.0.0',
92
94
  keywords: ['directus', 'directus-extension', `directus-custom-${options.type}`],
93
95
  [EXTENSION_PKG_KEY]: options,
94
96
  scripts: {
95
97
  build: 'directus-extension build',
96
98
  dev: 'directus-extension build -w --no-minify',
99
+ link: 'directus-extension link',
97
100
  },
98
101
  devDependencies: deps,
99
102
  };
103
+ if (options.type === 'bundle') {
104
+ packageManifest.scripts['add'] = 'directus-extension add';
105
+ }
106
+ return packageManifest;
100
107
  }
101
108
  function getDoneMessage(type, targetDir, targetPath, packageManager) {
102
109
  return `
@@ -1,4 +1,4 @@
1
- import { ExtensionPackageType } from '@directus/shared/types';
1
+ import { ExtensionType } from '@directus/shared/types';
2
2
  import { Language } from '../../types';
3
- export default function copyTemplate(type: ExtensionPackageType, extensionPath: string, sourcePath?: string, language?: Language): Promise<void>;
3
+ export default function copyTemplate(type: ExtensionType, extensionPath: string, sourcePath?: string, language?: Language): Promise<void>;
4
4
  //# sourceMappingURL=copy-template.d.ts.map
@@ -1,9 +1,9 @@
1
1
  import path from 'path';
2
- import { API_OR_HYBRID_EXTENSION_TYPES, APP_OR_HYBRID_EXTENSION_TYPES, HYBRID_EXTENSION_TYPES, } from '@directus/shared/constants';
2
+ import { API_EXTENSION_TYPES, APP_EXTENSION_TYPES, HYBRID_EXTENSION_TYPES } from '@directus/shared/constants';
3
3
  import { isIn, isTypeIn, pluralize } from '@directus/shared/utils';
4
4
  import { pathToRelativeUrl } from '@directus/shared/utils/node';
5
5
  export default function generateBundleEntrypoint(mode, entries) {
6
- const types = mode === 'app' ? APP_OR_HYBRID_EXTENSION_TYPES : API_OR_HYBRID_EXTENSION_TYPES;
6
+ const types = [...(mode === 'app' ? APP_EXTENSION_TYPES : API_EXTENSION_TYPES), ...HYBRID_EXTENSION_TYPES];
7
7
  const entriesForTypes = entries.filter((entry) => isIn(entry.type, types));
8
8
  const imports = entriesForTypes.map((entry, i) => `import e${i} from './${pathToRelativeUrl(path.resolve(isTypeIn(entry, HYBRID_EXTENSION_TYPES)
9
9
  ? mode === 'app'
@@ -1,4 +1,4 @@
1
- import { ExtensionPackageType } from '@directus/shared/types';
1
+ import { ExtensionType } from '@directus/shared/types';
2
2
  import { Language } from '../../types';
3
- export default function getExtensionDevDeps(type: ExtensionPackageType | ExtensionPackageType[], language?: Language | Language[]): Promise<Record<string, string>>;
3
+ export default function getExtensionDevDeps(type: ExtensionType | ExtensionType[], language?: Language | Language[]): Promise<Record<string, string>>;
4
4
  //# sourceMappingURL=get-extension-dev-deps.d.ts.map
@@ -1,4 +1,4 @@
1
- import { API_OR_HYBRID_EXTENSION_TYPES, APP_OR_HYBRID_EXTENSION_TYPES } from '@directus/shared/constants';
1
+ import { APP_EXTENSION_TYPES, API_EXTENSION_TYPES, HYBRID_EXTENSION_TYPES } from '@directus/shared/constants';
2
2
  import { isIn } from '@directus/shared/utils';
3
3
  import getPackageVersion from '../../utils/get-package-version';
4
4
  import getSdkVersion from '../../utils/get-sdk-version';
@@ -9,12 +9,12 @@ export default async function getExtensionDevDeps(type, language = []) {
9
9
  '@directus/extensions-sdk': getSdkVersion(),
10
10
  };
11
11
  if (languages.includes('typescript')) {
12
- if (types.some((type) => isIn(type, API_OR_HYBRID_EXTENSION_TYPES))) {
12
+ if (types.some((type) => isIn(type, [...API_EXTENSION_TYPES, ...HYBRID_EXTENSION_TYPES]))) {
13
13
  deps['@types/node'] = `^${await getPackageVersion('@types/node')}`;
14
14
  }
15
15
  deps['typescript'] = `^${await getPackageVersion('typescript')}`;
16
16
  }
17
- if (types.some((type) => isIn(type, APP_OR_HYBRID_EXTENSION_TYPES))) {
17
+ if (types.some((type) => isIn(type, [...APP_EXTENSION_TYPES, ...HYBRID_EXTENSION_TYPES]))) {
18
18
  deps['vue'] = `^${await getPackageVersion('vue')}`;
19
19
  }
20
20
  return deps;
@@ -1,4 +1,3 @@
1
- import { ExtensionOptionsBundleEntry, JsonValue, SplitEntrypoint } from '@directus/shared/types';
1
+ import { JsonValue, SplitEntrypoint } from '@directus/shared/types';
2
2
  export declare function validateSplitEntrypointOption(option: JsonValue | undefined): option is SplitEntrypoint;
3
- export declare function validateBundleEntriesOption(option: JsonValue | undefined): option is ExtensionOptionsBundleEntry[];
4
3
  //# sourceMappingURL=validate-cli-options.d.ts.map
@@ -1,4 +1,3 @@
1
- import { validateExtensionOptionsBundleEntry } from '@directus/shared/utils';
2
1
  function validateNonPrimitive(value) {
3
2
  if (value === undefined ||
4
3
  value === null ||
@@ -18,17 +17,3 @@ export function validateSplitEntrypointOption(option) {
18
17
  }
19
18
  return true;
20
19
  }
21
- export function validateBundleEntriesOption(option) {
22
- if (!validateNonPrimitive(option) || !Array.isArray(option)) {
23
- return false;
24
- }
25
- if (!option.every((entry) => {
26
- if (!validateNonPrimitive(entry) || Array.isArray(entry)) {
27
- return false;
28
- }
29
- return validateExtensionOptionsBundleEntry(entry);
30
- })) {
31
- return false;
32
- }
33
- return true;
34
- }
@@ -0,0 +1,2 @@
1
+ export default function link(extensionsPath: string): Promise<void>;
2
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1,48 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { log } from '../utils/logger';
4
+ import { ExtensionManifest } from '@directus/shared/types';
5
+ export default async function link(extensionsPath) {
6
+ var _a;
7
+ const extensionPath = process.cwd();
8
+ const absoluteExtensionsPath = path.resolve(extensionsPath);
9
+ if (!fs.existsSync(absoluteExtensionsPath)) {
10
+ log(`Extensions folder does not exist at ${absoluteExtensionsPath}`, 'error');
11
+ return;
12
+ }
13
+ const packagePath = path.resolve('package.json');
14
+ if (!(await fs.pathExists(packagePath))) {
15
+ log(`Current directory is not a valid package.`, 'error');
16
+ return;
17
+ }
18
+ let manifestFile;
19
+ try {
20
+ manifestFile = await fs.readJSON(packagePath);
21
+ }
22
+ catch (err) {
23
+ log(`Current directory is not a valid Directus extension.`, 'error');
24
+ return;
25
+ }
26
+ const extensionManifest = ExtensionManifest.parse(manifestFile);
27
+ const extensionName = extensionManifest.name;
28
+ if (!extensionName) {
29
+ log(`Extension name not found in package.json`, 'error');
30
+ return;
31
+ }
32
+ const type = (_a = extensionManifest['directus:extension']) === null || _a === void 0 ? void 0 : _a.type;
33
+ if (!type) {
34
+ log(`Extension type not found in package.json`, 'error');
35
+ return;
36
+ }
37
+ const extensionTarget = path.join(absoluteExtensionsPath, extensionName);
38
+ try {
39
+ fs.ensureSymlinkSync(extensionPath, extensionTarget);
40
+ }
41
+ catch (error) {
42
+ log(error.message, 'error');
43
+ log(`Try running this command with administrator privileges`, 'info');
44
+ return;
45
+ }
46
+ log(`Linked ${extensionName} to ${extensionTarget}`);
47
+ return;
48
+ }
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  import create from './commands/create';
3
3
  import add from './commands/add';
4
4
  import build from './commands/build';
5
+ import link from './commands/link';
5
6
  const pkg = require('../../../package.json');
6
7
  const program = new Command();
7
8
  program.name('directus-extension').usage('[command] [options]');
@@ -25,4 +26,9 @@ program
25
26
  .option('--no-minify', 'disable minification')
26
27
  .option('--sourcemap', 'include source maps in output')
27
28
  .action(build);
29
+ program
30
+ .command('link')
31
+ .description('Creates a symlink to the extension in the Directus extensions folder')
32
+ .argument('<path>', 'path to the extension folder of directus')
33
+ .action(link);
28
34
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/extensions-sdk",
3
- "version": "9.22.1",
3
+ "version": "9.22.4",
4
4
  "description": "A toolkit to develop extensions to extend Directus",
5
5
  "homepage": "https://directus.io",
6
6
  "bugs": {
@@ -41,33 +41,33 @@
41
41
  "!**/*.d.ts.map"
42
42
  ],
43
43
  "dependencies": {
44
- "@rollup/plugin-commonjs": "23.0.2",
45
- "@rollup/plugin-json": "5.0.1",
44
+ "@rollup/plugin-commonjs": "24.0.0",
45
+ "@rollup/plugin-json": "6.0.0",
46
46
  "@rollup/plugin-node-resolve": "15.0.1",
47
- "@rollup/plugin-replace": "5.0.1",
48
- "@rollup/plugin-terser": "0.1.0",
47
+ "@rollup/plugin-replace": "5.0.2",
48
+ "@rollup/plugin-terser": "0.2.1",
49
49
  "@rollup/plugin-virtual": "3.0.1",
50
50
  "@vue/compiler-sfc": "3.2.45",
51
51
  "chalk": "4.1.1",
52
52
  "commander": "9.4.1",
53
- "esbuild": "0.15.14",
53
+ "esbuild": "0.16.10",
54
54
  "execa": "5.1.1",
55
- "fs-extra": "10.1.0",
55
+ "fs-extra": "11.1.0",
56
56
  "inquirer": "8.2.4",
57
57
  "ora": "5.4.0",
58
- "rollup": "3.3.0",
58
+ "rollup": "3.7.5",
59
59
  "rollup-plugin-esbuild": "5.0.0",
60
60
  "rollup-plugin-styles": "4.0.0",
61
61
  "rollup-plugin-vue": "6.0.0",
62
- "@directus/shared": "9.22.1"
62
+ "@directus/shared": "9.22.3"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/fs-extra": "9.0.13",
66
66
  "@types/inquirer": "8.2.1",
67
- "@vitest/coverage-c8": "0.25.2",
68
- "concurrently": "7.5.0",
69
- "typescript": "4.9.3",
70
- "vitest": "0.25.2"
67
+ "@vitest/coverage-c8": "0.26.2",
68
+ "concurrently": "7.6.0",
69
+ "typescript": "4.9.4",
70
+ "vitest": "0.26.2"
71
71
  },
72
72
  "engines": {
73
73
  "node": ">=12.20.0"