mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
build: move packaging in separate folder (#4719)
* build: move packaging in separate folder Moves all related packaging code into a `packaging` folder inside of gulp. At some point we can move the folder completely to `tools/` and can share it with other repositories like `flex-layout`. * Split up build utils
This commit is contained in:
committed by
tinayuangao
parent
8f2a11e260
commit
3569805a4a
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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`));
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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<s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Inlines HTML and CSS resources into `metadata.json` files. */
|
||||
export 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<string, string>();
|
||||
|
||||
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');
|
||||
});
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
@@ -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<any> {
|
||||
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',
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
@@ -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";'
|
||||
);
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
}
|
||||
@@ -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<string, string>();
|
||||
|
||||
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');
|
||||
}
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user