diff --git a/src/cdk/testing/tsconfig-build.json b/src/cdk/testing/tsconfig-build.json deleted file mode 100644 index ecd051d8af9..00000000000 --- a/src/cdk/testing/tsconfig-build.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig-build", - "files": [ - "public_api.ts" - ], - "angularCompilerOptions": { - "annotateForClosureCompiler": true, - "strictMetadataEmit": true, - "flatModuleOutFile": "index.js", - "flatModuleId": "@angular/cdk/testing", - "skipTemplateCodegen": true - } -} diff --git a/tools/gulp/tsconfig.json b/tools/gulp/tsconfig.json index c74891af5ea..0387f0388d5 100644 --- a/tools/gulp/tsconfig.json +++ b/tools/gulp/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "experimentalDecorators": true, "noUnusedParameters": true, - "lib": ["es2015", "dom"], + "lib": ["es2015", "dom", "es2016.array.include"], "module": "commonjs", "moduleResolution": "node", "outDir": "../../dist/tools/gulp", diff --git a/tools/package-tools/build-package.ts b/tools/package-tools/build-package.ts index 39041c11b67..d90af089973 100644 --- a/tools/package-tools/build-package.ts +++ b/tools/package-tools/build-package.ts @@ -2,10 +2,8 @@ import {join} from 'path'; import {main as tsc} from '@angular/tsc-wrapped'; import {buildConfig} from './build-config'; import {getSecondaryEntryPointsForPackage} from './secondary-entry-points'; -import { - buildPrimaryEntryPointBundles, - buildSecondaryEntryPointBundles -} from 'material2-build-tools'; +import {buildPrimaryEntryPointBundles, buildSecondaryEntryPointBundles} from './build-bundles'; + const {packagesDir, outputDir} = buildConfig; @@ -23,9 +21,6 @@ export class BuildPackage { /** Path to the package output. */ packageOut: string; - /** Secondary entry points for the package. */ - secondaryEntryPoints: string[]; - /** Path to the entry file of the package in the output directory. */ private entryFilePath: string; @@ -35,6 +30,17 @@ export class BuildPackage { /** Path to the tsconfig file, which will be used to build the tests. */ private tsconfigTests: string; + /** Secondary entry points for the package. */ + get secondaryEntryPoints(): string[] { + if (!this._secondaryEntryPoints) { + this._secondaryEntryPoints = getSecondaryEntryPointsForPackage(this); + } + + return this._secondaryEntryPoints; + } + + private _secondaryEntryPoints: string[]; + constructor(public packageName: string, public dependencies: BuildPackage[] = []) { this.packageRoot = join(packagesDir, packageName); this.packageOut = join(outputDir, 'packages', packageName); @@ -43,8 +49,6 @@ export class BuildPackage { this.tsconfigTests = join(this.packageRoot, testsTsconfigName); this.entryFilePath = join(this.packageOut, 'index.js'); - - this.secondaryEntryPoints = getSecondaryEntryPointsForPackage(packageName); } /** Compiles the package sources with all secondary entry points. */ diff --git a/tools/package-tools/build-release.ts b/tools/package-tools/build-release.ts index cfc8f49e2b0..c9a3c9fdb52 100644 --- a/tools/package-tools/build-release.ts +++ b/tools/package-tools/build-release.ts @@ -47,7 +47,7 @@ export function composeRelease(buildPackage: BuildPackage) { function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePath: string) { const {packageName, packageOut} = buildPackage; - getSecondaryEntryPointsForPackage(packageName).forEach(entryPointName => { + getSecondaryEntryPointsForPackage(buildPackage).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 diff --git a/tools/package-tools/rollup-globals.ts b/tools/package-tools/rollup-globals.ts index cb1062375f1..aec25e1ae6f 100644 --- a/tools/package-tools/rollup-globals.ts +++ b/tools/package-tools/rollup-globals.ts @@ -1,10 +1,12 @@ -import {getSecondaryEntryPointsForPackage} from './secondary-entry-points'; +import {join} from 'path'; +import {getSubdirectoryNames} from './secondary-entry-points'; +import {buildConfig} from './build-config'; /** Method that converts dash-case strings to a camel-based string. */ const dashCaseToCamelCase = (str: string) => str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); -/** List of available secondary entry-points for the CDK package. */ -const cdkSecondaryEntryPoints = getSecondaryEntryPointsForPackage('cdk'); +/** List of potential secondary entry-points for the CDK package. */ +const cdkSecondaryEntryPoints = getSubdirectoryNames(join(buildConfig.packagesDir, 'cdk')); /** Object with all CDK entry points in the format of Rollup globals. */ const rollupCdkEntryPoints = cdkSecondaryEntryPoints.reduce((globals: any, entryPoint: string) => { diff --git a/tools/package-tools/secondary-entry-points.ts b/tools/package-tools/secondary-entry-points.ts index 4660b0e76f6..9d742a5538a 100644 --- a/tools/package-tools/secondary-entry-points.ts +++ b/tools/package-tools/secondary-entry-points.ts @@ -1,35 +1,87 @@ -/** - * List of cdk entry-points in the order that they must be built. This is necessary because - * some of the entry-points depend on each other. This is temporary until we switch to bazel. - */ -const CDK_ENTRY_POINTS = [ - 'coercion', - 'rxjs', - 'keyboard', - 'platform', - 'bidi', - 'table', - 'portal', - 'observe-content', - 'a11y', -]; +import {join} from 'path'; +import {readdirSync, lstatSync, existsSync} from 'fs'; +import {spawnSync} from 'child_process'; +import {BuildPackage} from './build-package'; + /** - * Gets secondary entry-points for a given package. + * 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. This may - * not always be desired, in which case we can add an extra build configuration for specifying the - * entry-points. + * This currently assumes that every directory under a package should be an entry-point except for + * specifically black-listed directories. * - * @param packageName The package name for which to get entry points, e.g., 'cdk'. + * @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(packageName: string) { - // For now, we hard-code the fact that only the CDK has secondary entry-points until we switch - // to bazel. - if (packageName === 'cdk') { - return CDK_ENTRY_POINTS; +export function getSecondaryEntryPointsForPackage(pkg: BuildPackage) { + const packageName = pkg.packageName; + const packageDir = pkg.packageRoot; + + // 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-build.json'))); + + // Create nodes that comprise the build graph. + const buildNodes: BuildNode[] = entryPoints.map(p => ({name: p, deps: []})); + + // 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 => { + // 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('egrep', [ + '-roh', + '--include', '*.ts', + `from.'@angular/${packageName}/.+';`, + `${packageDir}/${node.name}/` + ]) + .stdout + .toString() + .split('\n') + .filter(String) + .map(importStatement => importStatement.match(importRegex)![1]) + .map(depName => nodeLookup.get(depName)!) || []; + }); + + // Concatenate the build order for each node into one global build order. + // Duplicates are automatically omitted by getBuildOrder. + return buildNodes.reduce((order: string[], node) => { + return [...order, ...getBuildOrder(node)]; + }, []); +} + +/** Gets the build order for a given node with DFS. */ +function getBuildOrder(node: BuildNode): string[] { + if (node.visited) { + return []; } - return []; + let buildOrder: string[] = []; + for (const dep of node.deps) { + buildOrder = [...buildOrder, ...getBuildOrder(dep)]; + } + + node.visited = true; + return [...buildOrder, node.name]; +} + +/** 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; }