-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build: Implement build process that complies with the Angular package…
… format (#13)
- Loading branch information
1 parent
41758b1
commit f4baa3e
Showing
78 changed files
with
1,048 additions
and
8,391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import * as tasks from './tasks'; | ||
import { createBuilder } from './util'; | ||
|
||
|
||
export default createBuilder([ | ||
[ 'Removing "./dist" Folder', tasks.removeDistFolder ], | ||
[ 'Compiling packages with NGC', tasks.compilePackagesWithNgc ], | ||
[ 'Bundling FESMs', tasks.bundleFesms ], | ||
[ 'Down-leveling FESMs to ES5', tasks.downLevelFesmsToES5 ], | ||
[ 'Creating UMD Bundles', tasks.createUmdBundles ], | ||
[ 'Renaming package entry files', tasks.renamePackageEntryFiles ], | ||
[ 'Cleaning TypeScript files', tasks.cleanTypeScriptFiles ], | ||
[ 'Removing remaining sourcemap files', tasks.removeRemainingSourceMapFiles ], | ||
[ 'Copying type definition files', tasks.copyTypeDefinitionFiles ], | ||
[ 'Minifying UMD bundles', tasks.minifyUmdBundles ], | ||
[ 'Copying package documents', tasks.copyPackageDocs ], | ||
[ 'Removing "./dist/packages" Folder', tasks.removePackagesFolder ], | ||
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface Config { | ||
packages: string[]; | ||
scope: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import build from './builder'; | ||
|
||
|
||
build({ | ||
scope: '@ngrx', | ||
packages: [ | ||
'store', | ||
'effects', | ||
'router-store', | ||
'store-devtools', | ||
] | ||
}).catch(err => { | ||
console.error(err); | ||
process.exit(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import { Config } from './config'; | ||
import * as util from './util'; | ||
|
||
|
||
/** | ||
* Cleans the top level dist folder. All npm-ready packages are created | ||
* in the dist folder. | ||
*/ | ||
export async function removeDistFolder(config: Config) { | ||
const args = [ | ||
'./dist' | ||
]; | ||
|
||
return await util.exec('rimraf', args); | ||
} | ||
|
||
|
||
/** | ||
* Uses the 'tsconfig-build.json' file in each package directory to produce | ||
* AOT and Closure compatible JavaScript | ||
*/ | ||
export async function compilePackagesWithNgc(config: Config) { | ||
for (let pkg of config.packages) { | ||
await util.exec('ngc', [ | ||
`-p ./modules/${pkg}/tsconfig-build.json` | ||
]); | ||
|
||
const entryTypeDefinition = `export * from './${pkg}/index';`; | ||
const entryMetadata = `{"__symbolic":"module","version":3,"metadata":{},"exports":[{"from":"./${pkg}/index"}]}`; | ||
|
||
util.writeFile(`./dist/packages/${pkg}.d.ts`, entryTypeDefinition); | ||
util.writeFile(`./dist/packages/${pkg}.metadata.json`, entryMetadata); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Uses Rollup to bundle the JavaScript into a single flat file called | ||
* a FESM (Flat Ecma Script Module) | ||
*/ | ||
export async function bundleFesms(config: Config) { | ||
for (let pkg of config.packages) { | ||
await util.exec('rollup', [ | ||
`-i ./dist/packages/${pkg}/index.js`, | ||
`-o ./dist/${pkg}/${config.scope}/${pkg}.js`, | ||
`--sourcemap`, | ||
]); | ||
|
||
await util.mapSources(`./dist/${pkg}/${config.scope}/${pkg}.js`); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Copies each FESM into a TS file then uses TypeScript to downlevel | ||
* the FESM into ES5 with ESM modules | ||
*/ | ||
export async function downLevelFesmsToES5(config: Config) { | ||
const tscArgs = [ | ||
'--target es5', | ||
'--module es2015', | ||
'--noLib', | ||
'--sourceMap', | ||
]; | ||
|
||
for (let pkg of config.packages) { | ||
const file = `./dist/${pkg}/${config.scope}/${pkg}.js`; | ||
const target = `./dist/${pkg}/${config.scope}/${pkg}.es5.ts`; | ||
|
||
util.copy(file, target); | ||
|
||
await util.ignoreErrors(util.exec('tsc', [ target, ...tscArgs ])); | ||
await util.mapSources(target.replace('.ts', '.js')); | ||
} | ||
|
||
await util.removeRecursively(`./dist/?(${config.packages.join('|')})/${config.scope}/*.ts`); | ||
} | ||
|
||
|
||
/** | ||
* Re-runs Rollup on the downleveled ES5 to produce a UMD bundle | ||
*/ | ||
export async function createUmdBundles(config: Config) { | ||
for (let pkg of config.packages) { | ||
const rollupArgs = [ | ||
`-c ./modules/${pkg}/rollup.config.js`, | ||
`--sourcemap`, | ||
]; | ||
|
||
await util.exec('rollup', rollupArgs); | ||
await util.mapSources(`./dist/${pkg}/bundles/${pkg}.umd.js`); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Removes any leftover TypeScript files from previous compilation steps, | ||
* leaving any type definition files in place | ||
*/ | ||
export async function cleanTypeScriptFiles(config: Config) { | ||
const tsFilesGlob = './dist/packages/**/*.js'; | ||
const dtsFilesFlob = './dist/packages/**/*.d.ts'; | ||
const filesToRemove = await util.getListOfFiles(tsFilesGlob, dtsFilesFlob); | ||
|
||
for (let file of filesToRemove) { | ||
util.remove(file); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Renames the index files in each package to the name | ||
* of the package. | ||
*/ | ||
export async function renamePackageEntryFiles(config: Config) { | ||
for (let pkg of config.packages) { | ||
const files = await util.getListOfFiles(`./dist/packages/${pkg}/index.**`); | ||
|
||
for (let file of files) { | ||
const target = file.replace('index', pkg); | ||
util.copy(file, target); | ||
util.remove(file); | ||
} | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Removes any remaining source map files from running NGC | ||
*/ | ||
export async function removeRemainingSourceMapFiles(config: Config) { | ||
await util.removeRecursively(`./dist/packages/?(${config.packages.join('|')})/**/*.map`); | ||
} | ||
|
||
|
||
/** | ||
* Copies the type definition files and NGC metadata files to | ||
* the root of the distribution | ||
*/ | ||
export async function copyTypeDefinitionFiles(config: Config) { | ||
const files = await util.getListOfFiles(`./dist/packages/?(${config.packages.join('|')})/**/*`); | ||
|
||
for (let file of files) { | ||
const target = file.replace('packages/', ''); | ||
util.copy(file, target); | ||
} | ||
|
||
await util.removeRecursively(`./dist/packages/?(${config.packages.join('|')})`); | ||
} | ||
|
||
|
||
/** | ||
* Creates minified copies of each UMD bundle | ||
*/ | ||
export async function minifyUmdBundles(config: Config) { | ||
const uglifyArgs = [ | ||
'-c', | ||
'--screw-ie8', | ||
'--comments', | ||
]; | ||
|
||
for (let pkg of config.packages) { | ||
const file = `./dist/${pkg}/bundles/${pkg}.umd.js`; | ||
const out = `./dist/${pkg}/bundles/${pkg}.umd.min.js`; | ||
|
||
await util.exec('uglifyjs', [ | ||
...uglifyArgs, | ||
`-o ${out}`, | ||
`--source-map ${out}.map`, | ||
`--source-map-include-sources ${file}`, | ||
`--in-source-map ${file}.map` | ||
]); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Copies the README.md, LICENSE, and package.json files into | ||
* each package | ||
*/ | ||
export async function copyPackageDocs(config: Config) { | ||
for (let pkg of config.packages) { | ||
const source = `./modules/${pkg}`; | ||
const target = `./dist/${pkg}`; | ||
|
||
util.copy(`${source}/package.json`, `${target}/package.json`); | ||
util.copy(`${source}/README.md`, `${target}/README.md`); | ||
util.copy('./LICENSE', `${target}/LICENSE`); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Removes the packages folder | ||
*/ | ||
export async function removePackagesFolder(config: Config) { | ||
await util.removeRecursively('./dist/packages'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import * as fs from 'fs'; | ||
import * as cp from 'child_process'; | ||
import * as glob from 'glob'; | ||
import * as fsExtra from 'fs-extra'; | ||
import * as path from 'path'; | ||
import * as rimraf from 'rimraf'; | ||
import { Config } from './config'; | ||
|
||
|
||
export function copy(target: string, destination: string) { | ||
fsExtra.copySync(target, destination); | ||
} | ||
|
||
export function remove(target: string) { | ||
fsExtra.removeSync(target); | ||
} | ||
|
||
export function writeFile(target: string, contents: string) { | ||
fs.writeFileSync(target, contents); | ||
} | ||
|
||
export function getListOfFiles(globPath: string, exclude?: string): Promise<string[]> { | ||
return new Promise((resolve, reject) => { | ||
const options = exclude ? { ignore: exclude } : { }; | ||
|
||
glob(globPath, options, (error, matches) => { | ||
if (error) { | ||
return reject(error); | ||
} | ||
|
||
resolve(matches); | ||
}); | ||
}); | ||
} | ||
|
||
export function removeRecursively(glob: string) { | ||
return new Promise((resolve, reject) => { | ||
rimraf(glob, err => { | ||
if (err) { | ||
reject(err); | ||
} | ||
else { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
export function exec(command: string, args: string[]): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
cp.exec(fromNpm(command) + ' ' + args.join(' '), (err, stdout, stderr) => { | ||
if (err) { | ||
return reject(err); | ||
} | ||
|
||
resolve(stdout.toString()); | ||
}); | ||
}); | ||
} | ||
|
||
export async function ignoreErrors<T>(promise: Promise<T>): Promise<T | null> { | ||
try { | ||
const result = await promise; | ||
return result; | ||
} | ||
catch (err) { | ||
return null; | ||
} | ||
} | ||
|
||
export function fromNpm(command: string) { | ||
return `./node_modules/.bin/${command}`; | ||
} | ||
|
||
export function getPackageFilePath(pkg: string, filename: string) { | ||
return path.resolve(process.cwd(), `./modules/${pkg}/${filename}`); | ||
} | ||
|
||
const sorcery = require('sorcery'); | ||
export function mapSources(file: string) { | ||
return new Promise((resolve, reject) => { | ||
sorcery.load(file) | ||
.then((chain: any) => { | ||
chain.write(); | ||
resolve(); | ||
}) | ||
.catch(reject); | ||
}); | ||
} | ||
|
||
const ora = require('ora'); | ||
async function runTask(name: string, taskFn: () => Promise<any>) { | ||
const spinner = ora(name); | ||
|
||
try { | ||
spinner.start(); | ||
|
||
await taskFn(); | ||
|
||
spinner.succeed(); | ||
} catch (e) { | ||
spinner.fail(); | ||
|
||
throw e; | ||
} | ||
} | ||
|
||
export function createBuilder(tasks: [ string, (config: Config) => Promise<any> ][]) { | ||
return async function (config: Config) { | ||
for (let [ name, runner ] of tasks) { | ||
await runTask(name, () => runner(config)); | ||
} | ||
}; | ||
} |
Oops, something went wrong.