diff --git a/build-config.js b/build-config.js index 76687cb25..a7f0d2fd3 100644 --- a/build-config.js +++ b/build-config.js @@ -25,7 +25,7 @@ const buildLicense = `/** */`; /** The namespace for the packages. Instead of @angular, we use @nguniversal */ -const namespace = '@nguniversal'; +const namespace = 'nguniversal'; /** The library entrypoints that are built under the namespace */ const libNames = [ @@ -40,7 +40,7 @@ module.exports = { projectVersion: buildVersion, angularVersion: angularVersion, projectDir: __dirname, - packagesDir: join(__dirname, 'src'), + packagesDir: join(__dirname, 'modules'), outputDir: join(__dirname, 'dist'), licenseBanner: buildLicense, namespace, diff --git a/build-test.ts b/build-test.ts index ccfc63672..34c320a3b 100644 --- a/build-test.ts +++ b/build-test.ts @@ -1,13 +1,19 @@ -import {buildConfig, buildLib} from './tools/package-tools'; +import {buildConfig, BuildPackage, packages} from './tools/package-tools'; declare var require: any; const rimraf = require('rimraf'); +const buildPkg = async (pkg: BuildPackage) => { + pkg.dependencies.forEach(buildPkg); + await pkg.compileTests(); +}; + + rimraf(buildConfig.outputDir, async () => { - for (const lib of buildConfig.libNames) { - const exitCode = await buildLib(lib, true); - console.log(exitCode === 0 ? `Build succeeded for ${lib}` : `Build failed for ${lib}`); - console.log(); + for (const pkg of packages) { + console.log(`Building package to test: ${pkg.name}`); + await buildPkg(pkg); + console.log(`Finished building: ${pkg.name}`); } }); diff --git a/build.ts b/build.ts index 58c1b5731..1ef19a945 100644 --- a/build.ts +++ b/build.ts @@ -1,13 +1,21 @@ -import {buildConfig, buildLib} from './tools/package-tools'; +import {buildConfig, BuildPackage, composeRelease, packages} from './tools/package-tools'; declare var require: any; const rimraf = require('rimraf'); +const buildPkg = async (pkg: BuildPackage) => { + pkg.dependencies.forEach(buildPkg); + await pkg.compile(); + await pkg.createBundles(); +}; + + rimraf(buildConfig.outputDir, async () => { - for (const lib of buildConfig.libNames) { - const exitCode = await buildLib(lib); - console.log(exitCode === 0 ? `Build succeeded for ${lib}` : `Build failed for ${lib}`); - console.log(); + for (const pkg of packages) { + console.log(`Building package: ${pkg.name}`); + await buildPkg(pkg); + composeRelease(pkg); + console.log(`Finished building: ${pkg.name}`); } }); diff --git a/modules/aspnetcore-engine/package.json b/modules/aspnetcore-engine/package.json index ff9173798..785a9c8e8 100644 --- a/modules/aspnetcore-engine/package.json +++ b/modules/aspnetcore-engine/package.json @@ -1,6 +1,6 @@ { "name": "@nguniversal/aspnetcore-engine", - "version": "5.0.0-beta.6", + "version": "0.0.0-PLACEHOLDER", "description": "ASP.NET Core Engine for running Server Angular Apps", "main": "./bundles/aspnetcore-engine.umd.js", "module": "./esm5/aspnetcore-engine.es5.js", @@ -21,8 +21,8 @@ "universal" ], "peerDependencies": { - "@angular/common": "^5.0.0", - "@angular/core": "^5.0.0" + "@angular/common": "0.0.0-NG", + "@angular/core": "0.0.0-NG" }, "repository": { "type": "git", diff --git a/modules/aspnetcore-engine/src/main.ts b/modules/aspnetcore-engine/src/main.ts index bf5a7ca44..05a63e712 100644 --- a/modules/aspnetcore-engine/src/main.ts +++ b/modules/aspnetcore-engine/src/main.ts @@ -5,12 +5,12 @@ * 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 { Type, NgModuleFactory, CompilerFactory, Compiler } from '@angular/core'; +import {Type, NgModuleFactory, CompilerFactory, Compiler, StaticProvider} from '@angular/core'; import { platformDynamicServer } from '@angular/platform-server'; import { DOCUMENT } from '@angular/common'; import { ResourceLoader } from '@angular/compiler'; -import { REQUEST, ORIGIN_URL } from '../tokens'; +import { REQUEST, ORIGIN_URL } from '@nguniversal/aspnetcore-engine/tokens'; import { FileLoader } from './file-loader'; import { IEngineOptions } from './interfaces/engine-options'; import { IEngineRenderResult } from './interfaces/engine-render-result'; @@ -121,16 +121,8 @@ export function ngAspnetCoreEngine(options: IEngineOptions): Promise { @@ -167,6 +159,23 @@ export function ngAspnetCoreEngine(options: IEngineOptions): Promise, NgModuleFactory<{}>>(); function getFactory( diff --git a/modules/aspnetcore-engine/tokens/index.ts b/modules/aspnetcore-engine/tokens/index.ts new file mode 100644 index 000000000..ca845d822 --- /dev/null +++ b/modules/aspnetcore-engine/tokens/index.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './public-api'; diff --git a/modules/aspnetcore-engine/tokens/public-api.ts b/modules/aspnetcore-engine/tokens/public-api.ts new file mode 100644 index 000000000..cbafb81ee --- /dev/null +++ b/modules/aspnetcore-engine/tokens/public-api.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './tokens'; diff --git a/modules/aspnetcore-engine/tokens.ts b/modules/aspnetcore-engine/tokens/tokens.ts similarity index 100% rename from modules/aspnetcore-engine/tokens.ts rename to modules/aspnetcore-engine/tokens/tokens.ts diff --git a/modules/aspnetcore-engine/tokens/tsconfig.lib.json b/modules/aspnetcore-engine/tokens/tsconfig.lib.json new file mode 100644 index 000000000..de4b8fafd --- /dev/null +++ b/modules/aspnetcore-engine/tokens/tsconfig.lib.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.lib", + "files": [ + "public-api.ts" + ], + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 + "flatModuleOutFile": "index.js", + "flatModuleId": "@nguniversal/aspnetcore-engine/tokens", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true + } +} diff --git a/modules/aspnetcore-engine/tsconfig.fortokens.json b/modules/aspnetcore-engine/tsconfig.fortokens.json deleted file mode 100644 index f64a63ad3..000000000 --- a/modules/aspnetcore-engine/tsconfig.fortokens.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "files": [ - "./index.ts", - "./tokens.ts" - ] -} \ No newline at end of file diff --git a/modules/aspnetcore-engine/tsconfig.lib.json b/modules/aspnetcore-engine/tsconfig.lib.json index 7250800c0..1866564c4 100644 --- a/modules/aspnetcore-engine/tsconfig.lib.json +++ b/modules/aspnetcore-engine/tsconfig.lib.json @@ -21,16 +21,20 @@ "lib": ["es2015", "dom"], "skipLibCheck": true, "types": ["node"], - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@nguniversal/aspnetcore-engine/*": ["../../dist/packages/aspnetcore-engine/*"] + } }, "files": [ - "./public-api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "skipTemplateCodegen": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "flatModuleOutFile": "index.js", - "flatModuleId": "@nguniversal/aspnetcore-engine" + "flatModuleId": "@nguniversal/aspnetcore-engine", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true } } \ No newline at end of file diff --git a/modules/aspnetcore-engine/tsconfig.spec.json b/modules/aspnetcore-engine/tsconfig.spec.json index 7d2ff1ccb..a8c87d4c0 100644 --- a/modules/aspnetcore-engine/tsconfig.spec.json +++ b/modules/aspnetcore-engine/tsconfig.spec.json @@ -10,7 +10,7 @@ "types": ["jasmine", "node"] }, "angularCompilerOptions": { - "strictMetadataEmit": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "skipTemplateCodegen": true, "emitDecoratorMetadata": true, "fullTemplateTypeCheck": true diff --git a/modules/common/package.json b/modules/common/package.json index 048f5d029..5efda8060 100644 --- a/modules/common/package.json +++ b/modules/common/package.json @@ -1,6 +1,6 @@ { "name": "@nguniversal/common", - "version": "5.0.0-beta.6", + "version": "0.0.0-PLACEHOLDER", "description": "Angular Universal common utilities", "main": "./bundles/common.umd.js", "module": "./esm5/common.es5.js", @@ -12,8 +12,8 @@ "universal" ], "peerDependencies": { - "@angular/common": "^5.0.0", - "@angular/core": "^5.0.0" + "@angular/common": "0.0.0-NG", + "@angular/core": "0.0.0-NG" }, "repository": { "type": "git", diff --git a/modules/common/tsconfig.lib.json b/modules/common/tsconfig.lib.json index 0b4352276..f40857c62 100644 --- a/modules/common/tsconfig.lib.json +++ b/modules/common/tsconfig.lib.json @@ -21,16 +21,20 @@ "lib": ["es2015", "dom"], "skipLibCheck": true, "types": [], - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@nguniversal/common/*": ["../../dist/packages/common/*"] + } }, "files": [ - "./public-api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "skipTemplateCodegen": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "flatModuleOutFile": "index.js", - "flatModuleId": "@nguniversal/common" + "flatModuleId": "@nguniversal/common", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true } } \ No newline at end of file diff --git a/modules/common/tsconfig.spec.json b/modules/common/tsconfig.spec.json index 07a15fefc..8d92a6b97 100644 --- a/modules/common/tsconfig.spec.json +++ b/modules/common/tsconfig.spec.json @@ -10,7 +10,7 @@ "types": ["jasmine"] }, "angularCompilerOptions": { - "strictMetadataEmit": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "skipTemplateCodegen": true, "emitDecoratorMetadata": true, "fullTemplateTypeCheck": true diff --git a/modules/express-engine/package.json b/modules/express-engine/package.json index 58bdbed57..6792c7ac6 100644 --- a/modules/express-engine/package.json +++ b/modules/express-engine/package.json @@ -1,6 +1,6 @@ { "name": "@nguniversal/express-engine", - "version": "5.0.0-beta.6", + "version": "0.0.0-PLACEHOLDER", "description": "Express Engine for running Server Angular Apps", "main": "./bundles/express-engine.umd.js", "module": "./esm5/express-engine.es5.js", @@ -13,9 +13,9 @@ "universal" ], "peerDependencies": { - "@angular/common": "^5.0.0", - "@angular/core": "^5.0.0", - "@angular/platform-server": "^5.0.0", + "@angular/common": "0.0.0-NG", + "@angular/core": "0.0.0-NG", + "@angular/platform-server": "0.0.0-NG", "express": "^4.15.2" }, "repository": { diff --git a/modules/express-engine/src/main.ts b/modules/express-engine/src/main.ts index 386a52f4e..7aa685044 100644 --- a/modules/express-engine/src/main.ts +++ b/modules/express-engine/src/main.ts @@ -17,7 +17,7 @@ import { } from '@angular/platform-server'; import { FileLoader } from './file-loader'; -import { REQUEST, RESPONSE } from '../tokens'; +import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; /** * These are the allowed options for the engine diff --git a/modules/express-engine/tokens/index.ts b/modules/express-engine/tokens/index.ts new file mode 100644 index 000000000..ca845d822 --- /dev/null +++ b/modules/express-engine/tokens/index.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './public-api'; diff --git a/modules/express-engine/tokens/public-api.ts b/modules/express-engine/tokens/public-api.ts new file mode 100644 index 000000000..cbafb81ee --- /dev/null +++ b/modules/express-engine/tokens/public-api.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './tokens'; diff --git a/modules/express-engine/tokens.ts b/modules/express-engine/tokens/tokens.ts similarity index 100% rename from modules/express-engine/tokens.ts rename to modules/express-engine/tokens/tokens.ts diff --git a/modules/express-engine/tokens/tsconfig.lib.json b/modules/express-engine/tokens/tsconfig.lib.json new file mode 100644 index 000000000..ea171974b --- /dev/null +++ b/modules/express-engine/tokens/tsconfig.lib.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.lib", + "files": [ + "public-api.ts" + ], + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 + "flatModuleOutFile": "index.js", + "flatModuleId": "@nguniversal/express-engine/tokens", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true + } +} diff --git a/modules/express-engine/tsconfig.fortokens.json b/modules/express-engine/tsconfig.fortokens.json deleted file mode 100644 index f64a63ad3..000000000 --- a/modules/express-engine/tsconfig.fortokens.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "files": [ - "./index.ts", - "./tokens.ts" - ] -} \ No newline at end of file diff --git a/modules/express-engine/tsconfig.lib.json b/modules/express-engine/tsconfig.lib.json index 9c26dab73..ad472ddea 100644 --- a/modules/express-engine/tsconfig.lib.json +++ b/modules/express-engine/tsconfig.lib.json @@ -21,16 +21,20 @@ "lib": ["es2015", "dom"], "skipLibCheck": true, "types": [], - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@nguniversal/express-engine/*": ["../../dist/packages/express-engine/*"] + } }, "files": [ - "./public-api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "skipTemplateCodegen": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "flatModuleOutFile": "index.js", - "flatModuleId": "@nguniversal/express-engine" + "flatModuleId": "@nguniversal/express-engine", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true } } \ No newline at end of file diff --git a/modules/express-engine/tsconfig.spec.json b/modules/express-engine/tsconfig.spec.json index 07a15fefc..8d92a6b97 100644 --- a/modules/express-engine/tsconfig.spec.json +++ b/modules/express-engine/tsconfig.spec.json @@ -10,7 +10,7 @@ "types": ["jasmine"] }, "angularCompilerOptions": { - "strictMetadataEmit": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "skipTemplateCodegen": true, "emitDecoratorMetadata": true, "fullTemplateTypeCheck": true diff --git a/modules/hapi-engine/package.json b/modules/hapi-engine/package.json index be2eb1a06..f72197719 100644 --- a/modules/hapi-engine/package.json +++ b/modules/hapi-engine/package.json @@ -1,6 +1,6 @@ { "name": "@nguniversal/hapi-engine", - "version": "5.0.0-beta.6", + "version": "0.0.0-PLACEHOLDER", "description": "Hapi Engine for running Server Angular Apps", "main": "./bundles/hapi-engine.umd.js", "module": "./esm5/hapi-engine.es5.js", @@ -13,9 +13,9 @@ "universal" ], "peerDependencies": { - "@angular/common": "^5.0.0", - "@angular/core": "^5.0.0", - "@angular/platform-server": "^5.0.0", + "@angular/common": "0.0.0-NG", + "@angular/core": "0.0.0-NG", + "@angular/platform-server": "0.0.0-NG", "hapi": "^16.1.1" }, "repository": { diff --git a/modules/hapi-engine/src/main.ts b/modules/hapi-engine/src/main.ts index d489b923c..cb4e4258c 100644 --- a/modules/hapi-engine/src/main.ts +++ b/modules/hapi-engine/src/main.ts @@ -17,7 +17,7 @@ import { } from '@angular/platform-server'; import { FileLoader } from './file-loader'; -import { REQUEST, RESPONSE } from '../tokens'; +import { REQUEST, RESPONSE } from '@nguniversal/hapi-engine/tokens'; /** * These are the allowed options for the engine diff --git a/modules/hapi-engine/tokens/index.ts b/modules/hapi-engine/tokens/index.ts new file mode 100644 index 000000000..ca845d822 --- /dev/null +++ b/modules/hapi-engine/tokens/index.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './public-api'; diff --git a/modules/hapi-engine/tokens/public-api.ts b/modules/hapi-engine/tokens/public-api.ts new file mode 100644 index 000000000..cbafb81ee --- /dev/null +++ b/modules/hapi-engine/tokens/public-api.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC 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 + */ +export * from './tokens'; diff --git a/modules/hapi-engine/tokens.ts b/modules/hapi-engine/tokens/tokens.ts similarity index 100% rename from modules/hapi-engine/tokens.ts rename to modules/hapi-engine/tokens/tokens.ts diff --git a/modules/hapi-engine/tokens/tsconfig.lib.json b/modules/hapi-engine/tokens/tsconfig.lib.json new file mode 100644 index 000000000..ed732060f --- /dev/null +++ b/modules/hapi-engine/tokens/tsconfig.lib.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.lib", + "files": [ + "public-api.ts" + ], + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 + "flatModuleOutFile": "index.js", + "flatModuleId": "@nguniversal/hapi-engine/tokens", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true + } +} diff --git a/modules/hapi-engine/tsconfig.fortokens.json b/modules/hapi-engine/tsconfig.fortokens.json deleted file mode 100644 index f64a63ad3..000000000 --- a/modules/hapi-engine/tsconfig.fortokens.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "files": [ - "./index.ts", - "./tokens.ts" - ] -} \ No newline at end of file diff --git a/modules/hapi-engine/tsconfig.lib.json b/modules/hapi-engine/tsconfig.lib.json index 9b8966b05..33809b716 100644 --- a/modules/hapi-engine/tsconfig.lib.json +++ b/modules/hapi-engine/tsconfig.lib.json @@ -21,16 +21,20 @@ "lib": ["es2015", "dom"], "skipLibCheck": true, "types": [], - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@nguniversal/hapi-engine/*": ["../../dist/packages/hapi-engine/*"] + } }, "files": [ - "./public-api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "skipTemplateCodegen": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "flatModuleOutFile": "index.js", - "flatModuleId": "@nguniversal/hapi-engine" + "flatModuleId": "@nguniversal/common", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true } } \ No newline at end of file diff --git a/modules/hapi-engine/tsconfig.spec.json b/modules/hapi-engine/tsconfig.spec.json index 07a15fefc..8d92a6b97 100644 --- a/modules/hapi-engine/tsconfig.spec.json +++ b/modules/hapi-engine/tsconfig.spec.json @@ -10,7 +10,7 @@ "types": ["jasmine"] }, "angularCompilerOptions": { - "strictMetadataEmit": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "skipTemplateCodegen": true, "emitDecoratorMetadata": true, "fullTemplateTypeCheck": true diff --git a/modules/module-map-ngfactory-loader/package.json b/modules/module-map-ngfactory-loader/package.json index 027108560..fa95e4d93 100644 --- a/modules/module-map-ngfactory-loader/package.json +++ b/modules/module-map-ngfactory-loader/package.json @@ -1,6 +1,6 @@ { "name": "@nguniversal/module-map-ngfactory-loader", - "version": "5.0.0-beta.6", + "version": "0.0.0-PLACEHOLDER", "description": "NgFactoryLoader which uses a Map to load ngfactories without lazy loading", "main": "./bundles/module-map-ngfactory-loader.umd.js", "module": "./esm5/module-map-ngfactory-loader.es5.js", @@ -14,9 +14,9 @@ "universal" ], "peerDependencies": { - "@angular/common": "^5.0.0", - "@angular/core": "^5.0.0", - "@angular/platform-server": "^5.0.0" + "@angular/common": "0.0.0-NG", + "@angular/core": "0.0.0-NG", + "@angular/platform-server": "0.0.0-NG" }, "repository": { "type": "git", diff --git a/modules/module-map-ngfactory-loader/tsconfig.lib.json b/modules/module-map-ngfactory-loader/tsconfig.lib.json index b08d3b134..72419dc5f 100644 --- a/modules/module-map-ngfactory-loader/tsconfig.lib.json +++ b/modules/module-map-ngfactory-loader/tsconfig.lib.json @@ -24,13 +24,14 @@ "baseUrl": "." }, "files": [ - "./public-api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "skipTemplateCodegen": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "flatModuleOutFile": "index.js", - "flatModuleId": "@nguniversal/module-map-ngfactory-loader" + "flatModuleId": "@nguniversal/module-map-ngfactory-loader", + "skipTemplateCodegen": true, + "fullTemplateTypeCheck": true } } \ No newline at end of file diff --git a/modules/module-map-ngfactory-loader/tsconfig.spec.json b/modules/module-map-ngfactory-loader/tsconfig.spec.json index 07a15fefc..8d92a6b97 100644 --- a/modules/module-map-ngfactory-loader/tsconfig.spec.json +++ b/modules/module-map-ngfactory-loader/tsconfig.spec.json @@ -10,7 +10,7 @@ "types": ["jasmine"] }, "angularCompilerOptions": { - "strictMetadataEmit": true, + "strictMetadataEmit": false, // Workaround for Angular #22210 "skipTemplateCodegen": true, "emitDecoratorMetadata": true, "fullTemplateTypeCheck": true diff --git a/package.json b/package.json index 599d1df61..0621b6e2b 100644 --- a/package.json +++ b/package.json @@ -74,23 +74,25 @@ "bazel:test:watch": "ibazel test //..." }, "devDependencies": { - "@angular/animations": "6.0.0-beta.6", - "@angular/bazel": "github:angular/bazel-builds#144cd753ff9f0fbd085d1ed695b4d70335877892", - "@angular/common": "6.0.0-beta.6", - "@angular/compiler": "6.0.0-beta.6", - "@angular/compiler-cli": "6.0.0-beta.6", - "@angular/core": "6.0.0-beta.6", - "@angular/http": "6.0.0-beta.6", - "@angular/platform-browser": "6.0.0-beta.6", - "@angular/platform-browser-dynamic": "6.0.0-beta.6", - "@angular/platform-server": "6.0.0-beta.6", + "@angular/animations": "6.0.0-beta.7", + "@angular/bazel": "6.0.0-beta.7", + "@angular/common": "6.0.0-beta.7", + "@angular/compiler": "6.0.0-beta.7", + "@angular/compiler-cli": "6.0.0-beta.7", + "@angular/core": "6.0.0-beta.7", + "@angular/http": "6.0.0-beta.7", + "@angular/platform-browser": "6.0.0-beta.7", + "@angular/platform-browser-dynamic": "6.0.0-beta.7", + "@angular/platform-server": "6.0.0-beta.7", "@bazel/ibazel": "^0.3.1", "@types/express": "^4.0.39", + "@types/fs-extra": "^4.0.5", "@types/hapi": "^16.1.2", "@types/jasmine": "^2.8.6", "@types/node": "^9.4.6", "camelcase": "^4.1.0", "express": "^4.15.2", + "fs-extra": "^3.0.1", "glob": "^7.1.2", "hapi": "^16.1.1", "jasmine-core": "^2.8.0", @@ -101,22 +103,24 @@ "karma-typescript": "^3.0.12", "minimatch": "^3.0.4", "protractor": "^5.2.0", + "replace-in-file": "^3.1.1", "rimraf": "^2.6.1", - "rollup": "~0.41.6", + "rollup": "^0.56.5", "rollup-plugin-alias": "^1.4.0", "rollup-plugin-commonjs": "^8.2.6", "rollup-plugin-node-resolve": "^3.0.2", "rollup-plugin-sourcemaps": "^0.4.2", "rollup-plugin-uglify": "^2.0.1", "rxjs": "^5.5.6", + "sorcery": "^0.10.0", + "standard-version": "^4.3.0", "systemjs": "0.19.43", "ts-node": "^3.0.4", "tsconfig-paths": "^2.3.0", "tslint": "^5.9.1", "tsutils": "^2.21.2", "typescript": "~2.6.2", - "standard-version": "^4.3.0", - "replace-in-file": "^3.1.1", + "uglify-js": "^2.8.14", "zone.js": "^0.8.12" } } diff --git a/publish.sh b/publish.sh index e97d2c2f4..b133154fa 100755 --- a/publish.sh +++ b/publish.sh @@ -2,11 +2,11 @@ set -x -./build.sh +npm run build -npm publish --access public dist/packages/common -npm publish --access public dist/packages/express-engine -npm publish --access public dist/packages/aspnetcore-engine -npm publish --access public dist/packages/module-map-ngfactory-loader -npm publish --access public dist/packages/hapi-engine +npm publish --access public dist/releases/common +npm publish --access public dist/releases/express-engine +npm publish --access public dist/releases/aspnetcore-engine +npm publish --access public dist/releases/module-map-ngfactory-loader +npm publish --access public dist/releases/hapi-engine diff --git a/tools/package-tools/build-bundles.ts b/tools/package-tools/build-bundles.ts new file mode 100644 index 000000000..969567bae --- /dev/null +++ b/tools/package-tools/build-bundles.ts @@ -0,0 +1,206 @@ +import {join, dirname} from 'path'; +import {uglifyJsFile} from './minify-sources'; +import {buildConfig} from './build-config'; +import {BuildPackage} from './build-package'; +import {rollupRemoveLicensesPlugin} from './rollup-remove-licenses'; +import {rollupGlobals, dashCaseToCamelCase} from './rollup-globals'; +import {remapSourcemap} from './sourcemap-remap'; + +// There are no type definitions available for these imports. +const rollup = require('rollup'); +const rollupNodeResolutionPlugin = require('rollup-plugin-node-resolve'); +const rollupAlias = require('rollup-plugin-alias'); + +/** Directory where all bundles will be created in. */ +const bundlesDir = join(buildConfig.outputDir, 'bundles'); + + +/** Utility for creating bundles from raw ngc output. */ +export class PackageBundler { + constructor(private buildPackage: BuildPackage) {} + + /** Creates all bundles for the package and all associated entry points (UMD, ES5, ES2015). */ + async createBundles() { + for (const entryPoint of this.buildPackage.secondaryEntryPoints) { + await this.bundleSecondaryEntryPoint(entryPoint); + } + + await this.bundlePrimaryEntryPoint(); + } + + /** Bundles the primary entry-point w/ given entry file, e.g. @angular/cdk */ + private async bundlePrimaryEntryPoint() { + const packageName = this.buildPackage.name; + const dashedPackageName = dashCaseToCamelCase(packageName); + + return this.bundleEntryPoint({ + entryFile: this.buildPackage.entryFilePath, + esm5EntryFile: join(this.buildPackage.esm5OutputDir, 'index.js'), + importName: `@${buildConfig.namespace}/${packageName}`, + moduleName: `${buildConfig.namespace}.${dashedPackageName}`, + esm2015Dest: join(bundlesDir, `${packageName}.js`), + esm5Dest: join(bundlesDir, `${packageName}.es5.js`), + umdDest: join(bundlesDir, `${packageName}.umd.js`), + umdMinDest: join(bundlesDir, `${packageName}.umd.min.js`), + }); + } + + /** Bundles a single secondary entry-point w/ given entry file, e.g. @angular/cdk/a11y */ + private async bundleSecondaryEntryPoint(entryPoint: string) { + const packageName = this.buildPackage.name; + const entryFile = join(this.buildPackage.outputDir, entryPoint, 'index.js'); + const esm5EntryFile = join(this.buildPackage.esm5OutputDir, entryPoint, 'index.js'); + const dashedEntryName = dashCaseToCamelCase(entryPoint); + const dashedPackageName = dashCaseToCamelCase(packageName); + + return this.bundleEntryPoint({ + entryFile, + esm5EntryFile, + importName: `@${buildConfig.namespace}/${packageName}/${dashedEntryName}`, + moduleName: `${buildConfig.namespace}.${dashedPackageName}.${dashedEntryName}`, + esm2015Dest: join(bundlesDir, `${packageName}`, `${entryPoint}.js`), + esm5Dest: join(bundlesDir, `${packageName}`, `${entryPoint}.es5.js`), + umdDest: join(bundlesDir, `${packageName}-${entryPoint}.umd.js`), + umdMinDest: join(bundlesDir, `${packageName}-${entryPoint}.umd.min.js`), + }); + } + + /** + * Creates the ES5, ES2015, and UMD bundles for the specified entry-point. + * @param config Configuration that specifies the entry-point, module name, and output + * bundle paths. + */ + private async bundleEntryPoint(config: BundlesConfig) { + // Build FESM-2015 bundle file. + await this.createRollupBundle({ + importName: config.importName, + moduleName: config.moduleName, + entry: config.entryFile, + dest: config.esm2015Dest, + format: 'es', + }); + + // Build FESM-5 bundle file. + await this.createRollupBundle({ + importName: config.importName, + moduleName: config.moduleName, + entry: config.esm5EntryFile, + dest: config.esm5Dest, + format: 'es', + }); + + // Create UMD bundle of ES5 output. + await this.createRollupBundle({ + importName: config.importName, + moduleName: config.moduleName, + entry: config.esm5Dest, + dest: config.umdDest, + format: 'umd' + }); + + // Create a minified UMD bundle using UglifyJS + uglifyJsFile(config.umdDest, config.umdMinDest); + + // Remaps the sourcemaps to be based on top of the original TypeScript source files. + await remapSourcemap(config.esm2015Dest); + await remapSourcemap(config.esm5Dest); + await remapSourcemap(config.umdDest); + await remapSourcemap(config.umdMinDest); + } + + /** Creates a rollup bundle of a specified JavaScript file. */ + private async createRollupBundle(config: RollupBundleConfig) { + const bundleOptions = { + context: 'this', + external: Object.keys(rollupGlobals), + input: config.entry, + onwarn: (message: string) => { + if (/but never used/.test(message)) { + return false; + } + + console.warn(message); + }, + plugins: [ + rollupRemoveLicensesPlugin, + ] + }; + + const writeOptions = { + name: config.moduleName || `${buildConfig.namespace}.common`, + amd: {id: config.importName}, + banner: buildConfig.licenseBanner, + format: config.format, + file: config.dest, + globals: rollupGlobals, + sourcemap: true + }; + + // For UMD bundles, we need to adjust the `external` bundle option in order to include + // all necessary code in the bundle. + if (config.format === 'umd') { + bundleOptions.plugins.push(rollupNodeResolutionPlugin()); + + // For all UMD bundles, we want to exclude tslib from the `external` bundle option so that + // it is inlined into the bundle. + let external = Object.keys(rollupGlobals); + external.splice(external.indexOf('tslib'), 1); + + // If each secondary entry-point is re-exported at the root, we want to exlclude those + // secondary entry-points from the rollup globals because we want the UMD for this package + // to include *all* of the sources for those entry-points. + if (this.buildPackage.exportsSecondaryEntryPointsAtRoot && + config.moduleName === `${buildConfig.namespace}.${this.buildPackage.name}`) { + + const importRegex = new RegExp(`@${buildConfig.namespace}/${this.buildPackage.name}/.+`); + external = external.filter(e => !importRegex.test(e)); + + // Use the rollup-alias plugin to map imports of the form `@angular/material/button` + // to the actual file location so that rollup can resolve the imports (otherwise they + // will be treated as external dependencies and not included in the bundle). + bundleOptions.plugins.push( + rollupAlias(this.getResolvedSecondaryEntryPointImportPaths(config.dest))); + } + + bundleOptions.external = external; + } + + return rollup.rollup(bundleOptions).then((bundle: any) => bundle.write(writeOptions)); + } + + /** + * Gets mapping of import aliases (e.g. `@angular/material/button`) to the path of the es5 + * bundle output. + * @param bundleOutputDir Path to the bundle output directory. + * @returns Map of alias to resolved path. + */ + private getResolvedSecondaryEntryPointImportPaths(bundleOutputDir: string) { + return this.buildPackage.secondaryEntryPoints.reduce((map, p) => { + map[`@${buildConfig.namespace}/${this.buildPackage.name}/${p}`] = + join(dirname(bundleOutputDir), this.buildPackage.name, `${p}.es5.js`); + return map; + }, {} as {[key: string]: string}); + } +} + + +/** Configuration for creating library bundles. */ +interface BundlesConfig { + entryFile: string; + esm5EntryFile: string; + importName: string; + moduleName: string; + esm2015Dest: string; + esm5Dest: string; + umdDest: string; + umdMinDest: string; +} + +/** Configuration for creating a bundle via rollup. */ +interface RollupBundleConfig { + entry: string; + dest: string; + format: string; + moduleName: string; + importName: string; +} diff --git a/tools/package-tools/build-package.ts b/tools/package-tools/build-package.ts index 4d6b0ce04..c846fc382 100644 --- a/tools/package-tools/build-package.ts +++ b/tools/package-tools/build-package.ts @@ -1,184 +1,109 @@ -import {existsSync, readFileSync, writeFileSync, mkdirSync} from 'fs'; -import {join, dirname} from 'path'; - +import {join} from 'path'; +import {red} from 'chalk'; +import {PackageBundler} from './build-bundles'; import {buildConfig} from './build-config'; -import {rollupGlobals} from './rollup-globals'; - -const glob = require('glob'); -const camelCase = require('camelcase'); -const ngc = require('@angular/compiler-cli/src/main').main; -const rollup = require('rollup'); -const uglify = require('rollup-plugin-uglify'); -const sourcemaps = require('rollup-plugin-sourcemaps'); -const nodeResolve = require('rollup-plugin-node-resolve'); - -const packagesFolder = join(buildConfig.outputDir, 'packages'); -const releasesFolder = join(buildConfig.outputDir, 'releases'); - -export async function buildLib(libName: string, test: boolean = false) { - const srcFolder = join(buildConfig.projectDir, 'modules', libName); - const libPackageFolder = join(packagesFolder, libName); - const libReleaseFolder = join(releasesFolder, libName); - const libBundlesFolder = join(libReleaseFolder, 'bundles'); - const es5DistFolder = join(libReleaseFolder, 'esm5'); - const es2015DistFolder = join(libReleaseFolder, 'esm2015'); - const es5OutputFolder = join(libPackageFolder, 'lib-es5'); - const es2015OutputFolder = libPackageFolder; - const tsConfig = test ? 'tsconfig.spec.json' : 'tsconfig.lib.json'; - - console.log(`#### BUILDING ${libName} ${test ? 'W/ TESTS ' : ''}####`); - - // Compile to ES2015. - let exitCode = ngc(['-p', join(srcFolder, tsConfig)]); - if (exitCode !== 0) { - return exitCode; +import {getSecondaryEntryPointsForPackage} from './secondary-entry-points'; +import {compileEntryPoint, renamePrivateReExportsToBeUnique} from './compile-entry-point'; +import {ngcCompile} from './ngc-compile'; + +const {packagesDir, outputDir} = buildConfig; + +/** Name of the tsconfig file that is responsible for building an ES2015 package. */ +const buildTsconfigName = 'tsconfig.lib.json'; + +/** Name of the tsconfig file that is responsible for building the tests. */ +const testsTsconfigName = 'tsconfig.spec.json'; + +export class BuildPackage { + /** Path to the package sources. */ + sourceDir: string; + + /** Path to the ES2015 package output. */ + outputDir: string; + + /** Path to the ES5 package output. */ + esm5OutputDir: string; + + /** Whether this package will re-export its secondary-entry points at the root module. */ + exportsSecondaryEntryPointsAtRoot = false; + + /** Path to the entry file of the package in the output directory. */ + readonly entryFilePath: string; + + /** Package bundler instance. */ + private bundler = new PackageBundler(this); + + /** Secondary entry-points partitioned by their build depth. */ + private get secondaryEntryPointsByDepth(): string[][] { + this.cacheSecondaryEntryPoints(); + return this._secondaryEntryPointsByDepth; } + private _secondaryEntryPointsByDepth: string[][]; - console.log('ES2015 compilation succeeded.'); + /** Secondary entry points for the package. */ + get secondaryEntryPoints(): string[] { + this.cacheSecondaryEntryPoints(); + return this._secondaryEntryPoints; + } + private _secondaryEntryPoints: string[]; - if (test) { - return 0; + constructor(readonly name: string, readonly dependencies: BuildPackage[] = []) { + this.sourceDir = join(packagesDir, name); + this.outputDir = join(outputDir, 'packages', name); + this.esm5OutputDir = join(outputDir, 'packages', name, 'esm5'); + this.entryFilePath = join(this.outputDir, 'index.js'); } - // Compile to ES5. - exitCode = ngc([ - '-p', - join(srcFolder, 'tsconfig.lib.json'), - '--outDir', - es5OutputFolder, - '--target', - 'ES5' - ]); - if (exitCode !== 0) { - return exitCode; + /** Compiles the package sources with all secondary entry points. */ + async compile() { + // Compile all secondary entry-points with the same depth in parallel, and each separate depth + // group in sequence. This will look something like: + // Depth 0: coercion, platform, keycodes, bidi + // Depth 1: a11y, scrolling + // Depth 2: overlay + for (const entryPointGroup of this.secondaryEntryPointsByDepth) { + await Promise.all(entryPointGroup.map(p => this._compileBothTargets(p))); + } + + // Compile the primary entry-point. + await this._compileBothTargets(); } - console.log('ES5 compilation succeeded.'); - - // Copy typings and metadata to `dist/` folder. - await _relativeCopy('**/*.+(d.ts|metadata.json)', es2015OutputFolder, - join(libReleaseFolder, 'typings')); - - _metaDataReExport(libReleaseFolder, `./typings/index`, libName, libName); - _typingsReExport(libReleaseFolder, `./typings/index`, libName); - - console.log('Typings and metadata copy succeeded.'); - - // Bundle lib. - const es5Entry = join(es5OutputFolder, `index.js`); - const es2015Entry = join(es2015OutputFolder, `index.js`); - - // Base configuration. - const rollupBaseConfig = { - moduleName: 'nguniversal.' + camelCase(libName), - sourceMap: true, - // ATTENTION: - // Add any dependency or peer dependency your library to `globals` and `external`. - // This is required for UMD bundle users. - globals: rollupGlobals, - external: Object.keys(rollupGlobals), - plugins: [ - sourcemaps(), - nodeResolve() - ] - }; - - // UMD bundle. - const umdConfig = Object.assign({}, rollupBaseConfig, { - entry: es5Entry, - dest: join(libBundlesFolder, `${libName}.umd.js`), - format: 'umd', - }); - - // Minified UMD bundle. - const minifiedUmdConfig = Object.assign({}, rollupBaseConfig, { - entry: es5Entry, - dest: join(libBundlesFolder, `${libName}.umd.min.js`), - format: 'umd', - plugins: rollupBaseConfig.plugins.concat([uglify({})]) - }); - - // ESM+ES5 flat module bundle. - const fesm5config = Object.assign({}, rollupBaseConfig, { - entry: es5Entry, - dest: join(es5DistFolder, `${libName}.es5.js`), - format: 'es' - }); - - // ESM+ES2015 flat module bundle. - const fesm2015config = Object.assign({}, rollupBaseConfig, { - entry: es2015Entry, - dest: join(es2015DistFolder, `${libName}.js`), - format: 'es' - }); - - const allBundles = [ - umdConfig, - minifiedUmdConfig, - fesm5config, - fesm2015config - ].map(cfg => rollup.rollup(cfg).then(bundle => bundle.write(cfg))); - - await Promise.all(allBundles) - .then(() => console.log('All bundles generated successfully.')); - - // Copy package files - await _relativeCopy('LICENSE', buildConfig.projectDir, libReleaseFolder); - await _relativeCopy('package.json', srcFolder, libReleaseFolder); - await _relativeCopy('README.md', srcFolder, libReleaseFolder); - - console.log('Package files copy succeeded.'); - - return 0; -} + /** Compiles the TypeScript test source files for the package. */ + async compileTests() { + await this._compileTestEntryPoint(testsTsconfigName); + } -// Copy files maintaining relative paths. -function _relativeCopy(fileGlob: string, from: string, to: string) { - return new Promise((resolve, reject) => { - glob(fileGlob, { cwd: from, nodir: true }, (err: string, files: string[]) => { - if (err) { - reject(err); - } - files.forEach((file: string) => { - const origin = join(from, file); - const dest = join(to, file); - const data = readFileSync(origin, 'utf-8'); - _recursiveMkDir(dirname(dest)); - writeFileSync(dest, data); - }); - resolve(); - }); - }); -} + /** Creates all bundles for the package and all associated entry points. */ + async createBundles() { + await this.bundler.createBundles(); + } -// Recursively create a dir. -function _recursiveMkDir(dir: string) { - if (!existsSync(dir)) { - _recursiveMkDir(dirname(dir)); - mkdirSync(dir); + /** Compiles TS into both ES2015 and ES5, then updates exports. */ + private async _compileBothTargets(p = '') { + return compileEntryPoint(this, buildTsconfigName, p) + .then(() => compileEntryPoint(this, buildTsconfigName, p, this.esm5OutputDir)) + .then(() => renamePrivateReExportsToBeUnique(this, p)); } -} -function _typingsReExport(outDir: string, from: string, fileName: string) { - writeFileSync(join(outDir, `${fileName}.d.ts`), - `export * from '${from}';\n`, - 'utf-8'); -} + /** Compiles the TypeScript sources of a primary or secondary entry point. */ + private _compileTestEntryPoint(tsconfigName: string, secondaryEntryPoint = ''): Promise { + const entryPointPath = join(this.sourceDir, secondaryEntryPoint); + const entryPointTsconfigPath = join(entryPointPath, tsconfigName); -function _metaDataReExport(destDir: string, - from: string|string[], - entryPointName: string, - importAsName: string) { - from = Array.isArray(from) ? from : [from]; - - const metadataJsonContent = JSON.stringify({ - __symbolic: 'module', - version: 3, - metadata: {}, - exports: from.map(f => ({from: f})), - flatModuleIndexRedirect: true, - importAs: `${buildConfig.namespace}/${importAsName}` - }, null, 2); - - writeFileSync(join(destDir, `${entryPointName}.metadata.json`), metadataJsonContent, 'utf-8'); + return ngcCompile(['-p', entryPointTsconfigPath]).catch(() => { + const error = red(`Failed to compile ${secondaryEntryPoint} using ${entryPointTsconfigPath}`); + console.error(error); + return Promise.reject(error); + }); + } + + /** Stores the secondary entry-points for this package if they haven't been computed already. */ + private cacheSecondaryEntryPoints() { + if (!this._secondaryEntryPoints) { + this._secondaryEntryPointsByDepth = getSecondaryEntryPointsForPackage(this); + this._secondaryEntryPoints = + this._secondaryEntryPointsByDepth.reduce((list, p) => list.concat(p), []); + } + } } diff --git a/tools/package-tools/build-release.ts b/tools/package-tools/build-release.ts new file mode 100644 index 000000000..229e42aa8 --- /dev/null +++ b/tools/package-tools/build-release.ts @@ -0,0 +1,108 @@ +import {appendFileSync} from 'fs'; +import {mkdirpSync} from 'fs-extra'; +import {join} from 'path'; +import {buildConfig} from './build-config'; +import {BuildPackage} from './build-package'; +import {copyFiles} from './copy-files'; +import {createEntryPointPackageJson} from './entry-point-package-json'; +import {inlinePackageMetadataFiles} from './metadata-inlining'; +import {createMetadataReexportFile} from './metadata-reexport'; +import {createTypingsReexportFile} from './typings-reexport'; +import {replaceVersionPlaceholders} from './version-placeholders'; + +const {packagesDir, outputDir, projectDir} = buildConfig; + +/** Directory where all bundles will be created in. */ +const bundlesDir = join(outputDir, 'bundles'); + +/** + * 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(buildPackage: BuildPackage) { + const {name, sourceDir} = buildPackage; + const packageOut = buildPackage.outputDir; + const releasePath = join(outputDir, 'releases', name); + const importAsName = `${buildConfig.namespace}/${name}`; + + inlinePackageMetadataFiles(packageOut); + + // Copy all d.ts and metadata files to the `typings/` directory + copyFiles(packageOut, '**/*.+(d.ts|metadata.json)', join(releasePath, 'typings')); + + // Copy UMD bundles. + copyFiles(bundlesDir, `${name}?(-*).umd?(.min).js?(.map)`, join(releasePath, 'bundles')); + + // Copy ES5 bundles. + copyFiles(bundlesDir, `${name}.es5.js?(.map)`, join(releasePath, 'esm5')); + copyFiles(join(bundlesDir, name), `*.es5.js?(.map)`, join(releasePath, 'esm5')); + + // Copy ES2015 bundles + copyFiles(bundlesDir, `${name}.js?(.map)`, join(releasePath, 'esm2015')); + copyFiles(join(bundlesDir, name), `!(*.es5|*.umd).js?(.map)`, join(releasePath, 'esm2015')); + + // Copy any additional files that belong in the package. + copyFiles(projectDir, 'LICENSE', releasePath); + copyFiles(packagesDir, 'README.md', releasePath); + copyFiles(sourceDir, 'package.json', releasePath); + + replaceVersionPlaceholders(releasePath); + createTypingsReexportFile(releasePath, './typings/index', name); + createMetadataReexportFile(releasePath, './typings/index', name, importAsName); + + if (buildPackage.secondaryEntryPoints.length) { + createFilesForSecondaryEntryPoint(buildPackage, releasePath); + } + + if (buildPackage.exportsSecondaryEntryPointsAtRoot) { + // Add re-exports to the root d.ts file to prevent errors of the form + // "@angular/material/material has no exported member 'MATERIAL_SANITY_CHECKS." + const es2015Exports = buildPackage.secondaryEntryPoints + .map(p => `export * from './${p}';`).join('\n'); + appendFileSync(join(releasePath, `${name}.d.ts`), es2015Exports, 'utf-8'); + + // When re-exporting secondary entry-points, we need to manually create a metadata file that + // re-exports everything. + createMetadataReexportFile( + releasePath, + buildPackage.secondaryEntryPoints.concat(['typings/index']).map(p => `./${p}`), + name, + importAsName); + } +} + +/** Creates files necessary for a secondary entry-point. */ +function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePath: string) { + const {name} = buildPackage; + const packageOut = buildPackage.outputDir; + + buildPackage.secondaryEntryPoints.forEach(entryPointName => { + // Create a directory in the root of the package for this entry point that contains + // * A package.json that lists the different bundle locations + // * An index.d.ts file that re-exports the index.d.ts from the typings/ directory + // * A metadata.json re-export for this entry-point's metadata. + const entryPointDir = join(releasePath, entryPointName); + const importAsName = `${buildConfig.namespace}/${name}/${entryPointName}`; + + mkdirpSync(entryPointDir); + createEntryPointPackageJson(entryPointDir, name, entryPointName); + + // Copy typings and metadata from tsc output location into the entry-point. + copyFiles( + join(packageOut, entryPointName), + '**/*.+(d.ts|metadata.json)', + join(entryPointDir, 'typings')); + + // Create a typings and a metadata re-export within the entry-point to point to the + // typings we just copied. + createTypingsReexportFile(entryPointDir, `./typings/index`, 'index'); + createMetadataReexportFile(entryPointDir, `./typings/index`, 'index', importAsName); + + // Finally, create both a d.ts and metadata file for this entry-point in the root of + // the package that re-exports from the entry-point's directory. + createTypingsReexportFile(releasePath, `./${entryPointName}/index`, entryPointName); + createMetadataReexportFile(releasePath, `./${entryPointName}/index`, entryPointName, + importAsName); + }); +} diff --git a/tools/package-tools/compile-entry-point.ts b/tools/package-tools/compile-entry-point.ts new file mode 100644 index 000000000..42f765db3 --- /dev/null +++ b/tools/package-tools/compile-entry-point.ts @@ -0,0 +1,54 @@ +import {join} from 'path'; +import {writeFileSync, readFileSync} from 'fs'; +import {sync as glob} from 'glob'; +import {red} from 'chalk'; +import {BuildPackage} from './build-package'; +import {ngcCompile} from './ngc-compile'; + +/** Incrementing ID counter. */ +let nextId = 0; + +/** Compiles the TypeScript sources of a primary or secondary entry point. */ +export async function compileEntryPoint(buildPackage: BuildPackage, tsconfigName: string, + secondaryEntryPoint = '', es5OutputPath?: string) { + const entryPointPath = join(buildPackage.sourceDir, secondaryEntryPoint); + const entryPointTsconfigPath = join(entryPointPath, tsconfigName); + const ngcFlags = ['-p', entryPointTsconfigPath]; + + if (es5OutputPath) { + ngcFlags.push('--outDir', es5OutputPath, '--target', 'ES5'); + } + + return ngcCompile(ngcFlags).catch(() => { + const error = red(`Failed to compile ${secondaryEntryPoint} using ${entryPointTsconfigPath}`); + console.error(error); + return Promise.reject(error); + }); +} + +/** Renames `ɵa`-style re-exports generated by Angular to be unique across compilation units. */ +export function renamePrivateReExportsToBeUnique(buildPackage: BuildPackage, + secondaryEntryPoint = '') { + // When we compiled the typescript sources with ngc, we do entry-point individually. + // If the root-level module re-exports multiple of these entry-points, the private-export + // identifiers (e.g., `ɵa`) generated by ngc will collide. We work around this by suffixing + // each of these identifiers with an ID specific to this entry point. We make this + // replacement in the js, d.ts, and metadata output. + if (buildPackage.exportsSecondaryEntryPointsAtRoot && secondaryEntryPoint) { + const entryPointId = nextId++; + const outputPath = join(buildPackage.outputDir, secondaryEntryPoint); + const esm5OutputPath = join(buildPackage.esm5OutputDir, secondaryEntryPoint); + + addIdToGlob(outputPath, entryPointId); + addIdToGlob(esm5OutputPath, entryPointId); + } +} + +/** Updates exports in designated folder with identifier specified. */ +function addIdToGlob(outputPath: string, entryPointId: number): void { + glob(join(outputPath, '**/*.+(js|d.ts|metadata.json)')).forEach(filePath => { + let fileContent = readFileSync(filePath, 'utf-8'); + fileContent = fileContent.replace(/(ɵ[a-z]+)/g, `$1${entryPointId}`); + writeFileSync(filePath, fileContent, 'utf-8'); + }); +} diff --git a/tools/package-tools/copy-files.ts b/tools/package-tools/copy-files.ts new file mode 100644 index 000000000..c9baf5fe0 --- /dev/null +++ b/tools/package-tools/copy-files.ts @@ -0,0 +1,12 @@ +import {sync as glob} from 'glob'; +import {mkdirpSync, copySync} from 'fs-extra'; +import {join, dirname} from 'path'; + +/** Function to copy files that match a glob to another directory. */ +export function copyFiles(fromPath: string, fileGlob: string, outDir: string) { + glob(fileGlob, {cwd: fromPath}).forEach(filePath => { + const fileDestPath = join(outDir, filePath); + mkdirpSync(dirname(fileDestPath)); + copySync(join(fromPath, filePath), fileDestPath); + }); +} diff --git a/tools/package-tools/entry-point-package-json.ts b/tools/package-tools/entry-point-package-json.ts new file mode 100644 index 000000000..fb8cc1e9b --- /dev/null +++ b/tools/package-tools/entry-point-package-json.ts @@ -0,0 +1,17 @@ +import {join} from 'path'; +import {writeFileSync} from 'fs'; +import {buildConfig} from './build-config'; + +/** Creates a package.json for a secondary entry-point with the different bundle locations. */ +export function createEntryPointPackageJson(destDir: string, packageName: string, + entryPointName: string) { + const content = { + name: `@${buildConfig.namespace}/${packageName}/${entryPointName}`, + typings: `../${entryPointName}.d.ts`, + main: `../bundles/${packageName}-${entryPointName}.umd.js`, + module: `../esm5/${entryPointName}.es5.js`, + es2015: `../esm2015/${entryPointName}.js`, + }; + + writeFileSync(join(destDir, 'package.json'), JSON.stringify(content, null, 2), 'utf-8'); +} diff --git a/tools/package-tools/index.ts b/tools/package-tools/index.ts index b3f792304..24ac24c9f 100644 --- a/tools/package-tools/index.ts +++ b/tools/package-tools/index.ts @@ -1,3 +1,7 @@ // Expose general package utilities. export * from './build-config'; +export * from './build-bundles'; +export * from './build-release'; export * from './build-package'; +export * from './copy-files'; +export * from './packages'; diff --git a/tools/package-tools/metadata-inlining.ts b/tools/package-tools/metadata-inlining.ts new file mode 100644 index 000000000..2760652af --- /dev/null +++ b/tools/package-tools/metadata-inlining.ts @@ -0,0 +1,57 @@ +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. + * Note: this assumes that all html and css files have a unique name. + */ +export function inlineMetadataResources(metadata: any, componentResources: Map) { + // Convert `templateUrl` to `template` + if (metadata.templateUrl) { + const fullResourcePath = componentResources.get(basename(metadata.templateUrl)); + metadata.template = readFileSync(fullResourcePath!, 'utf-8'); + delete metadata.templateUrl; + } + + // Convert `styleUrls` to `styles` + if (metadata.styleUrls && metadata.styleUrls.length) { + metadata.styles = []; + for (const styleUrl of metadata.styleUrls) { + const fullResourcePath = componentResources.get(basename(styleUrl)); + metadata.styles.push(readFileSync(fullResourcePath!, 'utf-8')); + } + delete metadata.styleUrls; + } + + // We we did nothing at this node, go deeper. + if (!metadata.template && !metadata.styles) { + for (const property in metadata) { + if (typeof metadata[property] == 'object' && metadata[property]) { + inlineMetadataResources(metadata[property], componentResources); + } + } + } +} + + +/** 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(); + + 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/package-tools/metadata-reexport.ts b/tools/package-tools/metadata-reexport.ts new file mode 100644 index 000000000..adf3b6b92 --- /dev/null +++ b/tools/package-tools/metadata-reexport.ts @@ -0,0 +1,19 @@ +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(destDir: string, from: string|string[], + entryPointName: string, importAsName: string) { + from = Array.isArray(from) ? from : [from]; + + const metadataJsonContent = JSON.stringify({ + __symbolic: 'module', + version: 3, + metadata: {}, + exports: from.map(f => ({from: f})), + flatModuleIndexRedirect: true, + importAs: importAsName + }, null, 2); + + writeFileSync(join(destDir, `${entryPointName}.metadata.json`), metadataJsonContent, 'utf-8'); +} diff --git a/tools/package-tools/minify-sources.ts b/tools/package-tools/minify-sources.ts new file mode 100644 index 000000000..c4de6e46a --- /dev/null +++ b/tools/package-tools/minify-sources.ts @@ -0,0 +1,21 @@ +import {writeFileSync} from 'fs'; +import {basename} from 'path'; + +// 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 sourceMapPath = `${outputPath}.map`; + + const result = uglify.minify(inputPath, { + inSourceMap: `${inputPath}.map`, + outSourceMap: basename(sourceMapPath), + output: { + comments: 'some' + } + }); + + writeFileSync(outputPath, result.code); + writeFileSync(sourceMapPath, result.map); +} diff --git a/tools/package-tools/ngc-compile.ts b/tools/package-tools/ngc-compile.ts new file mode 100644 index 000000000..77d0ae9f1 --- /dev/null +++ b/tools/package-tools/ngc-compile.ts @@ -0,0 +1,20 @@ +import {resolve as resolvePath} from 'path'; +import {spawn} from 'child_process'; +import {red} from 'chalk'; + +/** + * Spawns a child process that compiles using ngc. + * @param flags Command-line flags to be passed to ngc. + * @returns Promise that resolves/rejects when the child process exits. + */ +export function ngcCompile(flags: string[]) { + return new Promise((resolve, reject) => { + const ngcPath = resolvePath('./node_modules/.bin/ngc'); + const childProcess = spawn(ngcPath, flags, {shell: true}); + + // Pipe stdout and stderr from the child process. + childProcess.stdout.on('data', (data: string|Buffer) => console.log(`${data}`)); + childProcess.stderr.on('data', (data: string|Buffer) => console.error(red(`${data}`))); + childProcess.on('exit', (exitCode: number) => exitCode === 0 ? resolve() : reject()); + }); +} diff --git a/tools/package-tools/packages.ts b/tools/package-tools/packages.ts new file mode 100644 index 000000000..2b70330ca --- /dev/null +++ b/tools/package-tools/packages.ts @@ -0,0 +1,15 @@ +import {BuildPackage} from './build-package'; + +const commonPackage = new BuildPackage('common', []); +const aspnetcoreEnginePackage = new BuildPackage('aspnetcore-engine', [commonPackage]); +const expressEnginePackage = new BuildPackage('express-engine', [commonPackage]); +const hapiEnginePackage = new BuildPackage('hapi-engine', [commonPackage]); +const moduleMapNgfactoryLoaderPackage = new BuildPackage('module-map-ngfactory-loader', []); + +export const packages = [ + commonPackage, + aspnetcoreEnginePackage, + expressEnginePackage, + hapiEnginePackage, + moduleMapNgfactoryLoaderPackage, +]; diff --git a/tools/package-tools/rollup-globals.ts b/tools/package-tools/rollup-globals.ts index ba89890aa..dcc44e039 100644 --- a/tools/package-tools/rollup-globals.ts +++ b/tools/package-tools/rollup-globals.ts @@ -1,3 +1,70 @@ +import {join} from 'path'; +import {buildConfig} from './build-config'; +import {getSubdirectoryNames} from './secondary-entry-points'; + +/** Method that converts dash-case strings to a camel-based string. */ +export const dashCaseToCamelCase = + (str: string) => str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); + +/** List of potential secondary entry-points for the common package. */ +const commonSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, 'common')); + +/** Object with all common entry points in the format of Rollup globals. */ +const rollupCommonEntryPoints = commonSecondaryEntryPoints + .reduce((globals: any, entryPoint: string) => { + globals[`@${buildConfig.namespace}/common/${entryPoint}`] = + `${buildConfig.namespace}.common.${dashCaseToCamelCase(entryPoint)}`; + return globals; + }, {}); + +/** List of potential secondary entry-points for the aspnet core engine package. */ +const aspSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, + 'aspnetcore-engine')); + +/** Object with all aspnet core engine entry points in the format of Rollup globals. */ +const rollupAspEntryPoints = aspSecondaryEntryPoints + .reduce((globals: any, entryPoint: string) => { + globals[`@${buildConfig.namespace}/aspnetcore-engine/${entryPoint}`] = + `${buildConfig.namespace}.aspnetcoreEngine.${dashCaseToCamelCase(entryPoint)}`; + return globals; + }, {}); + +/** List of potential secondary entry-points for the express engine package. */ +const expressSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, + 'express-engine')); + +/** Object with all express engine entry points in the format of Rollup globals. */ +const rollupExpressEntryPoints = expressSecondaryEntryPoints + .reduce((globals: any, entryPoint: string) => { + globals[`@${buildConfig.namespace}/express-engine/${entryPoint}`] = + `${buildConfig.namespace}.expressEngine.${dashCaseToCamelCase(entryPoint)}`; + return globals; + }, {}); + +/** List of potential secondary entry-points for the hapi engine package. */ +const hapiSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, + 'hapi-engine')); + +/** Object with all hapi engine entry points in the format of Rollup globals. */ +const rollupHapiEntryPoints = hapiSecondaryEntryPoints + .reduce((globals: any, entryPoint: string) => { + globals[`@${buildConfig.namespace}/hapi-engine/${entryPoint}`] = + `${buildConfig.namespace}.hapiEngine.${dashCaseToCamelCase(entryPoint)}`; + return globals; + }, {}); + +/** List of potential secondary entry-points for the hapi engine package. */ +const moduleMapSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, + 'module-map-ngfactory-loader')); + +/** Object with all hapi engine entry points in the format of Rollup globals. */ +const rollupModuleMapEntryPoints = moduleMapSecondaryEntryPoints + .reduce((globals: any, entryPoint: string) => { + globals[`@${buildConfig.namespace}/module-map-ngfactory-loader/${entryPoint}`] = + `${buildConfig.namespace}.moduleMapNgfactoryLoader.${dashCaseToCamelCase(entryPoint)}`; + return globals; + }, {}); + /** Map of globals that are used inside of the different packages. */ export const rollupGlobals = { // The key here is library name, and the value is the the name of the global variable name @@ -23,5 +90,17 @@ export const rollupGlobals = { 'rxjs/observable/of': 'Rx.Observable', 'fs': 'fs', 'express': 'express', - 'hapi': 'hapi' + 'hapi': 'hapi', + + '@nguniversal/aspnetcore-engine': 'nguniversal.aspnetcoreEngine', + '@nguniversal/common': 'nguniversal.common', + '@nguniversal/express-engine': 'nguniversal.expressEngine', + '@nguniversal/hapi-engine': 'nguniversal.hapiEngine', + '@nguniversal/module-map-ngfactory-loader': 'nguniversal.moduleMapNgfactoryLoader', + + ...rollupAspEntryPoints, + ...rollupCommonEntryPoints, + ...rollupExpressEntryPoints, + ...rollupHapiEntryPoints, + ...rollupModuleMapEntryPoints, }; diff --git a/tools/package-tools/rollup-remove-licenses.ts b/tools/package-tools/rollup-remove-licenses.ts new file mode 100644 index 000000000..4ee720b43 --- /dev/null +++ b/tools/package-tools/rollup-remove-licenses.ts @@ -0,0 +1,26 @@ +import {buildConfig} from './build-config'; +import MagicString from 'magic-string'; + +/** License banner from the build config that will be removed in all source files. */ +const licenseBanner = buildConfig.licenseBanner; + +/** + * Rollup plugin that removes all license banners of source files. + * This is necessary to avoid having the license comment repeated in the output. + */ +export const rollupRemoveLicensesPlugin = { + name: 'rollup-clean-duplicate-licenses', + transform: (code: string) => { + const newContent = new MagicString(code); + + // Walks through every occurrence of a license comment and overwrites it with an empty string. + for (let pos = -1; (pos = code.indexOf(licenseBanner, pos + 1)) !== -1; null) { + newContent.overwrite(pos, pos + licenseBanner.length, ''); + } + + return { + code: newContent.toString(), + map: newContent.generateMap({ hires: true }) + }; + } +}; diff --git a/tools/package-tools/secondary-entry-points.ts b/tools/package-tools/secondary-entry-points.ts new file mode 100644 index 000000000..bbebf825f --- /dev/null +++ b/tools/package-tools/secondary-entry-points.ts @@ -0,0 +1,132 @@ +import {join} from 'path'; +import {platform} from 'os'; +import {readdirSync, lstatSync, existsSync} from 'fs'; +import {spawnSync} from 'child_process'; + +import {BuildPackage} from './build-package'; +import {buildConfig} from './build-config'; + + +/** + * Gets secondary entry-points for a given package in the order they should be built. + * + * This currently assumes that every directory under a package should be an entry-point except for + * specifically black-listed directories. + * + * @param pkg The package for which to get entry points, e.g., 'cdk'. + * @returns An array of secondary entry-points names, e.g., ['a11y', 'bidi', ...] + */ +export function getSecondaryEntryPointsForPackage(pkg: BuildPackage) { + const packageName = pkg.name; + const packageDir = pkg.sourceDir; + + // Get the list of all entry-points as the list of directories in the package that have a + // tsconfig-build.json + const entryPoints = getSubdirectoryNames(packageDir) + .filter(d => existsSync(join(packageDir, d, 'tsconfig.lib.json'))); + + // Create nodes that comprise the build graph. + const buildNodes: BuildNode[] = entryPoints.map(p => ({name: p, deps: [], depth: 0})); + + // Create a lookup for name -> build graph node. + const nodeLookup = buildNodes.reduce((lookup, node) => { + return lookup.set(node.name, node); + }, new Map()); + + // Regex used to extract entry-point name from an import statement referencing that entry-point. + // E.g., extract "portal" from "from '@angular/cdk/portal';". + const importRegex = new RegExp(`${packageName}/(.+)';`); + + // Update the deps for each node to point to the appropriate BuildNodes. + buildNodes.forEach(node => { + const importStatementFindCommand = buildPackageImportStatementFindCommand( + join(packageDir, node.name), packageName); + + // Look for any imports that reference this same umbrella package and get the corresponding + // BuildNode for each by looking at the import statements with grep. + node.deps = spawnSync(importStatementFindCommand.binary, importStatementFindCommand.args) + .stdout + .toString() + .split('\n') + .filter(n => n) + .map(importStatement => importStatement.match(importRegex)![1]) + .filter(n => nodeLookup.has(n) && n !== node.name) + .map(depName => nodeLookup.get(depName)!) || []; + }); + + // Concatenate the build order for each node into one global build order. + // Duplicates are automatically omitted by getBuildOrder. + const buildOrder = buildNodes.reduce((order: BuildNode[], node) => { + return [...order, ...getBuildOrder(node)]; + }, []); + + return partitionNodesByDepth(buildOrder).map(level => level.map(node => node.name)); +} + +/** Gets the build order for node with DFS. As a side-effect, sets the depth on visited nodes. */ +function getBuildOrder(node: BuildNode): BuildNode[] { + if (node.visited) { + return []; + } + + let buildOrder: BuildNode[] = []; + for (const dep of node.deps) { + buildOrder = [...buildOrder, ...getBuildOrder(dep)]; + node.depth = node.deps.reduce((maxDepth, d) => Math.max(d.depth + 1, maxDepth), -1); + } + + node.visited = true; + return [...buildOrder, node]; +} + +/** Gets the names of all subdirectories for a given path. */ +export function getSubdirectoryNames(dir: string): string[] { + return readdirSync(dir).filter(f => lstatSync(join(dir, f)).isDirectory()); +} + +/** A node in the build graph of a package's entry-points. */ +interface BuildNode { + name: string; + deps: BuildNode[]; + visited?: boolean; + depth: number; +} + + +/** + * `Partitions nodes into groups by depth. For example, + * [{name: a11y, depth: 1}, {name: bidi, depth: 0}, {name: platform, depth: 0}] + * => + * [ [{name: bidi, depth: 0}, {name: platform, depth: 0}], [{name: a11y, depth: 1}] ]` + */ +function partitionNodesByDepth(nodes: BuildNode[]): BuildNode[][] { + const result: BuildNode[][] = [[]]; + let lastDepth = 0; + + nodes.sort((a, b) => a.depth - b.depth).forEach(node => { + if (node.depth === lastDepth) { + result[result.length - 1].push(node); + } else { + result.push([node]); + lastDepth = node.depth; + } + }); + + return result; +} + +/** Builds the command that will be executed to find all import statements for a package. */ +function buildPackageImportStatementFindCommand(searchDirectory: string, packageName: string) { + if (platform() === 'win32') { + return { + binary: 'findstr', + args: ['/r', `from.'@${buildConfig.namespace}/${packageName}/.*'`, `${searchDirectory}\\*.ts`] + }; + } else { + return { + binary: 'grep', + args: ['-Eroh', '--include', '*.ts', `from '@${buildConfig.namespace}/${packageName}/.+';`, + searchDirectory] + }; + } +} diff --git a/tools/package-tools/sourcemap-remap.ts b/tools/package-tools/sourcemap-remap.ts new file mode 100644 index 000000000..cc9a7b912 --- /dev/null +++ b/tools/package-tools/sourcemap-remap.ts @@ -0,0 +1,11 @@ +// 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/package-tools/typings-reexport.ts b/tools/package-tools/typings-reexport.ts new file mode 100644 index 000000000..523fa7430 --- /dev/null +++ b/tools/package-tools/typings-reexport.ts @@ -0,0 +1,11 @@ +import {writeFileSync} from 'fs'; +import {join} from 'path'; + +import {buildConfig} from './build-config'; + +/** Create a typing file that links to the bundled definitions of NGC. */ +export function createTypingsReexportFile(outDir: string, from: string, fileName: string) { + writeFileSync(join(outDir, `${fileName}.d.ts`), + `${buildConfig.licenseBanner}\nexport * from '${from}';\n`, + 'utf-8'); +} diff --git a/tools/package-tools/version-placeholders.ts b/tools/package-tools/version-placeholders.ts new file mode 100644 index 000000000..f91b8e942 --- /dev/null +++ b/tools/package-tools/version-placeholders.ts @@ -0,0 +1,60 @@ +import {readFileSync, writeFileSync} from 'fs'; +import {platform} from 'os'; +import {buildConfig} from './build-config'; +import {spawnSync} from 'child_process'; + +/** Variable that is set to the string for version placeholders. */ +const versionPlaceholderText = '0.0.0-PLACEHOLDER'; + +/** Placeholder that will be replaced with the required Angular version. */ +const ngVersionPlaceholderText = '0.0.0-NG'; + +/** RegExp that matches version placeholders inside of a file. */ +const ngVersionPlaceholderRegex = new RegExp(ngVersionPlaceholderText, 'g'); + +/** Expression that matches Angular version placeholders within a file. */ +const versionPlaceholderRegex = new RegExp(versionPlaceholderText, 'g'); + +/** + * Walks through every file in a directory and replaces the version placeholders with the current + * version of Library. + */ +export function replaceVersionPlaceholders(packageDir: string) { + // Resolve files that contain version placeholders using Grep or Findstr since those are + // extremely fast and also have a very simple usage. + const files = findFilesWithPlaceholders(packageDir); + + // Walk through every file that contains version placeholders and replace those with the current + // version of the root package.json file. + files.forEach(filePath => { + const fileContent = readFileSync(filePath, 'utf-8') + .replace(ngVersionPlaceholderRegex, buildConfig.angularVersion) + .replace(versionPlaceholderRegex, buildConfig.projectVersion); + + writeFileSync(filePath, fileContent); + }); +} + +/** Finds all files in the specified package dir where version placeholders are included. */ +function findFilesWithPlaceholders(packageDir: string): string[] { + const findCommand = buildPlaceholderFindCommand(packageDir); + return spawnSync(findCommand.binary, findCommand.args).stdout + .toString() + .split(/[\n\r]/) + .filter(String); +} + +/** Builds the command that will be executed to find all files containing version placeholders. */ +function buildPlaceholderFindCommand(packageDir: string) { + if (platform() === 'win32') { + return { + binary: 'findstr', + args: ['/msi', `${ngVersionPlaceholderText} ${versionPlaceholderText}`, `${packageDir}\\*`] + }; + } else { + return { + binary: 'grep', + args: ['-ril', `${ngVersionPlaceholderText}\\|${versionPlaceholderText}`, packageDir] + }; + } +} diff --git a/tsconfig.json b/tsconfig.json index f6713a73a..5ab35bcd0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,14 @@ ], "noStrictGenericChecks": true, "baseUrl": "./", - "rootDir": "./" + "rootDir": "./", + "paths": { + "@nguniversal/common/*": ["./modules/common/*"], + "@nguniversal/aspnetcore-engine/*": ["./modules/aspnetcore-engine/*"], + "@nguniversal/express-engine/*": ["./modules/express-engine/*"], + "@nguniversal/hapi-engine/*": ["./modules/hapi-engine/*"], + "@nguniversal/module-map-ngfactory-loader/*": ["./modules/module-map-ngfactory-loader/*"] + } }, "exclude": [ "bazel-out", diff --git a/yarn.lock b/yarn.lock index 03761ef2b..6f8214575 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,15 +2,15 @@ # yarn lockfile v1 -"@angular/animations@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-6.0.0-beta.6.tgz#e999618ff383eaef3857ad0be3d8146354a750da" +"@angular/animations@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-6.0.0-beta.7.tgz#adbefae905eb5bdda5d9365f708736be19a973e5" dependencies: tslib "^1.7.1" -"@angular/bazel@github:angular/bazel-builds#144cd753ff9f0fbd085d1ed695b4d70335877892": - version "6.0.0-beta.7-b5be18f" - resolved "https://codeload.github.com/angular/bazel-builds/tar.gz/144cd753ff9f0fbd085d1ed695b4d70335877892" +"@angular/bazel@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/bazel/-/bazel-6.0.0-beta.7.tgz#16d279236c3b64b90abbf7f16a31f60c24a17224" dependencies: "@bazel/typescript" "^0.11.1" "@types/node" "6.0.84" @@ -18,54 +18,54 @@ protobufjs "5.0.0" shelljs "0.7.8" -"@angular/common@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-6.0.0-beta.6.tgz#fc13994b9b1315b310d806a82267099c065c3ed2" +"@angular/common@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-6.0.0-beta.7.tgz#7fa99f39dce3ec2a75bebee46fe79f8f91d0628f" dependencies: tslib "^1.7.1" -"@angular/compiler-cli@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-6.0.0-beta.6.tgz#16df2892af38f72449a1d011f59398602bb115f4" +"@angular/compiler-cli@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-6.0.0-beta.7.tgz#a1f18415b78c887c25691b6200b84210d53c7f59" dependencies: chokidar "^1.4.2" minimist "^1.2.0" reflect-metadata "^0.1.2" tsickle "^0.27.2" -"@angular/compiler@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-6.0.0-beta.6.tgz#d0603d09393404f4bcf93f829be2487dadd82b4a" +"@angular/compiler@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-6.0.0-beta.7.tgz#220d8b98de7cc8db2f8abdfd4671bb5bec6b6684" dependencies: tslib "^1.7.1" -"@angular/core@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.0-beta.6.tgz#80107291012e817a4ed5f75e3a5baeaf67c96519" +"@angular/core@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.0-beta.7.tgz#8d080dbc3f078a08dc65608083078587ab67d575" dependencies: tslib "^1.7.1" -"@angular/http@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/http/-/http-6.0.0-beta.6.tgz#6dbb4f56855fa35c56289d855e399ac57b12d8a4" +"@angular/http@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-6.0.0-beta.7.tgz#5a34ea5b61bd36b6541c96e7fe7b6adaeff492e4" dependencies: tslib "^1.7.1" -"@angular/platform-browser-dynamic@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.0-beta.6.tgz#4bce23007101bde28071b461be0c87862e7a5c0c" +"@angular/platform-browser-dynamic@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.0-beta.7.tgz#8b2ae57d9b6b3d4c852ee95e7e8819521cdd2623" dependencies: tslib "^1.7.1" -"@angular/platform-browser@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-6.0.0-beta.6.tgz#7d6cbe1f949aa70cfd956c55cb42877f3eacf5a7" +"@angular/platform-browser@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-6.0.0-beta.7.tgz#252997b7bdc26039a9f07fd6ae5bfbddc13b24d2" dependencies: tslib "^1.7.1" -"@angular/platform-server@6.0.0-beta.6": - version "6.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-6.0.0-beta.6.tgz#c214b53c95c9ec7633ee3e7c3ec1c60225178e3b" +"@angular/platform-server@6.0.0-beta.7": + version "6.0.0-beta.7" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-6.0.0-beta.7.tgz#68260800284388ae2ef965153945aa43f893ed1d" dependencies: domino "^2.0.1" tslib "^1.7.1" @@ -115,6 +115,12 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" +"@types/fs-extra@^4.0.5": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.8.tgz#6957ddaf9173195199cb96da3db44c74700463d2" + dependencies: + "@types/node" "*" + "@types/glob@*": version "5.0.35" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" @@ -803,6 +809,10 @@ browserify@^14.5.0: vm-browserify "~0.0.1" xtend "^4.0.0" +buffer-crc32@^0.2.5: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + buffer-more-ints@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" @@ -1680,6 +1690,10 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es6-promise@^3.1.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + es6-promise@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" @@ -1987,6 +2001,14 @@ fs-access@^1.0.0: dependencies: null-check "^1.0.0" +fs-extra@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2163,7 +2185,7 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.2: +graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2810,6 +2832,12 @@ json-stringify-safe@5.0.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0. version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4361,11 +4389,9 @@ rollup-pluginutils@^2.0.1: estree-walker "^0.3.0" micromatch "^2.3.11" -rollup@~0.41.6: - version "0.41.6" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.41.6.tgz#e0d05497877a398c104d816d2733a718a7a94e2a" - dependencies: - source-map-support "^0.4.0" +rollup@^0.56.5: + version "0.56.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.56.5.tgz#40fe3cf0cd1659d469baad11f4d5b6336c14ce84" rxjs@^5.5.6: version "5.5.6" @@ -4377,6 +4403,15 @@ safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +sander@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad" + dependencies: + es6-promise "^3.1.2" + graceful-fs "^4.1.3" + mkdirp "^0.5.1" + rimraf "^2.5.2" + saucelabs@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.3.0.tgz#d240e8009df7fa87306ec4578a69ba3b5c424fee" @@ -4613,6 +4648,15 @@ socks@~1.1.5: ip "^1.1.4" smart-buffer "^1.0.13" +sorcery@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.10.0.tgz#8ae90ad7d7cb05fc59f1ab0c637845d5c15a52b7" + dependencies: + buffer-crc32 "^0.2.5" + minimist "^1.2.0" + sander "^0.5.0" + sourcemap-codec "^1.3.0" + source-map-resolve@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" @@ -4659,6 +4703,10 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" +sourcemap-codec@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2" + spdx-correct@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" @@ -5145,7 +5193,7 @@ typescript@~2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" -uglify-js@^2.6: +uglify-js@^2.6, uglify-js@^2.8.14: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: @@ -5185,6 +5233,10 @@ underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"