diff --git a/scripts/release/inline-resources.js b/scripts/release/inline-resources.js deleted file mode 100644 index 150556da4af..00000000000 --- a/scripts/release/inline-resources.js +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env node -'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 simplify 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); - - -function inlineResources(globs) { - if (typeof globs == 'string') { - globs = [globs]; - } - - /** - * For every argument, inline the templates and styles under it and write the new file. - */ - return Promise.all(globs.map(pattern => { - if (pattern.indexOf('*') < 0) { - // Argument is a directory target, add glob patterns to include every files. - pattern = path.join(pattern, '**', '*'); - } - - const files = glob.sync(pattern, {}) - .filter(name => /\.js$/.test(name)); // Matches only JavaScript files. - - // Generate all files content with inlined templates. - return Promise.all(files.map(filePath => { - return readFile(filePath, 'utf-8') - .then(content => inlineResourcesFromString(content, url => { - return path.join(path.dirname(filePath), url); - })) - .then(content => writeFile(filePath, content)) - .catch(err => { - console.error('An error occurred: ', 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, - removeModuleId - ].reduce((content, fn) => fn(content, urlResolver), content); -} - -if (require.main === module) { - inlineResources(process.argv.slice(2)); -} - - -/** - * 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') - + ']'; - }); -} - - -/** - * Remove every mention of `moduleId: module.id`. - * @param content {string} The source file's content. - * @returns {string} The content with all moduleId: mentions removed. - */ -function removeModuleId(content) { - return content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, ''); -} - - -module.exports = inlineResources; -module.exports.inlineResourcesFromString = inlineResourcesFromString; diff --git a/tools/gulp/packaging/build-tasks-gulp.ts b/tools/gulp/packaging/build-tasks-gulp.ts index 1a85e74796f..5ff8995599f 100644 --- a/tools/gulp/packaging/build-tasks-gulp.ts +++ b/tools/gulp/packaging/build-tasks-gulp.ts @@ -5,9 +5,9 @@ import {SOURCE_ROOT, DIST_ROOT, HTML_MINIFIER_OPTIONS} from '../build-config'; import {sequenceTask, sassBuildTask, copyTask, triggerLivereload} from '../util/task_helpers'; import {composeRelease} from './build-release'; import {buildPackageBundles} from './build-bundles'; +import {inlineResourcesForDirectory} from './inline-resources'; // There are no type definitions available for these imports. -const inlineResources = require('../../../scripts/release/inline-resources'); const htmlmin = require('gulp-htmlmin'); /** @@ -84,7 +84,7 @@ export function createPackageBuildTasks(packageName: string, requiredPackages: s return src(htmlGlob).pipe(htmlmin(HTML_MINIFIER_OPTIONS)).pipe(dest(packageOut)); }); - task(`${packageName}:assets:inline`, () => inlineResources(packageOut)); + task(`${packageName}:assets:inline`, () => inlineResourcesForDirectory(packageOut)); /** * Watch tasks, that will rebuild the package whenever TS, SCSS, or HTML files change. diff --git a/tools/gulp/packaging/inline-resources.ts b/tools/gulp/packaging/inline-resources.ts new file mode 100644 index 00000000000..6d4002089ec --- /dev/null +++ b/tools/gulp/packaging/inline-resources.ts @@ -0,0 +1,57 @@ +/* tslint:disable:no-eval */ + +import {dirname, join} from 'path'; +import {readFileSync, writeFileSync} from 'fs'; +import {sync as glob} from 'glob'; + +/** Finds all JavaScript files in a directory and inlines all resources of Angular components. */ +export function inlineResourcesForDirectory(folderPath: string) { + glob(join(folderPath, '**/*.js')).forEach(filePath => inlineResources(filePath)); +} + +/** Inlines the external resources of Angular components of a file. */ +export function inlineResources(filePath: string) { + let fileContent = readFileSync(filePath, 'utf-8'); + + fileContent = inlineTemplate(fileContent, filePath); + fileContent = inlineStyles(fileContent, filePath); + fileContent = removeModuleId(fileContent); + + writeFileSync(filePath, fileContent, 'utf-8'); +} + +/** Inlines the templates of Angular components for a specified source file. */ +function inlineTemplate(fileContent: string, filePath: string) { + return fileContent.replace(/templateUrl:\s*'([^']+?\.html)'/g, (match, templateUrl) => { + const templatePath = join(dirname(filePath), templateUrl); + const templateContent = loadResourceFile(templatePath); + return `template: "${templateContent}"`; + }); +} + +/** Inlines the external styles of Angular components for a specified source file. */ +function inlineStyles(fileContent: string, filePath: string) { + return fileContent.replace(/styleUrls:\s*(\[[\s\S]*?])/gm, (match, styleUrlsValue) => { + // The RegExp matches the array of external style files. This is a string right now and + // can to be parsed using the `eval` method. The value looks like "['AAA.css', 'BBB.css']" + const styleUrls = eval(styleUrlsValue) as string[]; + + const styleContents = styleUrls + .map(url => join(dirname(filePath), url)) + .map(path => loadResourceFile(path)); + + return `styles: ["${styleContents.join(',')}"]`; + }); +} + +/** Remove every mention of `moduleId: module.id` */ +function removeModuleId(fileContent: string) { + return fileContent.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, ''); +} + +/** Loads the specified resource file and drops line-breaks of the content. */ +function loadResourceFile(filePath: string): string { + return readFileSync(filePath, 'utf-8') + .replace(/([\n\r]\s*)+/gm, ' ') + .replace(/"/g, '\\"'); +}