From afa92a8d6085efc9221dc389e995c743695b5d75 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 29 Sep 2017 00:40:16 +0200 Subject: [PATCH] build: copy stylesheets to release output (#7204) * Copies stylesheets of secondary entry-points to the root of the release output * Better deduping for the `_theming.scss` file (currently includes multiple `cdk-a11y` mixins) * Removes unused `cdk-a11y` import in the `button-toggle` component. * Moves some logic out of the `BuildPackage` class (in favor of readability) --- src/cdk/a11y/a11y-prebuilt.scss | 3 + src/lib/button/button.scss | 2 +- src/lib/chips/chips.scss | 2 +- src/lib/core/option/_option.scss | 2 +- src/lib/menu/menu.scss | 3 +- tools/gulp/packages.ts | 5 ++ tools/gulp/tasks/material-release.ts | 4 +- tools/package-tools/build-package.ts | 65 ++++------------------ tools/package-tools/build-release.ts | 14 +++++ tools/package-tools/compile-entry-point.ts | 48 ++++++++++++++++ 10 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 src/cdk/a11y/a11y-prebuilt.scss create mode 100644 tools/package-tools/compile-entry-point.ts diff --git a/src/cdk/a11y/a11y-prebuilt.scss b/src/cdk/a11y/a11y-prebuilt.scss new file mode 100644 index 00000000000..e30963e8ccf --- /dev/null +++ b/src/cdk/a11y/a11y-prebuilt.scss @@ -0,0 +1,3 @@ +@import './a11y'; + +@include cdk-a11y(); diff --git a/src/lib/button/button.scss b/src/lib/button/button.scss index 277c0be3f9a..f698b24bbee 100644 --- a/src/lib/button/button.scss +++ b/src/lib/button/button.scss @@ -1,8 +1,8 @@ // TODO(jelbourn): Measure perf benefits for translate3d and will-change. // TODO(jelbourn): Figure out if anchor hover underline actually happens in any browser. @import 'button-base'; -@import '../../cdk/a11y/a11y'; @import '../core/style/layout-common'; +@import '../../cdk/a11y/a11y'; .mat-button, .mat-icon-button { @extend %mat-button-base; diff --git a/src/lib/chips/chips.scss b/src/lib/chips/chips.scss index fc1b13a851b..ba5a529a376 100644 --- a/src/lib/chips/chips.scss +++ b/src/lib/chips/chips.scss @@ -1,5 +1,5 @@ -@import '../../cdk/a11y/a11y'; @import '../core/style/elevation'; +@import '../../cdk/a11y/a11y'; $mat-chip-vertical-padding: 7px; $mat-chip-horizontal-padding: 12px; diff --git a/src/lib/core/option/_option.scss b/src/lib/core/option/_option.scss index 6cbae7e330e..c87b65e598d 100644 --- a/src/lib/core/option/_option.scss +++ b/src/lib/core/option/_option.scss @@ -1,7 +1,7 @@ @import '../style/menu-common'; @import '../style/vendor-prefixes'; -@import '../../../cdk/a11y/a11y'; @import '../style/layout-common'; +@import '../../../cdk/a11y/a11y'; /** * This mixin contains shared option styles between the select and diff --git a/src/lib/menu/menu.scss b/src/lib/menu/menu.scss index 5fcc8a88965..b2173add544 100644 --- a/src/lib/menu/menu.scss +++ b/src/lib/menu/menu.scss @@ -1,10 +1,9 @@ // TODO(kara): update vars for desktop when MD team responds - @import '../core/style/button-common'; @import '../core/style/layout-common'; @import '../core/style/menu-common'; -@import '../../cdk/a11y/a11y'; @import '../core/style/layout-common'; +@import '../../cdk/a11y/a11y'; $mat-menu-vertical-padding: 8px !default; $mat-menu-border-radius: 2px !default; diff --git a/tools/gulp/packages.ts b/tools/gulp/packages.ts index 4186ee478d9..c65002dad84 100644 --- a/tools/gulp/packages.ts +++ b/tools/gulp/packages.ts @@ -12,3 +12,8 @@ materialPackage.exportsSecondaryEntryPointsAtRoot = true; // To avoid refactoring of the project the material package will map to the source path `lib/`. materialPackage.sourceDir = join(buildConfig.packagesDir, 'lib'); + +// Some CDK secondary entry-points include SCSS files that should be exposed individually at the +// release output root. This is different in the Material package because here a full SCSS bundle +// will be generated. +cdkPackage.copySecondaryEntryPointStylesToRoot = true; diff --git a/tools/gulp/tasks/material-release.ts b/tools/gulp/tasks/material-release.ts index 201ab1c1799..be3a259e1db 100644 --- a/tools/gulp/tasks/material-release.ts +++ b/tools/gulp/tasks/material-release.ts @@ -22,8 +22,8 @@ const themingEntryPointPath = join(sourceDir, 'core', 'theming', '_all-theme.scs const themingBundlePath = join(releasePath, '_theming.scss'); // Matches all pre-built theme css files const prebuiltThemeGlob = join(outputDir, '**/theming/prebuilt/*.css?(.map)'); -// Matches all SCSS files in the library. -const allScssGlob = join(sourceDir, '**/*.scss'); +// Matches all SCSS files in the different packages. +const allScssGlob = join(buildConfig.packagesDir, '**/*.scss'); /** * Overwrite the release task for the material package. The material release will include special diff --git a/tools/package-tools/build-package.ts b/tools/package-tools/build-package.ts index 381955c58f7..ba6b3227608 100644 --- a/tools/package-tools/build-package.ts +++ b/tools/package-tools/build-package.ts @@ -1,13 +1,9 @@ -import {red} from 'chalk'; -import {spawn} from 'child_process'; -import {readFileSync, writeFileSync} from 'fs'; -import {sync as glob} from 'glob'; -import {join, resolve as resolvePath} from 'path'; +import {join} from 'path'; import {main as ngc} from '@angular/tsc-wrapped'; import {PackageBundler} from './build-bundles'; import {buildConfig} from './build-config'; import {getSecondaryEntryPointsForPackage} from './secondary-entry-points'; - +import {compileEntryPoint, renamePrivateReExportsToBeUnique} from './compile-entry-point'; const {packagesDir, outputDir} = buildConfig; @@ -17,9 +13,6 @@ const buildTsconfigName = 'tsconfig-build.json'; /** Name of the tsconfig file that is responsible for building the tests. */ const testsTsconfigName = 'tsconfig-tests.json'; -/** Incrementing ID counter. */ -let nextId = 0; - export class BuildPackage { /** Path to the package sources. */ sourceDir: string; @@ -30,6 +23,9 @@ export class BuildPackage { /** Whether this package will re-export its secondary-entry points at the root module. */ exportsSecondaryEntryPointsAtRoot = false; + /** Whether the secondary entry-point styles should be copied to the release output. */ + copySecondaryEntryPointStylesToRoot = false; + /** Path to the entry file of the package in the output directory. */ readonly entryFilePath: string; @@ -39,22 +35,22 @@ export class BuildPackage { /** Path to the tsconfig file, which will be used to build the tests. */ private readonly tsconfigTests: string; - private _secondaryEntryPoints: string[]; - private _secondaryEntryPointsByDepth: 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 readonly bundler = new PackageBundler(this); + private _secondaryEntryPointsByDepth: string[][]; /** Secondary entry points for the package. */ get secondaryEntryPoints(): string[] { this.cacheSecondaryEntryPoints(); return this._secondaryEntryPoints; } + private _secondaryEntryPoints: string[]; constructor(public readonly name: string, public readonly dependencies: BuildPackage[] = []) { this.sourceDir = join(packagesDir, name); @@ -74,11 +70,11 @@ export class BuildPackage { // Depth 1: a11y, scrolling // Depth 2: overlay for (const entryPointGroup of this.secondaryEntryPointsByDepth) { - await Promise.all(entryPointGroup.map(p => this._compileEntryPoint(buildTsconfigName, p))); + await Promise.all(entryPointGroup.map(p => compileEntryPoint(this, buildTsconfigName, p))); } // Compile the primary entry-point. - await this._compileEntryPoint(buildTsconfigName); + await compileEntryPoint(this, buildTsconfigName); } /** Compiles the TypeScript test source files for the package. */ @@ -91,50 +87,13 @@ export class BuildPackage { await this.bundler.createBundles(); } - /** Compiles the TypeScript sources of a primary or secondary entry point. */ - private async _compileEntryPoint(tsconfigName: string, secondaryEntryPoint = '') { - const entryPointPath = join(this.sourceDir, secondaryEntryPoint); - const entryPointTsconfigPath = join(entryPointPath, tsconfigName); - - return new Promise((resolve, reject) => { - const ngcPath = resolvePath('./node_modules/.bin/ngc'); - const childProcess = spawn(ngcPath, ['-p', entryPointTsconfigPath], {shell: true}); - - // Pipe stdout and stderr from the child process. - childProcess.stdout.on('data', (data: any) => console.log(`${data}`)); - childProcess.stderr.on('data', (data: any) => console.error(red(`${data}`))); - - childProcess.on('exit', (exitCode: number) => exitCode === 0 ? resolve() : reject()); - }) - .catch(() => console.error(red(`Failed to compile ${secondaryEntryPoint}`))) - .then(() => this.renamePrivateReExportsToBeUnique(secondaryEntryPoint)); - } - /** Compiles the TypeScript sources of a primary or secondary entry point. */ private async _compileTestEntryPoint(tsconfigName: string, secondaryEntryPoint = '') { const entryPointPath = join(this.sourceDir, secondaryEntryPoint); const entryPointTsconfigPath = join(entryPointPath, tsconfigName); await ngc(entryPointTsconfigPath, {basePath: entryPointPath}); - this.renamePrivateReExportsToBeUnique(secondaryEntryPoint); - } - - /** Renames `ɵa`-style re-exports generated by Angular to be unique across compilation units. */ - private renamePrivateReExportsToBeUnique(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 (this.exportsSecondaryEntryPointsAtRoot && secondaryEntryPoint) { - const entryPointId = nextId++; - const outputPath = join(this.outputDir, secondaryEntryPoint); - 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'); - }); - } + renamePrivateReExportsToBeUnique(this, secondaryEntryPoint); } /** Stores the secondary entry-points for this package if they haven't been computed already. */ diff --git a/tools/package-tools/build-release.ts b/tools/package-tools/build-release.ts index 9ddb0150f53..6eb7991e686 100644 --- a/tools/package-tools/build-release.ts +++ b/tools/package-tools/build-release.ts @@ -54,6 +54,10 @@ export function composeRelease(buildPackage: BuildPackage) { createFilesForSecondaryEntryPoint(buildPackage, releasePath); } + if (buildPackage.copySecondaryEntryPointStylesToRoot) { + copySecondaryEntryPointStylesheets(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." @@ -102,3 +106,13 @@ function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePa createMetadataReexportFile(releasePath, `./${entryPointName}/index`, entryPointName); }); } + +/** Copies the stylesheets for secondary entry-points that generate one to the release output. */ +function copySecondaryEntryPointStylesheets(buildPackage: BuildPackage, releasePath: string) { + buildPackage.secondaryEntryPoints.forEach(entryPointName => { + const entryPointDir = join(buildPackage.outputDir, entryPointName); + + copyFiles(entryPointDir, `_${entryPointName}.scss`, releasePath); + copyFiles(entryPointDir, `${entryPointName}-prebuilt.css`, releasePath); + }); +} diff --git a/tools/package-tools/compile-entry-point.ts b/tools/package-tools/compile-entry-point.ts new file mode 100644 index 00000000000..40dd9b2d46b --- /dev/null +++ b/tools/package-tools/compile-entry-point.ts @@ -0,0 +1,48 @@ +import {join, resolve as resolvePath} from 'path'; +import {spawn} from 'child_process'; +import {writeFileSync, readFileSync} from 'fs'; +import {sync as glob} from 'glob'; +import {red} from 'chalk'; +import {BuildPackage} from './build-package'; + +/** 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 = '') { + const entryPointPath = join(buildPackage.sourceDir, secondaryEntryPoint); + const entryPointTsconfigPath = join(entryPointPath, tsconfigName); + + return new Promise((resolve, reject) => { + const ngcPath = resolvePath('./node_modules/.bin/ngc'); + const childProcess = spawn(ngcPath, ['-p', entryPointTsconfigPath], {shell: true}); + + // Pipe stdout and stderr from the child process. + childProcess.stdout.on('data', (data: any) => console.log(`${data}`)); + childProcess.stderr.on('data', (data: any) => console.error(red(`${data}`))); + + childProcess.on('exit', (exitCode: number) => exitCode === 0 ? resolve() : reject()); + }) + .catch(() => console.error(red(`Failed to compile ${secondaryEntryPoint}`))) + .then(() => renamePrivateReExportsToBeUnique(buildPackage, secondaryEntryPoint)); +} + +/** 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); + 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'); + }); + } +}