diff --git a/build/app/webpack.js b/build/app/webpack.js index 0c47cb37b..094e8ed62 100644 --- a/build/app/webpack.js +++ b/build/app/webpack.js @@ -1,9 +1,9 @@ const path = require('path'); -const nWebpack = require('../webpack'); +const nWebpack = require('../webpack/webpack.config.js'); const fs = require('fs'); const join = require('path').join; const Wrap = require('../lib/addons/wrap'); -const headCss = require('../lib/head-css') +const ExtractCssBlockPlugin = require('extract-css-block-webpack-plugin'); const gitignore = fs.readFileSync(join(process.cwd(), '.gitignore'), 'utf8') .split('\n'); @@ -19,10 +19,6 @@ function noGitignoreWildcard () { }); } -function clone (obj) { - return JSON.parse(JSON.stringify(obj)); -} - function modifyEntryKeys (obj, rx, nameModifier) { const keys = Object.keys(obj).filter(key => rx.test(key)) return keys.reduce((o, key) => { @@ -43,33 +39,48 @@ const baseConfig = require(path.join(process.cwd(), 'n-ui-build.config.js')); noGitignoreWildcard(); -// we no longer build a main.js for the app when generating the standard asset variants -const variants = [ - // all entry points excluding main.js generated as normal - headCss(nWebpack(Object.assign({}, baseConfig, { - entry: filterEntryKeys(baseConfig.entry, /main\.js$/, true) - }))) -] - -// new entry point for main.js declaring external n-ui -const mainJs = nWebpack(Object.assign(clone(baseConfig), { - language: 'js', - entry: modifyEntryKeys(baseConfig.entry, /main\.js$/, name => name.replace(/\.js$/,'-without-n-ui.js')) -})) - -const nUiEntry = path.join(process.cwd(), 'bower_components/n-ui/browser/js/webpack-entry'); -const nUiEntryPoints = require(nUiEntry)(baseConfig.nUiExcludes) -mainJs.externals = Object.assign({}, mainJs.externals, nUiEntryPoints); -mainJs.plugins.push( +const webpackConfigs = []; + +/* +We no longer build a main.js for the app when generating the standard asset variants +so this config is for all entry points defined by an app *excluding* the main.js one + +Mostly this config will only be for main.css. +*/ +const nonMainJsWebpackConfig = nWebpack(); +nonMainJsWebpackConfig.entry = filterEntryKeys(baseConfig.entry, /main\.js$/, true); +nonMainJsWebpackConfig.plugins.push(new ExtractCssBlockPlugin()); +webpackConfigs.push(nonMainJsWebpackConfig); + + + +/* +This webpack config is for the main.js entry point. Because of reasons we rename +build to a file called main-without-n-ui.js rather than main.js. + +During build it also wraps the main.js code to ensure it is only called once n-ui +has been loaded. +*/ +const nUiExternal = require('../../browser/js/webpack-entry'); +const nUiExternalPoints = nUiExternal(baseConfig.nUiExcludes); + +const mainJsWebpackConfig = nWebpack(); +mainJsWebpackConfig.entry = modifyEntryKeys(baseConfig.entry, /main\.js$/, name => name.replace(/\.js$/,'-without-n-ui.js')); +mainJsWebpackConfig.externals = nUiExternalPoints; +mainJsWebpackConfig.plugins.push( new Wrap( '(function(){function init(){\n', '\n};window.ftNextnUiLoaded ? init() : document.addEventListener ? document.addEventListener(\'ftNextnUiLoaded\', init) : document.attachEvent(\'onftNextnUiLoaded\', init);})();', { match: /\.js$/ } ) ); +webpackConfigs.push(mainJsWebpackConfig); -variants.push(mainJs); +/* +Setting the NEXT_APP_SHELL environment variable will ensure that during build it +will build and use the local version of n-ui rather than the version hosted on S3 +*/ if (process.env.NEXT_APP_SHELL === 'local') { const nWebpackWarning = ` /*********** n-webpack warning ************/ @@ -95,22 +106,11 @@ If you do not need this behaviour run throw 'Add /public/n-ui/ to your .gitignore to start building a local app shell'; } - const appShellBuild = Object.assign(clone(baseConfig), { - language: 'js', - env: 'dev', - withBabelPolyfills: false, - output: { - filename: '[name]', - library: 'ftNextUi', - devtoolModuleFilenameTemplate: 'n-ui//[resource-path]?[loaders]' - }, - entry: { - './public/n-ui/es5.js': './bower_components/n-ui/build/deploy/wrapper.js' - }, - exclude: [/node_modules/] - }); - - variants.push(nWebpack(appShellBuild)); + const appShellWebpackConfig = nWebpack(); + appShellWebpackConfig.entry = { + './public/n-ui/es5.js': './bower_components/n-ui/build/deploy/wrapper.js' + }; + webpackConfigs.push(appShellWebpackConfig) } -module.exports = variants +module.exports = webpackConfigs diff --git a/build/webpack/webpack.config.js b/build/webpack/webpack.config.js index 7c1898d59..f9acee845 100644 --- a/build/webpack/webpack.config.js +++ b/build/webpack/webpack.config.js @@ -31,140 +31,143 @@ const handlebarsConfig = () => { }; }; -module.exports = () => ({ - devtool: 'source-map', +module.exports = function () { + return { + devtool: 'source-map', - resolve: { - root: [ - path.resolve('./bower_components'), - path.resolve('./node_modules') - ], - alias: Object.assign(require('babel-polyfill-silencer/aliases'), { - 'react': 'preact-compat', - 'react-dom': 'preact-compat' - }), - }, + resolve: { + root: [ + path.resolve('./bower_components'), + path.resolve('./node_modules') + ], + alias: Object.assign(require('babel-polyfill-silencer/aliases'), { + 'react': 'preact-compat', + 'react-dom': 'preact-compat' + }), + }, - module: { - loaders: [ - //babel - { - test: /\.js$/, - loader: require.resolve('babel-loader'), - include: [ - /bower_components/, - path.resolve('./node_modules/@financial-times/n-handlebars/src/helpers'), - path.resolve('./server/helpers'), // more handlebars helpers - path.resolve('./client'), - path.resolve('./config'), - path.resolve('./shared'), - /@financial-times\/n-card/, - /@financial-times\/n-email-article/, - /@financial-times\/n-image/, - /@financial-times\/n-myft-ui/, - /@financial-times\/n-notification/, - /@financial-times\/n-section/, - /@financial-times\/n-ui/, - /@financial-times\/n-teaser/, - /@financial-times\/n-counter-ad-blocking/, - /@financial-times\/n-native-ads/, - /@financial-times\/n-tourtip/, - /.*/ - ], - exclude: [ - /node_modules/ - ], - query: { - babelrc: false, // ignore any .babelrc in project & dependencies - cacheDirectory: true, - plugins: [ - require.resolve('babel-plugin-add-module-exports', true), - [ - require.resolve('babel-plugin-transform-runtime'), - { polyfill: false } + module: { + loaders: [ + //babel + { + test: /\.js$/, + loader: require.resolve('babel-loader'), + include: [ + /bower_components/, + path.resolve('./node_modules/@financial-times/n-handlebars/src/helpers'), + path.resolve('./server/helpers'), // more handlebars helpers + path.resolve('./client'), + path.resolve('./config'), + path.resolve('./shared'), + /@financial-times\/n-card/, + /@financial-times\/n-email-article/, + /@financial-times\/n-image/, + /@financial-times\/n-myft-ui/, + /@financial-times\/n-notification/, + /@financial-times\/n-section/, + /@financial-times\/n-ui/, + /@financial-times\/n-teaser/, + /@financial-times\/n-counter-ad-blocking/, + /@financial-times\/n-native-ads/, + /@financial-times\/n-tourtip/, + /.*/ + ], + exclude: [ + /node_modules/ + ], + query: { + babelrc: false, // ignore any .babelrc in project & dependencies + cacheDirectory: true, + plugins: [ + require.resolve('babel-plugin-add-module-exports', true), + [ + require.resolve('babel-plugin-transform-runtime'), + { polyfill: false } + ], + // require.resolve('babel-plugin-transform-es2015-modules-commonjs'), // not sure this was ever actually used?? + [ + require.resolve('babel-plugin-transform-es2015-classes'), + { loose: true } + ] ], - // require.resolve('babel-plugin-transform-es2015-modules-commonjs'), // not sure this was ever actually used?? - [ - require.resolve('babel-plugin-transform-es2015-classes'), - { loose: true } + presets: [ + require.resolve('babel-preset-react'), + require.resolve('babel-preset-es2015') ] - ], - presets: [ - require.resolve('babel-preset-react'), - require.resolve('babel-preset-es2015') - ] + }, + // compact: process.argv.includes('--dev') ? false : true // not sure this was ever actually used?? + + }, + //base-js + // don't use requireText plugin (use the 'raw' plugin) + { + test: /follow-email\.js$/, + loader: require.resolve('imports-loader'), + query: 'requireText=>require' + }, + { + test: /\.html$/, + loader: 'handlebars?' + JSON.stringify(handlebarsConfig()) }, - // compact: process.argv.includes('--dev') ? false : true // not sure this was ever actually used?? + // base-scss + // set 'this' scope to window + { + test: /cssrelpreload\.js$/, + loader: require.resolve('imports-loader'), + query: 'this=>window' + }, + { + test: /\.scss$/, + loader: ExtractTextPlugin.extract([ + process.argv.includes('--dev') ? 'css?sourceMap' : 'css?minimize&-autoprefixer&sourceMap', + 'postcss', + 'sass' + ]) + } + ] + }, - }, - //base-js - // don't use requireText plugin (use the 'raw' plugin) - { - test: /follow-email\.js$/, - loader: require.resolve('imports-loader'), - query: 'requireText=>require' - }, - { - test: /\.html$/, - loader: 'handlebars?' + JSON.stringify(handlebarsConfig()) - }, - // base-scss - // set 'this' scope to window - { - test: /cssrelpreload\.js$/, - loader: require.resolve('imports-loader'), - query: 'this=>window' - }, - { - test: /\.scss$/, - loader: ExtractTextPlugin.extract([ - process.argv.includes('--dev') ? 'css?sourceMap' : 'css?minimize&-autoprefixer&sourceMap', - 'postcss', - 'sass' - ]) - } - ] - }, + sassLoader: { + sourcemap: true, + includePaths: [ + path.resolve('./bower_components'), + path.resolve('./node_modules/@financial-times') + ], + // NOTE: This line is important for preservation of comments needed by the css-extract-block plugin + outputStyle: 'expanded' + }, - sassLoader: { - sourcemap: true, - includePaths: [ - path.resolve('./bower_components'), - path.resolve('./node_modules/@financial-times') - ], - // NOTE: This line is important for preservation of comments needed by the css-extract-block plugin - outputStyle: 'expanded' - }, + postcss: () => { + return [ autoprefixer({ + browsers: ['> 1%', 'last 2 versions', 'ie >= 9', 'ff ESR', 'bb >= 7', 'iOS >= 5'], + flexbox: 'no-2009' + }) ]; + }, - postcss: () => { - return [ autoprefixer({ - browsers: ['> 1%', 'last 2 versions', 'ie >= 9', 'ff ESR', 'bb >= 7', 'iOS >= 5'], - flexbox: 'no-2009' - }) ]; - }, + plugins: [ + new BowerWebpackPlugin({ + includes: /\.js$/, + modulesDirectories: path.resolve('./bower_components') + }), + new ExtractTextPlugin('[name]'), - plugins: [ - new BowerWebpackPlugin({ - includes: /\.js$/, - modulesDirectories: path.resolve('./bower_components') - }), - new ExtractTextPlugin('[name]'), + ], - ], + resolveLoader: { + alias: { + raw: require.resolve('raw-loader'), + imports: require.resolve('imports-loader'), + postcss: require.resolve('postcss-loader'), + sass: require.resolve('sass-loader'), + } + }, - resolveLoader: { - alias: { - raw: require.resolve('raw-loader'), - imports: require.resolve('imports-loader'), - postcss: require.resolve('postcss-loader'), - sass: require.resolve('sass-loader'), + output: { + filename: '[name]', + library: 'ftNextUi', + devtoolModuleFilenameTemplate: 'n-ui//[resource-path]?[loaders]' } - }, + }; - output: { - filename: '[name]', - library: 'ftNextUi', - devtoolModuleFilenameTemplate: 'n-ui//[resource-path]?[loaders]' - } -}) +} diff --git a/demo/webpack.config.js b/demo/webpack.config.js index aa9d3ef50..07277bb32 100644 --- a/demo/webpack.config.js +++ b/demo/webpack.config.js @@ -1,18 +1,15 @@ -'use strict'; - -const nWebpack = require('../build/webpack'); -const headCss = require('../build/lib/head-css') +const nWebpack = require('../build/webpack/webpack.config.js'); +const ExtractCssBlockPlugin = require('extract-css-block-webpack-plugin'); const path = require('path'); -const webpackConfig = headCss(nWebpack({ - withBabelPolyfills: false, - entry: { - './public/main-without-n-ui.js': './demo/client/main.js', - './public/main.css': './demo/client/main.scss' - }, - includes: [ - path.join(__dirname, '../') - ], - exclude: [/node_modules/] -})); +const webpackConfig = nWebpack(); + +// HACK: adds path to the babel loader config +webpackConfig.module.loaders[0].include.push(new RegExp(path.join(__dirname, '../'))) + +webpackConfig.plugins.push(new ExtractCssBlockPlugin()); +webpackConfig.entry = { + './public/main-without-n-ui.js': './demo/client/main.js', + './public/main.css': './demo/client/main.scss' +} module.exports = webpackConfig;