Skip to content

Commit

Permalink
feat: implement ng-packagr
Browse files Browse the repository at this point in the history
Packaging an Angular library includes:
- Inlining assets from `templateUrl`, `styleUrls`, and compiling `.scss` styles to `.css`
- Compiling TypeScript sources with `ngc`, producing `.metadata.json` and `.d.ts` files
- Bundling an FESM15 module in `@<prefix>/<name>.js` (ES2015 syntax and ES2015 module format)
- Bundling an FESM5 module in `@<prefix>/<name>.es5.js` (ES5 syntax with ES2015 module format)
- Bundling an UMD version in `bundles/<name>.umd.js` (ES5 syntax with UMD module format)
- Generating a `package.json`
  • Loading branch information
dherges committed May 19, 2017
1 parent 2cb2066 commit 8474e36
Show file tree
Hide file tree
Showing 16 changed files with 761 additions and 203 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ typings/

# Angular Build directory
.ng_build/
dist/
22 changes: 22 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const path = require('path');

// Read CLI arguments
const ARGS = require('minimist')(process.argv.slice(2));

const SRC_DIR = ARGS.src || path.resolve(process.cwd());
const DEST_DIR = ARGS.dest || path.resolve(SRC_DIR, 'dist');
const WORKING_DIR = ARGS.workingDirectory || path.resolve(SRC_DIR, '.ng_build');


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

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

ngPackagr.packageAngular({
src: SRC_DIR,
dest: DEST_DIR,
workingDirectory: WORKING_DIR
});
128 changes: 0 additions & 128 deletions ng-build.ts

This file was deleted.

82 changes: 82 additions & 0 deletions ng-packagr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// BUILD STEP IMPLEMENTATIONS
import { processAssets } from './steps/assets';
import { copyFiles } from './steps/copy';
import { ngc } from './steps/ngc';
import { createPackage, readPackage, preparePackage } from './steps/package';
import { rimraf } from './steps/rimraf';
import { rollup } from './steps/rollup';
import { downlevelWithTsc } from './steps/tsc';


// Logging
import { error, warn, info, success, debug } from './util/log';

// There are no type definitions available for these imports.
const sorcery = require('sorcery');
const uglify = require('uglify-js');


export interface NgPackagrOptions {
src: string,
dest: string,
workingDirectory: string
}


export const packageAngular = (opts: NgPackagrOptions): Promise<any> => {
info(`Building Angular library in ${opts.src}`);

let sourcePkg: any;

return rimraf(opts.dest)
.then(() => rimraf(opts.workingDirectory))
// 1. READ PACKGE
.then(() => preparePackage(opts.src))
.then((pkg) => {
sourcePkg = pkg;

return Promise.resolve(pkg);
})
// 2. ASSETS
.then(() => processAssets(opts.src, `${opts.workingDirectory}/sources`))
// 3. NGC
.then(() => ngc(`${opts.src}/tsconfig.lib.json`, `${opts.workingDirectory}/sources`))
// 4. FESM15: ROLLUP
.then(() => rollup({
moduleName: `${sourcePkg.meta.name}`,
entry: `${opts.workingDirectory}/sources/index.js`,
format: 'es',
dest: `${opts.workingDirectory}/packages/${sourcePkg.dest.es2015}`
}))
// 5. FESM5: TSC
.then(() => downlevelWithTsc(
`${opts.workingDirectory}/packages/${sourcePkg.dest.es2015}`,
`${opts.workingDirectory}/packages/${sourcePkg.dest.module}`))
// 6. UMD: ROLLUP
.then(() => rollup({
moduleName: `${sourcePkg.meta.name}`,
entry: `${opts.workingDirectory}/packages/${sourcePkg.dest.module}`,
format: 'umd',
dest: `${opts.workingDirectory}/packages/${sourcePkg.dest.main}`
}))
// 7. COPY FILES
.then(() => copyFiles(`${opts.workingDirectory}/packages/**/*.{js,js.map}`, `${opts.dest}`))
.then(() => copyFiles(`${opts.workingDirectory}/sources/**/*.{d.ts,metadata.json}`, `${opts.dest}/src`))
.then(() => copyFiles(`${opts.src}/README.md`, opts.dest))
.then(() => copyFiles(`${opts.src}/LICENSE`, opts.dest))
// 8. PACKAGE
.then(() => createPackage(`${opts.src}`, `${opts.dest}`, sourcePkg.dest))
.then(() => {
success(`Built Angular library in ${opts.src}, written to ${opts.dest}`);
})

}



// TODO: need to re-map sourcemaps for EVERY transformation step to keep reference to original sources
// X. REMAPE SOURCEMAP
async function remapSourcemap(sourceFile: string) {
// Once sorcery loaded the chain of sourcemaps, the new sourcemap will be written asynchronously.
return (await sorcery.load(sourceFile)).write();
}
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,27 @@
"dependencies": {
"@angular/tsc-wrapped": "^4.1.2",
"@types/node": "^7.0.18",
"cpx": "^1.5.0",
"gulp-inline-ng2-template": "^4.0.0",
"node-sass": "^4.5.3",
"rimraf": "^2.6.1",
"rollup": "^0.41.6",
"rollup-plugin-node-resolve": "^3.0.0",
"sorcery": "^0.10.0",
"ts-node": "^3.0.4",
"typescript": "^2.3.2",
"uglify-js": "^3.0.7",
"vinyl-fs": "^2.4.4"
},
"devDependencies": {
"@angular/core": "^4.1.2"
"@angular/common": "^4.1.3",
"@angular/core": "^4.1.3",
"@angular/http": "^4.1.3",
"@angular/platform-browser": "^4.1.3",
"rxjs": "^5.4.0",
"zone.js": "^0.8.10"
},
"scripts": {
"build": "ts-node ng-build.ts"
"build": "node build.js --src sample"
}
}
46 changes: 46 additions & 0 deletions steps/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const inlineNg2Template = require('gulp-inline-ng2-template');
const sass = require('node-sass');
const vfs = require('vinyl-fs');

import { debug } from '../util/log';


/**
* Process Angular components assets (HTML and Stylesheets).
*
* Inlines 'templateUrl' and 'styleUrl', compiles .scss to .css, and write .ts files to
* destination directory.
*
* @param src Source folder
* @param dest Destination folder
*/
export const processAssets = (src: string, dest: string): Promise<any> => {

return new Promise((resolve, reject) => {
debug(`processAssets ${src} to ${dest}`);

vfs.src(`${src}/**/*.ts`)
.pipe(inlineNg2Template({
base: `${src}`,
useRelativePaths: true,
styleProcessor: (path, ext, file, cb) => {

debug(`sass.render ${path}`);

sass.render({
file: path
}, (err, result) => {
if (err) {
cb(err);
} else {
cb(null, result.css.toString());
}
});
}
}))
.on('error', reject)
.pipe(vfs.dest(`${dest}`))
.on('end', resolve);
});

}
27 changes: 27 additions & 0 deletions steps/copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const cpx = require("cpx");

export const copyFiles = (src: string, dest: string, options?: any): Promise<any> => {

return new Promise((resolve, reject) => {

if (options) {
cpx.copy(src, dest, options, (err) => {
if (err) {
reject();
}

resolve();
});
} else {
cpx.copy(src, dest, (err) => {
if (err) {
reject();
}

resolve();
});
}

});

}
14 changes: 14 additions & 0 deletions steps/ngc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {main as tsc} from '@angular/tsc-wrapped';
import { debug } from '../util/log';


/**
* Compiles typescript sources with 'ngc'.
*
* @param basePath
*/
export const ngc = (tsconfig: string, basePath: string): Promise<any> => {
debug(`ngc ${tsconfig}, { basePath: ${basePath} })`);

return tsc(tsconfig, { basePath });
}
Loading

0 comments on commit 8474e36

Please sign in to comment.