From de3c27536d58a9f41418f988b70456d6bbaf24b5 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Sat, 8 Oct 2016 14:53:25 +0100 Subject: [PATCH] feat(build): add support for assets array (#2570) --- package.json | 1 - .../ng2/files/__path__/assets/.gitignore | 0 .../blueprints/ng2/files/angular-cli.json | 5 +- packages/angular-cli/lib/config/schema.json | 7 ++- .../models/json-schema/schema-tree.ts | 6 +++ .../models/webpack-build-common.ts | 12 ++--- .../models/webpack-build-mobile.ts | 15 ++---- packages/angular-cli/package.json | 1 - .../plugins/glob-copy-webpack-plugin.ts | 51 +++++++++++++++++++ tests/e2e/tests/misc/assets.ts | 11 +++- 10 files changed, 87 insertions(+), 22 deletions(-) delete mode 100644 packages/angular-cli/blueprints/ng2/files/__path__/assets/.gitignore create mode 100644 packages/angular-cli/plugins/glob-copy-webpack-plugin.ts diff --git a/package.json b/package.json index 6736e494ceea..9d1c591289ae 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "chalk": "^1.1.3", "common-tags": "^1.3.1", "compression-webpack-plugin": "github:webpack/compression-webpack-plugin#7e55907cd54a2e91b96d25a660acc6a2a6453f54", - "copy-webpack-plugin": "^3.0.1", "core-js": "^2.4.0", "css-loader": "^0.23.1", "denodeify": "^1.2.1", diff --git a/packages/angular-cli/blueprints/ng2/files/__path__/assets/.gitignore b/packages/angular-cli/blueprints/ng2/files/__path__/assets/.gitignore deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/angular-cli/blueprints/ng2/files/angular-cli.json b/packages/angular-cli/blueprints/ng2/files/angular-cli.json index a5b78b8ebc76..3de359c217ea 100644 --- a/packages/angular-cli/blueprints/ng2/files/angular-cli.json +++ b/packages/angular-cli/blueprints/ng2/files/angular-cli.json @@ -7,7 +7,10 @@ { "root": "<%= sourceDir %>", "outDir": "dist", - "assets": "assets", + "assets": [ + "assets", + "favicon.ico" + ], "index": "index.html", "main": "main.ts", "test": "test.ts", diff --git a/packages/angular-cli/lib/config/schema.json b/packages/angular-cli/lib/config/schema.json index 098d52a4d2ad..b02dbb6c1026 100644 --- a/packages/angular-cli/lib/config/schema.json +++ b/packages/angular-cli/lib/config/schema.json @@ -32,7 +32,12 @@ "default": "dist/" }, "assets": { - "type": "string" + "fixme": true, + "type": "array", + "items": { + "type": "string" + }, + "default": [] }, "index": { "type": "string", diff --git a/packages/angular-cli/models/json-schema/schema-tree.ts b/packages/angular-cli/models/json-schema/schema-tree.ts index 51c46b7f013b..36c3c9a55417 100644 --- a/packages/angular-cli/models/json-schema/schema-tree.ts +++ b/packages/angular-cli/models/json-schema/schema-tree.ts @@ -128,6 +128,12 @@ export abstract class NonLeafSchemaTreeNode extends SchemaTreeNode { // Helper function to create a child based on its schema. protected _createChildProperty(name: string, value: T, forward: SchemaTreeNode, schema: Schema, define = true): SchemaTreeNode { + + // TODO: fix this + if (schema['fixme'] && typeof value === 'string') { + value = ([ value ]); + } + const type = schema['type']; let Klass: any = null; diff --git a/packages/angular-cli/models/webpack-build-common.ts b/packages/angular-cli/models/webpack-build-common.ts index ed79dc8aba9d..1b6d43b70455 100644 --- a/packages/angular-cli/models/webpack-build-common.ts +++ b/packages/angular-cli/models/webpack-build-common.ts @@ -1,8 +1,8 @@ import * as webpack from 'webpack'; import * as path from 'path'; +import {GlobCopyWebpackPlugin} from '../plugins/glob-copy-webpack-plugin'; import {BaseHrefWebpackPlugin} from '@angular-cli/base-href-webpack'; -const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -127,12 +127,10 @@ export function getWebpackCommonConfig( filename: 'inline.js', sourceMapFilename: 'inline.map' }), - new CopyWebpackPlugin([{ - context: path.resolve(appRoot, appConfig.assets), - from: { glob: '**/*', dot: true }, - ignore: [ '.gitkeep' ], - to: path.resolve(projectRoot, appConfig.outDir, appConfig.assets) - }]) + new GlobCopyWebpackPlugin({ + patterns: appConfig.assets, + globOptions: {cwd: appRoot, dot: true, ignore: '**/.gitkeep'} + }) ], node: { fs: 'empty', diff --git a/packages/angular-cli/models/webpack-build-mobile.ts b/packages/angular-cli/models/webpack-build-mobile.ts index 7e8a2d5a42bf..27f7d3658827 100644 --- a/packages/angular-cli/models/webpack-build-mobile.ts +++ b/packages/angular-cli/models/webpack-build-mobile.ts @@ -1,6 +1,6 @@ import * as path from 'path'; const OfflinePlugin = require('offline-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); +import { GlobCopyWebpackPlugin } from '../plugins/glob-copy-webpack-plugin'; import { PrerenderWebpackPlugin } from '../utilities/prerender-webpack-plugin'; export const getWebpackMobileConfigPartial = function (projectRoot: string, appConfig: any) { @@ -8,15 +8,10 @@ export const getWebpackMobileConfigPartial = function (projectRoot: string, appC // reworking the mobile app functionality return { plugins: [ - new CopyWebpackPlugin([ - { - from: path.resolve(projectRoot, appConfig.root, 'icons'), - to: path.resolve(projectRoot, appConfig.outDir, 'icons') - }, { - from: path.resolve(projectRoot, appConfig.root, 'manifest.webapp'), - to: path.resolve(projectRoot, appConfig.outDir) - } - ]), + new GlobCopyWebpackPlugin({ + patterns: [ 'icons', 'manifest.webapp'], + globOptions: {cwd: appConfig.root, dot: true, ignore: '**/.gitkeep'} + }), new PrerenderWebpackPlugin({ templatePath: 'index.html', configPath: path.resolve(projectRoot, appConfig.root, 'main-app-shell.ts'), diff --git a/packages/angular-cli/package.json b/packages/angular-cli/package.json index 9c4779904ec1..db6abf8e9ecf 100644 --- a/packages/angular-cli/package.json +++ b/packages/angular-cli/package.json @@ -39,7 +39,6 @@ "chalk": "^1.1.3", "common-tags": "^1.3.1", "compression-webpack-plugin": "github:webpack/compression-webpack-plugin#7e55907cd54a2e91b96d25a660acc6a2a6453f54", - "copy-webpack-plugin": "^3.0.1", "core-js": "^2.4.0", "css-loader": "^0.23.1", "denodeify": "^1.2.1", diff --git a/packages/angular-cli/plugins/glob-copy-webpack-plugin.ts b/packages/angular-cli/plugins/glob-copy-webpack-plugin.ts new file mode 100644 index 000000000000..c5deea9c658e --- /dev/null +++ b/packages/angular-cli/plugins/glob-copy-webpack-plugin.ts @@ -0,0 +1,51 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as glob from 'glob'; +import * as denodeify from 'denodeify'; + +const globPromise = denodeify(glob); +const statPromise = denodeify(fs.stat); + +export interface GlobCopyWebpackPluginOptions { + patterns: string[]; + globOptions: any; +} + +export class GlobCopyWebpackPlugin { + constructor(private options: GlobCopyWebpackPluginOptions) { } + + apply(compiler: any): void { + let { patterns, globOptions } = this.options; + let context = globOptions.cwd || compiler.options.context; + + // convert dir patterns to globs + patterns = patterns.map(pattern => fs.statSync(path.resolve(context, pattern)).isDirectory() + ? pattern += '/**/*' + : pattern + ); + + // force nodir option, since we can't add dirs to assets + globOptions.nodir = true; + + compiler.plugin('emit', (compilation: any, cb: any) => { + let globs = patterns.map(pattern => globPromise(pattern, globOptions)); + + let addAsset = (relPath: string) => compilation.assets[relPath] + // don't re-add to assets + ? Promise.resolve() + : statPromise(path.resolve(context, relPath)) + .then((stat: any) => compilation.assets[relPath] = { + size: () => stat.size, + source: () => fs.readFileSync(path.resolve(context, relPath)) + }); + + Promise.all(globs) + // flatten results + .then(globResults => [].concat.apply([], globResults)) + // add each file to compilation assets + .then(relPaths => relPaths.forEach((relPath: string) => addAsset(relPath))) + .catch((err) => compilation.errors.push(err)) + .then(cb); + }); + } +} diff --git a/tests/e2e/tests/misc/assets.ts b/tests/e2e/tests/misc/assets.ts index aed01b53cf26..9dd9504cfac2 100644 --- a/tests/e2e/tests/misc/assets.ts +++ b/tests/e2e/tests/misc/assets.ts @@ -1,5 +1,6 @@ import {writeFile, expectFileToExist, expectFileToMatch} from '../../utils/fs'; import {ng} from '../../utils/process'; +import {updateJsonFile} from '../../utils/project'; import {expectToFail} from '../../utils/utils'; @@ -7,7 +8,15 @@ export default function() { return writeFile('src/assets/.file', '') .then(() => writeFile('src/assets/test.abc', 'hello world')) .then(() => ng('build')) + .then(() => expectFileToExist('dist/favicon.ico')) .then(() => expectFileToExist('dist/assets/.file')) .then(() => expectFileToMatch('dist/assets/test.abc', 'hello world')) - .then(() => expectToFail(() => expectFileToExist('dist/assets/.gitkeep'))); + .then(() => expectToFail(() => expectFileToExist('dist/assets/.gitkeep'))) + // doesn't break beta.16 projects + .then(() => updateJsonFile('angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['assets'] = 'assets'; + })) + .then(() => expectFileToExist('dist/assets/.file')) + .then(() => expectFileToMatch('dist/assets/test.abc', 'hello world')); }