Skip to content

Commit

Permalink
feat: resource inlining w/ TypeScript transformations (#279)
Browse files Browse the repository at this point in the history
Significantly changes the transformation process for inlining `templateUrl` and `stylesUrl` by using TypeScript Transformation API. Resource inlining becomes a two-pass TypeScript AST transformation:
- First pass: extracts `templateUrl` and `stylesUrl` from the AT
- In between: processes assets (html templates, css/scss/less/styl stylesheets) and keep transformation results in memory
- Second pass: inlines `templateUrl` and `stylesUrl` in a dedicated AST transformation
- Further processing w/ `ngc` uses the TypeScript transformation result in-memory for reading source files
  • Loading branch information
dherges committed Nov 28, 2017
1 parent 122c972 commit 4753066
Show file tree
Hide file tree
Showing 26 changed files with 795 additions and 732 deletions.
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@
"request": "launch",
"name": "yarn samples:dev",
"program": "${workspaceRoot}/integration/samples.js"
},
{
"type": "node",
"request": "launch",
"name": "sample: material",
"program": "${workspaceRoot}/integration/samples/material/build.dev.js"
},
{
"type": "node",
"request": "launch",
"name": "sample: same-name",
"program": "${workspaceRoot}/integration/samples/same-name/build.dev.js"
}
],
"compounds": []
Expand Down
5 changes: 4 additions & 1 deletion integration/samples/core/specs/es5-api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm5/core.js';

describe(`@sample/core`, () => {

describe(`esm5/core.js`, () => {
let API;
before(() => {
API = require('../dist/esm5/core.js');
})

it(`should exist`, () => {
expect(API).to.be.ok;
Expand Down
5 changes: 4 additions & 1 deletion integration/samples/custom/specs/es5-api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm5/sample-custom.js';

describe(`sample-custom`, () => {

describe(`esm5/sample-custom.js`, () => {
let API;
before(() => {
API = require('../dist/esm5/sample-custom.js');
})

it(`should exist`, () => {
expect(API).to.be.ok;
Expand Down
5 changes: 4 additions & 1 deletion integration/samples/jsx/specs/react-jsx.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm5/jsx.js';

describe(`@sample/jsx`, () => {

describe(`jsx.es5.js`, () => {
let API;
before(() => {
API = require('../dist/esm5/jsx.js');
});

it(`should exist`, () => {
expect(API).to.be.ok;
Expand Down
13 changes: 13 additions & 0 deletions integration/samples/material/build.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const path = require('path');
process.env.DEBUG = true;

// @see https://github.com/TypeStrong/ts-node#programmatic-usage
require('ts-node').register({
project: path.join(__dirname, '..', '..', '..', 'tsconfig.packagr.json')
});

const ngPackagr = require('../../../src/lib/ng-packagr');

ngPackagr.createNgPackage({
project: path.resolve(__dirname, 'ng-package.json')
});
13 changes: 13 additions & 0 deletions integration/samples/same-name/build.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const path = require('path');
process.env.DEBUG = true;

// @see https://github.com/TypeStrong/ts-node#programmatic-usage
require('ts-node').register({
project: path.join(__dirname, '..', '..', '..', 'tsconfig.packagr.json')
});

const ngPackagr = require('../../../src/lib/ng-packagr');

ngPackagr.createNgPackage({
project: path.resolve(__dirname, 'ng-package.json')
});
10 changes: 8 additions & 2 deletions integration/samples/same-name/specs/es5-api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm5/testing.js';
import * as APITesting from '../dist/esm5/testing/testing.js';

describe(`@sample/same-name`, () => {

describe(`esm5/testing.js`, () => {
let API;
before(() => {
API = require('../dist/esm5/testing.js');
})

it(`should exist`, () => {
expect(API).to.be.ok;
Expand All @@ -19,6 +21,10 @@ describe(`@sample/same-name`, () => {
});

describe(`esm5/testing/testing.js`, () => {
let APITesting;
before(() => {
APITesting = require('../dist/esm5/testing/testing.js');
});

it(`should exist`, () => {
expect(APITesting).to.be.ok;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm2015/secondary-lib.js';
import * as SECONDARY from '../dist/esm2015/sub-module.js';

describe(`@sample/secondary-lib`, () => {

describe(`secondary-lib.js`, () => {
describe(`es2015: secondary-lib.js`, () => {
let API;
before(() => {
API = require('../dist/esm2015/secondary-lib.js');
});

it(`should exist`, () => {
expect(API).to.be.ok;
Expand All @@ -21,7 +23,11 @@ describe(`@sample/secondary-lib`, () => {

describe(`@sample/secondary-lib/sub-module`, () => {

describe(`sub-module.js`, () => {
describe(`es2015: sub-module.js`, () => {
let SECONDARY;
before(() => {
SECONDARY = require('../dist/esm2015/secondary-lib/sub-module.js');
});

it(`should exist`, () => {
expect(SECONDARY).to.be.ok;
Expand Down
14 changes: 10 additions & 4 deletions integration/samples/secondary/specs/es5-api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import * as API from '../dist/esm5/secondary-lib.js';
import * as SECONDARY from '../dist/esm5/sub-module.js';

describe(`@sample/secondary-lib`, () => {

describe(`secondary-lib.es5.js`, () => {
describe(`es5: secondary-lib.js`, () => {
let API;
before(() => {
API = require('../dist/esm5/secondary-lib.js');
});

it(`should exist`, () => {
expect(API).to.be.ok;
Expand All @@ -21,7 +23,11 @@ describe(`@sample/secondary-lib`, () => {

describe(`@sample/secondary-lib/sub-module`, () => {

describe(`sub-module.es5.js`, () => {
describe(`es5: sub-module.js`, () => {
let SECONDARY;
before(() => {
SECONDARY = require('../dist/esm5/secondary-lib/sub-module.js');
});

it(`should exist`, () => {
expect(SECONDARY).to.be.ok;
Expand Down
4 changes: 2 additions & 2 deletions integration/samples/secondary/specs/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ describe(`@sample/secondary`, () => {
});

it(`should reference "module" bundle (FESM5, also FESM2015)`, () => {
expect(PACKAGE['module']).to.equal('../esm5/sub-module.js');
expect(PACKAGE['module']).to.equal('../esm5/secondary-lib/sub-module.js');
});

it(`should reference "es2015" bundle (FESM2015)`, () => {
expect(PACKAGE['es2015']).to.equal('../esm2015/sub-module.js');
expect(PACKAGE['es2015']).to.equal('../esm2015/secondary-lib/sub-module.js');
});

it(`should reference "typings" files`, () => {
Expand Down
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"cpx": "^1.5.0",
"fs-extra": "^4.0.2",
"glob": "^7.1.2",
"gulp-inline-ng2-template": "^4.0.0",
"less": "^2.7.2",
"lodash": "^4.17.4",
"node-sass": "^4.5.3",
Expand All @@ -44,17 +43,15 @@
"rollup-plugin-node-resolve": "^3.0.0",
"sorcery": "^0.10.0",
"stylus": "^0.54.5",
"ts-node": "^3.0.4",
"uglify-js": "^3.0.7",
"vinyl-fs": "^2.4.4"
"uglify-js": "^3.0.7"
},
"peerDependencies": {
"@angular/compiler": "~5.0.0",
"@angular/compiler-cli": "~5.0.0",
"typescript": "~2.4.2"
},
"devDependencies": {
"@angular/cdk": "~5.0.0-rc0",
"@angular/cdk": "~5.0.0-rc.0",
"@angular/common": "~5.0.0",
"@angular/compiler": "~5.0.0",
"@angular/compiler-cli": "~5.0.0",
Expand Down Expand Up @@ -88,6 +85,7 @@
"rxjs": "^5.4.0",
"sinon": "^4.0.0",
"standard-version": "^4.0.0",
"ts-node": "^3.0.4",
"typescript": "~2.4.2",
"zone.js": "^0.8.10"
},
Expand All @@ -103,7 +101,7 @@
"publish:ci": "yarn prerelease && yarn postrelease",
"samples": "integration/samples.sh",
"samples:dev": "node integration/samples.js",
"samples:test": "cross-env TS_NODE_PROJECT=integration/tsconfig.specs.json mocha --compilers ts:ts-node/register integration/samples/*/specs/**/*.ts",
"samples:test": "cross-env TS_NODE_PROJECT=integration/tsconfig.specs.json mocha --require ts-node/register integration/samples/*/specs/**/*.ts",
"consumer": "integration/consumer.sh",
"test": "yarn build && yarn samples && yarn samples:test && yarn consumer",
"commitmsg": "commitlint -e",
Expand Down
102 changes: 58 additions & 44 deletions src/lib/bundler.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,103 @@
import * as path from 'path';
import { NgArtefacts } from './domain/ng-artefacts';
import { NgPackageData } from './model/ng-package-data';
import { NgArtifacts } from './model/ng-artifacts';
import { NgArtifactsFactory } from './model/ng-artifacts-factory';
import { writePackage } from './steps/package';
import { processAssets } from './steps/assets';
import { ngc } from './steps/ngc';
import { ngc, prepareTsConfig, collectTemplateAndStylesheetFiles, inlineTemplatesAndStyles } from './steps/ngc';
import { minifyJsFile } from './steps/uglify';
import { remapSourceMap, relocateSourceMapRoot } from './steps/sorcery';
import { remapSourceMap } from './steps/sorcery';
import { rollup } from './steps/rollup';
import { downlevelWithTsc } from './steps/tsc';
import { copySourceFilesToDestination } from './steps/transfer';
import { rimraf } from './util/rimraf';
import * as log from './util/log';
import { ensureUnixPath } from './util/path';
import { rimraf } from './util/rimraf';

/**
* Main Angular bundling processing.
* Transforms TypeScript source files to Angular Package Format.
*
* @param ngPkg Parent Angular package.
*/
export async function generateNgBundle(ngPkg: NgPackageData): Promise<void> {

log.info(`Generating bundle for ${ngPkg.fullPackageName}`);
const artifactFactory = new NgArtifactsFactory();
const baseBuildPath = `${ngPkg.buildDirectory}/ts${ngPkg.pathOffsetFromSourceRoot}`;
const artifactPaths = artifactFactory.calculateArtifactPathsForBuild(ngPkg);
export async function transformSources(ngPkg: NgPackageData): Promise<void> {
log.info(`Building from sources for entry point '${ngPkg.fullPackageName}'`);
const artefacts = new NgArtefacts(ngPkg);

// 0. CLEAN BUILD DIRECTORY
log.info('Cleaning bundle build directory');
log.info('Cleaning build directory');
await rimraf(ngPkg.buildDirectory);

// 1. ASSETS
// 0. TWO-PASS TSC TRANSFORMATION
artefacts.tsConfig = prepareTsConfig(ngPkg);

// First pass: collect templateUrl and stylesUrl referencing source files.
log.info('Extracting templateUrl and stylesUrl');
const result = collectTemplateAndStylesheetFiles(artefacts.tsConfig, artefacts);
result.dispose();

// Then, process assets keeping transformed contents in memory.
log.info('Processing assets');
await processAssets(ngPkg.sourcePath, baseBuildPath);
await processAssets(artefacts, ngPkg);

// 2. NGC
log.info('Running ngc');
const es2015EntryFile = await ngc(ngPkg, baseBuildPath);
// XX: see #46 - ngc only references to closure-annotated ES6 sources
await remapSourceMap(es2015EntryFile);
// Second pass: inline templateUrl and stylesUrl
log.info('Inlining templateUrl and stylesUrl');
artefacts.tsSources = inlineTemplatesAndStyles(artefacts.tsConfig, artefacts);

// 1. NGC
log.info('Compiling with ngc');
const tsOutput = await ngc(ngPkg, artefacts.tsSources, artefacts.tsConfig);
artefacts.tsSources.dispose();

// await remapSourceMap(tsOutput.js);

// 3. FESM15: ROLLUP
log.info('Compiling to FESM15');
log.info('Bundling to FESM15');
const fesm15File = path.resolve(ngPkg.buildDirectory, 'esm2015', ngPkg.esmPackageName);
await rollup({
moduleName: ngPkg.moduleName,
entry: es2015EntryFile,
entry: tsOutput.js,
format: 'es',
dest: artifactPaths.es2015,
dest: fesm15File,
externals: ngPkg.libExternals
});
await remapSourceMap(artifactPaths.es2015);
await remapSourceMap(fesm15File);

// 4. FESM5: TSC
log.info('Compiling to FESM5');
await downlevelWithTsc(
artifactPaths.es2015,
artifactPaths.module);
await remapSourceMap(artifactPaths.module);
log.info('Bundling to FESM5');
const fesm5File = path.resolve(ngPkg.buildDirectory, 'esm5', ngPkg.esmPackageName);
await downlevelWithTsc(fesm15File, fesm5File);
await remapSourceMap(fesm5File);

// 5. UMD: ROLLUP
log.info('Compiling to UMD');
log.info('Bundling to UMD');
const umdFile = path.resolve(ngPkg.buildDirectory, 'bundles', ngPkg.umdPackageName);
await rollup({
moduleName: ngPkg.moduleName,
entry: artifactPaths.module,
entry: fesm5File,
format: 'umd',
dest: artifactPaths.main,
dest: umdFile,
externals: ngPkg.libExternals
});
await remapSourceMap(artifactPaths.main);
await remapSourceMap(umdFile);

// 6. UMD: Minify
log.info('Minifying UMD bundle');
const minifiedFilePath = await minifyJsFile(artifactPaths.main);
await remapSourceMap(minifiedFilePath);

// 7. SOURCEMAPS: RELOCATE ROOT PATHS
log.info('Remapping source maps');
await relocateSourceMapRoot(ngPkg);
const minUmdFile: string = await minifyJsFile(umdFile);
await remapSourceMap(minUmdFile);

// 8. COPY SOURCE FILES TO DESTINATION
log.info('Copying staged files');
await copySourceFilesToDestination(ngPkg, baseBuildPath);
await copySourceFilesToDestination(ngPkg);

// 9. WRITE PACKAGE.JSON and OTHER DOC FILES
// 9. WRITE PACKAGE.JSON
log.info('Writing package metadata');
const packageJsonArtifactPaths = artifactFactory.calculateArtifactPathsForPackageJson(ngPkg);
await writePackage(ngPkg, packageJsonArtifactPaths);
const rootPathFromSelf: string = path.relative(ngPkg.sourcePath, ngPkg.rootSourcePath);
await writePackage(ngPkg, {
main: ensureUnixPath(path.join(rootPathFromSelf, 'bundles', ngPkg.umdPackageName)),
module: ensureUnixPath(path.join(rootPathFromSelf, 'esm5', ngPkg.esmPackageName)),
es2015: ensureUnixPath(path.join(rootPathFromSelf, 'esm2015', ngPkg.esmPackageName)),
typings: ensureUnixPath(`${ngPkg.flatModuleFileName}.d.ts`),
metadata: ensureUnixPath(`${ngPkg.flatModuleFileName}.metadata.json`)
});

log.success(`Built Angular bundle for ${ngPkg.fullPackageName}`);
log.success(`Built ${ngPkg.fullPackageName}`);
}
2 changes: 1 addition & 1 deletion src/lib/conf/tsconfig.ngc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
"**/*.shim.ts",
"**/*.spec.ts"
],
"files": []
"files": ["AUTOGENERATED"]
}

0 comments on commit 4753066

Please sign in to comment.