Skip to content

Commit

Permalink
fix: TS compiler options from angular-quickstart-lib (es5, es2015, umd)
Browse files Browse the repository at this point in the history
  • Loading branch information
kmturley committed Dec 23, 2018
1 parent da00655 commit 8519475
Show file tree
Hide file tree
Showing 19 changed files with 2,222 additions and 889 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/dist
/tmp
/lib
/out-tsc

# dependencies
/node_modules
Expand Down
152 changes: 152 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
'use strict';

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const camelCase = require('camelcase');
const ngc = require('@angular/compiler-cli/src/main').main;
const rollup = require('rollup');
const uglify = require('rollup-plugin-uglify');
const sourcemaps = require('rollup-plugin-sourcemaps');
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');

const inlineResources = require('./inline-resources');

const libName = require('./package.json').name;
const rootFolder = path.join(__dirname);
const compilationFolder = path.join(rootFolder, 'out-tsc');
const srcFolder = path.join(rootFolder, 'src/lib');
const distFolder = path.join(rootFolder, 'dist');
const tempLibFolder = path.join(compilationFolder, 'lib');
const es5OutputFolder = path.join(compilationFolder, 'lib-es5');
const es2015OutputFolder = path.join(compilationFolder, 'lib-es2015');

return Promise.resolve()
// Copy library to temporary folder and inline html/css.
.then(() => _relativeCopy(`**/*`, srcFolder, tempLibFolder)
.then(() => inlineResources(tempLibFolder))
.then(() => console.log('Inlining succeeded.'))
)
// Compile to ES2015.
.then(() => ngc([ '--project', `${tempLibFolder}/tsconfig.lib.json` ]))
.then(exitCode => exitCode === 0 ? Promise.resolve() : Promise.reject())
.then(() => console.log('ES2015 compilation succeeded.'))
// Compile to ES5.
.then(() => ngc([ '--project', `${tempLibFolder}/tsconfig.es5.json` ]))
.then(exitCode => exitCode === 0 ? Promise.resolve() : Promise.reject())
.then(() => console.log('ES5 compilation succeeded.'))
// Copy typings and metadata to `dist/` folder.
.then(() => Promise.resolve()
.then(() => _relativeCopy('**/*.d.ts', es2015OutputFolder, distFolder))
.then(() => _relativeCopy('**/*.metadata.json', es2015OutputFolder, distFolder))
.then(() => console.log('Typings and metadata copy succeeded.'))
)
// Bundle lib.
.then(() => {
// Base configuration.
const es5Entry = path.join(es5OutputFolder, `${libName}.js`);
const es2015Entry = path.join(es2015OutputFolder, `${libName}.js`);
const rollupBaseConfig = {
moduleName: camelCase(libName),
sourceMap: true,
// ATTENTION:
// Add any dependency or peer dependency your library to `globals` and `external`.
// This is required for UMD bundle users.
globals: {
// The key here is library name, and the value is the the name of the global variable name
// the window object.
// See https://github.com/rollup/rollup/wiki/JavaScript-API#globals for more.
'@angular/core': 'ng.core'
},
external: [
// List of dependencies
// See https://github.com/rollup/rollup/wiki/JavaScript-API#external for more.
'@angular/core'
],
plugins: [
commonjs({
include: ['node_modules/rxjs/**']
}),
sourcemaps(),
nodeResolve({ jsnext: true, module: true })
]
};

// UMD bundle.
const umdConfig = Object.assign({}, rollupBaseConfig, {
entry: es5Entry,
dest: path.join(distFolder, `bundles`, `${libName}.umd.js`),
format: 'umd',
});

// Minified UMD bundle.
const minifiedUmdConfig = Object.assign({}, rollupBaseConfig, {
entry: es5Entry,
dest: path.join(distFolder, `bundles`, `${libName}.umd.min.js`),
format: 'umd',
plugins: rollupBaseConfig.plugins.concat([uglify({})])
});

// ESM+ES5 flat module bundle.
const fesm5config = Object.assign({}, rollupBaseConfig, {
entry: es5Entry,
dest: path.join(distFolder, `${libName}.es5.js`),
format: 'es'
});

// ESM+ES2015 flat module bundle.
const fesm2015config = Object.assign({}, rollupBaseConfig, {
entry: es2015Entry,
dest: path.join(distFolder, `${libName}.js`),
format: 'es'
});

const allBundles = [
umdConfig,
minifiedUmdConfig,
fesm5config,
fesm2015config
].map(cfg => rollup.rollup(cfg).then(bundle => bundle.write(cfg)));

return Promise.all(allBundles)
.then(() => console.log('All bundles generated successfully.'))
})
// Copy package files
.then(() => Promise.resolve()
.then(() => _relativeCopy('LICENSE', rootFolder, distFolder))
.then(() => _relativeCopy('package.json', rootFolder, distFolder))
.then(() => _relativeCopy('README.md', rootFolder, distFolder))
.then(() => console.log('Package files copy succeeded.'))
)
.catch(e => {
console.error('\Build failed. See below for errors.\n');
console.error(e);
process.exit(1);
});


// Copy files maintaining relative paths.
function _relativeCopy(fileGlob, from, to) {
return new Promise((resolve, reject) => {
glob(fileGlob, { cwd: from, nodir: true }, (err, files) => {
if (err) reject(err);
files.forEach(file => {
const origin = path.join(from, file);
const dest = path.join(to, file);
const data = fs.readFileSync(origin, 'utf-8');
_recursiveMkDir(path.dirname(dest));
fs.writeFileSync(dest, data);
resolve();
})
})
});
}

// Recursively create a dir.
function _recursiveMkDir(dir) {
if (!fs.existsSync(dir)) {
_recursiveMkDir(path.dirname(dir));
fs.mkdirSync(dir);
}
}
119 changes: 119 additions & 0 deletions inline-resources.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use strict';

const fs = require('fs');
const path = require('path');
const glob = require('glob');


/**
* Simple Promiseify function that takes a Node API and return a version that supports promises.
* We use promises instead of synchronized functions to make the process less I/O bound and
* faster. It also simplifies the code.
*/
function promiseify(fn) {
return function () {
const args = [].slice.call(arguments, 0);
return new Promise((resolve, reject) => {
fn.apply(this, args.concat([function (err, value) {
if (err) {
reject(err);
} else {
resolve(value);
}
}]));
});
};
}

const readFile = promiseify(fs.readFile);
const writeFile = promiseify(fs.writeFile);

/**
* Inline resources in a tsc/ngc compilation.
* @param projectPath {string} Path to the project.
*/
function inlineResources(projectPath) {

// Match only TypeScript files in projectPath.
const files = glob.sync('**/*.ts', {cwd: projectPath});

// For each file, inline the templates and styles under it and write the new file.
return Promise.all(files.map(filePath => {
const fullFilePath = path.join(projectPath, filePath);
return readFile(fullFilePath, 'utf-8')
.then(content => inlineResourcesFromString(content, url => {
// Resolve the template url.
return path.join(path.dirname(fullFilePath), url);
}))
.then(content => writeFile(fullFilePath, content))
.catch(err => {
console.error('An error occured: ', err);
});
}));
}

/**
* Inline resources from a string content.
* @param content {string} The source file's content.
* @param urlResolver {Function} A resolver that takes a URL and return a path.
* @returns {string} The content with resources inlined.
*/
function inlineResourcesFromString(content, urlResolver) {
// Curry through the inlining functions.
return [
inlineTemplate,
inlineStyle
].reduce((content, fn) => fn(content, urlResolver), content);
}

/**
* Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and
* replace with `template: ...` (with the content of the file included).
* @param content {string} The source file's content.
* @param urlResolver {Function} A resolver that takes a URL and return a path.
* @return {string} The content with all templates inlined.
*/
function inlineTemplate(content, urlResolver) {
return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function (m, templateUrl) {
const templateFile = urlResolver(templateUrl);
const templateContent = fs.readFileSync(templateFile, 'utf-8');
const shortenedTemplate = templateContent
.replace(/([\n\r]\s*)+/gm, ' ')
.replace(/"/g, '\\"');
return `template: "${shortenedTemplate}"`;
});
}


/**
* Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and
* replace with `styles: [...]` (with the content of the file included).
* @param urlResolver {Function} A resolver that takes a URL and return a path.
* @param content {string} The source file's content.
* @return {string} The content with all styles inlined.
*/
function inlineStyle(content, urlResolver) {
return content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, function (m, styleUrls) {
const urls = eval(styleUrls);
return 'styles: ['
+ urls.map(styleUrl => {
const styleFile = urlResolver(styleUrl);
const styleContent = fs.readFileSync(styleFile, 'utf-8');
const shortenedStyle = styleContent
.replace(/([\n\r]\s*)+/gm, ' ')
.replace(/"/g, '\\"');
return `"${shortenedStyle}"`;
})
.join(',\n')
+ ']';
});
}

module.exports = inlineResources;
module.exports.inlineResourcesFromString = inlineResourcesFromString;

// Run inlineResources if module is being called directly from the CLI with arguments.
if (require.main === module && process.argv.length > 2) {
console.log('Inlining resources from project:', process.argv[2]);
return inlineResources(process.argv[2]);
}

0 comments on commit 8519475

Please sign in to comment.