diff --git a/packages/ngtools/webpack/src/transformers/ast_helpers.ts b/packages/ngtools/webpack/src/transformers/ast_helpers.ts index 319e473cea5d..75dd12d729ac 100644 --- a/packages/ngtools/webpack/src/transformers/ast_helpers.ts +++ b/packages/ngtools/webpack/src/transformers/ast_helpers.ts @@ -5,11 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import { virtualFs } from '@angular-devkit/core'; -import { readFileSync, readdirSync } from 'fs'; -import { dirname, join } from 'path'; import * as ts from 'typescript'; -import { WebpackCompilerHost } from '../compiler_host'; // Find all nodes from the AST in the subtree of node of SyntaxKind kind. @@ -45,129 +41,3 @@ export function getLastNode(sourceFile: ts.SourceFile): ts.Node | null { return null; } - - -// Test transform helpers. -const basePath = '/project/src/'; -const fileName = basePath + 'test-file.ts'; -const typeScriptLibFiles = loadTypeScriptLibFiles(); -const tsLibFiles = loadTsLibFiles(); - -export function createTypescriptContext( - content: string, - additionalFiles?: Record, - useLibs = false, - extraCompilerOptions: ts.CompilerOptions = {}, -) { - // Set compiler options. - const compilerOptions: ts.CompilerOptions = { - noEmitOnError: useLibs, - allowJs: true, - newLine: ts.NewLineKind.LineFeed, - moduleResolution: ts.ModuleResolutionKind.NodeJs, - module: ts.ModuleKind.ESNext, - target: ts.ScriptTarget.ESNext, - skipLibCheck: true, - sourceMap: false, - importHelpers: true, - experimentalDecorators: true, - ...extraCompilerOptions, - }; - - // Create compiler host. - const compilerHost = new WebpackCompilerHost( - compilerOptions, - basePath, - new virtualFs.SimpleMemoryHost(), - false, - ); - - // Add a dummy file to host content. - compilerHost.writeFile(fileName, content, false); - - if (useLibs) { - // Write the default libs. - // These are needed for tests that use import(), because it relies on a Promise being there. - const compilerLibFolder = dirname(compilerHost.getDefaultLibFileName(compilerOptions)); - for (const [k, v] of Object.entries(typeScriptLibFiles)) { - compilerHost.writeFile(join(compilerLibFolder, k), v, false); - } - } - - if (compilerOptions.importHelpers) { - for (const [k, v] of Object.entries(tsLibFiles)) { - compilerHost.writeFile(k, v, false); - } - } - - if (additionalFiles) { - for (const key in additionalFiles) { - compilerHost.writeFile(basePath + key, additionalFiles[key], false); - } - } - - // Create the TypeScript program. - const program = ts.createProgram([fileName], compilerOptions, compilerHost); - - return { compilerHost, program }; -} - -export function transformTypescript( - content: string | undefined, - transformers: ts.TransformerFactory[], - program?: ts.Program, - compilerHost?: WebpackCompilerHost, -): string | undefined { - // Use given context or create a new one. - if (content !== undefined) { - const typescriptContext = createTypescriptContext(content); - if (!program) { - program = typescriptContext.program; - } - if (!compilerHost) { - compilerHost = typescriptContext.compilerHost; - } - } else if (!program || !compilerHost) { - throw new Error('transformTypescript needs either `content` or a `program` and `compilerHost'); - } - - // Emit. - const { emitSkipped, diagnostics } = program.emit( - undefined, undefined, undefined, undefined, { before: transformers }, - ); - - // Throw error with diagnostics if emit wasn't successfull. - if (emitSkipped) { - throw new Error(ts.formatDiagnostics(diagnostics, compilerHost)); - } - - // Return the transpiled js. - return compilerHost.readFile(fileName.replace(/\.tsx?$/, '.js')); -} - -function loadTypeScriptLibFiles(): Record { - const libFolderPath = dirname(require.resolve('typescript/lib/lib.d.ts')); - const libFolderFiles = readdirSync(libFolderPath); - const libFileNames = libFolderFiles.filter(f => f.startsWith('lib.') && f.endsWith('.d.ts')); - - // Return a map of the lib names to their content. - const libs: Record = {}; - for (const f of libFileNames) { - libs[f] = readFileSync(join(libFolderPath, f), 'utf-8'); - } - - return libs; -} - -function loadTsLibFiles(): Record { - const libFolderPath = dirname(require.resolve('tslib/package.json')); - const libFolderFiles = readdirSync(libFolderPath); - - // Return a map of the lib names to their content. - const libs: Record = {}; - for (const f of libFolderFiles) { - libs[join('node_modules/tslib', f)] = readFileSync(join(libFolderPath, f), 'utf-8'); - } - - return libs; -} diff --git a/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts b/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts index 45b88e5aac22..7c868621b27c 100644 --- a/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts +++ b/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { downlevelConstructorParameters } from './ctor-parameters'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; function transform(input: string, additionalFiles?: Record) { const { program, compilerHost } = createTypescriptContext(input, additionalFiles); diff --git a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts index 93da5aec7061..2311cadc7a48 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts @@ -8,9 +8,10 @@ // tslint:disable:no-big-function import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies import * as ts from 'typescript'; -import { createTypescriptContext, getLastNode, transformTypescript } from './ast_helpers'; +import { getLastNode } from './ast_helpers'; import { RemoveNodeOperation } from './interfaces'; import { makeTransform } from './make_transform'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('elide_imports', () => { diff --git a/packages/ngtools/webpack/src/transformers/export_lazy_module_map_spec.ts b/packages/ngtools/webpack/src/transformers/export_lazy_module_map_spec.ts index 33af27da8dfc..f485a0a93e47 100644 --- a/packages/ngtools/webpack/src/transformers/export_lazy_module_map_spec.ts +++ b/packages/ngtools/webpack/src/transformers/export_lazy_module_map_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { transformTypescript } from './ast_helpers'; import { exportLazyModuleMap } from './export_lazy_module_map'; +import { transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('export_lazy_module_map', () => { diff --git a/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts b/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts index 52dc70c098dc..1a92d4851dac 100644 --- a/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts +++ b/packages/ngtools/webpack/src/transformers/export_ngfactory_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { transformTypescript } from './ast_helpers'; import { exportNgFactory } from './export_ngfactory'; +import { transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('export_ngfactory', () => { diff --git a/packages/ngtools/webpack/src/transformers/import_factory_spec.ts b/packages/ngtools/webpack/src/transformers/import_factory_spec.ts index 06745ca9272c..9a21797f0132 100644 --- a/packages/ngtools/webpack/src/transformers/import_factory_spec.ts +++ b/packages/ngtools/webpack/src/transformers/import_factory_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { importFactory } from './import_factory'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('import_factory', () => { diff --git a/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts b/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts index 0726efb20dad..5f9a8a42d156 100644 --- a/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts +++ b/packages/ngtools/webpack/src/transformers/multiple_transformers_spec.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { exportLazyModuleMap } from './export_lazy_module_map'; import { exportNgFactory } from './export_ngfactory'; import { removeDecorators } from './remove_decorators'; import { replaceBootstrap } from './replace_bootstrap'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { diff --git a/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts b/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts index 1310bfce3e77..3765bfcdb599 100644 --- a/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts +++ b/packages/ngtools/webpack/src/transformers/register_locale_data_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { transformTypescript } from './ast_helpers'; import { registerLocaleData } from './register_locale_data'; +import { transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('register_locale_data', () => { diff --git a/packages/ngtools/webpack/src/transformers/remove-ivy-jit-support-calls_spec.ts b/packages/ngtools/webpack/src/transformers/remove-ivy-jit-support-calls_spec.ts index d5d965a0db59..8cb927f933cf 100644 --- a/packages/ngtools/webpack/src/transformers/remove-ivy-jit-support-calls_spec.ts +++ b/packages/ngtools/webpack/src/transformers/remove-ivy-jit-support-calls_spec.ts @@ -7,8 +7,8 @@ */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies import * as ts from 'typescript'; -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { removeIvyJitSupportCalls } from './remove-ivy-jit-support-calls'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; function transform( input: string, diff --git a/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts b/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts index aaa10bb82727..9cd037c23048 100644 --- a/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts +++ b/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts @@ -7,8 +7,8 @@ */ // tslint:disable:no-big-function import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { removeDecorators } from './remove_decorators'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('remove_decorators', () => { diff --git a/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts b/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts index be3ae55831fb..2a9afa19952c 100644 --- a/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_bootstrap_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { replaceBootstrap } from './replace_bootstrap'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('replace_bootstrap', () => { diff --git a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts index bc0da19b8eba..b3a79adacca7 100644 --- a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { replaceResources } from './replace_resources'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; function transform( input: string, diff --git a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts index bc4c589f7ce9..bd7b51fdcc9d 100644 --- a/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_server_bootstrap_spec.ts @@ -7,8 +7,8 @@ */ // tslint:disable:no-big-function import { tags } from '@angular-devkit/core'; // tslint:disable-line:no-implicit-dependencies -import { createTypescriptContext, transformTypescript } from './ast_helpers'; import { replaceServerBootstrap } from './replace_server_bootstrap'; +import { createTypescriptContext, transformTypescript } from './spec_helpers'; describe('@ngtools/webpack transformers', () => { describe('replace_server_bootstrap', () => { diff --git a/packages/ngtools/webpack/src/transformers/spec_helpers.ts b/packages/ngtools/webpack/src/transformers/spec_helpers.ts new file mode 100644 index 000000000000..5a4cafdd0f02 --- /dev/null +++ b/packages/ngtools/webpack/src/transformers/spec_helpers.ts @@ -0,0 +1,138 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { virtualFs } from '@angular-devkit/core'; +import { readFileSync, readdirSync } from 'fs'; +import { dirname, join } from 'path'; +import * as ts from 'typescript'; +import { WebpackCompilerHost } from '../compiler_host'; + + +// Test transform helpers. +const basePath = '/project/src/'; +const fileName = basePath + 'test-file.ts'; +const typeScriptLibFiles = loadTypeScriptLibFiles(); +const tsLibFiles = loadTsLibFiles(); + +export function createTypescriptContext( + content: string, + additionalFiles?: Record, + useLibs = false, + extraCompilerOptions: ts.CompilerOptions = {}, +) { + // Set compiler options. + const compilerOptions: ts.CompilerOptions = { + noEmitOnError: useLibs, + allowJs: true, + newLine: ts.NewLineKind.LineFeed, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.ESNext, + skipLibCheck: true, + sourceMap: false, + importHelpers: true, + experimentalDecorators: true, + ...extraCompilerOptions, + }; + + // Create compiler host. + const compilerHost = new WebpackCompilerHost( + compilerOptions, + basePath, + new virtualFs.SimpleMemoryHost(), + false, + ); + + // Add a dummy file to host content. + compilerHost.writeFile(fileName, content, false); + + if (useLibs) { + // Write the default libs. + // These are needed for tests that use import(), because it relies on a Promise being there. + const compilerLibFolder = dirname(compilerHost.getDefaultLibFileName(compilerOptions)); + for (const [k, v] of Object.entries(typeScriptLibFiles)) { + compilerHost.writeFile(join(compilerLibFolder, k), v, false); + } + } + + if (compilerOptions.importHelpers) { + for (const [k, v] of Object.entries(tsLibFiles)) { + compilerHost.writeFile(k, v, false); + } + } + + if (additionalFiles) { + for (const key in additionalFiles) { + compilerHost.writeFile(basePath + key, additionalFiles[key], false); + } + } + + // Create the TypeScript program. + const program = ts.createProgram([fileName], compilerOptions, compilerHost); + + return { compilerHost, program }; +} + +export function transformTypescript( + content: string | undefined, + transformers: ts.TransformerFactory[], + program?: ts.Program, + compilerHost?: WebpackCompilerHost, +): string | undefined { + // Use given context or create a new one. + if (content !== undefined) { + const typescriptContext = createTypescriptContext(content); + if (!program) { + program = typescriptContext.program; + } + if (!compilerHost) { + compilerHost = typescriptContext.compilerHost; + } + } else if (!program || !compilerHost) { + throw new Error('transformTypescript needs either `content` or a `program` and `compilerHost'); + } + + // Emit. + const { emitSkipped, diagnostics } = program.emit( + undefined, undefined, undefined, undefined, { before: transformers }, + ); + + // Throw error with diagnostics if emit wasn't successfull. + if (emitSkipped) { + throw new Error(ts.formatDiagnostics(diagnostics, compilerHost)); + } + + // Return the transpiled js. + return compilerHost.readFile(fileName.replace(/\.tsx?$/, '.js')); +} + +function loadTypeScriptLibFiles(): Record { + const libFolderPath = dirname(require.resolve('typescript/lib/lib.d.ts')); + const libFolderFiles = readdirSync(libFolderPath); + const libFileNames = libFolderFiles.filter(f => f.startsWith('lib.') && f.endsWith('.d.ts')); + + // Return a map of the lib names to their content. + const libs: Record = {}; + for (const f of libFileNames) { + libs[f] = readFileSync(join(libFolderPath, f), 'utf-8'); + } + + return libs; +} + +function loadTsLibFiles(): Record { + const libFolderPath = dirname(require.resolve('tslib/package.json')); + const libFolderFiles = readdirSync(libFolderPath); + + // Return a map of the lib names to their content. + const libs: Record = {}; + for (const f of libFolderFiles) { + libs[join('node_modules/tslib', f)] = readFileSync(join(libFolderPath, f), 'utf-8'); + } + + return libs; +}