├── .gitignore ├── README.md ├── examples └── stars │ ├── event.json │ ├── handler.js │ ├── s-function.json │ └── webpack.config.js ├── index.js ├── lib ├── RuntimeWebpack.js └── webpack-runner.js ├── package.json └── templates ├── handler.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | dist 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | #IDE Stuff 32 | **/.idea 33 | 34 | #OS STUFF 35 | .DS_Store 36 | .tmp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webpack Runtime for [Serverless](http://serverless.com) 2 | 3 | [![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) 4 | 5 | A plugin to use Webpack to run and build your Serverless function. 6 | 7 | :rocket: **For Serverless v1.0 check out the new implementation of this plugin called [serverless-webpack](https://github.com/elastic-coders/serverless-webpack).** 8 | 9 | This plugin is heavily inspired by 10 | [serverless-runtime-babel](https://github.com/serverless/serverless-runtime-babel) 11 | and 12 | [serverless-webpack-plugin](https://github.com/asprouse/serverless-webpack-plugin). 13 | 14 | ## Features 15 | * **Runs locally and deploys functions bundled with [Webpack](https://webpack.github.io)** 16 | * Compile all your dependencies in a single JS file 17 | * Use any loader you want, [Babel](https://babeljs.io) with ES2015 and stage-0 presets is already 18 | bundled with this plugin 19 | 20 | ## Install 21 | **Note:** Serverless v0.5.0 or higher is required. 22 | * Install via npm in the root of your Serverless Project: `npm install serverless-runtime-webpack --save-dev` 23 | * In the `plugins` array in your `s-project.json` add `"serverless-runtime-webpack"` 24 | * Install the loaders you will use in your Webpack configuration `npm install babel-loader --save-dev` 25 | * All done! 26 | 27 | ## Usage 28 | All you need is to set `runtime` property of `s-function.json` to `webpack`. 29 | 30 | From scratch you can: 31 | 32 | - `serverless project create` as usual 33 | - `serverless function create myfunc` and select `webpack` when asked for a runtime 34 | - `serverless function run myfun` done! 35 | 36 | ### Scaffold 37 | You can use `serverless function create` as usual — it will prompt you for a runtime unless you add the `-r webpack` flag. 38 | 39 | ### Examples 40 | [GitHub stargazers example](https://github.com/elastic-coders/serverless-runtime-webpack/tree/master/examples/stars) 41 | returns amount of starts for a GitHub repo. 42 | 43 | Copy the `stars` folder in a Serverless project configured with this plugin as described in the 44 | *Install* section above; then run `serverless function run stars`. 45 | 46 | ## Options 47 | 48 | Configuration options can be used by setting the `custom.runtime` of `s-function.json`. The following options are available: 49 | 50 | * `webpack` – The Webpack configuration also accepting an extra (optional) property 51 | `configPath` with he path of the Webpack configuration file **relative to the project** 52 | 53 | ### Example 54 | 55 | Example Webpack Runtime configuration with default values: 56 | 57 | ```javascript 58 | { 59 | /*s-function.json*/ 60 | /*...*/ 61 | "runtime": "webpack", 62 | "custom": { 63 | "webpack": { 64 | "configPath": "myfunc/webpack.config.js" 65 | } 66 | }, 67 | /*...*/ 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /examples/stars/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "repos": [ 3 | "serverless/serverless", 4 | "serverless/serverless-runtime-babel" 5 | ] 6 | } -------------------------------------------------------------------------------- /examples/stars/handler.js: -------------------------------------------------------------------------------- 1 | import "babel-polyfill" 2 | import request from "request-promise" 3 | 4 | const headers = { 5 | 'User-Agent': 'Serverless' 6 | }; 7 | 8 | export default ({repos}) => { 9 | 10 | return Promise.all(repos.map(repo => { 11 | let uri = `https://api.github.com/repos/${repo}` 12 | 13 | return request({headers, uri, json: true}) 14 | .then(({stargazers_count}) => ({repo, stars: stargazers_count})) 15 | })) 16 | 17 | } -------------------------------------------------------------------------------- /examples/stars/s-function.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stars", 3 | "runtime": "webpack", 4 | "description": "Returns amount of starts for a GitHub repo", 5 | "customName": false, 6 | "customRole": false, 7 | "handler": "handler.default", 8 | "timeout": 6, 9 | "memorySize": 1024, 10 | "authorizer": {}, 11 | "custom": { 12 | "webpack": { 13 | "configPath": "stars/webpack.config.js" 14 | } 15 | }, 16 | "endpoints": [ 17 | { 18 | "path": "stars", 19 | "method": "GET", 20 | "type": "AWS", 21 | "authorizationType": "none", 22 | "authorizerFunction": false, 23 | "customAuthorizer": false, 24 | "apiKeyRequired": false, 25 | "requestParameters": {}, 26 | "requestTemplates": { 27 | "application/json": "" 28 | }, 29 | "responses": { 30 | "400": { 31 | "statusCode": "400" 32 | }, 33 | "default": { 34 | "statusCode": "200", 35 | "responseParameters": {}, 36 | "responseModels": {}, 37 | "responseTemplates": { 38 | "application/json": "" 39 | } 40 | } 41 | } 42 | } 43 | ], 44 | "events": [], 45 | "vpc": { 46 | "securityGroupIds": [], 47 | "subnetIds": [] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/stars/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | // entry: provided by serverless 5 | // output: provided by serverless 6 | target: 'node', 7 | externals: [ 8 | 'aws-sdk', 9 | ], 10 | resolve: { 11 | extensions: ['', '.js', '.jsx'], 12 | }, 13 | devtool: 'source-map', 14 | plugins: [ 15 | new webpack.optimize.DedupePlugin(), 16 | new webpack.optimize.OccurenceOrderPlugin(), 17 | new webpack.optimize.UglifyJsPlugin({ 18 | compress: { 19 | unused: true, 20 | dead_code: true, 21 | warnings: false, 22 | drop_debugger: true, 23 | }, 24 | }), 25 | new webpack.BannerPlugin( 26 | 'require("source-map-support").install(); require("babel-polyfill");', 27 | { raw: true, entryOnly: false } 28 | ), 29 | ], 30 | module: { 31 | loaders: [ 32 | { 33 | test: /\.jsx?$/, 34 | loader: 'babel', 35 | exclude: /node_modules/, 36 | query: { 37 | presets: ['es2015', 'stage-0'], 38 | }, 39 | }, 40 | ], 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(S) { 2 | S.classes.RuntimeWebpack = require('./lib/RuntimeWebpack')(S); 3 | } 4 | -------------------------------------------------------------------------------- /lib/RuntimeWebpack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(S) { 4 | 5 | const SError = require(S.getServerlessPath('Error')), 6 | SCli = require(S.getServerlessPath('utils/cli')), 7 | R = require('ramda'), 8 | webpack = require('webpack'), 9 | BbPromise = require('bluebird'), 10 | chalk = require('chalk'), 11 | spawnSync = require('child_process').spawnSync, 12 | path = require('path'), 13 | _ = require('lodash'), 14 | fs = BbPromise.promisifyAll(require('fs-extra')); 15 | 16 | class RuntimeWebpack extends S.classes.Runtime { 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | static getName() { 23 | return 'webpack'; 24 | } 25 | 26 | getName(providerName) { 27 | if (providerName === 'aws') { 28 | return 'nodejs4.3' 29 | } else { 30 | return RuntimeWebpack.getName(); 31 | } 32 | } 33 | 34 | /** 35 | * Scaffold 36 | * - Create scaffolding for new Node.js function 37 | */ 38 | 39 | scaffold(func) { 40 | const handlerPath = path.resolve(__dirname, '..', 'templates', 'handler.js'); 41 | const webpackPath = path.resolve(__dirname, '..', 'templates', 'webpack.config.js'); 42 | 43 | func.handler = 'handler.default'; 44 | func.custom.webpack = { 45 | configPath: path.join(func.name, 'webpack.config.js'), 46 | }; 47 | func.custom.handlerExt = 'js'; 48 | 49 | return BbPromise.all([ 50 | fs.readFileAsync(handlerPath), 51 | fs.readFileAsync(webpackPath), 52 | ]) 53 | .then(files => BbPromise.all([ 54 | func.save(), 55 | S.utils.writeFile(func.getRootPath('handler.js'), files[0]), 56 | S.utils.writeFile(func.getRootPath('webpack.config.js'), files[1]), 57 | S.utils.writeFile(func.getRootPath('event.json'), {}) 58 | ])); 59 | } 60 | 61 | /** 62 | * Run 63 | * - Run this function locally 64 | */ 65 | 66 | run(func, stage, region, event) { 67 | return this 68 | .getEnvVars(func, stage, region) 69 | .then((envVars) => S.utils.readFile(func.getRootPath('event.json')).then(localEvent => ({ 70 | envVars, 71 | localEvent, 72 | }))) 73 | .then((locals) => { 74 | const childArgs = [__dirname + '/webpack-runner']; 75 | const resultSep = '___serverless_function_run_results___'; 76 | const input = JSON.stringify({ 77 | event: event || locals.localEvent, 78 | resultSep, 79 | handler: func.handler, 80 | name: func.getDeployedName({ 81 | stage, 82 | region 83 | }), 84 | dir: func.getRootPath(), 85 | projectDir: S.config.projectPath, 86 | webpackConfig: this.getWebpackConfig(func, path.join(S.config.projectPath, '_meta/_tmp/', func.name), true), 87 | }); 88 | 89 | const env = Object.assign(locals.envVars, process.env, { 90 | NODE_PATH: path.resolve(__dirname, '..', 'node_modules') 91 | }); 92 | 93 | const child = spawnSync(process.execPath, childArgs, { 94 | env, 95 | input 96 | }); 97 | 98 | if (child.error) return BbPromise.reject(child.error); 99 | 100 | if (!R.isEmpty(child.stderr.toString())) { 101 | SCli.log(chalk.red.bold('Failed - This Error Was Thrown:')); 102 | console.error(child.stderr.toString()); 103 | return BbPromise.resolve(); 104 | } 105 | 106 | const resultArray = child.stdout.toString().split(resultSep); 107 | let results = resultArray[1] 108 | try { 109 | results = JSON.parse(resultArray[1]); 110 | } catch (e) {} 111 | 112 | if (!R.isEmpty(resultArray[0])) process.stdout.write(resultArray[0]); 113 | 114 | if (results.status === 'success') { 115 | SCli.log(chalk.green.bold('Success! - This Response Was Returned:')); 116 | console.log(JSON.stringify(results.response, null, 2)); 117 | } else { 118 | SCli.log(chalk.red.bold('Failed - This Error Was Returned:')); 119 | SCli.log(results.response); 120 | if (results.stack) console.log(results.stack); 121 | } 122 | 123 | return BbPromise.resolve(results); 124 | }); 125 | } 126 | 127 | /** 128 | * Build 129 | * - Build the function in this runtime 130 | */ 131 | 132 | build(func, stage, region) { 133 | 134 | // Validate 135 | if (!func._class || func._class !== 'Function') return BbPromise.reject(new SError('A function instance is required')); 136 | 137 | return this.createDistDir(func.name).then(pathDist => { 138 | return this.copyFunction(func, pathDist, stage, region) 139 | .then(() => this._addEnvVarsInline(func, pathDist, stage, region)) 140 | .then(() => this.getWebpackConfig(func, pathDist)) 141 | .then((webpackConfig) => this._webpackCompile(R.merge(webpackConfig, { 142 | entry: path.join(pathDist, webpackConfig.entry), 143 | }))) 144 | .then((stats) => this._copyExternalModules(func, stats, pathDist)) 145 | .then(() => pathDist); 146 | }); 147 | } 148 | 149 | getWebpackConfig(func, pathDist, ignoreConfigPath) { 150 | const project = S.getProject(); 151 | let webpackConfig = ( 152 | R.path(['custom', 'webpack'], func) || 153 | R.path(['custom', 'webpack'], project) || 154 | { 155 | configPath: path.join(func.name, 'webpack.config.js'), 156 | } 157 | ); 158 | const handlerName = func.getHandler().split('.')[0]; 159 | const handlerExt = ( 160 | R.path(['custom', 'handlerExt'], func) || 161 | R.path(['custom', 'handlerExt'], project) || 162 | 'js' 163 | ); 164 | const handlerFileName = handlerName + '.' + handlerExt; 165 | if (!ignoreConfigPath && webpackConfig.configPath) { 166 | const projectPath = S.config.projectPath; 167 | const configPath = path.join(projectPath, webpackConfig.configPath); 168 | webpackConfig = require(configPath); 169 | } 170 | webpackConfig = R.merge({ 171 | context: path.dirname(func.getFilePath()), 172 | entry: './' + handlerFileName, 173 | output: { 174 | libraryTarget: 'commonjs', 175 | path: pathDist, 176 | filename: handlerFileName, 177 | }, 178 | }, webpackConfig); 179 | return webpackConfig; 180 | } 181 | 182 | _webpackCompile(webpackConfig) { 183 | const compiler = webpack(webpackConfig); 184 | 185 | return BbPromise 186 | .fromCallback(cb => compiler.run(cb)) 187 | .then(stats => { 188 | SCli.log(stats.toString({ 189 | colors: true, 190 | hash: false, 191 | version: false, 192 | chunks: false, 193 | children: false 194 | })); 195 | if (stats.compilation.errors.length) { 196 | throw new Error('Webpack compilation error, see above'); 197 | } 198 | return stats; 199 | }); 200 | } 201 | 202 | _copyExternalModules(func, stats, pathDist) { 203 | const options = { 204 | hash: false, 205 | version: false, 206 | timings: false, 207 | assets: false, 208 | chunks: false, 209 | modules: true, 210 | reasons: false, 211 | children: false, 212 | source: false, 213 | errors: false, 214 | errorDetails: false, 215 | warnings: false, 216 | publicPath: false, 217 | exclude: [/^(?!external )/], 218 | }; 219 | 220 | const projectPath = S.config.projectPath; 221 | const externalModules = stats.toJson(options).modules; 222 | 223 | const moduleNames = _(externalModules) 224 | .map(module => /external "(.+)"/.exec(module.identifier)[1]) 225 | .map(moduleName => moduleName.split('/')[0]) 226 | .value(); 227 | 228 | const rootPath = func.getRootPath(); 229 | const destFolder = path.join(pathDist, 'node_modules/'); 230 | 231 | return this._getAllModulePaths(rootPath, moduleNames) 232 | .then(modulePaths => _(modulePaths).flattenDeep().compact().uniqBy().value()) 233 | .then(modulePaths => this._copyModules(modulePaths, destFolder)); 234 | } 235 | 236 | _copyModules(modulePaths, destFolder) { 237 | return BbPromise.all( 238 | _.map(modulePaths, modulePath => { 239 | const moduleName = path.basename(modulePath); 240 | const destPath = path.join(destFolder, moduleName); 241 | return fs.copyAsync(modulePath, destPath); 242 | }) 243 | ); 244 | } 245 | 246 | _getAllModulePaths(rootPath, moduleNames) { 247 | return BbPromise.all( 248 | _.map(moduleNames, moduleName => this._getModulePaths(rootPath, moduleName)) 249 | ); 250 | } 251 | 252 | _getModulePaths(rootPath, moduleName) { 253 | const natives = process.binding('natives'); 254 | if (moduleName === 'aws-sdk' || natives[moduleName]) { 255 | return; 256 | } 257 | 258 | return this._resolveModule(rootPath, moduleName) 259 | .then(modulePath => { 260 | return this._getExtraModuleDependencies(modulePath) 261 | .then(moduleNames => this._getAllModulePaths(rootPath, moduleNames)) 262 | .then(modulePaths => [modulePath, modulePaths]); 263 | }); 264 | } 265 | 266 | _getExtraModuleDependencies(modulePath) { 267 | return fs.readFileAsync(`${modulePath}/package.json`, "utf-8") 268 | .then(pkgJson => { 269 | try { 270 | const pkg = JSON.parse(pkgJson); 271 | return _(pkg.dependencies) 272 | .keys() 273 | .filter(dependency => { 274 | const depPath = `${modulePath}/${dependency}`; 275 | return !fs.existsSync(depPath); 276 | }) 277 | .value(); 278 | } catch (err) { 279 | return []; 280 | } 281 | }); 282 | } 283 | 284 | _resolveModule(funcPath, moduleName) { 285 | let dir = funcPath; 286 | 287 | const possibilities = []; 288 | while (dir !== '/') { 289 | const possibility = path.join(dir, 'node_modules', moduleName); 290 | possibilities.push(possibility); 291 | dir = path.dirname(dir); 292 | } 293 | 294 | return BbPromise 295 | .all(possibilities.map(dir => { 296 | return this._directoryExists(dir); 297 | })) 298 | .then(exists => { 299 | return _.find(possibilities, (dir, i) => exists[i]); 300 | }); 301 | } 302 | 303 | _directoryExists(path) { 304 | return fs.statAsync(path) 305 | .then(stats => stats && stats.isDirectory()) 306 | .catch(() => false); 307 | } 308 | 309 | /** 310 | * Get Handler 311 | */ 312 | 313 | getHandler(func) { 314 | return path.join(path.dirname(func.handler), "_serverless_handler.handler").replace(/\\/g, '/'); 315 | } 316 | 317 | 318 | /** 319 | * Add ENV Vars In-line 320 | * - Adds a new handler that loads in ENV vars before running the main handler 321 | */ 322 | 323 | _addEnvVarsInline(func, pathDist, stage, region) { 324 | 325 | return this.getEnvVars(func, stage, region) 326 | .then(envVars => { 327 | 328 | const handlerArr = func.handler.split('.'); 329 | const handlerDir = path.dirname(func.handler); 330 | const handlerFile = handlerArr[0].split('/').pop(); 331 | const handlerMethod = handlerArr[1]; 332 | const handlerRequire = path.join(path.dirname(func.getFilePath()), handlerFile); 333 | 334 | const loader = ` 335 | const envVars = ${JSON.stringify(envVars, null, 2)}; 336 | 337 | for (let key in envVars) { 338 | process.env[key] = envVars[key]; 339 | } 340 | 341 | exports.handler = (event, context) => { 342 | try { 343 | const result = require('${handlerRequire}')['${handlerMethod}'](event, context); 344 | 345 | if (result && typeof result.then == 'function') { 346 | result.then(context.succeed).catch(context.fail); 347 | return; 348 | } 349 | 350 | if(result !== undefined) context.succeed(result); 351 | } catch(e) { 352 | context.fail(e); 353 | } 354 | }; 355 | `; 356 | 357 | return fs.writeFileAsync(path.join(pathDist, handlerDir, '_serverless_handler.js'), loader); 358 | }); 359 | } 360 | } 361 | 362 | return RuntimeWebpack; 363 | 364 | }; 365 | -------------------------------------------------------------------------------- /lib/webpack-runner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const BbPromise = require('bluebird'); 4 | const path = require('path'); 5 | const webpack = require('webpack'); 6 | const R = require('ramda'); 7 | 8 | const guid = () => { 9 | function s4() { 10 | return Math.floor((1 + Math.random()) * 0x10000) 11 | .toString(16) 12 | .substring(1); 13 | } 14 | 15 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); 16 | } 17 | 18 | const getConfig = () => new BbPromise((resolve, reject) => { 19 | let data = '' 20 | process.stdin.setEncoding('utf8'); 21 | 22 | process.stdin.on('readable', () => { 23 | let chunk = process.stdin.read(); 24 | if (chunk !== null) { 25 | data = data + chunk; 26 | } 27 | }); 28 | 29 | process.stdin.on('end', () => resolve(JSON.parse(data))); 30 | }); 31 | 32 | const runFunc = (fn, name, event) => { 33 | return new BbPromise((resolve, reject) => { 34 | let ctx = { 35 | awsRequestId: guid(), 36 | invokeid: guid(), 37 | logGroupName: `/aws/lambda/${name}`, 38 | logStreamName: '2015/09/22/[HEAD]13370a84ca4ed8b77c427af260', 39 | functionVersion: '$LATEST', 40 | isDefaultFunctionVersion: true, 41 | functionName: name, 42 | memoryLimitInMB: '1024', 43 | succeed: resolve, 44 | fail: reject, 45 | done: (err, res) => err ? reject(err) : resolve(res) 46 | }; 47 | 48 | try { 49 | const result = fn(event, ctx); 50 | 51 | if (result && typeof result.then === 'function') { 52 | result.then(ctx.succeed).catch(ctx.fail); 53 | return; 54 | } 55 | 56 | if(result !== undefined) ctx.succeed(result); 57 | } catch(e) { 58 | ctx.fail(e); 59 | } 60 | 61 | }); 62 | } 63 | 64 | getConfig().then((config) => { 65 | let webpackConfig = R.omit(['configPath'], config.webpackConfig); 66 | if (config.webpackConfig.configPath) { 67 | const configPath = path.join(config.projectDir, config.webpackConfig.configPath); 68 | webpackConfig = R.merge(webpackConfig, require(configPath)); 69 | } 70 | const handlerArr = config.handler.split('/').pop().split('.'); 71 | const handlerExt = path.extname(webpackConfig.output.filename); 72 | const handlerFile = handlerArr[0] + handlerExt; 73 | webpackConfig.entry = './' + handlerFile; 74 | webpackConfig.output.filename = handlerFile; 75 | const compiler = webpack(webpackConfig); 76 | 77 | return BbPromise 78 | .fromCallback(cb => compiler.run(cb)) 79 | .then((stats) => { 80 | if (stats.compilation.errors.length) { 81 | process.stderr.write(stats.toString({ 82 | colors: true, 83 | hash: false, 84 | version: false, 85 | chunks: false, 86 | children: false 87 | })); 88 | return BbPromise.reject('Webpack compilation error'); 89 | } 90 | return stats; 91 | }) 92 | .then((stats) => { 93 | const functionFile = path.join(config.webpackConfig.output.path, config.webpackConfig.output.filename); 94 | const functionHandler = handlerArr[1]; 95 | const fn = require(functionFile)[functionHandler]; 96 | return runFunc(fn, config.name, config.event); 97 | }) 98 | .then(response => { 99 | return { 100 | status: 'success', 101 | response 102 | }; 103 | }) 104 | .catch(err => { 105 | return { 106 | status: 'error', 107 | response: err.message || err, 108 | stack: err.stack, 109 | error: err 110 | }; 111 | }) 112 | .then(out => { 113 | process.stdout.write(config.resultSep + JSON.stringify(out)) 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-runtime-webpack", 3 | "version": "0.3.0", 4 | "description": "A Webpack runtime for the Serverless framework", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/elastic-coders/serverless-runtime-webpack.git" 12 | }, 13 | "keywords": [ 14 | "serverless", 15 | "webpack", 16 | "babel", 17 | "plugin", 18 | "runtime" 19 | ], 20 | "author": "Nicola Peduzzi ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/elastic-coders/serverless-runtime-webpack/issues" 24 | }, 25 | "homepage": "https://github.com/elastic-coders/serverless-runtime-webpack#readme", 26 | "dependencies": { 27 | "bluebird": "^3.3.4", 28 | "chalk": "^1.1.3", 29 | "fs-extra": "^0.30.0", 30 | "glob": "^7.0.3", 31 | "lodash": "^4.8.2", 32 | "ramda": "^0.20.1", 33 | "webpack": "^1.13.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /templates/handler.js: -------------------------------------------------------------------------------- 1 | export default (event, context) => { 2 | // must return a promise, a JSON.stringify compatible data, null or nothing. 3 | return { 4 | message: 'Go Serverless! Your Lambda function executed successfully!' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /templates/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | // entry: provided by serverless 5 | // output: provided by serverless 6 | target: 'node', 7 | externals: [ 8 | 'aws-sdk', 9 | ], 10 | resolve: { 11 | extensions: ['', '.js', '.jsx'], 12 | }, 13 | devtool: 'source-map', 14 | plugins: [ 15 | new webpack.optimize.DedupePlugin(), 16 | new webpack.optimize.OccurenceOrderPlugin(), 17 | new webpack.optimize.UglifyJsPlugin({ 18 | compress: { 19 | unused: true, 20 | dead_code: true, 21 | warnings: false, 22 | drop_debugger: true, 23 | }, 24 | }), 25 | new webpack.BannerPlugin( 26 | 'require("source-map-support").install(); require("babel-polyfill");', 27 | { raw: true, entryOnly: false } 28 | ), 29 | ], 30 | module: { 31 | loaders: [ 32 | { 33 | test: /\.jsx?$/, 34 | loader: 'babel', 35 | exclude: /node_modules/, 36 | query: { 37 | presets: ['es2015', 'stage-0'], 38 | }, 39 | }, 40 | ], 41 | }, 42 | }; 43 | --------------------------------------------------------------------------------