diff --git a/tools/gulp/gulpfile.ts b/tools/gulp/gulpfile.ts index 82687c0af014..f1ddbd2d5ffe 100644 --- a/tools/gulp/gulpfile.ts +++ b/tools/gulp/gulpfile.ts @@ -1,4 +1,4 @@ -import {createPackageBuildTasks} from './util/package-tasks'; +import {createPackageBuildTasks} from './packaging/build-tasks-gulp'; /** Create gulp tasks to build the different packages in the project. */ createPackageBuildTasks('cdk'); diff --git a/tools/gulp/packaging/build-bundles.ts b/tools/gulp/packaging/build-bundles.ts new file mode 100644 index 000000000000..015afaa0b282 --- /dev/null +++ b/tools/gulp/packaging/build-bundles.ts @@ -0,0 +1,56 @@ +import {join} from 'path'; +import {ScriptTarget, ModuleKind} from 'typescript'; +import {DIST_BUNDLES} from '../constants'; +import {uglifyJsFile} from './minify-sources'; +import {createRollupBundle} from './rollup-helpers'; +import {remapSourcemap} from './sourcemap-remap'; +import {transpileFile} from './typescript-transpile'; + +// There are no type definitions available for these imports. +const uglify = require('uglify-js'); +const sorcery = require('sorcery'); + +/** Builds the bundles for the specified package. */ +export async function buildPackageBundles(entryFile: string, packageName: string) { + const moduleName = `ng.${packageName}`; + + // List of paths to the package bundles. + const fesm2015File = join(DIST_BUNDLES, `${packageName}.js`); + const fesm2014File = join(DIST_BUNDLES, `${packageName}.es5.js`); + const umdFile = join(DIST_BUNDLES, `${packageName}.umd.js`); + const umdMinFile = join(DIST_BUNDLES, `${packageName}.umd.min.js`); + + // Build FESM-2015 bundle file. + await createRollupBundle({ + moduleName: moduleName, + entry: entryFile, + dest: fesm2015File, + format: 'es', + }); + + await remapSourcemap(fesm2015File); + + // Downlevel FESM-2015 file to ES5. + transpileFile(fesm2015File, fesm2014File, { + target: ScriptTarget.ES5, + module: ModuleKind.ES2015, + allowJs: true + }); + + await remapSourcemap(fesm2014File); + + // Create UMD bundle of FESM-2014 output. + await createRollupBundle({ + moduleName: moduleName, + entry: fesm2014File, + dest: umdFile, + format: 'umd' + }); + + await remapSourcemap(umdFile); + + // Create a minified UMD bundle using UglifyJS + uglifyJsFile(umdFile, umdMinFile); + + await remapSourcemap(umdMinFile); +} diff --git a/tools/gulp/packaging/build-release.ts b/tools/gulp/packaging/build-release.ts new file mode 100644 index 000000000000..4c06362f4533 --- /dev/null +++ b/tools/gulp/packaging/build-release.ts @@ -0,0 +1,34 @@ +import {join} from 'path'; +import {DIST_BUNDLES, DIST_ROOT, SOURCE_ROOT, PROJECT_ROOT} from '../constants'; +import {copyFiles} from '../util/copy-files'; +import {addPureAnnotationsToFile} from './pure-annotations'; +import {updatePackageVersion} from './package-versions'; +import {inlinePackageMetadataFiles} from './metadata-inlining'; +import {createTypingsReexportFile} from './typings-reexport'; +import {createMetadataReexportFile} from './metadata-reexport'; + +/** + * Copies different output files into a folder structure that follows the `angular/angular` + * release folder structure. The output will also contain a README and the according package.json + * file. Additionally the package will be Closure Compiler and AOT compatible. + */ +export function composeRelease(packageName: string) { + // To avoid refactoring of the project the package material will map to the source path `lib/`. + const sourcePath = join(SOURCE_ROOT, packageName === 'material' ? 'lib' : packageName); + const packagePath = join(DIST_ROOT, 'packages', packageName); + const releasePath = join(DIST_ROOT, 'releases', packageName); + + inlinePackageMetadataFiles(packagePath); + + copyFiles(packagePath, '**/*.+(d.ts|metadata.json)', join(releasePath, 'typings')); + copyFiles(DIST_BUNDLES, `${packageName}.umd?(.min).js?(.map)`, join(releasePath, 'bundles')); + copyFiles(DIST_BUNDLES, `${packageName}?(.es5).js?(.map)`, join(releasePath, '@angular')); + copyFiles(PROJECT_ROOT, 'LICENSE', releasePath); + copyFiles(SOURCE_ROOT, 'README.md', releasePath); + copyFiles(sourcePath, 'package.json', releasePath); + + updatePackageVersion(releasePath); + createTypingsReexportFile(releasePath, packageName); + createMetadataReexportFile(releasePath, packageName); + addPureAnnotationsToFile(join(releasePath, '@angular', `${packageName}.es5.js`)); +} diff --git a/tools/gulp/util/package-tasks.ts b/tools/gulp/packaging/build-tasks-gulp.ts similarity index 96% rename from tools/gulp/util/package-tasks.ts rename to tools/gulp/packaging/build-tasks-gulp.ts index 4c351d0c9132..7f97977735a0 100644 --- a/tools/gulp/util/package-tasks.ts +++ b/tools/gulp/packaging/build-tasks-gulp.ts @@ -2,8 +2,9 @@ import {task, watch} from 'gulp'; import {join} from 'path'; import {main as tsc} from '@angular/tsc-wrapped'; import {SOURCE_ROOT, DIST_ROOT} from '../constants'; -import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from './task_helpers'; -import {buildPackageBundles, composeRelease} from './package-build'; +import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from '../util/task_helpers'; +import {composeRelease} from './build-release'; +import {buildPackageBundles} from './build-bundles'; // There are no type definitions available for these imports. const inlineResources = require('../../../scripts/release/inline-resources'); diff --git a/tools/gulp/util/inline-resources.ts b/tools/gulp/packaging/metadata-inlining.ts similarity index 52% rename from tools/gulp/util/inline-resources.ts rename to tools/gulp/packaging/metadata-inlining.ts index eccb1205e92f..258d9e958c9d 100644 --- a/tools/gulp/util/inline-resources.ts +++ b/tools/gulp/packaging/metadata-inlining.ts @@ -1,5 +1,7 @@ -import {readFileSync} from 'fs'; +import {readFileSync, writeFileSync} from 'fs'; import {basename} from 'path'; +import {sync as glob} from 'glob'; +import {join} from 'path'; /** * Recurse through a parsed metadata.json file and inline all html and css. @@ -32,3 +34,24 @@ export function inlineMetadataResources(metadata: any, componentResources: Map fullFilePath. This is needed because the templateUrl and + // styleUrls for each component use just the filename because, in the source, the component + // and the resources live in the same directory. + const componentResources = new Map(); + + glob(join(packagePath, '**/*.+(html|css)')).forEach(resourcePath => { + componentResources.set(basename(resourcePath), resourcePath); + }); + + // Find all metadata files. For each one, parse the JSON content, inline the resources, and + // reserialize and rewrite back to the original location. + glob(join(packagePath, '**/*.metadata.json')).forEach(path => { + const metadata = JSON.parse(readFileSync(path, 'utf-8')); + inlineMetadataResources(metadata, componentResources); + writeFileSync(path , JSON.stringify(metadata), 'utf-8'); + }); +} diff --git a/tools/gulp/packaging/metadata-reexport.ts b/tools/gulp/packaging/metadata-reexport.ts new file mode 100644 index 000000000000..37735f8bc1fe --- /dev/null +++ b/tools/gulp/packaging/metadata-reexport.ts @@ -0,0 +1,9 @@ +import {writeFileSync} from 'fs'; +import {join} from 'path'; + +/** Creates a metadata file that re-exports the metadata bundle inside of the typings. */ +export function createMetadataReexportFile(packageDir: string, packageName: string) { + const metadataReExport = + `{"__symbolic":"module","version":3,"metadata":{},"exports":[{"from":"./typings/index"}]}`; + writeFileSync(join(packageDir, `${packageName}.metadata.json`), metadataReExport, 'utf-8'); +} diff --git a/tools/gulp/packaging/minify-sources.ts b/tools/gulp/packaging/minify-sources.ts new file mode 100644 index 000000000000..382a955aa9cc --- /dev/null +++ b/tools/gulp/packaging/minify-sources.ts @@ -0,0 +1,16 @@ +import {writeFileSync} from 'fs'; + +// There are no type definitions available for these imports. +const uglify = require('uglify-js'); + +/** Minifies a JavaScript file by using UglifyJS2. Also writes sourcemaps to the output. */ +export function uglifyJsFile(inputPath: string, outputPath: string) { + const sourcemapOut = `${outputPath}.map`; + const result = uglify.minify(inputPath, { + preserveComments: 'license', + outSourceMap: sourcemapOut + }); + + writeFileSync(outputPath, result.code); + writeFileSync(sourcemapOut, result.map); +} diff --git a/tools/gulp/packaging/package-versions.ts b/tools/gulp/packaging/package-versions.ts new file mode 100644 index 000000000000..7eebc0da3936 --- /dev/null +++ b/tools/gulp/packaging/package-versions.ts @@ -0,0 +1,14 @@ +import {writeFileSync} from 'fs'; +import {join} from 'path'; +import {MATERIAL_VERSION} from '../constants'; + +/** Updates the `package.json` file of the specified package. Replaces the version placeholder. */ +export function updatePackageVersion(packageDir: string) { + const packagePath = join(packageDir, 'package.json'); + const packageConfig = require(packagePath); + + // Replace the `0.0.0-PLACEHOLDER` version name with the version of the root package.json file. + packageConfig.version = packageConfig.version.replace('0.0.0-PLACEHOLDER', MATERIAL_VERSION); + + writeFileSync(packagePath, JSON.stringify(packageConfig, null, 2)); +} diff --git a/tools/gulp/packaging/pure-annotations.ts b/tools/gulp/packaging/pure-annotations.ts new file mode 100644 index 000000000000..615b0e357653 --- /dev/null +++ b/tools/gulp/packaging/pure-annotations.ts @@ -0,0 +1,33 @@ +import {readFileSync, writeFileSync} from 'fs'; + +/** Regex that matches downleveled class IIFE expressions. Used to add the pure annotations. */ +const classIfeeRegex = + new RegExp('^(var (\\S+) = )(\\(function \\(\\) \\{[\\n\\r]*(?: (?:\\/\\*\\*| \\*|\\*\\/|' + + '\\/\\/)[^\\n\\r]*[\\n\\r]*)* function \\2\\([^\\)]*\\) \\{[\\n\\r]*)', 'mg'); + +/** Regex that matches downleveled class IIFE expressions with _extends statements */ +const classExtendsIfeeRegex = + /^(var (\S+) = )(\(function \(_super\) \{[\n\r]* __extends\(\2, _super\);[\n\r]*)/gm; + +/** + * Adds `@__PURE__` annotation comments to IIFEs containing ES5-downleveled classes generated by + * TypeScript so that Uglify can tree-shake classes that are not referenced. + * + * @param fileContent The content of the file for which `@__PURE__` will be added. + * @returns The content of the file with `@__PURE__` annotations added. + */ +export function addPureAnnotations(fileContent: string) { + return fileContent + // Prefix downleveled classes w/ the @__PURE__ annotation. + .replace(classIfeeRegex, '$1/*@__PURE__*/$3') + // Prefix downleveled classes that extend another class w/ the @__PURE__ annotation + .replace(classExtendsIfeeRegex, '$1/*@__PURE__*/$3'); +} + +/** Adds Uglify "@__PURE__" decorations to the specified file. */ +export function addPureAnnotationsToFile(inputFile: string) { + const originalContent = readFileSync(inputFile, 'utf-8'); + const annotatedContent = addPureAnnotations(originalContent); + + writeFileSync(inputFile, annotatedContent, 'utf-8'); +} diff --git a/tools/gulp/util/rollup-helper.ts b/tools/gulp/packaging/rollup-helpers.ts similarity index 95% rename from tools/gulp/util/rollup-helper.ts rename to tools/gulp/packaging/rollup-helpers.ts index e1b7c158dc65..ab1c10b8e94e 100644 --- a/tools/gulp/util/rollup-helper.ts +++ b/tools/gulp/packaging/rollup-helpers.ts @@ -48,15 +48,15 @@ export type BundleConfig = { moduleName: string; }; -/** Creates a rollup bundles of the Material components.*/ +/** Creates a rollup bundle of a specified JavaScript file.*/ export function createRollupBundle(config: BundleConfig): Promise { - let bundleOptions = { + const bundleOptions = { context: 'this', external: Object.keys(ROLLUP_GLOBALS), entry: config.entry }; - let writeOptions = { + const writeOptions = { // Keep the moduleId empty because we don't want to force developers to a specific moduleId. moduleId: '', moduleName: config.moduleName || 'ng.material', diff --git a/tools/gulp/packaging/sourcemap-remap.ts b/tools/gulp/packaging/sourcemap-remap.ts new file mode 100644 index 000000000000..4d5b9fdcb3af --- /dev/null +++ b/tools/gulp/packaging/sourcemap-remap.ts @@ -0,0 +1,13 @@ +// There are no type definitions available for these imports. +const sorcery = require('sorcery'); + +/** + * Finds the original sourcemap of the file and maps it to the current file. + * This is useful when multiple transformation happen (e.g TSC -> Rollup -> Uglify) + **/ +export async function remapSourcemap(sourceFile: string) { + // Once sorcery loaded the chain of sourcemaps, the new sourcemap will be written asynchronously. + return (await sorcery.load(sourceFile)).write(); +} + + diff --git a/tools/gulp/packaging/typescript-transpile.ts b/tools/gulp/packaging/typescript-transpile.ts new file mode 100644 index 000000000000..6c140a331a0d --- /dev/null +++ b/tools/gulp/packaging/typescript-transpile.ts @@ -0,0 +1,43 @@ +import * as ts from 'typescript'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as chalk from 'chalk'; + +/** Reads a input file and transpiles it into a new file. */ +export function transpileFile(inputPath: string, outputPath: string, options: ts.CompilerOptions) { + const inputFile = fs.readFileSync(inputPath, 'utf-8'); + const transpiled = ts.transpileModule(inputFile, { compilerOptions: options }); + + reportDiagnostics(transpiled.diagnostics); + + fs.writeFileSync(outputPath, transpiled.outputText); + + if (transpiled.sourceMapText) { + fs.writeFileSync(`${outputPath}.map`, transpiled.sourceMapText); + } +} + +/** Formats the TypeScript diagnostics into a error string. */ +function formatDiagnostics(diagnostics: ts.Diagnostic[], baseDir: string): string { + return diagnostics.map(diagnostic => { + let res = `• ${chalk.red(`TS${diagnostic.code}`)} - `; + + if (diagnostic.file) { + const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + const filePath = path.relative(baseDir, diagnostic.file.fileName); + + res += `${filePath}(${line + 1},${character + 1}): `; + } + res += `${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`; + + return res; + }).join('\n'); +} + +/** Checks and reports diagnostics if present. */ +function reportDiagnostics(diagnostics: ts.Diagnostic[], baseDir?: string) { + if (diagnostics && diagnostics.length && diagnostics[0]) { + console.error(formatDiagnostics(diagnostics, baseDir)); + throw new Error('TypeScript compilation failed.'); + } +} diff --git a/tools/gulp/packaging/typings-reexport.ts b/tools/gulp/packaging/typings-reexport.ts new file mode 100644 index 000000000000..3fba3b8347e6 --- /dev/null +++ b/tools/gulp/packaging/typings-reexport.ts @@ -0,0 +1,10 @@ +import {writeFileSync} from 'fs'; +import {LICENSE_BANNER} from '../constants'; +import {join} from 'path'; + +/** Create a typing file that links to the bundled definitions of NGC. */ +export function createTypingsReexportFile(outputDir: string, entryName: string) { + writeFileSync(join(outputDir, `${entryName}.d.ts`), + LICENSE_BANNER + '\nexport * from "./typings/index";' + ); +} diff --git a/tools/gulp/tasks/material-release.ts b/tools/gulp/tasks/material-release.ts index 37fcdda63cc8..7f05d7a02896 100644 --- a/tools/gulp/tasks/material-release.ts +++ b/tools/gulp/tasks/material-release.ts @@ -3,8 +3,8 @@ import {join} from 'path'; import {writeFileSync} from 'fs'; import {Bundler} from 'scss-bundle'; import {sequenceTask} from '../util/task_helpers'; -import {composeRelease} from '../util/package-build'; import {COMPONENTS_DIR, DIST_MATERIAL, DIST_RELEASES} from '../constants'; +import {composeRelease} from '../packaging/build-release'; // There are no type definitions available for these imports. const gulpRename = require('gulp-rename'); diff --git a/tools/gulp/util/annotate-pure.ts b/tools/gulp/util/annotate-pure.ts deleted file mode 100644 index e0b0a7fdbc5b..000000000000 --- a/tools/gulp/util/annotate-pure.ts +++ /dev/null @@ -1,22 +0,0 @@ - -const iifeExpression = - new RegExp('^(var (\\S+) = )(\\(function \\(\\) \\{\\n(?: ' + - '(?:\\/\\*\\*| \\*|\\*\\/|\\/\\/)[^\\n]*\\n)* function \\2\\([^\\)]*\\) \\{\\n)', 'mg'); - -/** - * Adds `@__PURE__` annotation comments to IIFEs containing ES5-downleveled classes generated by - * TypeScript so that Uglify can tree-shake classes that are not referenced. - * - * @param fileContent The content of the file for which `@__PURE__` will be added. - * @returns The content of the file with `@__PURE__` annotations added. - */ -export function addPureAnnotations(fileContent: string) { - return fileContent - // Prefix downleveled classes w/ the @__PURE__ annotation. - .replace(iifeExpression, '$1/*@__PURE__*/$3') - // Prefix downleveled classes that extend another class w/ the @__PURE__ annotation - .replace( - /^(var (\S+) = )(\(function \(_super\) \{\n __extends\(\2, _super\);\n)/mg, - '$1/*@__PURE__*/$3' - ); -} diff --git a/tools/gulp/util/package-build.ts b/tools/gulp/util/package-build.ts deleted file mode 100644 index c60652ed4f0a..000000000000 --- a/tools/gulp/util/package-build.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {join, basename} from 'path'; -import {createRollupBundle} from './rollup-helper'; -import {inlineMetadataResources} from './inline-resources'; -import {transpileFile} from './ts-compiler'; -import {ScriptTarget, ModuleKind} from 'typescript'; -import {sync as glob} from 'glob'; -import {writeFileSync, readFileSync} from 'fs-extra'; -import { - DIST_BUNDLES, DIST_ROOT, SOURCE_ROOT, PROJECT_ROOT, LICENSE_BANNER, MATERIAL_VERSION -} from '../constants'; -import {addPureAnnotations} from './annotate-pure'; -import {copyFiles} from './copy-files'; - -// There are no type definitions available for these imports. -const uglify = require('uglify-js'); -const sorcery = require('sorcery'); - -/** - * Copies different output files into a folder structure that follows the `angular/angular` - * release folder structure. The output will also contain a README and the according package.json - * file. Additionally the package will be Closure Compiler and AOT compatible. - */ -export function composeRelease(packageName: string) { - // To avoid refactoring of the project the package material will map to the source path `lib/`. - let sourcePath = join(SOURCE_ROOT, packageName === 'material' ? 'lib' : packageName); - let packagePath = join(DIST_ROOT, 'packages', packageName); - let releasePath = join(DIST_ROOT, 'releases', packageName); - - inlinePackageMetadataFiles(packagePath); - - copyFiles(packagePath, '**/*.+(d.ts|metadata.json)', join(releasePath, 'typings')); - copyFiles(DIST_BUNDLES, `${packageName}.umd?(.min).js?(.map)`, join(releasePath, 'bundles')); - copyFiles(DIST_BUNDLES, `${packageName}?(.es5).js?(.map)`, join(releasePath, '@angular')); - copyFiles(PROJECT_ROOT, 'LICENSE', releasePath); - copyFiles(SOURCE_ROOT, 'README.md', releasePath); - copyFiles(sourcePath, 'package.json', releasePath); - - updatePackageVersion(releasePath); - createTypingFile(releasePath, packageName); - createMetadataFile(releasePath, packageName); - addPureAnnotationCommentsToEs5Bundle(releasePath, packageName); -} - -/** Builds the bundles for the specified package. */ -export async function buildPackageBundles(entryFile: string, packageName: string) { - let moduleName = `ng.${packageName}`; - - // List of paths to the package bundles. - let fesm2015File = join(DIST_BUNDLES, `${packageName}.js`); - let fesm2014File = join(DIST_BUNDLES, `${packageName}.es5.js`); - let umdFile = join(DIST_BUNDLES, `${packageName}.umd.js`); - let umdMinFile = join(DIST_BUNDLES, `${packageName}.umd.min.js`); - - // Build FESM-2015 bundle file. - await createRollupBundle({ - moduleName: moduleName, - entry: entryFile, - dest: fesm2015File, - format: 'es', - }); - - await remapSourcemap(fesm2015File); - - // Downlevel FESM-2015 file to ES5. - transpileFile(fesm2015File, fesm2014File, { - target: ScriptTarget.ES5, - module: ModuleKind.ES2015, - allowJs: true - }); - - await remapSourcemap(fesm2014File); - - // Create UMD bundle of FESM-2014 output. - await createRollupBundle({ - moduleName: moduleName, - entry: fesm2014File, - dest: umdFile, - format: 'umd' - }); - - await remapSourcemap(umdFile); - - uglifyFile(umdFile, umdMinFile); - - await remapSourcemap(umdMinFile); -} - -/** - * Finds the original sourcemap of the file and maps it to the current file. - * This is useful when multiple transformation happen (e.g TSC -> Rollup -> Uglify) - **/ -async function remapSourcemap(sourceFile: string) { - // Once sorcery loaded the chain of sourcemaps, the new sourcemap will be written asynchronously. - return (await sorcery.load(sourceFile)).write(); -} - -/** Minifies a JavaScript file using UglifyJS2. Also writes sourcemaps to the output. */ -function uglifyFile(inputPath: string, outputPath: string) { - let sourcemapOut = `${outputPath}.map`; - let result = uglify.minify(inputPath, { - preserveComments: 'license', - outSourceMap: sourcemapOut - }); - - writeFileSync(outputPath, result.code); - writeFileSync(sourcemapOut, result.map); -} - -/** Updates the `package.json` file of the specified package. Replaces the version placeholder. */ -function updatePackageVersion(packageDir: string) { - let packagePath = join(packageDir, 'package.json'); - let packageConfig = require(packagePath); - - // Replace the `0.0.0-PLACEHOLDER` version name with the version of the root package.json file. - packageConfig.version = packageConfig.version.replace('0.0.0-PLACEHOLDER', MATERIAL_VERSION); - - writeFileSync(packagePath, JSON.stringify(packageConfig, null, 2)); -} - -/** Create a typing file that links to the bundled definitions of NGC. */ -function createTypingFile(outputDir: string, entryName: string) { - writeFileSync(join(outputDir, `${entryName}.d.ts`), - LICENSE_BANNER + '\nexport * from "./typings/index";' - ); -} - -/** Creates a metadata file that re-exports the metadata bundle inside of the typings. */ -function createMetadataFile(packageDir: string, packageName: string) { - const metadataReExport = - `{"__symbolic":"module","version":3,"metadata":{},"exports":[{"from":"./typings/index"}]}`; - writeFileSync(join(packageDir, `${packageName}.metadata.json`), metadataReExport, 'utf-8'); -} - -/** Inlines HTML and CSS resources into `metadata.json` files. */ -function inlinePackageMetadataFiles(packagePath: string) { - // Create a map of fileName -> fullFilePath. This is needed because the templateUrl and - // styleUrls for each component use just the filename because, in the source, the component - // and the resources live in the same directory. - const componentResources = new Map(); - - glob(join(packagePath, '**/*.+(html|css)')).forEach(resourcePath => { - componentResources.set(basename(resourcePath), resourcePath); - }); - - // Find all metadata files. For each one, parse the JSON content, inline the resources, and - // reserialize and rewrite back to the original location. - glob(join(packagePath, '**/*.metadata.json')).forEach(path => { - let metadata = JSON.parse(readFileSync(path, 'utf-8')); - inlineMetadataResources(metadata, componentResources); - writeFileSync(path , JSON.stringify(metadata), 'utf-8'); - }); -} - -/** Adds Uglify "@__PURE__" decorations to the generated ES5 bundle. */ -function addPureAnnotationCommentsToEs5Bundle(outputDir: string, entryName: string) { - const es5BundlePath = join(outputDir, '@angular', `${entryName}.es5.js`); - const originalContent = readFileSync(es5BundlePath, 'utf-8'); - const annotatedContent = addPureAnnotations(originalContent); - - writeFileSync(es5BundlePath, annotatedContent, 'utf-8'); -} diff --git a/tools/gulp/util/ts-compiler.ts b/tools/gulp/util/ts-compiler.ts deleted file mode 100644 index 44f5f4e1d8d9..000000000000 --- a/tools/gulp/util/ts-compiler.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as ts from 'typescript'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as chalk from 'chalk'; - -/** Compiles a TypeScript project with possible extra options. */ -export function compileProject(project: string, options: ts.CompilerOptions = {}) { - let parsed = parseProjectConfig(project, options); - let program = ts.createProgram(parsed.fileNames, parsed.options); - let baseDir = program.getCurrentDirectory(); - - // Report any invalid TypeScript options for the project. - reportDiagnostics(program.getOptionsDiagnostics(), baseDir); - - let emitResult = program.emit(); - - reportDiagnostics(emitResult.diagnostics, baseDir); -} - -/** Reads a input file and transpiles it into a new file. */ -export function transpileFile(inputPath: string, outputPath: string, options: ts.CompilerOptions) { - let inputFile = fs.readFileSync(inputPath, 'utf-8'); - let transpiled = ts.transpileModule(inputFile, { compilerOptions: options }); - - reportDiagnostics(transpiled.diagnostics); - - fs.writeFileSync(outputPath, transpiled.outputText); - - if (transpiled.sourceMapText) { - fs.writeFileSync(`${outputPath}.map`, transpiled.sourceMapText); - } -} - -/** Parses a TypeScript project configuration. */ -function parseProjectConfig(project: string, options: ts.CompilerOptions) { - let config = ts.readConfigFile(project, ts.sys.readFile).config; - let basePath = path.dirname(project); - - let host = { - useCaseSensitiveFileNames: true, - fileExists: ts.sys.fileExists, - readDirectory: ts.sys.readDirectory, - readFile: ts.sys.readFile - }; - - return ts.parseJsonConfigFileContent(config, host, basePath, options); -} - -/** Formats the TypeScript diagnostics into a error string. */ -export function formatDiagnostics(diagnostics: ts.Diagnostic[], baseDir: string): string { - return diagnostics.map(diagnostic => { - let res = `• ${chalk.red(`TS${diagnostic.code}`)} - `; - - if (diagnostic.file) { - let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); - let filePath = path.relative(baseDir, diagnostic.file.fileName); - - res += `${filePath}(${line + 1},${character + 1}): `; - } - res += `${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`; - - return res; - }).join('\n'); -} - -/** Checks and reports diagnostics if present. */ -export function reportDiagnostics(diagnostics: ts.Diagnostic[], baseDir?: string) { - if (diagnostics && diagnostics.length && diagnostics[0]) { - console.error(formatDiagnostics(diagnostics, baseDir)); - throw new Error('TypeScript compilation failed.'); - } -}