From f64ad3335c76a715747f0cf645f55a6c874dfd94 Mon Sep 17 00:00:00 2001 From: Nicola Krumschmidt Date: Wed, 7 Jul 2021 21:26:50 +0200 Subject: [PATCH] Configure build command based on extension manifest (#6706) * Add simple logger to extension-sdk CLI * Configure build command based on extension manifest The configuration can be overwritten by command line options. Adds a "force" option to ignore if there is no package.json in the cwd. --- .../extension-sdk/src/cli/commands/build.ts | 61 ++++++++++++------- .../extension-sdk/src/cli/commands/create.ts | 22 +++---- packages/extension-sdk/src/cli/index.ts | 6 +- .../extension-sdk/src/cli/utils/logger.ts | 15 +++++ .../cli/utils/validate-extension-package.ts | 13 ++++ 5 files changed, 81 insertions(+), 36 deletions(-) create mode 100644 packages/extension-sdk/src/cli/utils/logger.ts create mode 100644 packages/extension-sdk/src/cli/utils/validate-extension-package.ts diff --git a/packages/extension-sdk/src/cli/commands/build.ts b/packages/extension-sdk/src/cli/commands/build.ts index 9d9e694e14..b41023a051 100644 --- a/packages/extension-sdk/src/cli/commands/build.ts +++ b/packages/extension-sdk/src/cli/commands/build.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ - import path from 'path'; import chalk from 'chalk'; import fse from 'fs-extra'; @@ -11,37 +9,56 @@ import { terser } from 'rollup-plugin-terser'; import styles from 'rollup-plugin-styles'; import vue from 'rollup-plugin-vue'; import { APP_EXTENSION_TYPES, EXTENSION_PKG_KEY, SHARED_DEPS } from '@directus/shared/constants'; +import { AppExtensionType } from '@directus/shared/types'; +import log from '../utils/logger'; +import validateExtensionPackage from '../utils/validate-extension-package'; -export default async function build(options: { input: string; output: string }): Promise { +type BuildOptions = { type: AppExtensionType; input: string; output: string; force: boolean }; + +export default async function build(options: BuildOptions): Promise { const packagePath = path.resolve('package.json'); + let packageManifest: Record = {}; if (!(await fse.pathExists(packagePath))) { - console.log(`${chalk.bold.red('[Error]')} Current directory is not a package.`); + log(`Current directory is not a package.`, !options.force ? 'error' : 'warn'); + if (!options.force) process.exit(1); + } else { + packageManifest = await fse.readJSON(packagePath); + + if (!packageManifest[EXTENSION_PKG_KEY] || !validateExtensionPackage(packageManifest[EXTENSION_PKG_KEY])) { + log(`Current directory is not a Directus extension.`, !options.force ? 'error' : 'warn'); + if (!options.force) process.exit(1); + } + } + + const type = options.type || packageManifest[EXTENSION_PKG_KEY]?.type; + const input = options.input || packageManifest[EXTENSION_PKG_KEY]?.source; + const output = options.output || packageManifest[EXTENSION_PKG_KEY]?.path; + + if (!APP_EXTENSION_TYPES.includes(type)) { + log( + `Extension type ${chalk.bold(type)} is not supported. Available extension types: ${APP_EXTENSION_TYPES.map((t) => + chalk.bold.magenta(t) + ).join(', ')}.`, + !options.force ? 'error' : 'warn' + ); + if (!options.force) process.exit(1); + } + + if (!(await fse.pathExists(input)) || !(await fse.stat(input)).isFile()) { + log(`Entrypoint ${chalk.bold(input)} does not exist.`, 'error'); process.exit(1); } - const packageManifest = await fse.readJSON(packagePath); - - if (!packageManifest[EXTENSION_PKG_KEY] || !packageManifest[EXTENSION_PKG_KEY].type) { - console.log(`${chalk.bold.yellow('[Warn]')} Current directory is not a Directus extension.`); - } else { - const type = packageManifest[EXTENSION_PKG_KEY].type; - - if (!APP_EXTENSION_TYPES.includes(type)) { - console.log( - `${chalk.bold.yellow('[Warn]')} Extension type ${chalk.bold( - type - )} is not supported. Available extension types: ${APP_EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join( - ', ' - )}.` - ); - } + if (!output) { + log(`Output file must be a valid path.`, 'error'); + process.exit(1); } const spinner = ora('Building Directus extension...').start(); const bundle = await rollup({ - input: options.input, + input, external: SHARED_DEPS, plugins: [ vue({ preprocessStyles: true }), @@ -54,7 +71,7 @@ export default async function build(options: { input: string; output: string }): await bundle.write({ format: 'es', - file: options.output, + file: output, }); await bundle.close(); diff --git a/packages/extension-sdk/src/cli/commands/create.ts b/packages/extension-sdk/src/cli/commands/create.ts index 18c41966f4..ca63cdff48 100644 --- a/packages/extension-sdk/src/cli/commands/create.ts +++ b/packages/extension-sdk/src/cli/commands/create.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ - import path from 'path'; import chalk from 'chalk'; import fse from 'fs-extra'; @@ -7,6 +5,7 @@ import execa from 'execa'; import ora from 'ora'; import { EXTENSION_TYPES, EXTENSION_PKG_KEY } from '@directus/shared/constants'; import { ExtensionType } from '@directus/shared/types'; +import log from '../utils/logger'; const pkg = require('../../../../package.json'); @@ -16,10 +15,11 @@ export default async function create(type: ExtensionType, name: string): Promise const targetPath = path.resolve(name); if (!EXTENSION_TYPES.includes(type)) { - console.log( - `${chalk.bold.red('[Error]')} Extension type ${chalk.bold( - type - )} does not exist. Available extension types: ${EXTENSION_TYPES.map((t) => chalk.bold.magenta(t)).join(', ')}.` + log( + `Extension type ${chalk.bold(type)} does not exist. Available extension types: ${EXTENSION_TYPES.map((t) => + chalk.bold.magenta(t) + ).join(', ')}.`, + 'error' ); process.exit(1); } @@ -28,16 +28,14 @@ export default async function create(type: ExtensionType, name: string): Promise const info = await fse.stat(targetPath); if (!info.isDirectory()) { - console.log( - `${chalk.bold.red('[Error]')} Destination ${chalk.bold(name)} already exists and is not a directory.` - ); + log(`Destination ${chalk.bold(name)} already exists and is not a directory.`, 'error'); process.exit(1); } const files = await fse.readdir(targetPath); if (files.length > 0) { - console.log(`${chalk.bold.red('[Error]')} Destination ${chalk.bold(name)} already exists and is not empty.`); + log(`Destination ${chalk.bold(name)} already exists and is not empty.`, 'error'); process.exit(1); } } @@ -54,7 +52,7 @@ export default async function create(type: ExtensionType, name: string): Promise version: '1.0.0', keywords: ['directus', 'directus-extension', `directus-custom-${type}`], [EXTENSION_PKG_KEY]: { - type: type, + type, path: 'dist/index.js', source: 'src/index.js', host: `^${pkg.version}`, @@ -74,7 +72,7 @@ export default async function create(type: ExtensionType, name: string): Promise spinner.succeed('Done'); - console.log(` + log(` Your ${type} extension has been created at ${chalk.green(targetPath)} Build your extension by running: diff --git a/packages/extension-sdk/src/cli/index.ts b/packages/extension-sdk/src/cli/index.ts index 9b59558382..a49e2993af 100644 --- a/packages/extension-sdk/src/cli/index.ts +++ b/packages/extension-sdk/src/cli/index.ts @@ -14,8 +14,10 @@ program.command('create').arguments(' ').description('Scaffold a new program .command('build') .description('Bundle a Directus extension to a single entrypoint') - .option('-i, --input ', 'change the default entrypoint', 'src/index.js') - .option('-o, --output ', 'change the default output file', 'dist/index.js') + .option('-t, --type ', 'overwrite the extension type read from package manifest') + .option('-i, --input ', 'overwrite the entrypoint read from package manifest') + .option('-o, --output ', 'overwrite the output file read from package manifest') + .option('-f, --force', 'ignore the package manifest') .action(build); program.parse(process.argv); diff --git a/packages/extension-sdk/src/cli/utils/logger.ts b/packages/extension-sdk/src/cli/utils/logger.ts new file mode 100644 index 0000000000..7379b56da9 --- /dev/null +++ b/packages/extension-sdk/src/cli/utils/logger.ts @@ -0,0 +1,15 @@ +/* eslint-disable no-console */ + +import chalk from 'chalk'; + +export default function log(message: string, type?: 'info' | 'warn' | 'error'): void { + if (type === 'info') { + console.log(`${chalk.bold.gray('[Info]')} ${message}`); + } else if (type === 'warn') { + console.warn(`${chalk.bold.yellow('[Warn]')} ${message}`); + } else if (type === 'error') { + console.error(`${chalk.bold.red('[Error]')} ${message}`); + } else { + console.log(message); + } +} diff --git a/packages/extension-sdk/src/cli/utils/validate-extension-package.ts b/packages/extension-sdk/src/cli/utils/validate-extension-package.ts new file mode 100644 index 0000000000..90de2b2d0b --- /dev/null +++ b/packages/extension-sdk/src/cli/utils/validate-extension-package.ts @@ -0,0 +1,13 @@ +export default function validateExtensionPackage(extension: Record): boolean { + if ( + extension.type === undefined || + extension.path === undefined || + extension.source === undefined || + extension.host === undefined || + extension.hidden === undefined + ) { + return false; + } + + return true; +}