From 765f229357587fb66dfe04d3c48d62c6aae9ce71 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Sun, 4 Nov 2018 10:45:06 +1100 Subject: [PATCH] Improve robustness of lib builder. --- tools/ts_library_builder/ast_util.ts | 73 +++++++++++++++++--- tools/ts_library_builder/build_library.ts | 11 +++ tools/ts_library_builder/test.ts | 15 +++- tools/ts_library_builder/testdata/globals.ts | 4 ++ tools/ts_library_builder/testdata/moduleD.ts | 5 ++ tools/ts_library_builder/testdata/moduleE.ts | 5 ++ tools/ts_library_builder/testdata/moduleF.ts | 1 + 7 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 tools/ts_library_builder/testdata/moduleD.ts create mode 100644 tools/ts_library_builder/testdata/moduleE.ts create mode 100644 tools/ts_library_builder/testdata/moduleF.ts diff --git a/tools/ts_library_builder/ast_util.ts b/tools/ts_library_builder/ast_util.ts index 4590dcea586509..81fc45f0cf3469 100644 --- a/tools/ts_library_builder/ast_util.ts +++ b/tools/ts_library_builder/ast_util.ts @@ -75,14 +75,24 @@ export function checkDiagnostics(project: Project, onlyFor?: string[]) { }) .map(diagnostic => diagnostic.compilerObject); + logDiagnostics(diagnostics); + if (diagnostics.length) { - console.log( - ts.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticHost) - ); process.exit(1); } } +function createDeclarationError( + msg: string, + declaration: ImportDeclaration | ExportDeclaration +): Error { + return new Error( + `${msg}\n` + + ` In: "${declaration.getSourceFile().getFilePath()}"\n` + + ` Text: "${declaration.getText()}"` + ); +} + export interface FlattenNamespaceOptions { customSources?: { [sourceFilePath: string]: string }; debug?: boolean; @@ -151,7 +161,12 @@ export function flattenNamespace({ } sourceFile.getExportDeclarations().forEach(exportDeclaration => { - processSourceFile(exportDeclaration.getModuleSpecifierSourceFileOrThrow()); + const exportedSourceFile = exportDeclaration.getModuleSpecifierSourceFile(); + if (exportedSourceFile) { + processSourceFile(exportedSourceFile); + } else { + throw createDeclarationError("Missing source file.", exportDeclaration); + } exportDeclaration.remove(); }); @@ -254,9 +269,19 @@ export function loadFiles(project: Project, filePaths: string[]) { } } +/** Log diagnostics to the console with colour. */ +export function logDiagnostics(diagnostics: ts.Diagnostic[]): void { + if (diagnostics.length) { + console.log( + ts.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticHost) + ); + } +} + export interface NamespaceSourceFileOptions { debug?: boolean; namespace?: string; + namespaces: Set; rootPath: string; sourceFileMap: Map; } @@ -267,7 +292,13 @@ export interface NamespaceSourceFileOptions { */ export function namespaceSourceFile( sourceFile: SourceFile, - { debug, namespace, rootPath, sourceFileMap }: NamespaceSourceFileOptions + { + debug, + namespace, + namespaces, + rootPath, + sourceFileMap + }: NamespaceSourceFileOptions ): string { if (sourceFileMap.has(sourceFile)) { return ""; @@ -300,22 +331,42 @@ export function namespaceSourceFile( const output = sourceFile .getImportDeclarations() + .filter(declaration => { + const dsf = declaration.getModuleSpecifierSourceFile(); + if (dsf == null) { + try { + const namespaceName = declaration + .getNamespaceImportOrThrow() + .getText(); + if (!namespaces.has(namespaceName)) { + throw createDeclarationError( + "Already defined source file under different namespace.", + declaration + ); + } + } catch (e) { + throw createDeclarationError( + "Unsupported import clause.", + declaration + ); + } + declaration.remove(); + } + return dsf; + }) .map(declaration => { if ( declaration.getNamedImports().length || !declaration.getNamespaceImport() ) { - throw new Error( - "Unsupported import clause.\n" + - ` In: "${declaration.getSourceFile().getFilePath()}"\n` + - ` Text: "${declaration.getText()}"` - ); + throw createDeclarationError("Unsupported import clause.", declaration); } const text = namespaceSourceFile( declaration.getModuleSpecifierSourceFileOrThrow(), { debug, namespace: declaration.getNamespaceImportOrThrow().getText(), + namespaces, rootPath, sourceFileMap } @@ -328,6 +379,8 @@ export function namespaceSourceFile( .getExportDeclarations() .forEach(declaration => declaration.remove()); + namespaces.add(namespace); + return `${output} ${globalNamespaceText || ""} diff --git a/tools/ts_library_builder/build_library.ts b/tools/ts_library_builder/build_library.ts index e4111fe2135e4c..05cd4013baf356 100644 --- a/tools/ts_library_builder/build_library.ts +++ b/tools/ts_library_builder/build_library.ts @@ -18,6 +18,7 @@ import { getSourceComment, loadDtsFiles, loadFiles, + logDiagnostics, namespaceSourceFile, normalizeSlashes } from "./ast_util"; @@ -222,6 +223,7 @@ export function mergeGlobal({ // declaration source file into a namespace that exists within the merged // namespace const importDeclarations = sourceFile.getImportDeclarations(); + const namespaces = new Set(); for (const declaration of importDeclarations) { const declarationSourceFile = declaration.getModuleSpecifierSourceFile(); if ( @@ -241,6 +243,7 @@ export function mergeGlobal({ namespaceSourceFile(dtsSourceFile, { debug, namespace: declaration.getNamespaceImportOrThrow().getText(), + namespaces, rootPath: basePath, sourceFileMap }) @@ -308,6 +311,14 @@ export function main({ // emit the project, which will be only the declaration files const inputEmitResult = inputProject.emitToMemory(); + const inputDiagnostics = inputEmitResult + .getDiagnostics() + .map(d => d.compilerObject); + logDiagnostics(inputDiagnostics); + if (inputDiagnostics.length) { + process.exit(1); + } + // the declaration project will be the target for the emitted files from // the input project, these will be used to transfer information over to // the final library file diff --git a/tools/ts_library_builder/test.ts b/tools/ts_library_builder/test.ts index 70b6145ebd643d..3a18fe29c2f988 100644 --- a/tools/ts_library_builder/test.ts +++ b/tools/ts_library_builder/test.ts @@ -128,7 +128,10 @@ test(function buildLibraryMerge() { }); assert(targetSourceFile.getNamespace("moduleC") != null); - assertEqual(targetSourceFile.getNamespaces().length, 1); + assert(targetSourceFile.getNamespace("moduleD") != null); + assert(targetSourceFile.getNamespace("moduleE") != null); + assert(targetSourceFile.getNamespace("moduleF") != null); + assertEqual(targetSourceFile.getNamespaces().length, 4); assert(targetSourceFile.getInterface("FooBar") != null); assertEqual(targetSourceFile.getInterfaces().length, 1); const variableDeclarations = targetSourceFile.getVariableDeclarations(); @@ -138,7 +141,15 @@ test(function buildLibraryMerge() { variableDeclarations[2].getType().getText(), `typeof moduleC.qat` ); - assertEqual(variableDeclarations.length, 3); + assertEqual( + variableDeclarations[3].getType().getText(), + `typeof moduleE.process` + ); + assertEqual( + variableDeclarations[4].getType().getText(), + `typeof moduleD.reprocess` + ); + assertEqual(variableDeclarations.length, 5); }); // TODO author unit tests for `ast_util.ts` diff --git a/tools/ts_library_builder/testdata/globals.ts b/tools/ts_library_builder/testdata/globals.ts index 41a86bdf8896c0..e80862025a2bb6 100644 --- a/tools/ts_library_builder/testdata/globals.ts +++ b/tools/ts_library_builder/testdata/globals.ts @@ -1,6 +1,10 @@ import * as moduleC from "./moduleC"; +import * as moduleD from "./moduleD"; +import * as moduleE from "./moduleE"; // tslint:disable-next-line:no-any const foobarbaz: any = {}; foobarbaz.bar = new moduleC.Bar(); foobarbaz.qat = moduleC.qat; +foobarbaz.process = moduleE.process; +foobarbaz.reprocess = moduleD.reprocess; diff --git a/tools/ts_library_builder/testdata/moduleD.ts b/tools/ts_library_builder/testdata/moduleD.ts new file mode 100644 index 00000000000000..8752699d102de8 --- /dev/null +++ b/tools/ts_library_builder/testdata/moduleD.ts @@ -0,0 +1,5 @@ +import * as moduleF from "./moduleF"; + +export function reprocess(value: typeof moduleF.key) { + console.log(value); +} diff --git a/tools/ts_library_builder/testdata/moduleE.ts b/tools/ts_library_builder/testdata/moduleE.ts new file mode 100644 index 00000000000000..361a9ad0f4f8f2 --- /dev/null +++ b/tools/ts_library_builder/testdata/moduleE.ts @@ -0,0 +1,5 @@ +import * as moduleF from "./moduleF"; + +export function process(value: typeof moduleF.key) { + console.log(value); +} diff --git a/tools/ts_library_builder/testdata/moduleF.ts b/tools/ts_library_builder/testdata/moduleF.ts new file mode 100644 index 00000000000000..b2f8883ad089ee --- /dev/null +++ b/tools/ts_library_builder/testdata/moduleF.ts @@ -0,0 +1 @@ +export const key = "value";