202 lines
5.3 KiB
TypeScript

import { Architect } from '@angular-devkit/architect';
import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
import {
json,
logging,
schema,
terminal,
workspaces,
} from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { getLogger } from '../shared/logger';
import {
coerceTypes,
convertAliases,
convertToCamelCase,
handleErrors,
Options,
Schema,
} from '../shared/params';
import { commandName, printHelp } from '../shared/print-help';
import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace';
// @ts-ignore
import minimist = require('minimist');
export interface RunOptions {
project: string;
target: string;
configuration: string;
help: boolean;
runOptions: { [k: string]: any };
}
function throwInvalidInvocation() {
throw new Error(
`Specify the project name and the target (e.g., ${commandName} run proj:build)`
);
}
function parseRunOpts(
args: string[],
defaultProjectName: string | null,
logger: logging.Logger
): RunOptions {
const runOptions = convertToCamelCase(
minimist(args, {
boolean: ['help', 'prod'],
string: ['configuration', 'project'],
}) as any
);
const help = runOptions.help;
if (!runOptions._ || !runOptions._[0]) {
throwInvalidInvocation();
}
let [project, target, configuration] = runOptions._[0].split(':');
if (!project && defaultProjectName) {
logger.debug(
`No project name specified. Using default project : ${terminal.bold(
defaultProjectName
)}`
);
project = defaultProjectName;
}
if (runOptions.configuration) {
configuration = runOptions.configuration;
}
if (runOptions.prod) {
configuration = 'production';
}
if (runOptions.project) {
project = runOptions.project;
}
if (!project || !target) {
throwInvalidInvocation();
}
const res = { project, target, configuration, help, runOptions };
delete runOptions['help'];
delete runOptions['_'];
delete runOptions['configuration'];
delete runOptions['prod'];
delete runOptions['project'];
return res;
}
function printRunHelp(
opts: RunOptions,
schema: Schema,
logger: logging.Logger
) {
printHelp(
`${commandName} run ${opts.project}:${opts.target}`,
schema,
logger
);
}
export function validateTargetAndConfiguration(
workspace: WorkspaceDefinition,
opts: RunOptions
) {
const architect = workspace.projects.get(opts.project);
if (!architect) {
throw new Error(`Could not find project "${opts.project}"`);
}
const targets = architect.targets;
const availableTargets = [...targets.keys()];
const target = targets.get(opts.target);
if (!target) {
throw new Error(
`Could not find target "${opts.target}" in the ${
opts.project
} project. Valid targets are: ${terminal.bold(
availableTargets.join(', ')
)}`
);
}
// Not all targets have configurations
// and an undefined configuration is valid
if (opts.configuration) {
if (target.configurations) {
const configuration = target.configurations[opts.configuration];
if (!configuration) {
throw new Error(
`Could not find configuration "${opts.configuration}" in ${
opts.project
}:${opts.target}. Valid configurations are: ${Object.keys(
target.configurations
).join(', ')}`
);
}
} else {
throw new Error(
`No configurations are defined for ${opts.project}:${opts.target}, so "${opts.configuration}" is invalid.`
);
}
}
}
export async function run(root: string, args: string[], isVerbose: boolean) {
const logger = getLogger(isVerbose);
return handleErrors(logger, isVerbose, async () => {
const fsHost = new NodeJsSyncHost();
const { workspace } = await workspaces.readWorkspace(
'workspace.json',
workspaces.createWorkspaceHost(fsHost)
);
const opts = parseRunOpts(
args,
workspace.extensions['defaultProject'] as string,
logger
);
validateTargetAndConfiguration(workspace, opts);
const registry = new json.schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
const architectHost = new WorkspaceNodeModulesArchitectHost(
workspace,
root
);
const architect = new Architect(architectHost, registry);
const builderConf = await architectHost.getBuilderNameForTarget({
project: opts.project,
target: opts.target,
});
const builderDesc = await architectHost.resolveBuilder(builderConf);
const flattenedSchema = await registry
.flatten(builderDesc.optionSchema! as json.JsonObject)
.toPromise();
if (opts.help) {
printRunHelp(opts, flattenedSchema as any, logger);
return 0;
}
const runOptions = normalizeOptions(
opts.runOptions,
flattenedSchema as any
);
const run = await architect.scheduleTarget(
{
project: opts.project,
target: opts.target,
configuration: opts.configuration,
},
runOptions,
{ logger }
);
const result = await run.output.toPromise();
await run.stop();
return result.success ? 0 : 1;
});
}
function normalizeOptions(opts: Options, schema: Schema): Options {
return convertAliases(coerceTypes(opts, schema), schema, false);
}