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)
This commit is contained in:
Paul Gschwendtner
2017-09-29 00:40:16 +02:00
committed by Jeremy Elbourn
parent 1ac3fd25c9
commit afa92a8d60
10 changed files with 88 additions and 60 deletions
+3
View File
@@ -0,0 +1,3 @@
@import './a11y';
@include cdk-a11y();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+1 -2
View File
@@ -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;
+5
View File
@@ -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;
+2 -2
View File
@@ -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
+12 -53
View File
@@ -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. */
+14
View File
@@ -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);
});
}
@@ -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');
});
}
}