From e5f58019ac2702617708cb00f91a5bfef194c37f Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Thu, 7 Dec 2017 16:16:18 +0100 Subject: [PATCH 01/15] Fix `Chunk.modules is deprecated` by updating webpack-md5-hash --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6b3b2c..005f7ce 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "url-loader": "^0.5.8", "webpack": "^3.2.0", "webpack-dev-server": "^2.5.1", - "webpack-md5-hash": "0.0.5", + "webpack-md5-hash": "0.0.6", "webpack-merge": "^4.1.0", "yaml-loader": "^0.5.0" } From a8de42413165112d69bb96e4e7e684132d06c267 Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Thu, 7 Dec 2017 16:39:52 +0100 Subject: [PATCH 02/15] v11.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 005f7ce..02d65aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sagui", - "version": "11.4.2", + "version": "11.4.3", "description": "Front-end tooling in a single dependency", "preferGlobal": false, "bin": { From 52483f3ebbaf4787933321f9545633d1c062008c Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 16:07:13 +0100 Subject: [PATCH 03/15] Add support for `chunks` configuration for pages --- src/configure-webpack/index.js | 4 ++-- src/index.js | 4 ++++ src/sagui-config-schema.json | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/configure-webpack/index.js b/src/configure-webpack/index.js index 8baaafe..dce4554 100644 --- a/src/configure-webpack/index.js +++ b/src/configure-webpack/index.js @@ -19,7 +19,7 @@ import buildLoadersConfig from './loaders' * @return ready to use webpack configuration as an array */ export default (saguiConfig = {}) => { - const { pages = [], libraries = [], ...sharedSaguiConfig } = saguiConfig + const { pages = [], libraries = [], chunks, ...sharedSaguiConfig } = saguiConfig const sharedWebpackConfig = merge.smart( buildSharedWebpackConfig(saguiConfig), @@ -28,7 +28,7 @@ export default (saguiConfig = {}) => { ) return [ - ...(pages.length > 0 ? [buildPagesConfig(pages, sharedSaguiConfig)] : []), + ...(pages.length > 0 ? [buildPagesConfig(pages, chunks, sharedSaguiConfig)] : []), ...libraries.map((libraryConfig) => buildLibraryConfig(libraryConfig, sharedSaguiConfig)) ].map((entryPointWebpackConfig) => merge.smart( sharedWebpackConfig, diff --git a/src/index.js b/src/index.js index c45c2da..719ca41 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,10 @@ const DEFAULT_SAGUI_CONFIG = { optimize: false, coverage: false, pages: [], + chunks: { + vendor: false, + common: true + }, disableLoaders: [], javaScript: {}, additionalWebpackConfig: {}, diff --git a/src/sagui-config-schema.json b/src/sagui-config-schema.json index 1744f73..dd78aaa 100644 --- a/src/sagui-config-schema.json +++ b/src/sagui-config-schema.json @@ -67,8 +67,38 @@ "description": "https://github.com/saguijs/sagui#libraries" }, "pages": { - "description": "https://github.com/saguijs/sagui#pages", - "type": "array" + "anyOf": [ + { + "type": "array" + }, + { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "independent": { + "type": "boolean" + } + }, + "type": "object" + } + ], + "description": "https://github.com/saguijs/sagui#pages" + }, + "chunks": { + "additionalProperties": false, + "properties": { + "common": { + "description": "https://github.com/saguijs/sagui#chunkscommon", + "type": "boolean" + }, + "vendor": { + "description": "https://github.com/saguijs/sagui#chunksvendor", + "type": "boolean" + } + }, + "type": "object" }, "style": { "additionalProperties": false, From c2147f6caf4ac8f6a4423e71b10f8ef61515d7e4 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 16:08:13 +0100 Subject: [PATCH 04/15] Use `chunks` configuration when building pages in order to enable/disable "vendor" and "common" chunks --- src/configure-webpack/build-pages-config.js | 43 ++++- .../build-pages-config.spec.js | 152 ++++++++++++++---- 2 files changed, 157 insertions(+), 38 deletions(-) diff --git a/src/configure-webpack/build-pages-config.js b/src/configure-webpack/build-pages-config.js index e936fed..f8435f1 100644 --- a/src/configure-webpack/build-pages-config.js +++ b/src/configure-webpack/build-pages-config.js @@ -3,11 +3,11 @@ import { join } from 'path' import { optimize } from 'webpack' import actions from '../actions' -export default (pages = [], { action, projectPath }) => { +export default (pages = [], chunksConfig, { action, projectPath }) => { if (pages.length === 0 || action === actions.TEST_UNIT) { return {} } const entry = configureEntry(pages) - const plugins = configurePlugins(pages, action) + const plugins = configurePlugins(pages, chunksConfig) const filenamePattern = action === actions.BUILD ? '[name]-[chunkhash]' : '[name]' // For better performance during development @@ -33,17 +33,50 @@ function configureEntry (pages) { return entry } -function configurePlugins (pages, action) { +function configurePlugins (pages, chunksConfig) { const plugins = pages.map((page) => { + const chunks = Object.keys(chunksConfig) + .reduce((chunks, key) => { + if (chunksConfig[key]) { + chunks.unshift(key) + } + return chunks + }, [page]) + return new HtmlWebpackPlugin({ template: `${page}.html`, filename: `${page}.html`, - chunks: ['common', page] + chunks }) }) if (pages.length > 1) { - plugins.push(new optimize.CommonsChunkPlugin({ name: 'common', minChunks: 2 })) + const chunks = pages.reduce((chunks, page) => { + if (typeof page === 'string') { + chunks.push(page) + } + if (page.independent !== true) { + chunks.push(page.name) + } + return chunks + }, []) + + if (chunksConfig.vendor) { + plugins.push(new optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks: (module, context) => { + return /node_modules/.test(context) + }, + chunks + })) + } + if (chunksConfig.common) { + plugins.push(new optimize.CommonsChunkPlugin({ + name: 'common', + minChunks: 2, + chunks + })) + } } return plugins diff --git a/src/configure-webpack/build-pages-config.spec.js b/src/configure-webpack/build-pages-config.spec.js index 4a33e77..0cae955 100644 --- a/src/configure-webpack/build-pages-config.spec.js +++ b/src/configure-webpack/build-pages-config.spec.js @@ -9,17 +9,17 @@ import buildPagesConfig from './build-pages-config' const projectPath = '/tmp/projec-path' -describe('pages webpack preset', function () { +describe.only('pages webpack preset', function () { describe('empty pages', function () { it('should return an empty configuration', function () { - const webpackConfig = buildPagesConfig([], { }) + const webpackConfig = buildPagesConfig([], { }, { }) expect(webpackConfig).eql({}) }) }) describe('action is TEST_UNIT', function () { it('should return an empty configuration', function () { - const webpackConfig = buildPagesConfig(['index'], { action: actions.TEST_UNIT }) + const webpackConfig = buildPagesConfig(['index'], { }, { action: actions.TEST_UNIT }) expect(webpackConfig).eql({}) }) }) @@ -28,12 +28,12 @@ describe('pages webpack preset', function () { const baseConfig = { projectPath } it('should have the output path configured as the build folder', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.output.path).eql(path.join(projectPath, 'dist')) }) it('should have the entrypoints setup with the index', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.entry).eql({ index: ['./index'] @@ -41,7 +41,7 @@ describe('pages webpack preset', function () { }) it('should have a plugin setting up the HTML template', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { common: true }, baseConfig) const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) expect(html.length).equal(1) @@ -54,7 +54,7 @@ describe('pages webpack preset', function () { }) it('should NOT have the CommonsChunkPlugin enabled (not needed)', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons.length).equal(0) @@ -65,7 +65,7 @@ describe('pages webpack preset', function () { const baseConfig = { projectPath } it('should have two distinct entrypoints', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { }, baseConfig) expect(webpackConfig.entry).eql({ index: ['./index'], @@ -73,35 +73,121 @@ describe('pages webpack preset', function () { }) }) - it('should have a plugin setting up the HTML template for each chunk', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], baseConfig) + describe('when chunks.common is "true"', function () { + it('should have a plugin setting up the HTML template for each chunk', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) - const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) - expect(html.length).equal(2) + const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) + expect(html.length).equal(2) - const firstOptions = html[0].options - expect(firstOptions.chunks).deep.eql([ 'common', 'index' ]) - expect(firstOptions.filename).deep.eql('index.html') - expect(firstOptions.template).deep.eql('index.html') + const firstOptions = html[0].options + expect(firstOptions.chunks).deep.eql([ 'common', 'index' ]) + expect(firstOptions.filename).deep.eql('index.html') + expect(firstOptions.template).deep.eql('index.html') - const secondOptions = html[1].options - expect(secondOptions.chunks).deep.eql([ 'common', 'demo' ]) - expect(secondOptions.filename).deep.eql('demo.html') - expect(secondOptions.template).deep.eql('demo.html') - }) + const secondOptions = html[1].options + expect(secondOptions.chunks).deep.eql([ 'common', 'demo' ]) + expect(secondOptions.filename).deep.eql('demo.html') + expect(secondOptions.template).deep.eql('demo.html') + }) - it('should have the CommonsChunkPlugin enabled', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], baseConfig) + it('should have the CommonsChunkPlugin enabled', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) - const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) - expect(commons.length).equal(1) + const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(commons.length).equal(1) + }) + + it('should set name to "common" in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + + const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(commons[0].chunkNames[0]).equal('common') + }) + + it('should set minChunks to 2 in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + + const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(commons[0].minChunks).equal(2) + }) + + it('should set pages as chunks in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + + const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(commons[0].selectedChunks[0]).equal('index') + expect(commons[0].selectedChunks[2]).equal('demo') + }) }) - it('should set minChunks to 2 in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], baseConfig) + describe('when chunks.vendor is "true"', function () { + it('should have a plugin setting up the HTML template for each chunk', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) - const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) - expect(commons[0].minChunks).equal(2) + const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) + expect(html.length).equal(2) + + const firstOptions = html[0].options + expect(firstOptions.chunks).deep.eql([ 'vendor', 'index' ]) + expect(firstOptions.filename).deep.eql('index.html') + expect(firstOptions.template).deep.eql('index.html') + + const secondOptions = html[1].options + expect(secondOptions.chunks).deep.eql([ 'vendor', 'demo' ]) + expect(secondOptions.filename).deep.eql('demo.html') + expect(secondOptions.template).deep.eql('demo.html') + }) + + it('should have the CommonsChunkPlugin enabled', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + + const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(vendor.length).equal(1) + }) + + it('should set name to "vendor" in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + + const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(vendor[0].chunkNames[0]).equal('vendor') + }) + + it('should set minChunks to a function in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + + const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(vendor[0].minChunks).to.be.a('function') + }) + + it('should set pages as chunks in the CommonsChunkPlugin', function () { + const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + + const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + expect(vendor[0].selectedChunks[0]).equal('index') + expect(vendor[0].selectedChunks[2]).equal('demo') + }) + }) + + describe('when a page is flagged as independent', function () { + it('should be excluded from both the vendor and common chunks', function () { + const webpackConfig = buildPagesConfig([ + 'index', + { name: 'demo', independent: true } + ], { + vendor: true, + common: true + }, baseConfig) + + const plugins = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) + const vendor = plugins[0] + const common = plugins[1] + + expect(vendor.selectedChunks[0]).equal('index') + expect(vendor.selectedChunks[2]).equal(undefined) + expect(common.selectedChunks[0]).equal('index') + expect(common.selectedChunks[2]).equal(undefined) + }) }) }) @@ -111,12 +197,12 @@ describe('pages webpack preset', function () { } it('should setup the output filename of entrypoints based on the name of the page and chunkhash', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.output.filename).eql('[name]-[chunkhash].js') }) it('should setup the output filename of other files based on their name and chunkhash', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.output.chunkFilename).eql('[name]-[chunkhash].chunk.js') }) }) @@ -127,12 +213,12 @@ describe('pages webpack preset', function () { } it('should setup the output filename of entrypoints based on the name of the page', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.output.filename).eql('[name].js') }) it('should setup the output filename of other files based on their name', function () { - const webpackConfig = buildPagesConfig(['index'], baseConfig) + const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) expect(webpackConfig.output.chunkFilename).eql('[name].chunk.js') }) }) From 8142b02993c5f3967a4b42272d064f88b65af39c Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 16:08:25 +0100 Subject: [PATCH 05/15] Update README.md --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index a32a257..29d7ff2 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,47 @@ The previous configuration will expect and build the files: - `src/index.html` => `dist/index.html` - `src/index.js` => `dist/index.js` +#### Excluding a page from chunks + +If you want a page to be excluded from either the [vendor](#vendor) or [common](#common) chunk, then you can do so by providing an object with a `name` and `independent` flag (set to `true`) instead of just the name of the page. + +```js +// sagui.config.js +module.exports = { + pages: ['index', { name: 'about', independent: true }] +} +``` + +### `chunks` + +When multiple pages are configured, it's a good practice to bundle external or common dependencies of your pages into their own chunks in order to reduce the size of the built pages. This can be done with the following flags: + +#### `vendor` + +If you want all your external dependencies in your [pages](#pages) to be bundled together in a "vendor" chunk, then set this flag to `true`. By default it is set to `false`. + +```js +// sagui.config.js +module.exports = { + chunks: { + vendor: true + } +} +``` + +#### `common` + +If you do not want all the common dependencies of your [pages](#pages) to be bundled together in a "common" chunk, then set this flag to `false`. By default it is set to `true`. + +```js +// sagui.config.js +module.exports = { + chunks: { + common: false + } +} +``` + ### `libraries` Create **reusable libraries** that can be shared across applications. Sagui will take care of the build process so that external libraries are not bundled and that you have a CommonJS module as the output. From 38b4a22415cc1e535183aeb8202cd3fdd6809d43 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 16:12:18 +0100 Subject: [PATCH 06/15] Fix unit tests --- src/configure-webpack/build-pages-config.spec.js | 2 +- src/configure-webpack/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/configure-webpack/build-pages-config.spec.js b/src/configure-webpack/build-pages-config.spec.js index 0cae955..457b591 100644 --- a/src/configure-webpack/build-pages-config.spec.js +++ b/src/configure-webpack/build-pages-config.spec.js @@ -9,7 +9,7 @@ import buildPagesConfig from './build-pages-config' const projectPath = '/tmp/projec-path' -describe.only('pages webpack preset', function () { +describe('pages webpack preset', function () { describe('empty pages', function () { it('should return an empty configuration', function () { const webpackConfig = buildPagesConfig([], { }, { }) diff --git a/src/configure-webpack/index.js b/src/configure-webpack/index.js index dce4554..f02886c 100644 --- a/src/configure-webpack/index.js +++ b/src/configure-webpack/index.js @@ -19,7 +19,7 @@ import buildLoadersConfig from './loaders' * @return ready to use webpack configuration as an array */ export default (saguiConfig = {}) => { - const { pages = [], libraries = [], chunks, ...sharedSaguiConfig } = saguiConfig + const { pages = [], libraries = [], chunks = {}, ...sharedSaguiConfig } = saguiConfig const sharedWebpackConfig = merge.smart( buildSharedWebpackConfig(saguiConfig), From 09683cbdfa23f975414a3b308a8d2ddf73717360 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 18:51:11 +0100 Subject: [PATCH 07/15] Pass `chunks` as part of `sharedSaguiConfig` instead of an argument --- src/configure-webpack/build-pages-config.js | 4 +- .../build-pages-config.spec.js | 54 ++++++++++--------- src/configure-webpack/index.js | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/configure-webpack/build-pages-config.js b/src/configure-webpack/build-pages-config.js index f8435f1..9ca62f2 100644 --- a/src/configure-webpack/build-pages-config.js +++ b/src/configure-webpack/build-pages-config.js @@ -3,11 +3,11 @@ import { join } from 'path' import { optimize } from 'webpack' import actions from '../actions' -export default (pages = [], chunksConfig, { action, projectPath }) => { +export default (pages = [], { action, chunks = {}, projectPath }) => { if (pages.length === 0 || action === actions.TEST_UNIT) { return {} } const entry = configureEntry(pages) - const plugins = configurePlugins(pages, chunksConfig) + const plugins = configurePlugins(pages, chunks) const filenamePattern = action === actions.BUILD ? '[name]-[chunkhash]' : '[name]' // For better performance during development diff --git a/src/configure-webpack/build-pages-config.spec.js b/src/configure-webpack/build-pages-config.spec.js index 457b591..44a3d57 100644 --- a/src/configure-webpack/build-pages-config.spec.js +++ b/src/configure-webpack/build-pages-config.spec.js @@ -12,14 +12,14 @@ const projectPath = '/tmp/projec-path' describe('pages webpack preset', function () { describe('empty pages', function () { it('should return an empty configuration', function () { - const webpackConfig = buildPagesConfig([], { }, { }) + const webpackConfig = buildPagesConfig([], { }) expect(webpackConfig).eql({}) }) }) describe('action is TEST_UNIT', function () { it('should return an empty configuration', function () { - const webpackConfig = buildPagesConfig(['index'], { }, { action: actions.TEST_UNIT }) + const webpackConfig = buildPagesConfig(['index'], { action: actions.TEST_UNIT }) expect(webpackConfig).eql({}) }) }) @@ -28,12 +28,12 @@ describe('pages webpack preset', function () { const baseConfig = { projectPath } it('should have the output path configured as the build folder', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.output.path).eql(path.join(projectPath, 'dist')) }) it('should have the entrypoints setup with the index', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.entry).eql({ index: ['./index'] @@ -41,7 +41,7 @@ describe('pages webpack preset', function () { }) it('should have a plugin setting up the HTML template', function () { - const webpackConfig = buildPagesConfig(['index'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], { chunks: { common: true }, ...baseConfig }) const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) expect(html.length).equal(1) @@ -54,7 +54,7 @@ describe('pages webpack preset', function () { }) it('should NOT have the CommonsChunkPlugin enabled (not needed)', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons.length).equal(0) @@ -65,7 +65,7 @@ describe('pages webpack preset', function () { const baseConfig = { projectPath } it('should have two distinct entrypoints', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], baseConfig) expect(webpackConfig.entry).eql({ index: ['./index'], @@ -75,7 +75,7 @@ describe('pages webpack preset', function () { describe('when chunks.common is "true"', function () { it('should have a plugin setting up the HTML template for each chunk', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { common: true }, ...baseConfig }) const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) expect(html.length).equal(2) @@ -92,28 +92,28 @@ describe('pages webpack preset', function () { }) it('should have the CommonsChunkPlugin enabled', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { common: true }, ...baseConfig }) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons.length).equal(1) }) it('should set name to "common" in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { common: true }, ...baseConfig }) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons[0].chunkNames[0]).equal('common') }) it('should set minChunks to 2 in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { common: true }, ...baseConfig }) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons[0].minChunks).equal(2) }) it('should set pages as chunks in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { common: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { common: true }, ...baseConfig }) const commons = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(commons[0].selectedChunks[0]).equal('index') @@ -123,7 +123,7 @@ describe('pages webpack preset', function () { describe('when chunks.vendor is "true"', function () { it('should have a plugin setting up the HTML template for each chunk', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { vendor: true }, ...baseConfig }) const html = webpackConfig.plugins.filter((plugin) => plugin instanceof HtmlWebpackPlugin) expect(html.length).equal(2) @@ -140,28 +140,28 @@ describe('pages webpack preset', function () { }) it('should have the CommonsChunkPlugin enabled', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { vendor: true }, ...baseConfig }) const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(vendor.length).equal(1) }) it('should set name to "vendor" in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { vendor: true }, ...baseConfig }) const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(vendor[0].chunkNames[0]).equal('vendor') }) it('should set minChunks to a function in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { vendor: true }, ...baseConfig }) const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(vendor[0].minChunks).to.be.a('function') }) it('should set pages as chunks in the CommonsChunkPlugin', function () { - const webpackConfig = buildPagesConfig(['index', 'demo'], { vendor: true }, baseConfig) + const webpackConfig = buildPagesConfig(['index', 'demo'], { chunks: { vendor: true }, ...baseConfig }) const vendor = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) expect(vendor[0].selectedChunks[0]).equal('index') @@ -175,16 +175,22 @@ describe('pages webpack preset', function () { 'index', { name: 'demo', independent: true } ], { - vendor: true, - common: true - }, baseConfig) + chunks: { + vendor: true, + common: true + }, + ...baseConfig + }) const plugins = webpackConfig.plugins.filter((plugin) => plugin instanceof optimize.CommonsChunkPlugin) const vendor = plugins[0] const common = plugins[1] + expect(vendor.selectedChunks.length).equal(2) expect(vendor.selectedChunks[0]).equal('index') expect(vendor.selectedChunks[2]).equal(undefined) + + expect(common.selectedChunks.length).equal(2) expect(common.selectedChunks[0]).equal('index') expect(common.selectedChunks[2]).equal(undefined) }) @@ -197,12 +203,12 @@ describe('pages webpack preset', function () { } it('should setup the output filename of entrypoints based on the name of the page and chunkhash', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.output.filename).eql('[name]-[chunkhash].js') }) it('should setup the output filename of other files based on their name and chunkhash', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.output.chunkFilename).eql('[name]-[chunkhash].chunk.js') }) }) @@ -213,12 +219,12 @@ describe('pages webpack preset', function () { } it('should setup the output filename of entrypoints based on the name of the page', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.output.filename).eql('[name].js') }) it('should setup the output filename of other files based on their name', function () { - const webpackConfig = buildPagesConfig(['index'], { }, baseConfig) + const webpackConfig = buildPagesConfig(['index'], baseConfig) expect(webpackConfig.output.chunkFilename).eql('[name].chunk.js') }) }) diff --git a/src/configure-webpack/index.js b/src/configure-webpack/index.js index f02886c..8baaafe 100644 --- a/src/configure-webpack/index.js +++ b/src/configure-webpack/index.js @@ -19,7 +19,7 @@ import buildLoadersConfig from './loaders' * @return ready to use webpack configuration as an array */ export default (saguiConfig = {}) => { - const { pages = [], libraries = [], chunks = {}, ...sharedSaguiConfig } = saguiConfig + const { pages = [], libraries = [], ...sharedSaguiConfig } = saguiConfig const sharedWebpackConfig = merge.smart( buildSharedWebpackConfig(saguiConfig), @@ -28,7 +28,7 @@ export default (saguiConfig = {}) => { ) return [ - ...(pages.length > 0 ? [buildPagesConfig(pages, chunks, sharedSaguiConfig)] : []), + ...(pages.length > 0 ? [buildPagesConfig(pages, sharedSaguiConfig)] : []), ...libraries.map((libraryConfig) => buildLibraryConfig(libraryConfig, sharedSaguiConfig)) ].map((entryPointWebpackConfig) => merge.smart( sharedWebpackConfig, From 42e9b7e99ce0a27507e9a6dd2c7fbb83c0f4817a Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 18:51:32 +0100 Subject: [PATCH 08/15] Make it clear that external dependencies refer to `node_modules` in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29d7ff2..3250519 100644 --- a/README.md +++ b/README.md @@ -185,11 +185,11 @@ module.exports = { ### `chunks` -When multiple pages are configured, it's a good practice to bundle external or common dependencies of your pages into their own chunks in order to reduce the size of the built pages. This can be done with the following flags: +When multiple pages are configured, it's a good practice to bundle external (`node_modules`) or common dependencies of your pages into their own chunks in order to reduce the size of the built pages. This can be done with the following flags: #### `vendor` -If you want all your external dependencies in your [pages](#pages) to be bundled together in a "vendor" chunk, then set this flag to `true`. By default it is set to `false`. +If you want all your external dependencies (`node_modules`) in your [pages](#pages) to be bundled together in a "vendor" chunk, then set this flag to `true`. By default it is set to `false`. ```js // sagui.config.js From 2e517306fcbbba637dbe0357623da98b416fc656 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 18:52:47 +0100 Subject: [PATCH 09/15] Change example code for independent page in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3250519..242370e 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ If you want a page to be excluded from either the [vendor](#vendor) or [common]( ```js // sagui.config.js module.exports = { - pages: ['index', { name: 'about', independent: true }] + pages: ['index', 'about', { name: 'demo', independent: true }] } ``` From 5b316225e9276c3d45052e704407bbf47a5029d0 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 26 Dec 2017 19:10:53 +0100 Subject: [PATCH 10/15] Flatten `chunks` documentation in README.md --- README.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 242370e..2fced52 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ The previous configuration will expect and build the files: #### Excluding a page from chunks -If you want a page to be excluded from either the [vendor](#vendor) or [common](#common) chunk, then you can do so by providing an object with a `name` and `independent` flag (set to `true`) instead of just the name of the page. +If you want a page to be excluded from either the [vendor](#chunksvendor) or [common](#chunkscommon) chunk, then you can do so by providing an object with a `name` and `independent` flag (set to `true`) instead of just the name of the page. ```js // sagui.config.js @@ -183,11 +183,7 @@ module.exports = { } ``` -### `chunks` - -When multiple pages are configured, it's a good practice to bundle external (`node_modules`) or common dependencies of your pages into their own chunks in order to reduce the size of the built pages. This can be done with the following flags: - -#### `vendor` +### `chunks.vendor` If you want all your external dependencies (`node_modules`) in your [pages](#pages) to be bundled together in a "vendor" chunk, then set this flag to `true`. By default it is set to `false`. @@ -200,7 +196,7 @@ module.exports = { } ``` -#### `common` +### `chunks.common` If you do not want all the common dependencies of your [pages](#pages) to be bundled together in a "common" chunk, then set this flag to `false`. By default it is set to `true`. From 044ec932569a1d8e81ed4a61a9f406a64d5c0712 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 2 Jan 2018 10:59:12 +0100 Subject: [PATCH 11/15] Fix issues --- src/configure-webpack/build-pages-config.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/configure-webpack/build-pages-config.js b/src/configure-webpack/build-pages-config.js index 9ca62f2..1f46c46 100644 --- a/src/configure-webpack/build-pages-config.js +++ b/src/configure-webpack/build-pages-config.js @@ -35,17 +35,18 @@ function configureEntry (pages) { function configurePlugins (pages, chunksConfig) { const plugins = pages.map((page) => { + const pageName = typeof page === 'string' ? page : page.name const chunks = Object.keys(chunksConfig) .reduce((chunks, key) => { if (chunksConfig[key]) { chunks.unshift(key) } return chunks - }, [page]) + }, [pageName]) return new HtmlWebpackPlugin({ - template: `${page}.html`, - filename: `${page}.html`, + template: `${pageName}.html`, + filename: `${pageName}.html`, chunks }) }) @@ -64,8 +65,8 @@ function configurePlugins (pages, chunksConfig) { if (chunksConfig.vendor) { plugins.push(new optimize.CommonsChunkPlugin({ name: 'vendor', - minChunks: (module, context) => { - return /node_modules/.test(context) + minChunks: (module) => { + return /node_modules/.test(module.context) }, chunks })) From 52c110f42161049b568e3bd6a0b02a12e0ef0621 Mon Sep 17 00:00:00 2001 From: Pierre Wahlgren Date: Tue, 2 Jan 2018 11:14:35 +0100 Subject: [PATCH 12/15] Fix more issues --- src/configure-webpack/build-pages-config.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/configure-webpack/build-pages-config.js b/src/configure-webpack/build-pages-config.js index 1f46c46..87ace7f 100644 --- a/src/configure-webpack/build-pages-config.js +++ b/src/configure-webpack/build-pages-config.js @@ -27,7 +27,8 @@ function configureEntry (pages) { let entry = {} pages.forEach((page) => { - entry[page] = [`./${page}`] + const pageName = getPageName(page) + entry[pageName] = [`./${pageName}`] }) return entry @@ -35,7 +36,7 @@ function configureEntry (pages) { function configurePlugins (pages, chunksConfig) { const plugins = pages.map((page) => { - const pageName = typeof page === 'string' ? page : page.name + const pageName = getPageName(page) const chunks = Object.keys(chunksConfig) .reduce((chunks, key) => { if (chunksConfig[key]) { @@ -82,3 +83,7 @@ function configurePlugins (pages, chunksConfig) { return plugins } + +function getPageName (page) { + return (typeof page === 'string' ? page : page.name) +} From 5884302467e74f0766780f8bef68cf18b2740df4 Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Tue, 2 Jan 2018 16:06:07 +0100 Subject: [PATCH 13/15] Integration tests for chunks configuration --- package.json | 1 + .../node_modules/.DS_Store | Bin 0 -> 6148 bytes .../node_modules/dependencyA/.DS_Store | Bin 0 -> 6148 bytes .../node_modules/dependencyA/index.js | 3 + .../node_modules/dependencyA/package.json | 14 +++ .../node_modules/dependencyB/index.js | 3 + .../node_modules/dependencyB/package.json | 14 +++ .../sagui.config.js | 7 ++ .../src/about.html | 11 ++ .../src/about.js | 5 + .../src/demo.html | 11 ++ .../project-with-independent-page/src/demo.js | 7 ++ .../src/index.html | 11 ++ .../src/index.js | 5 + .../src/shared.js | 1 + .../sagui.config.js | 6 ++ .../src/about.html | 11 ++ .../src/about.js | 3 + .../src/index.html | 11 ++ .../src/index.js | 3 + .../src/shared.js | 1 + .../node_modules/.DS_Store | Bin 0 -> 6148 bytes .../node_modules/dependencyA/.DS_Store | Bin 0 -> 6148 bytes .../node_modules/dependencyA/index.js | 3 + .../node_modules/dependencyA/package.json | 14 +++ .../node_modules/dependencyB/index.js | 3 + .../node_modules/dependencyB/package.json | 14 +++ .../sagui.config.js | 6 ++ .../src/about.html | 11 ++ .../src/about.js | 5 + .../src/index.html | 11 ++ .../src/index.js | 5 + .../src/shared.js | 1 + .../project-with-two-pages/sagui.config.js | 3 + .../project-with-two-pages/src/about.html | 11 ++ .../project-with-two-pages/src/about.js | 3 + .../project-with-two-pages/src/index.html | 11 ++ .../project-with-two-pages/src/index.js | 3 + .../project-with-two-pages/src/shared.js | 1 + spec/integration/index.integration-spec.js | 100 ++++++++++++++++++ src/index.js | 8 +- 41 files changed, 335 insertions(+), 6 deletions(-) create mode 100644 spec/fixtures/project-with-independent-page/node_modules/.DS_Store create mode 100644 spec/fixtures/project-with-independent-page/node_modules/dependencyA/.DS_Store create mode 100644 spec/fixtures/project-with-independent-page/node_modules/dependencyA/index.js create mode 100644 spec/fixtures/project-with-independent-page/node_modules/dependencyA/package.json create mode 100644 spec/fixtures/project-with-independent-page/node_modules/dependencyB/index.js create mode 100644 spec/fixtures/project-with-independent-page/node_modules/dependencyB/package.json create mode 100644 spec/fixtures/project-with-independent-page/sagui.config.js create mode 100644 spec/fixtures/project-with-independent-page/src/about.html create mode 100644 spec/fixtures/project-with-independent-page/src/about.js create mode 100644 spec/fixtures/project-with-independent-page/src/demo.html create mode 100644 spec/fixtures/project-with-independent-page/src/demo.js create mode 100644 spec/fixtures/project-with-independent-page/src/index.html create mode 100644 spec/fixtures/project-with-independent-page/src/index.js create mode 100644 spec/fixtures/project-with-independent-page/src/shared.js create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/sagui.config.js create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/src/about.html create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/src/about.js create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/src/index.html create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/src/index.js create mode 100644 spec/fixtures/project-with-two-pages-disabled-common/src/shared.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/.DS_Store create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/dependencyA/.DS_Store create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/dependencyA/index.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/dependencyA/package.json create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/dependencyB/index.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/dependencyB/package.json create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/sagui.config.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/src/about.html create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/src/about.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/src/index.html create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/src/index.js create mode 100644 spec/fixtures/project-with-two-pages-enabled-vendor/src/shared.js create mode 100644 spec/fixtures/project-with-two-pages/sagui.config.js create mode 100644 spec/fixtures/project-with-two-pages/src/about.html create mode 100644 spec/fixtures/project-with-two-pages/src/about.js create mode 100644 spec/fixtures/project-with-two-pages/src/index.html create mode 100644 spec/fixtures/project-with-two-pages/src/index.js create mode 100644 spec/fixtures/project-with-two-pages/src/shared.js diff --git a/package.json b/package.json index 02d65aa..e03c398 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "karma-phantomjs-launcher": "^1.0.4", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^2.0.3", + "lodash.merge": "^4.6.0", "lodash.uniq": "^4.5.0", "node-sass": "^4.5.2", "null-loader": "^0.1.1", diff --git a/spec/fixtures/project-with-independent-page/node_modules/.DS_Store b/spec/fixtures/project-with-independent-page/node_modules/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5cd123be85664128fd04e9f50a303f1f0ff61225 GIT binary patch literal 6148 zcmeHKOHRW;4E2;s6m`=D3zl;M7QI1;!U=K#P=X3ln?xlA!77*JE-ZQesI*NhK^GN5 zwq(DFV>_8Q(M(K4AzhVIq6ra=pp1hHm_5SxS$i@t&p9wijfzTopo$85>qWccFEYSu zH9# z)LB-p+Jflg{q<8S{35qJI=6W|{+)Mt+L*QvodIXS88|ruysJY-V@0>lfHU9>tQp|_ zAwn4w!%k7Q4j5?xfJ3-NFy>x@bE08l*eT);Bxov7Q-ie_LDS)n7MB=yikeQrnvY;l z4%VRrc{=Jx38#=Kx^)Jefo%rP^m4-I|Lx)ae>=&qoB?OxUojAdS)QeslAW!+$?;hm rp?6Rgj_VZHDHzC6j95O3ub?9EM;-tZ!%h(%i2n!(4Q`x)lQQrHYb08< literal 0 HcmV?d00001 diff --git a/spec/fixtures/project-with-independent-page/node_modules/dependencyA/.DS_Store b/spec/fixtures/project-with-independent-page/node_modules/dependencyA/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-independent-page/src/about.js b/spec/fixtures/project-with-independent-page/src/about.js new file mode 100644 index 0000000..cc26831 --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/about.js @@ -0,0 +1,5 @@ +import shared from './shared' +import dependencyA from 'dependencyA' + +shared() +dependencyA() diff --git a/spec/fixtures/project-with-independent-page/src/demo.html b/spec/fixtures/project-with-independent-page/src/demo.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/demo.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-independent-page/src/demo.js b/spec/fixtures/project-with-independent-page/src/demo.js new file mode 100644 index 0000000..12daf59 --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/demo.js @@ -0,0 +1,7 @@ +import shared from './shared' +import dependencyA from 'dependencyA' +import dependencyB from 'dependencyB' + +shared() +dependencyA() +dependencyB() diff --git a/spec/fixtures/project-with-independent-page/src/index.html b/spec/fixtures/project-with-independent-page/src/index.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/index.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-independent-page/src/index.js b/spec/fixtures/project-with-independent-page/src/index.js new file mode 100644 index 0000000..67de6da --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/index.js @@ -0,0 +1,5 @@ +import shared from './shared' +import dependencyB from 'dependencyB' + +shared() +dependencyB() diff --git a/spec/fixtures/project-with-independent-page/src/shared.js b/spec/fixtures/project-with-independent-page/src/shared.js new file mode 100644 index 0000000..2ba7300 --- /dev/null +++ b/spec/fixtures/project-with-independent-page/src/shared.js @@ -0,0 +1 @@ +export default () => console.log('shared module') diff --git a/spec/fixtures/project-with-two-pages-disabled-common/sagui.config.js b/spec/fixtures/project-with-two-pages-disabled-common/sagui.config.js new file mode 100644 index 0000000..cf5fe22 --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/sagui.config.js @@ -0,0 +1,6 @@ +module.exports = { + pages: ['index', 'about'], + chunks: { + common: false + } +} diff --git a/spec/fixtures/project-with-two-pages-disabled-common/src/about.html b/spec/fixtures/project-with-two-pages-disabled-common/src/about.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/src/about.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages-disabled-common/src/about.js b/spec/fixtures/project-with-two-pages-disabled-common/src/about.js new file mode 100644 index 0000000..1781aab --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/src/about.js @@ -0,0 +1,3 @@ +import shared from './shared' + +shared() diff --git a/spec/fixtures/project-with-two-pages-disabled-common/src/index.html b/spec/fixtures/project-with-two-pages-disabled-common/src/index.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/src/index.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages-disabled-common/src/index.js b/spec/fixtures/project-with-two-pages-disabled-common/src/index.js new file mode 100644 index 0000000..1781aab --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/src/index.js @@ -0,0 +1,3 @@ +import shared from './shared' + +shared() diff --git a/spec/fixtures/project-with-two-pages-disabled-common/src/shared.js b/spec/fixtures/project-with-two-pages-disabled-common/src/shared.js new file mode 100644 index 0000000..2ba7300 --- /dev/null +++ b/spec/fixtures/project-with-two-pages-disabled-common/src/shared.js @@ -0,0 +1 @@ +export default () => console.log('shared module') diff --git a/spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/.DS_Store b/spec/fixtures/project-with-two-pages-enabled-vendor/node_modules/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..84549d09c235e1d809f7de4cd4f655488f4b4064 GIT binary patch literal 6148 zcmeHKIc~#13?vg32GqDrxnJ-PHiG;De;_~{xG|I*p4i_O%V(Ma6#EIarvR?VHc+Av1>RXAH8HHyPwrerDCT6UJ?w_2JA5|M`3K zIqctuU7UWWs=KPXeG9WxfC^9nDnJFOz)1o0Y_mlh$VdgK02MeZVBd!VH>`H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages-enabled-vendor/src/about.js b/spec/fixtures/project-with-two-pages-enabled-vendor/src/about.js new file mode 100644 index 0000000..cc26831 --- /dev/null +++ b/spec/fixtures/project-with-two-pages-enabled-vendor/src/about.js @@ -0,0 +1,5 @@ +import shared from './shared' +import dependencyA from 'dependencyA' + +shared() +dependencyA() diff --git a/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.html b/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.js b/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.js new file mode 100644 index 0000000..67de6da --- /dev/null +++ b/spec/fixtures/project-with-two-pages-enabled-vendor/src/index.js @@ -0,0 +1,5 @@ +import shared from './shared' +import dependencyB from 'dependencyB' + +shared() +dependencyB() diff --git a/spec/fixtures/project-with-two-pages-enabled-vendor/src/shared.js b/spec/fixtures/project-with-two-pages-enabled-vendor/src/shared.js new file mode 100644 index 0000000..2ba7300 --- /dev/null +++ b/spec/fixtures/project-with-two-pages-enabled-vendor/src/shared.js @@ -0,0 +1 @@ +export default () => console.log('shared module') diff --git a/spec/fixtures/project-with-two-pages/sagui.config.js b/spec/fixtures/project-with-two-pages/sagui.config.js new file mode 100644 index 0000000..74ab9d0 --- /dev/null +++ b/spec/fixtures/project-with-two-pages/sagui.config.js @@ -0,0 +1,3 @@ +module.exports = { + pages: ['index', 'about'] +} diff --git a/spec/fixtures/project-with-two-pages/src/about.html b/spec/fixtures/project-with-two-pages/src/about.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-two-pages/src/about.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages/src/about.js b/spec/fixtures/project-with-two-pages/src/about.js new file mode 100644 index 0000000..1781aab --- /dev/null +++ b/spec/fixtures/project-with-two-pages/src/about.js @@ -0,0 +1,3 @@ +import shared from './shared' + +shared() diff --git a/spec/fixtures/project-with-two-pages/src/index.html b/spec/fixtures/project-with-two-pages/src/index.html new file mode 100644 index 0000000..08824ab --- /dev/null +++ b/spec/fixtures/project-with-two-pages/src/index.html @@ -0,0 +1,11 @@ + + + + Hello + + + + +
+ + diff --git a/spec/fixtures/project-with-two-pages/src/index.js b/spec/fixtures/project-with-two-pages/src/index.js new file mode 100644 index 0000000..1781aab --- /dev/null +++ b/spec/fixtures/project-with-two-pages/src/index.js @@ -0,0 +1,3 @@ +import shared from './shared' + +shared() diff --git a/spec/fixtures/project-with-two-pages/src/shared.js b/spec/fixtures/project-with-two-pages/src/shared.js new file mode 100644 index 0000000..2ba7300 --- /dev/null +++ b/spec/fixtures/project-with-two-pages/src/shared.js @@ -0,0 +1 @@ +export default () => console.log('shared module') diff --git a/spec/integration/index.integration-spec.js b/spec/integration/index.integration-spec.js index b89b829..ee3be1c 100644 --- a/spec/integration/index.integration-spec.js +++ b/spec/integration/index.integration-spec.js @@ -137,6 +137,106 @@ describe('[integration] sagui', function () { }) }) + describe('chunks', () => { + describe('project with two pages on build', () => { + const projectFixture = path.join(__dirname, '../fixtures/project-with-two-pages') + beforeEach(function () { + fs.copySync(projectFixture, projectPath, { overwrite: true }) + return sagui({ projectPath, action: actions.BUILD }) + }) + + it('should have created a common.js file', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + expect(files.filter((file) => file.match(/common.+\.js$/))).not.to.be.empty + }) + + it('should NOT have created a vendor.js file', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + expect(files.filter((file) => file.match(/vendor.+\.js$/))).to.be.empty + }) + }) + + describe('project with two pages and disabled common chunks on build', () => { + const projectFixture = path.join(__dirname, '../fixtures/project-with-two-pages-disabled-common') + beforeEach(function () { + fs.copySync(projectFixture, projectPath, { overwrite: true }) + return sagui({ projectPath, action: actions.BUILD }) + }) + + it('should not have created a common.js file', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + expect(files.filter((file) => file.match(/common.+\.js$/))).to.be.empty + }) + + it('should NOT have created a vendor.js file', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + expect(files.filter((file) => file.match(/vendor.+\.js$/))).to.be.empty + }) + }) + + describe('project with two pages and enabled vendor chunks on build', () => { + const projectFixture = path.join(__dirname, '../fixtures/project-with-two-pages-enabled-vendor') + beforeEach(function () { + fs.copySync(projectFixture, projectPath, { overwrite: true }) + return sagui({ projectPath, action: actions.BUILD }) + }) + + it('should have created a common.js file without the content of the node_modules dependencies', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + const commonFiles = files.filter((file) => file.match(/common.+\.js$/)) + const commonContent = fs.readFileSync(path.join(projectPath, 'dist', commonFiles[0])).toString() + expect(commonFiles).not.to.be.empty + expect(commonContent).not.to.have.string('dependencyA') + expect(commonContent).not.to.have.string('dependencyB') + }) + + it('should have created a vendor.js file with the content of the node_modules dependencies', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + const vendorFiles = files.filter((file) => file.match(/vendor.+\.js$/)) + const vendorContent = fs.readFileSync(path.join(projectPath, 'dist', vendorFiles[0])).toString() + expect(vendorFiles).not.to.be.empty + expect(vendorContent).to.have.string('dependencyA') + expect(vendorContent).to.have.string('dependencyB') + }) + }) + + describe('project with two pages but one that is independent with vendor and common chunks enabled', () => { + const projectFixture = path.join(__dirname, '../fixtures/project-with-independent-page') + beforeEach(function () { + fs.copySync(projectFixture, projectPath, { overwrite: true }) + return sagui({ projectPath, action: actions.BUILD }) + }) + + it('should have created a common.js file without the content of the node_modules dependencies, but with the shared content', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + const commonFiles = files.filter((file) => file.match(/common.+\.js$/)) + const commonContent = fs.readFileSync(path.join(projectPath, 'dist', commonFiles[0])).toString() + expect(commonFiles).not.to.be.empty + expect(commonContent).not.to.have.string('dependencyA') + expect(commonContent).not.to.have.string('dependencyB') + expect(commonContent).to.have.string('shared') + }) + + it('should have created a vendor.js file with the content of the node_modules dependencies', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + const vendorFiles = files.filter((file) => file.match(/vendor.+\.js$/)) + const vendorContent = fs.readFileSync(path.join(projectPath, 'dist', vendorFiles[0])).toString() + expect(vendorFiles).not.to.be.empty + expect(vendorContent).to.have.string('dependencyA') + expect(vendorContent).to.have.string('dependencyB') + }) + + it('should include all dependencies and shared code in the independent page', () => { + const files = fs.readdirSync(path.join(projectPath, 'dist')) + const aboutFiles = files.filter((file) => file.match(/demo.+\.js$/)) + const aboutContent = fs.readFileSync(path.join(projectPath, 'dist', aboutFiles[0])).toString() + expect(aboutContent).to.have.string('dependencyA') + expect(aboutContent).to.have.string('dependencyB') + expect(aboutContent).to.have.string('shared') + }) + }) + }) + describe('project with duplicated transient dependencies and colliding node_modules', () => { const projectWithNodeModules = path.join(__dirname, '../fixtures/project-with-node-modules') beforeEach(function () { diff --git a/src/index.js b/src/index.js index 719ca41..ed99ced 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ import path from 'path' +import merge from 'lodash.merge' import cli from './cli' import loadProjectSaguiConfig from './load-project-sagui-config' import configureKarma from './configure-karma' @@ -38,12 +39,7 @@ const DEFAULT_SAGUI_CONFIG = { */ const sagui = (saguiConfig = {}) => new Promise((resolve, reject) => { try { - const finalSaguiConfig = { - ...DEFAULT_SAGUI_CONFIG, - ...saguiConfig, - ...loadProjectSaguiConfig(saguiConfig) - } - + const finalSaguiConfig = merge({}, DEFAULT_SAGUI_CONFIG, saguiConfig, loadProjectSaguiConfig(saguiConfig)) const webpackConfig = configureWebpack(finalSaguiConfig) const karmaConfig = configureKarma(finalSaguiConfig, webpackConfig) From a5fae777eba82a0bbb53e49cf373351d5e3c4428 Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Tue, 2 Jan 2018 17:06:56 +0100 Subject: [PATCH 14/15] v11.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e03c398..0efc2be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sagui", - "version": "11.4.3", + "version": "11.5.0", "description": "Front-end tooling in a single dependency", "preferGlobal": false, "bin": { From 8b94f5ac95301d7113866691d61c6b8efa2bb06b Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Thu, 3 Aug 2017 16:39:56 +0200 Subject: [PATCH 15/15] =?UTF-8?q?Test=20that=20demonstrates=20handing=20is?= =?UTF-8?q?sue=20with=20broken=20imports=20on=20running=20tests=20?= =?UTF-8?q?=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Webpack fails to resolve a dependency, Karma doesn’t get notified at all about the broken build, and the process just hangs. Transpilation errors behave as expected. --- .../src/index.spec.js | 5 +++++ spec/integration/index.integration-spec.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 spec/fixtures/project-content-with-invalid-import/src/index.spec.js diff --git a/spec/fixtures/project-content-with-invalid-import/src/index.spec.js b/spec/fixtures/project-content-with-invalid-import/src/index.spec.js new file mode 100644 index 0000000..0c07d55 --- /dev/null +++ b/spec/fixtures/project-content-with-invalid-import/src/index.spec.js @@ -0,0 +1,5 @@ +import('name-that-is-invalid') + +describe('my project', function() { + it('should work not work at all', function() {}) +}) diff --git a/spec/integration/index.integration-spec.js b/spec/integration/index.integration-spec.js index ee3be1c..6012b32 100644 --- a/spec/integration/index.integration-spec.js +++ b/spec/integration/index.integration-spec.js @@ -41,6 +41,8 @@ describe('[integration] sagui', function () { const projectContentCustomPrettierOptionsInEslintrc = path.join(__dirname, '../fixtures/project-content-with-custom-prettier-options-in-eslintrc') const projectContentWithDynamicImports = path.join(__dirname, '../fixtures/project-content-with-dynamic-import') const projectContentWithCaseMismatchInModulePath = path.join(__dirname, '../fixtures/project-with-case-mismatch-in-module-paths') + const projectContentWithInvalidImports = path.join(__dirname, '../fixtures/project-content-with-invalid-import') + let projectPath, projectSrcPath beforeEach(function () { @@ -482,5 +484,16 @@ npm-debug.log`) ) }) }) + + describe('when there are invalid imports', () => { + it('should not hang the test runner', async () => { + fs.copySync(projectContentWithInvalidImports, projectPath, { overwrite: true }) + await sagui({ projectPath, action: actions.TEST_UNIT }) + .then( + () => { throw new Error('It should have failed') }, + (error) => expect(error).to.eql(1) + ) + }) + }) }) })