build: use shared logic to build packages (#4202)

* build: use shared logic to build packages

* No longer creates the whole set of gulp tasks just to build a single package.
* Makes the package building & releasing more consistent. No extra `build:release` just for the `lib` package.

References #4108

* Address feedback
This commit is contained in:
Paul Gschwendtner
2017-04-25 18:50:47 +02:00
committed by mmalerba
parent 14b330eecd
commit 2b8f753028
20 changed files with 164 additions and 232 deletions
+2 -2
View File
@@ -9,8 +9,8 @@
5. From the root of the project, run `npm install`.
To build the components in dev mode, run `gulp build:components`.
To build the components in release mode, run `gulp build:release`
To build Material in dev mode, run `gulp material:build`.
To build Material in release mode, run `gulp material:build-release`
To bring up a local server, run `gulp serve:devapp`. This will automatically watch for changes
and rebuild. The browser should refresh automatically when changes are made.
+1 -1
View File
@@ -8,7 +8,7 @@
"url": "https://github.com/angular/material2.git"
},
"scripts": {
"build": "gulp build:release",
"build": "gulp material:build-release:clean",
"demo-app": "gulp serve:devapp",
"test": "gulp test",
"tslint": "gulp lint",
@@ -9,8 +9,8 @@ set -e -o pipefail
cd $(dirname $0)/../..
# Build a release of the library and of the CDK package.
$(npm bin)/gulp build:release
# Build a release of material and of the CDK package.
$(npm bin)/gulp material:build-release:clean
$(npm bin)/gulp cdk:build-release
# Build demo-app with ES2015 modules. Closure compiler is then able to parse imports.
+1 -1
View File
@@ -21,7 +21,7 @@ repoUrl="https://github.com/angular/material2-builds.git"
repoDir="tmp/$repoName"
# Create a release of the current repository.
$(npm bin)/gulp build:release
$(npm bin)/gulp material:build-release:clean
# Prepare cloning the builds repository
rm -rf $repoDir
+1 -1
View File
@@ -7,7 +7,7 @@
"experimentalDecorators": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../dist/packages/examples",
"outDir": "../../dist/packages/material-examples",
"rootDir": ".",
"sourceMap": true,
"inlineSources": true,
+9 -4
View File
@@ -1,3 +1,10 @@
import {createPackageBuildTasks} from './util/package-tasks';
/** Create gulp tasks to build the different packages in the project. */
createPackageBuildTasks('cdk');
createPackageBuildTasks('material', ['cdk']);
createPackageBuildTasks('material-examples', ['material']);
import './tasks/ci';
import './tasks/clean';
import './tasks/default';
@@ -5,12 +12,10 @@ import './tasks/development';
import './tasks/docs';
import './tasks/e2e';
import './tasks/lint';
import './tasks/release';
import './tasks/publish';
import './tasks/screenshots';
import './tasks/unit-test';
import './tasks/aot';
import './tasks/payload';
import './tasks/coverage';
import './tasks/library';
import './tasks/examples';
import './tasks/cdk';
import './tasks/material-release';
+2 -2
View File
@@ -6,10 +6,10 @@ import {join} from 'path';
const tsconfigFile = join(DIST_DEMOAPP, 'tsconfig-aot.json');
/** Builds the demo-app and library. To be able to run NGC, apply the metadata workaround. */
/** Builds the demo-app and material. To be able to run NGC, apply the metadata workaround. */
task('aot:deps', sequenceTask(
'build:devapp',
[':package:release', 'cdk:build-release'],
['material:build-release', 'cdk:build-release'],
'aot:copy-release'
));
-56
View File
@@ -1,56 +0,0 @@
import {task, watch} from 'gulp';
import {join} from 'path';
import {main as tsc} from '@angular/tsc-wrapped';
import {SOURCE_ROOT, DIST_CDK} from '../constants';
import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from '../util/task_helpers';
import {buildPackageBundles, composeRelease} from '../util/package-build';
// There are no type definitions available for these imports.
const inlineResources = require('../../../scripts/release/inline-resources');
const cdkRoot = join(SOURCE_ROOT, 'cdk');
const tsconfigBuild = join(cdkRoot, 'tsconfig-build.json');
const tsconfigTests = join(cdkRoot, 'tsconfig-tests.json');
// Paths to the different output files and directories.
const esmMainFile = join(DIST_CDK, 'index.js');
task('cdk:clean-build', sequenceTask('clean', 'cdk:build'));
task('cdk:build', sequenceTask(
['cdk:build:esm', 'cdk:assets'],
// Inline assets into ESM output.
'cdk:assets:inline',
// Build bundles on top of inlined ESM output.
'cdk:build:bundles',
));
task('cdk:build-release', ['cdk:build'], () => composeRelease('cdk'));
task('cdk:build-release:clean', sequenceTask('clean', 'cdk:build-release'));
/** [Watch task] Rebuilds the CDK whenever TS, SCSS, or HTML files change. */
task('cdk:watch', () => {
watch(join(cdkRoot, '**/*.ts'), ['cdk:build', triggerLivereload]);
watch(join(cdkRoot, '**/*.scss'), ['cdk:build', triggerLivereload]);
watch(join(cdkRoot, '**/*.html'), ['cdk:build', triggerLivereload]);
});
/**
* TypeScript compilation tasks. Tasks are creating ESM, FESM, UMD bundles for releases.
*/
task('cdk:build:esm', () => tsc(tsconfigBuild, {basePath: cdkRoot}));
task('cdk:build:bundles', () => buildPackageBundles(esmMainFile, 'cdk'));
task('cdk:build:esm:tests', () => tsc(tsconfigTests, {basePath: cdkRoot}));
/**
* Asset tasks. Building SASS files and inlining CSS, HTML files into the ESM output.
*/
task('cdk:assets', ['cdk:assets:scss', 'cdk:assets:html']);
task('cdk:assets:scss', sassBuildTask(DIST_CDK, cdkRoot, true));
task('cdk:assets:html', copyTask(join(cdkRoot, '**/*.+(html|scss)'), DIST_CDK));
task('cdk:assets:inline', () => inlineResources(DIST_CDK));
+1 -1
View File
@@ -26,5 +26,5 @@ task('build:devapp', buildAppTask('devapp'));
task(':serve:devapp', serverTask(outDir, true));
task('serve:devapp', ['build:devapp'], sequenceTask(
[':serve:devapp', 'library:watch', ':watch:devapp']
[':serve:devapp', 'material:watch', ':watch:devapp']
));
+1 -1
View File
@@ -48,7 +48,7 @@ task('serve:e2eapp', sequenceTask('build:e2eapp', ':serve:e2eapp'));
* [Watch task] Builds and serves e2e app, rebuilding whenever the sources change.
* This should only be used when running e2e tests locally.
*/
task('serve:e2eapp:watch', ['serve:e2eapp', 'library:watch', ':watch:e2eapp']);
task('serve:e2eapp:watch', ['serve:e2eapp', 'material:watch', ':watch:e2eapp']);
/**
* Builds and serves the e2e-app and runs protractor once the e2e-app is ready.
-46
View File
@@ -1,46 +0,0 @@
import {task} from 'gulp';
import {join} from 'path';
import {main as tsc} from '@angular/tsc-wrapped';
import {SOURCE_ROOT, DIST_EXAMPLES} from '../constants';
import {sequenceTask, copyTask} from '../util/task_helpers';
import {buildPackageBundles, composeRelease} from '../util/package-build';
// There are no type definitions available for these imports.
const inlineResources = require('../../../scripts/release/inline-resources');
const examplesRoot = join(SOURCE_ROOT, 'material-examples');
const tsconfigPath = join(examplesRoot, 'tsconfig-build.json');
// Paths to the different output files and directories.
const examplesOut = DIST_EXAMPLES;
const examplesMain = join(examplesOut, 'index.js');
task('examples:clean-build', sequenceTask('clean', 'examples:build'));
task('examples:build', sequenceTask(
// The examples depend on the library. Build the library first.
'library:build',
// Build ESM and copy HTML assets to the dist.
['examples:build:esm', 'examples:assets:html'],
// Inline assets into ESM output.
'examples:assets:inline',
// Build bundles on top of inlined ESM output.
'examples:build:bundles',
));
task('examples:build-release', ['examples:build'], () => composeRelease('material-examples'));
task('examples:build-release:clean', sequenceTask('clean', 'examples:build-release'));
/**
* TypeScript compilation tasks. Tasks are creating ESM, FESM, UMD bundles for releases.
*/
task('examples:build:esm', () => tsc(tsconfigPath, {basePath: examplesRoot}));
task('examples:build:bundles', () => buildPackageBundles(examplesMain, 'material-examples'));
/**
* Asset tasks. Copying and inlining CSS, HTML files into the ESM output.
*/
task('examples:assets:html', copyTask(join(examplesRoot, '**/*.+(html|css)'), examplesOut));
task('examples:assets:inline', () => inlineResources(examplesOut));
-56
View File
@@ -1,56 +0,0 @@
import {task, watch} from 'gulp';
import {join} from 'path';
import {main as tsc} from '@angular/tsc-wrapped';
import {SOURCE_ROOT, DIST_MATERIAL} from '../constants';
import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from '../util/task_helpers';
import {buildPackageBundles} from '../util/package-build';
// There are no type definitions available for these imports.
const inlineResources = require('../../../scripts/release/inline-resources');
const libraryRoot = join(SOURCE_ROOT, 'lib');
const tsconfigBuild = join(libraryRoot, 'tsconfig-build.json');
const tsconfigTests = join(libraryRoot, 'tsconfig-tests.json');
// Paths to the different output files and directories.
const materialDir = DIST_MATERIAL;
const esmMainFile = join(materialDir, 'index.js');
task('library:clean-build', sequenceTask('clean', 'library:build'));
task('library:build', sequenceTask(
// Library depends on the CDK package. Build the CDK package first.
'cdk:build',
// Build assets and ESM output concurrently.
['library:build:esm', 'library:assets'],
// Inline assets into ESM output.
'library:assets:inline',
// Build bundles on top of inlined ESM output.
'library:build:bundles',
));
/** [Watch task] Rebuilds the library whenever TS, SCSS, or HTML files change. */
task('library:watch', () => {
watch(join(libraryRoot, '**/*.ts'), ['library:build', triggerLivereload]);
watch(join(libraryRoot, '**/*.scss'), ['library:build', triggerLivereload]);
watch(join(libraryRoot, '**/*.html'), ['library:build', triggerLivereload]);
});
/**
* TypeScript compilation tasks. Tasks are creating ESM, FESM, UMD bundles for releases.
*/
task('library:build:esm', () => tsc(tsconfigBuild, {basePath: libraryRoot}));
task('library:build:bundles', () => buildPackageBundles(esmMainFile, 'material'));
task('library:build:esm:tests', () => tsc(tsconfigTests, {basePath: libraryRoot}));
/**
* Asset tasks. Building SaSS files and inlining CSS, HTML files into the ESM output.
*/
task('library:assets', ['library:assets:scss', 'library:assets:html']);
task('library:assets:scss', sassBuildTask(materialDir, libraryRoot, true));
task('library:assets:html', copyTask(join(libraryRoot, '**/*.+(html|scss)'), materialDir));
task('library:assets:inline', () => inlineResources(materialDir));
+1 -1
View File
@@ -5,7 +5,7 @@ import {DIST_MATERIAL} from '../constants';
gulp.task('lint', ['tslint', 'stylelint', 'madge', 'dashboardlint']);
/** Task that runs madge to detect circular dependencies. */
gulp.task('madge', ['library:clean-build'], execNodeTask('madge', ['--circular', DIST_MATERIAL]));
gulp.task('madge', ['material:clean-build'], execNodeTask('madge', ['--circular', DIST_MATERIAL]));
/** Task to lint Angular Material's scss stylesheets. */
gulp.task('stylelint', execNodeTask(
+53
View File
@@ -0,0 +1,53 @@
import {task, src, dest} from 'gulp';
import {join} from 'path';
import {writeFileSync} from 'fs';
import {Bundler} from 'scss-bundle';
import {execNodeTask, sequenceTask} from '../util/task_helpers';
import {composeRelease} from '../util/package-build';
import {COMPONENTS_DIR, DIST_MATERIAL, DIST_RELEASES} from '../constants';
// There are no type definitions available for these imports.
const gulpRename = require('gulp-rename');
// Path to the release output of material.
const releasePath = join(DIST_RELEASES, 'material');
// The entry-point for the scss theming bundle.
const themingEntryPointPath = join(COMPONENTS_DIR, 'core', 'theming', '_all-theme.scss');
// Output path for the scss theming bundle.
const themingBundlePath = join(releasePath, '_theming.scss');
// Matches all pre-built theme css files
const prebuiltThemeGlob = join(DIST_MATERIAL, '**/theming/prebuilt/*.css');
// Matches all SCSS files in the library.
const allScssGlob = join(COMPONENTS_DIR, '**/*.scss');
/**
* Overwrite the release task for the material package. The material release will include special
* files, like a bundled theming SCSS file or all prebuilt themes.
*/
task('material:build-release', ['material:prepare-release'], () => composeRelease('material'));
/**
* Task that will build the material package. It will also copy all prebuilt themes and build
* a bundled SCSS file for theming
*/
task('material:prepare-release', sequenceTask(
'material:build',
['material:copy-prebuilt-themes', 'material:bundle-theming-scss']
));
/** Copies all prebuilt themes into the release package under `prebuilt-themes/` */
task('material:copy-prebuilt-themes', () => {
src(prebuiltThemeGlob)
.pipe(gulpRename({dirname: ''}))
.pipe(dest(join(releasePath, 'prebuilt-themes')));
});
/** Bundles all scss requires for theming into a single scss file in the root of the package. */
task('material:bundle-theming-scss', () => {
// Instantiates the SCSS bundler and bundles all imports of the specified entry point SCSS file.
// A glob of all SCSS files in the library will be passed to the bundler. The bundler takes an
// array of globs, which will match SCSS files that will be only included once in the bundle.
new Bundler().Bundle(themingEntryPointPath, [allScssGlob]).then(result => {
writeFileSync(themingBundlePath, result.bundledContent);
});
});
+2 -2
View File
@@ -8,8 +8,8 @@ import {openFirebaseDashboardDatabase} from '../util/firebase';
const bundlesDir = join(DIST_ROOT, 'bundles');
/** Task which runs test against the size of whole library. */
task('payload', ['library:clean-build'], () => {
/** Task which runs test against the size of material. */
task('payload', ['material:clean-build'], () => {
let results = {
umd_kb: getBundleSize('material.umd.js'),
@@ -1,62 +1,19 @@
import {spawn} from 'child_process';
import {existsSync, statSync, writeFileSync} from 'fs-extra';
import {existsSync, statSync} from 'fs-extra';
import {join} from 'path';
import {dest, src, task} from 'gulp';
import {task} from 'gulp';
import {execTask, sequenceTask} from '../util/task_helpers';
import {composeRelease} from '../util/package-build';
import {Bundler} from 'scss-bundle';
import {
COMPONENTS_DIR,
DIST_MATERIAL,
DIST_RELEASES,
} from '../constants';
import {DIST_RELEASES} from '../constants';
import * as minimist from 'minimist';
// There are no type definitions available for these imports.
const gulpRename = require('gulp-rename');
/** Parse command-line arguments for release task. */
const argv = minimist(process.argv.slice(3));
// Path to the release output of material.
const releasePath = join(DIST_RELEASES, 'material');
// The entry-point for the scss theming bundle.
const themingEntryPointPath = join(COMPONENTS_DIR, 'core', 'theming', '_all-theme.scss');
// Output path for the scss theming bundle.
const themingBundlePath = join(releasePath, '_theming.scss');
// Matches all SCSS files in the library.
const allScssGlob = join(COMPONENTS_DIR, '**/*.scss');
// Matches all pre-built theme css files
const prebuiltThemeGlob = join(DIST_MATERIAL, '**/theming/prebuilt/*.css');
task('build:release', sequenceTask(
'library:clean-build',
':package:release',
));
/** Task that composes the different build files into the release structure. */
task(':package:release', [':package:theming'], () => composeRelease('material'));
/** Copies all prebuilt themes into the release package under `prebuilt-themes/` */
task(':package:theming', [':bundle:theming-scss'], () => {
src(prebuiltThemeGlob)
.pipe(gulpRename({dirname: ''}))
.pipe(dest(join(releasePath, 'prebuilt-themes')));
});
/** Bundles all scss requires for theming into a single scss file in the root of the package. */
task(':bundle:theming-scss', () => {
// Instantiates the SCSS bundler and bundles all imports of the specified entry point SCSS file.
// A glob of all SCSS files in the library will be passed to the bundler. The bundler takes an
// array of globs, which will match SCSS files that will be only included once in the bundle.
new Bundler().Bundle(themingEntryPointPath, [allScssGlob]).then(result => {
writeFileSync(themingBundlePath, result.bundledContent);
});
});
/** Task that builds all releases that will be published. */
task(':publish:build-releases', ['material:build-release:clean']);
/** Make sure we're logged in. */
task(':publish:whoami', execTask('npm', ['whoami'], {
@@ -132,7 +89,7 @@ task(':publish', function(done: (err?: any) => void) {
task('publish', sequenceTask(
':publish:whoami',
'build:release',
':publish:build-releases',
':publish',
':publish:logout',
));
+4 -4
View File
@@ -9,10 +9,10 @@ const runSequence = require('run-sequence');
/** Builds everything that is necessary for karma. */
task(':test:build', sequenceTask(
'clean',
// Build the library bundles without any test files. (CDK will be also built)
'library:build',
// Additionally build the test files for the library and the CDK.
['library:build:esm:tests', 'cdk:build:esm:tests']
// Build the material bundles without any test files. (CDK will be also built)
'material:build',
// Additionally build the test files for material and the CDK.
['material:build:esm:tests', 'cdk:build:esm:tests']
));
/**
+2 -1
View File
@@ -91,7 +91,8 @@ export async function buildPackageBundles(entryFile: string, packageName: string
* This is useful when multiple transformation happen (e.g TSC -> Rollup -> Uglify)
**/
async function remapSourcemap(sourceFile: string) {
(await sorcery.load(sourceFile)).write();
// Once sorcery loaded the chain of sourcemaps, the new sourcemap will be written asynchronously.
return (await sorcery.load(sourceFile)).write();
}
/** Minifies a JavaScript file using UglifyJS2. Also writes sourcemaps to the output. */
+75
View File
@@ -0,0 +1,75 @@
import {task, watch} from 'gulp';
import {join} from 'path';
import {main as tsc} from '@angular/tsc-wrapped';
import {SOURCE_ROOT, DIST_ROOT} from '../constants';
import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from './task_helpers';
import {buildPackageBundles, composeRelease} from './package-build';
// There are no type definitions available for these imports.
const inlineResources = require('../../../scripts/release/inline-resources');
/**
* Creates a set of gulp tasks that can build the specified package.
* @param packageName Name of the package. Needs to be similar to the directory name in `src/`.
* @param requiredPackages Required packages that will be built before building the current package.
*/
export function createPackageBuildTasks(packageName: string, requiredPackages: string[] = [], ) {
// To avoid refactoring of the project the package material will map to the source path `lib/`.
const packageRoot = join(SOURCE_ROOT, packageName === 'material' ? 'lib' : packageName);
const packageOut = join(DIST_ROOT, 'packages', packageName);
const tsconfigBuild = join(packageRoot, 'tsconfig-build.json');
const tsconfigTests = join(packageRoot, 'tsconfig-tests.json');
// Paths to the different output files and directories.
const esmMainFile = join(packageOut, 'index.js');
// Glob that matches all assets that should be copied to the package.
const assetsGlob = join(packageRoot, '**/*.+(html|scss|css)');
/**
* Main tasks for the package building. Tasks execute the different sub-tasks in the correct
* order.
*/
task(`${packageName}:clean-build`, sequenceTask('clean', `${packageName}:build`));
task(`${packageName}:build`, sequenceTask(
// Build all required packages before building.
...requiredPackages.map(pkgName => `${pkgName}:build`),
// Build ESM and assets output.
[`${packageName}:build:esm`, `${packageName}:assets`],
// Inline assets into ESM output.
`${packageName}:assets:inline`,
// Build bundles on top of inlined ESM output.
`${packageName}:build:bundles`,
));
/**
* Release tasks for the package. Tasks compose the release output for the package.
*/
task(`${packageName}:build-release:clean`, sequenceTask('clean', `${packageName}:build-release`));
task(`${packageName}:build-release`, [`${packageName}:build`], () => composeRelease(packageName));
/**
* TypeScript compilation tasks. Tasks are creating ESM, FESM, UMD bundles for releases.
*/
task(`${packageName}:build:esm`, () => tsc(tsconfigBuild, {basePath: packageRoot}));
task(`${packageName}:build:esm:tests`, () => tsc(tsconfigTests, {basePath: packageRoot}));
task(`${packageName}:build:bundles`, () => buildPackageBundles(esmMainFile, packageName));
/**
* Asset tasks. Building SASS files and inlining CSS, HTML files into the ESM output.
*/
task(`${packageName}:assets`, [`${packageName}:assets:scss`, `${packageName}:assets:html`]);
task(`${packageName}:assets:scss`, sassBuildTask(packageOut, packageRoot, true));
task(`${packageName}:assets:html`, copyTask(assetsGlob, packageOut));
task(`${packageName}:assets:inline`, () => inlineResources(packageOut));
/**
* Watch tasks, that will rebuild the package whenever TS, SCSS, or HTML files change.
*/
task(`${packageName}:watch`, () => {
watch(join(packageRoot, '**/*.+(ts|scss|html)'), [`${packageName}:build`, triggerLivereload]);
});
}
+1 -2
View File
@@ -140,8 +140,7 @@ export function buildAppTask(appName: string) {
return (done: () => void) => {
gulpRunSequence(
'clean',
'library:build',
'material:clean-build',
[...buildTasks],
done
);