[ts/build_ts_refs] add support for --clean flag (#91060)

Co-authored-by: spalger <spalger@users.noreply.github.com>
This commit is contained in:
Spencer 2021-02-10 21:38:06 -08:00 committed by GitHub
parent d9abaa180b
commit c0a974aa0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 166 additions and 51 deletions

View file

@ -7,4 +7,4 @@
*/
require('../src/setup_node_env');
require('../src/dev/typescript/build_refs').runBuildRefs();
require('../src/dev/typescript').runBuildRefsCli();

View file

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import execa from 'execa';
import { run, ToolingLog } from '@kbn/dev-utils';
export async function buildAllRefs(log: ToolingLog) {
await buildRefs(log, 'tsconfig.refs.json');
}
async function buildRefs(log: ToolingLog, projectPath: string) {
try {
log.debug(`Building TypeScript projects refs for ${projectPath}...`);
await execa(require.resolve('typescript/bin/tsc'), ['-b', projectPath, '--pretty']);
} catch (e) {
log.error(e);
process.exit(1);
}
}
export async function runBuildRefs() {
run(
async ({ log }) => {
await buildAllRefs(log);
},
{
description: 'Build TypeScript projects',
}
);
}

View file

@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import Path from 'path';
import execa from 'execa';
import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils';
export const REF_CONFIG_PATHS = [Path.resolve(REPO_ROOT, 'tsconfig.refs.json')];
export async function buildAllTsRefs(log: ToolingLog) {
for (const path of REF_CONFIG_PATHS) {
const relative = Path.relative(REPO_ROOT, path);
log.debug(`Building TypeScript projects refs for ${relative}...`);
await execa(require.resolve('typescript/bin/tsc'), ['-b', relative, '--pretty'], {
cwd: REPO_ROOT,
});
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { run } from '@kbn/dev-utils';
import del from 'del';
import { buildAllTsRefs, REF_CONFIG_PATHS } from './build_ts_refs';
import { getOutputsDeep } from './ts_configfile';
import { concurrentMap } from './concurrent_map';
export async function runBuildRefsCli() {
run(
async ({ log, flags }) => {
if (flags.clean) {
const outDirs = getOutputsDeep(REF_CONFIG_PATHS);
log.info('deleting', outDirs.length, 'ts output directories');
await concurrentMap(100, outDirs, (outDir) => del(outDir));
}
await buildAllTsRefs(log);
},
{
description: 'Build TypeScript projects',
flags: {
boolean: ['clean'],
},
log: {
defaultLevel: 'debug',
},
}
);
}

View file

@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as Rx from 'rxjs';
import { mergeMap, toArray, map } from 'rxjs/operators';
import { lastValueFrom } from '@kbn/std';
export async function concurrentMap<T, T2>(
concurrency: number,
arr: T[],
fn: (item: T, i: number) => Promise<T2>
): Promise<T2[]> {
return await lastValueFrom(
Rx.from(arr).pipe(
// execute items in parallel based on concurrency
mergeMap(async (item, index) => ({ index, result: await fn(item, index) }), concurrency),
// collect the results into an array
toArray(),
// sort items back into order and return array of just results
map((list) => list.sort((a, b) => a.index - b.index).map(({ result }) => result))
)
);
}

View file

@ -11,3 +11,4 @@ export { filterProjectsByFlag } from './projects';
export { getTsProjectForAbsolutePath } from './get_ts_project_for_absolute_path';
export { execInProjects } from './exec_in_projects';
export { runTypeCheckCli } from './run_type_check_cli';
export * from './build_ts_refs_cli';

View file

@ -6,14 +6,13 @@
* Side Public License, v 1.
*/
import { readFileSync } from 'fs';
import { basename, dirname, relative, resolve } from 'path';
import { IMinimatch, Minimatch } from 'minimatch';
import { parseConfigFileTextToJson } from 'typescript';
import { REPO_ROOT } from '@kbn/utils';
import { parseTsConfig } from './ts_configfile';
function makeMatchers(directory: string, patterns: string[]) {
return patterns.map(
(pattern) =>
@ -23,16 +22,6 @@ function makeMatchers(directory: string, patterns: string[]) {
);
}
function parseTsConfig(path: string) {
const { error, config } = parseConfigFileTextToJson(path, readFileSync(path, 'utf8'));
if (error) {
throw error;
}
return config;
}
function testMatchers(matchers: IMinimatch[], path: string) {
return matchers.some((matcher) => matcher.match(path));
}

View file

@ -13,7 +13,7 @@ import getopts from 'getopts';
import { execInProjects } from './exec_in_projects';
import { filterProjectsByFlag } from './projects';
import { buildAllRefs } from './build_refs';
import { buildAllTsRefs } from './build_ts_refs';
export async function runTypeCheckCli() {
const extraFlags: string[] = [];
@ -69,7 +69,7 @@ export async function runTypeCheckCli() {
process.exit();
}
await buildAllRefs(log);
await buildAllTsRefs(log);
const tscArgs = [
// composite project cannot be used with --noEmit

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import Fs from 'fs';
import Path from 'path';
import { parseConfigFileTextToJson } from 'typescript';
// yes, this is just `any`, but I'm hoping that TypeScript will give us some help here eventually
type TsConfigFile = ReturnType<typeof parseConfigFileTextToJson>['config'];
export function parseTsConfig(tsConfigPath: string): TsConfigFile {
const { error, config } = parseConfigFileTextToJson(
tsConfigPath,
Fs.readFileSync(tsConfigPath, 'utf8')
);
if (error) {
throw error;
}
return config;
}
export function getOutputsDeep(tsConfigPaths: string[]) {
const tsConfigs = new Map<string, TsConfigFile>();
const read = (path: string) => {
const cached = tsConfigs.get(path);
if (cached) {
return cached;
}
const config = parseTsConfig(path);
tsConfigs.set(path, config);
return config;
};
const outputDirs: string[] = [];
const seen = new Set<TsConfigFile>();
const traverse = (path: string) => {
const config = read(path);
if (seen.has(config)) {
return;
}
seen.add(config);
const dirname = Path.dirname(path);
const relativeOutDir: string | undefined = config.compilerOptions?.outDir;
if (relativeOutDir) {
outputDirs.push(Path.resolve(dirname, relativeOutDir));
}
const refs: undefined | Array<{ path: string }> = config.references;
for (const ref of refs ?? []) {
traverse(Path.resolve(dirname, ref.path));
}
};
for (const path of tsConfigPaths) {
traverse(path);
}
return outputDirs;
}