Skip to content

build: make packaging more flexible #6143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions tools/gulp/gulpfile.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {createPackageBuildTasks} from 'material2-build-tools';
import {cdkPackage, examplesPackage, materialPackage} from './packages';

// Create gulp tasks to build the different packages in the project.
createPackageBuildTasks('cdk', [], {useSecondaryEntryPoints: true});
createPackageBuildTasks('material', ['cdk']);
createPackageBuildTasks('material-examples', ['material', 'cdk']);
createPackageBuildTasks(cdkPackage);
createPackageBuildTasks(materialPackage);
createPackageBuildTasks(examplesPackage);

import './tasks/ci';
import './tasks/clean';
Expand Down
9 changes: 9 additions & 0 deletions tools/gulp/packages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {BuildPackage, buildConfig} from 'material2-build-tools';
import {join} from 'path';

export const cdkPackage = new BuildPackage('cdk');
export const materialPackage = new BuildPackage('material', [cdkPackage]);
export const examplesPackage = new BuildPackage('material-examples', [materialPackage, cdkPackage]);

// To avoid refactoring of the project the material package will map to the source path `lib/`.
materialPackage.packageRoot = join(buildConfig.packagesDir, 'lib');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you you think of making the second constructor arg a config / options object so that it looks like

export const materialPackage = new BuildPackage('material', {
  deps: [cdkPackage],
  root: join(buildConfig.packagesDir, 'lib'),
});

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to avoid something like that. It's really a special case that we want to overwrite the packageRoot here and it should not really be done through the build package.

Normally the build config is responsible for changing such things, but since this is really a special case I think this would be fine (it's just a single line; which is pretty clear IMO)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly either way

17 changes: 7 additions & 10 deletions tools/gulp/tasks/material-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,33 @@ import {join} from 'path';
import {writeFileSync, mkdirpSync} from 'fs-extra';
import {Bundler} from 'scss-bundle';
import {composeRelease, buildConfig, sequenceTask} from 'material2-build-tools';
import {materialPackage} from '../packages';

// There are no type definitions available for these imports.
const gulpRename = require('gulp-rename');

const {packagesDir, outputDir} = buildConfig;
const {outputDir} = buildConfig;
const {packageRoot, packageOut} = materialPackage;

/** Path to the directory where all releases are created. */
const releasesDir = join(outputDir, 'releases');

/** Path to the output of the Material package. */
const materialOutputPath = join(outputDir, 'packages', 'material');

// Path to the sources of the Material package.
const materialPath = join(packagesDir, 'lib');
// Path to the release output of material.
const releasePath = join(releasesDir, 'material');
// The entry-point for the scss theming bundle.
const themingEntryPointPath = join(materialPath, 'core', 'theming', '_all-theme.scss');
const themingEntryPointPath = join(packageRoot, '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(materialOutputPath, '**/theming/prebuilt/*.css?(.map)');
const prebuiltThemeGlob = join(packageOut, '**/theming/prebuilt/*.css?(.map)');
// Matches all SCSS files in the library.
const allScssGlob = join(materialPath, '**/*.scss');
const allScssGlob = join(packageRoot, '**/*.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('material:build-release', ['material:prepare-release'], () => composeRelease(materialPackage));

/**
* Task that will build the material package. It will also copy all prebuilt themes and build
Expand Down
15 changes: 2 additions & 13 deletions tools/package-tools/build-bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {createRollupBundle} from './rollup-helpers';
import {remapSourcemap} from './sourcemap-remap';
import {transpileFile} from './typescript-transpile';
import {buildConfig} from './build-config';
import {getSecondaryEntryPointsForPackage} from './secondary-entry-points';

/** Directory where all bundles will be created in. */
const bundlesDir = join(buildConfig.outputDir, 'bundles');


/** Builds bundles for the primary entry-point w/ given entry file, e.g. @angular/cdk */
export async function buildPrimaryEntryPointBundles(entryFile: string, packageName: string) {
return createBundlesForEntryPoint({
Expand All @@ -23,18 +21,9 @@ export async function buildPrimaryEntryPointBundles(entryFile: string, packageNa
});
}

/** Builds bundles for all secondary entry-points for a given package, e.g. 'cdk' */
export async function buildAllSecondaryEntryPointBundles(packageName: string) {
const rootPackageDir = join(buildConfig.outputDir, 'packages', packageName);

return Promise.all(getSecondaryEntryPointsForPackage(packageName)
.map(entryPointName => buildSecondaryEntryPointBundles(
join(rootPackageDir, entryPointName, `index.js`), packageName, entryPointName)));
}

/** Builds bundles for a single secondary entry-point w/ given entry file, e.g. @angular/cdk/a11y */
export async function buildSecondaryEntryPointBundles(
entryFile: string, packageName: string, entryPointName: string) {
export async function buildSecondaryEntryPointBundles(entryFile: string, packageName: string,
entryPointName: string) {
return createBundlesForEntryPoint({
entryFile,
moduleName: `ng.${packageName}.${entryPointName}`,
Expand Down
82 changes: 82 additions & 0 deletions tools/package-tools/build-package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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';

const {packagesDir, outputDir} = buildConfig;

/** Name of the tsconfig file that is responsible for building a package. */
const buildTsconfigName = 'tsconfig-build.json';

/** Name of the tsconfig file that is responsible for building the tests. */
const testsTsconfigName = 'tsconfig-tests.json';

export class BuildPackage {

/** Path to the package sources. */
packageRoot: string;

/** 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;

/** Path to the tsconfig file, which will be used to build the package. */
private tsconfigBuild: string;

/** Path to the tsconfig file, which will be used to build the tests. */
private tsconfigTests: string;

constructor(public packageName: string, public dependencies: BuildPackage[] = []) {
this.packageRoot = join(packagesDir, packageName);
this.packageOut = join(outputDir, 'packages', packageName);

this.tsconfigBuild = join(this.packageRoot, buildTsconfigName);
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. */
async compile() {
await this._compileEntryPoint(buildTsconfigName);

// Walk through every secondary entry point and build the TypeScript sources sequentially.
for (const entryPoint of this.secondaryEntryPoints) {
await this._compileEntryPoint(buildTsconfigName, entryPoint);
}
}

/** Compiles the TypeScript test source files for the package. */
async compileTests() {
await this._compileEntryPoint(testsTsconfigName);
}

/** Creates all bundles for the package and all associated entry points. */
async createBundles() {
await buildPrimaryEntryPointBundles(this.entryFilePath, this.packageName);

for (const entryPoint of this.secondaryEntryPoints) {
const entryPointEntryFilePath = join(this.packageOut, entryPoint, 'index.js');
await buildSecondaryEntryPointBundles(entryPointEntryFilePath, this.packageName, entryPoint);
}
}

/** Compiles the TypeScript sources of a primary or secondary entry point. */
private async _compileEntryPoint(tsconfigName: string, secondaryEntryPoint?: string) {
const entryPointPath = join(this.packageRoot, secondaryEntryPoint || '');
const entryPointTsconfigPath = join(entryPointPath, tsconfigName);

await tsc(entryPointTsconfigPath, {basePath: entryPointPath});
}
}
29 changes: 13 additions & 16 deletions tools/package-tools/build-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {createMetadataReexportFile} from './metadata-reexport';
import {getSecondaryEntryPointsForPackage} from './secondary-entry-points';
import {createEntryPointPackageJson} from './entry-point-package-json';
import {buildConfig} from './build-config';
import {BuildPackage} from './build-package';

const {packagesDir, outputDir, projectDir} = buildConfig;

Expand All @@ -20,48 +21,48 @@ const bundlesDir = join(outputDir, 'bundles');
* 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(packageName: string, options: ComposeReleaseOptions = {}) {
// To avoid refactoring of the project the package material will map to the source path `lib/`.
const sourcePath = join(packagesDir, packageName === 'material' ? 'lib' : packageName);
const packagePath = join(outputDir, 'packages', packageName);
export function composeRelease(buildPackage: BuildPackage) {
const {packageName, packageOut, packageRoot} = buildPackage;
const releasePath = join(outputDir, 'releases', packageName);

inlinePackageMetadataFiles(packagePath);
inlinePackageMetadataFiles(packageOut);

copyFiles(packagePath, '**/*.+(d.ts|metadata.json)', join(releasePath, 'typings'));
copyFiles(packageOut, '**/*.+(d.ts|metadata.json)', join(releasePath, 'typings'));
copyFiles(bundlesDir, `${packageName}?(-*).umd.js?(.map)`, join(releasePath, 'bundles'));
copyFiles(bundlesDir, `${packageName}?(.es5).js?(.map)`, join(releasePath, '@angular'));
copyFiles(join(bundlesDir, packageName), '**', join(releasePath, '@angular', packageName));
copyFiles(projectDir, 'LICENSE', releasePath);
copyFiles(packagesDir, 'README.md', releasePath);
copyFiles(sourcePath, 'package.json', releasePath);
copyFiles(packageRoot, 'package.json', releasePath);

replaceVersionPlaceholders(releasePath);
createTypingsReexportFile(releasePath, './typings/index', packageName);
createMetadataReexportFile(releasePath, './typings/index', packageName);

if (options.useSecondaryEntryPoints) {
createFilesForSecondaryEntryPoint(packageName, packagePath, releasePath);
if (buildPackage.secondaryEntryPoints.length) {
createFilesForSecondaryEntryPoint(buildPackage, releasePath);
}

addPureAnnotationsToFile(join(releasePath, '@angular', `${packageName}.es5.js`));
}

/** Creates files necessary for a secondary entry-point. */
function createFilesForSecondaryEntryPoint(packageName: string, packagePath: string,
releasePath: string) {
function createFilesForSecondaryEntryPoint(buildPackage: BuildPackage, releasePath: string) {
const {packageName, packageOut} = buildPackage;

getSecondaryEntryPointsForPackage(packageName).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);

mkdirpSync(entryPointDir);
createEntryPointPackageJson(entryPointDir, packageName, entryPointName);

// Copy typings and metadata from tsc output location into the entry-point.
copyFiles(
join(packagePath, entryPointName),
join(packageOut, entryPointName),
'**/*.+(d.ts|metadata.json)',
join(entryPointDir, 'typings'));

Expand All @@ -76,7 +77,3 @@ function createFilesForSecondaryEntryPoint(packageName: string, packagePath: str
createMetadataReexportFile(releasePath, `./${entryPointName}/index`, entryPointName);
});
}

interface ComposeReleaseOptions {
useSecondaryEntryPoints?: boolean;
}
Loading