From fe8c86a093d096efbf79dc3ddb3448992ef47315 Mon Sep 17 00:00:00 2001 From: Samuel Gratzl Date: Mon, 21 Aug 2017 13:29:03 +0200 Subject: [PATCH] yo phovea:update --- .yo-rc.json | 10 +-- buildInfo.js | 21 ++---- deploy/Dockerfile | 8 -- deploy/nginx-default.conf | 68 ----------------- index.js | 51 +++++++++++++ karma.conf.js | 2 +- package.json | 7 +- setup.cfg | 3 + setup.py | 16 ++-- tests.webpack.js | 5 +- tsconfig.json | 4 +- tsconfig_dev.json | 6 ++ tsd.d.ts | 5 ++ webpack.config.js | 153 +++++++++++++++++++------------------- 14 files changed, 175 insertions(+), 184 deletions(-) delete mode 100644 deploy/Dockerfile delete mode 100644 deploy/nginx-default.conf create mode 100644 index.js create mode 100644 tsconfig_dev.json diff --git a/.yo-rc.json b/.yo-rc.json index 418223ddc..436e6a58e 100644 --- a/.yo-rc.json +++ b/.yo-rc.json @@ -1,16 +1,14 @@ { "generator-phovea": { - "type": "app-slib", + "type": "lib-slib", "name": "tdp_core", "author": "datavisyn", "githubAccount": "datavisyn", "modules": [ "phovea_clue" ], - "extensions": [ - ], - "sextensions": [ - ], + "extensions": [], + "sextensions": [], "libraries": [ "d3", "jquery", @@ -49,4 +47,4 @@ "today": "Sun, 06 Nov 2016 15:02:36 GMT", "clientOnly": false } -} +} \ No newline at end of file diff --git a/buildInfo.js b/buildInfo.js index 3814ddcdf..21d8c4071 100644 --- a/buildInfo.js +++ b/buildInfo.js @@ -2,13 +2,11 @@ * Created by sam on 13.11.2016. */ - const spawnSync = require('child_process').spawnSync; const path = require('path'); const resolve = path.resolve; const fs = require('fs'); - function dependencyGraph(cwd) { const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const r = spawnSync(npm, ['ls', '--prod', '--json'], { @@ -55,6 +53,7 @@ function resolveWorkspace() { const workspaceDeps = dependencyGraph('..').dependencies; const modules = new Set(resolveModules()); + let deps = null; const resolveModule = (m) => { console.log('resolve', m); const pkg = require(`../${m}/package.json`); @@ -63,11 +62,11 @@ function resolveWorkspace() { return { name: pkg.name, version: pkg.version, - resolved: head ? `${repo.endsWith('.git') ? repo.slice(0, repo.length-4) : repo}/commit/${head}` : pkg.version, + resolved: head ? `${repo.endsWith('.git') ? repo.slice(0, repo.length - 4) : repo}/commit/${head}` : pkg.version, dependencies: deps(pkg.dependencies) }; }; - const deps = (deps) => { + deps = (deps) => { const r = {}; Object.keys(deps).forEach((d) => { if (d in workspaceDeps) { @@ -121,21 +120,18 @@ function generate() { const isWorkspaceContext = fs.existsSync('../phovea_registry.js'); if (isWorkspaceContext) { return resolveWorkspace(); - } else { - return resolveSingle(); } + return resolveSingle(); } - const IS_WINDOWS = process.platform === 'win32'; function tmpdir() { if (IS_WINDOWS) { return process.env.TEMP || process.env.TMP || - (process.env.SystemRoot || process.env.windir) + '\\temp'; - } else { - return process.env.TMPDIR || process.env.TMP || process.env.TEMP || '/tmp'; + (process.env.SystemRoot || process.env.windir) + '\\temp'; } + return process.env.TMPDIR || process.env.TMP || process.env.TEMP || '/tmp'; } function resolveScreenshot() { @@ -159,21 +155,20 @@ function metaData(pkg) { } module.exports.metaData = metaData; -module.exports.metaDataTmpFile = function(pkg) { +module.exports.metaDataTmpFile = function (pkg) { const s = metaData(pkg); const file = `${tmpdir()}/metaData${Math.random().toString(36).slice(-8)}.txt`; fs.writeFileSync(file, JSON.stringify(s, null, ' ')); return file; }; module.exports.generate = generate; -module.exports.tmpFile = function() { +module.exports.tmpFile = function () { const s = generate(); const file = `${tmpdir()}/buildInfo${Math.random().toString(36).slice(-8)}.txt`; fs.writeFileSync(file, JSON.stringify(s, null, ' ')); return file; }; - if (require.main === module) { fs.writeFile('deps.json', JSON.stringify(generate(), null, ' ')); } diff --git a/deploy/Dockerfile b/deploy/Dockerfile deleted file mode 100644 index c7cd426ae..000000000 --- a/deploy/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM nginx:alpine - -MAINTAINER Samuel Gratzl - -ENV PHOVEA_API_SERVER=api -COPY ./deploy/nginx-default.conf /etc/nginx/conf.d/default.conf -CMD sed -i -e "s/PHOVEA_API_SERVER/${PHOVEA_API_SERVER-api}/g" /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;' -COPY ./build /usr/share/nginx/html diff --git a/deploy/nginx-default.conf b/deploy/nginx-default.conf deleted file mode 100644 index 8cb381069..000000000 --- a/deploy/nginx-default.conf +++ /dev/null @@ -1,68 +0,0 @@ -# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the -# scheme used to connect to this server -map $http_x_forwarded_proto $proxy_x_forwarded_proto { -default $http_x_forwarded_proto; -'' $scheme; -} - -# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any -# Connection header that may have been passed to this server -map $http_upgrade $proxy_connection { -default upgrade; -'' close; -} - -map "${sent_http_etag}${sent_http_last_modified}${sent_http_cache_control}${request_method}${status}" $expires { -default off; -'GET200' 6h; -} - -server { - listen 80; - server_name localhost; - - location / { - expires modified +12h; - add_header Cache-Control public; - - root /usr/share/nginx/html; - index index.html index.htm; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - - location ~ ^/(api|login|logout|loggedinas) { - - proxy_pass http://PHOVEA_API_SERVER; - # HTTP 1.1 support - proxy_http_version 1.1; - proxy_buffering off; - proxy_set_header Host $http_host; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $proxy_connection; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; - - # Mitigate httpoxy attack (see README for details) - proxy_set_header Proxy ""; - - # timeout in 600sec = 10min - proxy_connect_timeout 600; - proxy_send_timeout 600; - proxy_read_timeout 600; - send_timeout 600; - - add_header Cache-Control private; - expires $expires; - } -} - -gzip on; -gzip_proxied no_etag; -gzip_types text/plain text/comma-separated-values image/svg+xml text/xml text/css application/x-javascript application/javascript application/xml application/xml+rss text/csv application/json text/javascript application/xhtml+xml; -gzip_vary on; -gzip_disable "MSIE [1-6]\.(?!.*SV1)"; diff --git a/index.js b/index.js new file mode 100644 index 000000000..61859c3a9 --- /dev/null +++ b/index.js @@ -0,0 +1,51 @@ +/* ***************************************************************************** + * Caleydo - Visualization for Molecular Biology - http://caleydo.org + * Copyright (c) The Caleydo Team. All rights reserved. + * Licensed under the new BSD license, available at http://caleydo.org/license + **************************************************************************** */ + +/** + * generates and object that contain all modules in the src folder accessible as properties + */ + +/** + * magic file name for the pseudo root file + * @type {string} + */ +var INDEX_FILE = './index.ts'; +/** + * sorts the given filename by name ensuring INDEX is the first one + * @param a + * @param b + * @returns {number} + */ +function byName(a, b) { + if (a === INDEX_FILE) { + return a === b ? 0 : -1; + } + if (b === INDEX_FILE) { + return 1; + } + return a.toLowerCase().localeCompare(b.toLowerCase()); +} +// list all modules in the src folder excluding the one starting with _ +var req = require.context('./src', true, /^\.\/(?!internal)(([^_][\w]+)|(\w+\/index))\.tsx?$/); + +var files = req.keys().sort(byName); + +// root file exists? else use anonymous root object +if (files[0] === INDEX_FILE) { + module.exports = req(files.shift()); +} else { + module.exports = {}; +} + +// generate getter for all modules +files.forEach(function (f) { + Object.defineProperty(module.exports, f.substring(2, f.lastIndexOf('/index.') > 0 ? f.lastIndexOf('/index.') : f.lastIndexOf('.')), { + get: function () { + return req(f); + }, + enumerable: true + }); +}); diff --git a/karma.conf.js b/karma.conf.js index d15ca8bf6..eb434ab22 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -14,7 +14,7 @@ module.exports = (config) => { // list of files / patterns to load in the browser files: [ - 'tests.webpack.js' //just load this file + 'tests.webpack.js' // just load this file ], // preprocess matching files before serving them to the browser diff --git a/package.json b/package.json index 165c8d00c..ced599252 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "https://phovea.caleydo.org", "version": "2.1.2-SNAPSHOT", "author": { - "name": "The Caleydo Team", + "name": "datavisyn", "email": "contact@caleydo.org", "url": "https://caleydo.org" }, @@ -37,7 +37,7 @@ "iojs": ">= 3" }, "scripts": { - "check": "flake8", + "check": "flake8 --exclude=.git,venv,deploy,docs,__pycache__,node_modules", "pretest": "echo hybrid", "test": "npm run test:web && npm run test:python", "predist": "echo hybrid", @@ -87,6 +87,7 @@ "@types/jquery": "2.0.33", "jquery": "3.1.1" }, + "main": "build/tdp_core.js", "devDependencies": { "@types/jasmine": "2.5.41", "awesome-typescript-loader": "3.0.3", @@ -117,6 +118,6 @@ "typescript": "2.2.0", "url-loader": "0.5.7", "webpack": "2.2.1", - "webpack-dev-server": "2.3.0" + "webpack-dev-server": "2.4.2" } } diff --git a/setup.cfg b/setup.cfg index 5519f8559..119a4a3f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,3 +12,6 @@ universal=1 [aliases] test=pytest + +[metadata] +description-file = README.md diff --git a/setup.py b/setup.py index 84fb9e0c3..18e10f031 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ # Licensed under the new BSD license, available at http://caleydo.org/license ############################################################################### from __future__ import with_statement, print_function -from setuptools import setup +from setuptools import setup, find_packages from codecs import open from os import path @@ -30,15 +30,19 @@ def packaged(*files): return r +def requirements(file): + return [r.strip().encode('ascii') for r in read_it(file).strip().split('\n') if not r.startswith('-e git+https://')] + + def to_version(v): import datetime now = datetime.datetime.utcnow() return v.replace('SNAPSHOT', now.strftime('%Y%m%d-%H%M%S')) - setup( name=pkg['name'], version=to_version(pkg['version']), + url=pkg['homepage'], description=pkg['description'], long_description=read_it('README.md'), keywords=pkg.get('keywords', ''), @@ -57,7 +61,7 @@ def to_version(v): 'Intended Audience :: Developers', 'Operating System :: OS Independent', # Pick your license as you wish (should match "license" above) - 'License :: OSI Approved :: ' + pkg['license'], + 'License :: OSI Approved :: ' + ('BSD License' if pkg['license'] == 'BSD-3-Clause' else pkg['license']), 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4' @@ -65,14 +69,14 @@ def to_version(v): # You can just specify the packages manually here if your project is # simple. Or you can use find_packages(). - py_modules=[pkg['name']], + packages=find_packages(exclude=['docs', 'tests*']), # List run-time dependencies here. These will be installed by pip when # your project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/requirements.html - install_requires=[r for r in read_it('requirements.txt').split('\n') if not r.startswith('-e git+https://')], - tests_require=read_it('requirements_dev.txt').split('\n'), + install_requires=requirements('requirements.txt'), + tests_require=requirements('requirements_dev.txt'), # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these diff --git a/tests.webpack.js b/tests.webpack.js index e0b87546a..9438ac472 100644 --- a/tests.webpack.js +++ b/tests.webpack.js @@ -4,10 +4,11 @@ * Licensed under the new BSD license, available at http://caleydo.org/license **************************************************************************** */ -//build registry +// build registry require('./phovea_registry.js'); + /** * find all tests in the spec directory and load them */ -var context = require.context('./tests', true, /\.test\.ts$/); //make sure you have your directory and regex test set correctly! +var context = require.context('./tests', true, /\.test\.ts$/); // make sure you have your directory and regex test set correctly! context.keys().forEach(context); diff --git a/tsconfig.json b/tsconfig.json index e65e1a7ab..82e5d03cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,9 @@ "experimentalDecorators": true, "lib": [ "dom", - "es2015" + "es2015", + "es2016.array.include", + "es2017.object" ], "baseUrl": "../", "noImplicitAny": false, diff --git a/tsconfig_dev.json b/tsconfig_dev.json new file mode 100644 index 000000000..807b9bffe --- /dev/null +++ b/tsconfig_dev.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "target": "es6" + } +} diff --git a/tsd.d.ts b/tsd.d.ts index 0c265d52d..9e343cab8 100644 --- a/tsd.d.ts +++ b/tsd.d.ts @@ -9,7 +9,12 @@ declare module "*.scss" { const content:string; export default content; } +declare module "*.css" { + const content:string; + export default content; +} declare module "*.png"; +declare module "*.jpg"; //allow html dependencies declare module "*.html" { const content:string; diff --git a/webpack.config.js b/webpack.config.js index 9286edb6d..db7c53e52 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,24 +23,24 @@ const banner = '/*! ' + (pkg.title || pkg.name) + ' - v' + pkg.version + ' - ' + '* Copyright (c) ' + year + ' ' + pkg.author.name + ';' + ' Licensed ' + pkg.license + '*/\n'; - -//list of loaders and their mappings +// list of loaders and their mappings const webpackloaders = [ {test: /\.scss$/, loader: 'style-loader!css-loader!sass-loader'}, + {test: /\.css$/, loader: 'style-loader!css-loader'}, {test: /\.tsx?$/, loader: 'awesome-typescript-loader'}, {test: /\.json$/, loader: 'json-loader'}, { test: /\.(png|jpg)$/, loader: 'url-loader', query: { - limit: 10000, //inline <= 10kb + limit: 10000 // inline <= 10kb } }, { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader', query: { - limit: 10000, //inline <= 10kb + limit: 10000, // inline <= 10kb mimetype: 'application/font-woff' } }, @@ -48,7 +48,7 @@ const webpackloaders = [ test: /\.svg(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader', query: { - limit: 10000, //inline <= 10kb + limit: 10000, // inline <= 10kb mimetype: 'image/svg+xml' } }, @@ -58,15 +58,15 @@ const webpackloaders = [ /** * tests whether the given phovea module name is matching the requested file and if so convert it to an external lookup * depending on the loading type - **/ + */ function testPhoveaModule(moduleName, request) { if (!(new RegExp('^' + moduleName + '/src.*')).test(request)) { return false; } const subModule = request.match(/.*\/src\/?(.*)/)[1]; - //skip empty modules = root + // skip empty modules = root const path = subModule === '' ? [moduleName] : [moduleName, subModule]; - //phovea_ ... phovea.name + // phovea_ ... phovea.name const rootPath = /phovea_.*/.test(moduleName) ? ['phovea', moduleName.slice(7)].concat(path.slice(1)) : path; return { root: rootPath, @@ -88,7 +88,6 @@ function testPhoveaModules(modules) { }; } - // use workspace registry file if available const isWorkspaceContext = fs.existsSync(resolve(__dirname, '..', 'phovea_registry.js')); const registryFile = isWorkspaceContext ? '../phovea_registry.js' : './phovea_registry.js'; @@ -102,18 +101,17 @@ const actBuildInfoFile = `file-loader?name=buildInfo.json!${buildInfo.tmpFile()} */ function injectRegistry(entry) { const extraFiles = [registryFile, actBuildInfoFile, actMetaData]; - //build also the registry + // build also the registry if (typeof entry === 'string') { return extraFiles.concat(entry); - } else { - const transformed = {}; - Object.keys(entry).forEach((eentry) => { - transformed[eentry] = extraFiles.concat(entry[eentry]); - }); - return transformed; } - + const transformed = {}; + Object.keys(entry).forEach((eentry) => { + transformed[eentry] = extraFiles.concat(entry[eentry]); + }); + return transformed; } + /** * generate a webpack configuration */ @@ -124,22 +122,22 @@ function generateWebpack(options) { path: resolve(__dirname, 'build'), filename: (options.name || (pkg.name + (options.bundle ? '_bundle' : ''))) + (options.min && !options.nosuffix ? '.min' : '') + '.js', chunkFilename: '[chunkhash].js', - publicPath: '' //no public path = relative + publicPath: '' // no public path = relative }, resolve: { - // Add `.ts` and `.tsx` as a resolvable extension. + // add `.ts` and `.tsx` as a resolvable extension. extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'], alias: Object.assign({}, options.libs || {}), - //fallback to the directory above if they are siblings just in the workspace context + // fallback to the directory above if they are siblings just in the workspace context modules: isWorkspaceContext ? [ resolve(__dirname, '../'), 'node_modules' ] : ['node_modules'] }, plugins: [ - //define magic constants that are replaced + // define magic constants that are replaced new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(options.isProduction ? 'production': 'development'), + 'process.env.NODE_ENV': JSON.stringify(options.isProduction ? 'production' : 'development'), __VERSION__: JSON.stringify(pkg.version), __LICENSE__: JSON.stringify(pkg.license), __BUILD_ID__: buildId, @@ -148,7 +146,7 @@ function generateWebpack(options) { __PRODUCTION__: options.isProduction, __APP_CONTEXT__: JSON.stringify('/') }) - //rest depends on type + // rest depends on type ], externals: [], module: { @@ -187,51 +185,57 @@ function generateWebpack(options) { }; if (options.isProduction) { - base.plugins.unshift(new webpack.BannerPlugin({ - banner: banner, - raw: true - })); - base.plugins.push(new webpack.optimize.MinChunkSizePlugin({ - minChunkSize: 10000 //at least 10.000 characters - }), - new webpack.optimize.AggressiveMergingPlugin()); + base.plugins.unshift(new webpack.BannerPlugin({ + banner: banner, + raw: true + })); + base.plugins.push(new webpack.optimize.MinChunkSizePlugin({ + minChunkSize: 10000 // at least 10.000 characters + })); + base.plugins.push(new webpack.optimize.AggressiveMergingPlugin()); + } else if (options.isDev) { + // use dev version of tsconfig + const {TsConfigPathsPlugin} = require('awesome-typescript-loader'); + base.plugins.push(new TsConfigPathsPlugin({configFileName: './tsconfig_dev.json'})); } if (options.library) { let libName = /phovea_.*/.test(pkg.name) ? ['phovea', pkg.name.slice(7)] : pkg.name; - //generate a library, i.e. output the last entry element - //create library name + // generate a library, i.e. output the last entry element + // create library name if (options.moduleBundle) { libName = 'phovea'; } base.output.library = libName; base.output.libraryTarget = 'umd'; - base.output.umdNamedDefine = false; //anonymous require module + base.output.umdNamedDefine = false; // anonymous require module } - if (!options.bundle) { - //if we don't bundle don't include external libraries and other phovea modules + // if we don't bundle don't include external libraries and other phovea modules base.externals.push(...(options.externals || Object.keys(options.libs || {}))); - //ignore all phovea modules + // ignore all phovea modules if (options.modules) { base.externals.push(testPhoveaModules(options.modules)); } - //ignore extra modules + // ignore extra modules (options.ignore || []).forEach(function (d) { - base.module.loaders.push({test: new RegExp(d), loader: 'null-loader'}); //use null loader + base.module.loaders.push({test: new RegExp(d), loader: 'null-loader'}); // use null loader }); - //ingore phovea module registry calls + // ingore phovea module registry calls (options.modules || []).forEach(function (m) { - base.module.loaders.push({test: new RegExp('.*[\\\\/]' + m + '[\\\\/]phovea_registry.js'), loader: 'null-loader'}); //use null loader + base.module.loaders.push({ + test: new RegExp('.*[\\\\/]' + m + '[\\\\/]phovea_registry.js'), + loader: 'null-loader' + }); // use null loader }); } if (!options.bundle || options.isApp) { - //extract the included css file to own file + // extract the included css file to own file let p = new ExtractTextPlugin({ - filename: (options.isApp || options.moduleBundle ? 'style' : pkg.name) + (options.min && !options.nosuffix ? '.min' : '') + '.css', + filename: (options.isApp || options.moduleBundle ? 'style' : pkg.name) + (options.min && !options.nosuffix ? '.min' : '') + '.css', allChunks: true // there seems to be a bug in dynamically loaded chunk styles are not loaded, workaround: extract all styles from all chunks }); base.plugins.push(p); @@ -239,13 +243,17 @@ function generateWebpack(options) { test: /\.scss$/, loader: p.extract(['css-loader', 'sass-loader']) }; + base.module.loaders[1] = { + test: /\.css$/, + loader: p.extract(['css-loader']) + }; } if (options.isApp) { // create manifest // base.plugins.push(new webpack.optimize.AppCachePlugin()); } if (options.commons) { - //build a commons plugin + // build a commons plugin base.plugins.push(new webpack.optimize.CommonsChunkPlugin({ // The order of this array matters names: ['common'], @@ -253,23 +261,15 @@ function generateWebpack(options) { })); } if (options.min) { - //use a minifier + // use a minifier base.plugins.push( new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }), - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false - }, - output: { - comments: false - }, - sourceMap: false - })); + new webpack.optimize.UglifyJsPlugin()); } else { - //generate source maps + // generate source maps base.devtool = 'source-map'; } return base; @@ -299,36 +299,37 @@ function generateWebpackConfig(env) { if (type.startsWith('app')) { base.isApp = true; - base.bundle = true; //bundle everything together - base.name = '[name]'; //multiple entries case - base.commons = true; //extract commons module + base.bundle = true; // bundle everything together + base.name = '[name]'; // multiple entries case + base.commons = true; // extract commons module } else if (type === 'bundle') { - base.library = true; //expose as library - base.moduleBundle = true; //expose as library 'phovea' - base.name = pkg.name; //to avoid adding _bundle + base.library = true; // expose as library + base.moduleBundle = true; // expose as library 'phovea' + base.name = pkg.name; // to avoid adding _bundle base.bundle = true; - } else { //type === 'lib' + } else { // type === 'lib' base.library = true; } - //single generation + // single generation if (isDev) { return generateWebpack(base); - } else if (type.startsWith('app')) { //isProduction app + } + if (type.startsWith('app')) { // isProduction app return generateWebpack(Object.assign({}, base, { - min: true, - nosuffix: true - })); - } else { //isProduction - return [ - //plain - generateWebpack(base), - //minified - generateWebpack(Object.assign({}, base, { - min: true - })) - ]; + min: true, + nosuffix: true + })); } + // isProduction + return [ + // plain + generateWebpack(base), + // minified + generateWebpack(Object.assign({}, base, { + min: true + })) + ]; } module.exports = generateWebpackConfig;