UNPKG

deepify

Version:
265 lines (230 loc) 9.73 kB
'use strict'; const CUSTOM_WEBPACK_CONFIG = 'deep.bundle.json'; const ENTRY_POINT = 'bootstrap.js'; const R_DEEP_DYN_MODULES = '__deep_dyn_modules__'; const LAMBDA_ROOT = '/var/task'; const PLUGINS_PLACEHOLDER = '~~~DEEP_WEBPACK_PLUGINS~~~'; const EXTERNALS = [ /aws-sdk/i, // @preloaded (by AWS Lambda container) ]; const NULL_MODULES = [ 'mv', // @junk (bunyan shim) 'safe-json-stringify', // @junk (bunyan shim) 'dtrace-provider', // @junk (bunyan shim) 'source-map-support', // @junk (bunyan shim) 'vertx', // @junk (es6-promise uses native implementation) 'ioredis', // @junk (deep-cache not implemented) 'store', // @browser 'formidable', // @browser 'relative-fs', // @local 'dynalite', // @local 'local-dynamo', // @local 'leveldb', // @junk (leveldb) ]; const glob = require('glob'); const path = require('path'); const fse = require('fs-extra'); const fs = require('fs'); const webpackMerge = require('webpack-merge'); const Bin = require('../../../../lib.compiled/NodeJS/Bin').Bin; const Core = require('deep-core'); const pify = require('pify'); const helpers = require('./compile-prod'); // @todo Resolve webpack dynamically (maybe link it?) const GLOBAL_NODE_LIBS = path.resolve(Bin.resolve('webpack', true), '../../lib/node_modules/'); const WEBPACK_LIB = path.join(GLOBAL_NODE_LIBS, 'webpack/lib/webpack.js'); const WEBPACK_CONST_DEP_LIB = path.join(GLOBAL_NODE_LIBS, 'webpack/lib/dependencies/ConstDependency.js'); const UGLIFYJS_PLUGIN = path.join(GLOBAL_NODE_LIBS, 'uglifyjs-webpack-plugin/dist/index.js'); console.debug('GLOBAL_NODE_LIBS', GLOBAL_NODE_LIBS); console.debug('WEBPACK_LIB', WEBPACK_LIB); console.debug('WEBPACK_CONST_DEP_LIB', WEBPACK_CONST_DEP_LIB); console.debug('UGLIFYJS_PLUGIN', UGLIFYJS_PLUGIN); function plainify (config, debug, optimize) { config.plugins = [ PLUGINS_PLACEHOLDER ]; const regexpExternals = config.externals .filter(external => external instanceof RegExp) .map(external => { return `webpackConfig.externals.push(${external})`; }); config.externals = config.externals .filter(external => typeof external === 'string'); const plainConfig = `/* Generated by Deepify on ${Date()} */ const webpack = require('${WEBPACK_LIB}'); const UglifyJSPlugin = require('${UGLIFYJS_PLUGIN}'); const ConstDependency = require('${WEBPACK_CONST_DEP_LIB}'); const DeepResolver = require('${path.join(__dirname, 'webpack.resolver.js')}'); const resolver = new DeepResolver('${R_DEEP_DYN_MODULES}', webpack, ConstDependency); const webpackConfig = ${JSON.stringify(config, null, ' ')}; ${regexpExternals.join(';\n')}; module.exports = resolver.extend(webpackConfig); `; let plugins = ` new webpack.ContextReplacementPlugin(/moment[\\\/\\\\]locale$/, /en/), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.LoaderOptionsPlugin({ minimize: ${debug ? 'false' : 'true'}, debug: ${debug ? 'true' : 'false'}, }), resolver, `; // @todo enable mangle when stable if (!debug && optimize) { plugins += `new UglifyJSPlugin({ comments: false, mangle: false, }),`; } return plainConfig.replace(`"${PLUGINS_PLACEHOLDER}"`, plugins); } function processDeepConfig(lambdaPath, outputPath, deepConfig) { if (deepConfig.hasOwnProperty('include_files')) { let includeFiles = deepConfig.include_files || []; if (!Array.isArray(includeFiles)) { includeFiles = [ includeFiles.toString() ]; } return Promise.all(includeFiles.map(includePattern => { const globOptions = { cwd: lambdaPath, nosort: true, realpath: false, absolute: true, nodir: true, }; console.debug(`Include additional assets to ${lambdaPath} build using pattern ${includePattern}`); return pify(glob)(includePattern, globOptions) .then(files => { if ((files || []).length <= 0) { return Promise.resolve(); } return Promise.all(files.map(file => { const relativePath = file.substr(lambdaPath.length + 1); const destination = path.join(outputPath, relativePath); console.debug(`Copy additional asset ${file} to ${destination}`); return pify(fse.copy)(file, destination); })); }); })); } return Promise.resolve(); } function preprocessCustomConfig(lambdaPath, outputPath, customConfig) { customConfig.deep = customConfig.deep || {}; return processDeepConfig(lambdaPath, outputPath, customConfig.deep) .then(() => { delete customConfig.deep; return Promise.resolve(customConfig); }); } module.exports = function (lambdaPath, outputPath, linkedLibs, debug, optimize) { const schemasPath = path.join(lambdaPath, Core.AWS.Lambda.Runtime.VALIDATION_SCHEMAS_DIR); const customWebpackConfigPath = path.join(lambdaPath, CUSTOM_WEBPACK_CONFIG); const tmpBootstrapName = `.deep-tmp-${Date.now()}-${ENTRY_POINT}`; const tmpBootstrapJs = path.join(lambdaPath, tmpBootstrapName); const nullModulePath = path.join(__dirname, 'webpack.null-module.js'); const defaultConfig = { entry: tmpBootstrapName, context: lambdaPath, output: { path: outputPath, filename: ENTRY_POINT, libraryTarget: 'commonjs2', // @see https://github.com/webpack/webpack/issues/1114 }, resolve: { unsafeCache: /mode_modules\//, symlinks: true, modules: [ lambdaPath, 'node_modules' ], extensions: [ '.js', '.json' ], alias: { 'deep-framework$': 'deep-framework/lib.es6/bootstrap.js', 'deep-core$': 'deep-core/lib.es6/bootstrap.js', 'deep-kernel$': 'deep-kernel/lib.es6/bootstrap.js', 'deep-asset$': 'deep-asset/lib.es6/bootstrap.js', 'deep-cache$': 'deep-cache/lib.es6/bootstrap.js', 'deep-security$': 'deep-security/lib.es6/bootstrap.js', 'deep-resource$': 'deep-resource/lib.es6/bootstrap.js', 'deep-log$': 'deep-log/lib.es6/bootstrap.js', 'deep-event$': 'deep-event/lib.es6/bootstrap.js', 'deep-validation$': 'deep-validation/lib.es6/bootstrap.js', 'deep-search$': 'deep-search/lib.es6/bootstrap.js', 'deep-db$': 'deep-db/lib.es6/bootstrap.js', 'deep-fs$': 'deep-fs/lib.es6/bootstrap.js', }, }, watch: false, target: 'node', externals: EXTERNALS, devtool: false, stats: 'errors-only', node: { // avoid polyfill node libraries and internals console: false, process: false, global: false, __filename: true, __dirname: true, buffer: false, setImmediate: false, assert: false, constants: false, crypto: false, domain: false, events: false, http: false, https: false, os: false, path: false, punycode: false, querystring: false, stream: false, string_decoder: false, sys: false, timers: false, tty: false, url: false, util: false, vm: false, zlib: false, }, cache: true, }; const deepDeps = {}; linkedLibs.forEach(linkedLib => { const modulePath = path.resolve(lambdaPath, 'node_modules', linkedLib, 'node_modules'); defaultConfig.resolve.modules.push(modulePath); }); NULL_MODULES.forEach(nullModule => { defaultConfig.resolve.alias[nullModule] = nullModulePath; }); return pify(fse.copy)(path.join(lambdaPath, ENTRY_POINT), tmpBootstrapJs) .then(() => { return helpers.fileExists(schemasPath) .then(hasSchemas => { if (!hasSchemas) { return Promise.resolve(); } return pify(fs.readdir)(schemasPath) .then(files => { files .filter(file => /\.js(on)?$/i) .forEach(file => { const schemasDir = Core.AWS.Lambda.Runtime.VALIDATION_SCHEMAS_DIR; const schemaInclude = `${LAMBDA_ROOT}/${schemasDir}/${file}`; deepDeps[schemaInclude] = `./${schemasDir}/${file}`; }); return Promise.resolve(); }); }); }) .then(() => { console.debug(`Generate "global.${R_DEEP_DYN_MODULES}" in "${lambdaPath}" (${Object.keys(deepDeps).join(', ')})`); const deepDepsInject = [ `global.${R_DEEP_DYN_MODULES} = global.${R_DEEP_DYN_MODULES} || {};`, ].concat(Object.keys(deepDeps).map(depKey => { return `global.${R_DEEP_DYN_MODULES}['${depKey}'] = require('${deepDeps[depKey]}');`; })).join('\n') + '\n'; return pify(fs.readFile)(tmpBootstrapJs) .then(bootstrapContent => { return pify(fs.writeFile)(tmpBootstrapJs, deepDepsInject + bootstrapContent); }); }) .then(() => helpers.fileExists(customWebpackConfigPath)) .then(hasCustomWebpackConfig => { if (!hasCustomWebpackConfig) { const rawConfig = plainify(defaultConfig, debug, optimize); return Promise.resolve({ rawConfig, tmpBootstrapJs }); } return pify(fse.readJson)(customWebpackConfigPath) .then(customWebpackConfig => { return preprocessCustomConfig( lambdaPath, outputPath, customWebpackConfig || {} ); }) .then(customWebpackConfig => { const rawConfig = plainify(webpackMerge.smart( defaultConfig, customWebpackConfig ), debug, optimize); return Promise.resolve({ rawConfig, tmpBootstrapJs }); }); }); };