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.
This commit is contained in:
Nicola Krumschmidt
2021-07-07 21:26:50 +02:00
committed by GitHub
parent ff393eab3b
commit f64ad3335c
5 changed files with 81 additions and 36 deletions

View File

@@ -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<void> {
type BuildOptions = { type: AppExtensionType; input: string; output: string; force: boolean };
export default async function build(options: BuildOptions): Promise<void> {
const packagePath = path.resolve('package.json');
let packageManifest: Record<string, any> = {};
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();

View File

@@ -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:

View File

@@ -14,8 +14,10 @@ program.command('create').arguments('<type> <name>').description('Scaffold a new
program
.command('build')
.description('Bundle a Directus extension to a single entrypoint')
.option('-i, --input <file>', 'change the default entrypoint', 'src/index.js')
.option('-o, --output <file>', 'change the default output file', 'dist/index.js')
.option('-t, --type <type>', 'overwrite the extension type read from package manifest')
.option('-i, --input <file>', 'overwrite the entrypoint read from package manifest')
.option('-o, --output <file>', 'overwrite the output file read from package manifest')
.option('-f, --force', 'ignore the package manifest')
.action(build);
program.parse(process.argv);

View File

@@ -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);
}
}

View File

@@ -0,0 +1,13 @@
export default function validateExtensionPackage(extension: Record<string, any>): boolean {
if (
extension.type === undefined ||
extension.path === undefined ||
extension.source === undefined ||
extension.host === undefined ||
extension.hidden === undefined
) {
return false;
}
return true;
}