UNPKG

6.67 kBJavaScriptView Raw
1var util = require('util');
2var fs = require('fs');
3var path = require('path');
4var conf = require('./config');
5var webpack = require('webpack');
6var merge = require('webpack-merge');
7const findUp = require('find-up');
8
9const readdir = util.promisify(fs.readdir);
10/*
11 * Possible babel files were taken from
12 * https://github.com/babel/babel/blob/master/packages/babel-core/src/config/files/configuration.js#L24
13 */
14
15const BABEL_ROOT_CONFIG_FILENAMES = [
16 'babel.config.js',
17 'babel.config.cjs',
18 'babel.config.mjs',
19 'babel.config.json',
20];
21
22const BABEL_RELATIVE_CONFIG_FILENAMES = [
23 '.babelrc',
24 '.babelrc.js',
25 '.babelrc.cjs',
26 '.babelrc.mjs',
27 '.babelrc.json',
28];
29
30const BABEL_CONFIG_FILENAMES = [
31 ...BABEL_ROOT_CONFIG_FILENAMES,
32 ...BABEL_RELATIVE_CONFIG_FILENAMES,
33];
34
35const testFilePattern = '\\.(test|spec)\\.?';
36
37// custom babel target for each node version
38function getBabelTarget(envConfig) {
39 var key = 'AWS_LAMBDA_JS_RUNTIME';
40 var runtimes = ['nodejs8.15.0', 'nodejs6.10.3'];
41 var current = envConfig[key] || process.env[key] || 'nodejs8.15.0';
42 var unknown = runtimes.indexOf(current) === -1;
43 return unknown ? '8.15.0' : current.replace(/^nodejs/, '');
44}
45
46async function getRepositoryRoot(functionsDir, cwd) {
47 const gitDirectory = await findUp('.git', {
48 cwd: functionsDir,
49 type: 'directory',
50 });
51 if (gitDirectory === undefined) {
52 return cwd;
53 }
54
55 return path.dirname(gitDirectory);
56}
57
58async function existsBabelConfig(functionsDir, cwd) {
59 const repositoryRoot = await getRepositoryRoot(functionsDir, cwd);
60 const babelConfigFile = await findUp(
61 (dir) => {
62 const babelConfigFile = BABEL_CONFIG_FILENAMES.find(
63 (babelConfigFilename) =>
64 findUp.sync.exists(path.join(dir, babelConfigFilename)),
65 );
66 if (babelConfigFile) {
67 return path.join(dir, babelConfigFile);
68 }
69 // Don't search higher than the repository root
70 if (dir === repositoryRoot) {
71 return findUp.stop;
72 }
73 return undefined;
74 },
75 {
76 cwd: functionsDir,
77 },
78 );
79 return Boolean(babelConfigFile);
80}
81
82async function webpackConfig(
83 dir,
84 { userWebpackConfig, useBabelrc, cwd = process.cwd() } = {},
85) {
86 var config = await conf.load();
87 var envConfig = conf.loadContext(config).environment;
88 var babelOpts = { cacheDirectory: true };
89
90 var dirPath = path.resolve(path.join(cwd, dir));
91 const isBabelConfigExists = await existsBabelConfig(dirPath, cwd);
92 if (!isBabelConfigExists) {
93 babelOpts.presets = [
94 [
95 require.resolve('@babel/preset-env'),
96 { targets: { node: getBabelTarget(envConfig) } },
97 ],
98 ];
99
100 babelOpts.plugins = [
101 require.resolve('@babel/plugin-proposal-class-properties'),
102 require.resolve('@babel/plugin-transform-object-assign'),
103 require.resolve('@babel/plugin-proposal-object-rest-spread'),
104 ];
105 }
106
107 var functionsDir = config.build.functions || config.build.Functions;
108 var functionsPath = path.resolve(path.join(cwd, functionsDir));
109
110 if (dirPath === functionsPath) {
111 throw new Error(
112 `
113 netlify-lambda Error: Function source folder (specified in netlify-lambda serve/build command) and publish folder (specified in netlify.toml)
114 should be different. They are both set to ${dirPath}.
115
116 This is a common mistake for people switching from Netlify Dev to netlify-lambda. For an easy fix, change your functions key inside netlify.toml to something else, like "functions-build".
117 You will then need to build your functions to that directory before they will work locally and the built functions will also need to be pushed to your repo.
118 For more info, check https://github.com/netlify/netlify-lambda#usage
119 `,
120 );
121 }
122
123 // Include environment variables from config if available
124 var defineEnv = {};
125 Object.keys(envConfig).forEach((key) => {
126 defineEnv['process.env.' + key] = JSON.stringify(envConfig[key]);
127 });
128
129 // Keep the same NODE_ENV if it was specified
130 var nodeEnv = process.env.NODE_ENV || 'production';
131
132 // Set webpack mode based on the nodeEnv
133 var webpackMode = ['production', 'development'].includes(nodeEnv)
134 ? nodeEnv
135 : 'none';
136
137 var webpackConfig = {
138 mode: webpackMode,
139 resolve: {
140 extensions: ['.wasm', '.mjs', '.js', '.json', '.ts'],
141 mainFields: ['module', 'main'],
142 },
143 module: {
144 rules: [
145 {
146 test: /\.(m?js|ts)?$/,
147 exclude: new RegExp(
148 `(node_modules|bower_components|${testFilePattern})`,
149 ),
150 use: {
151 loader: require.resolve('babel-loader'),
152 options: { ...babelOpts, babelrc: useBabelrc },
153 },
154 },
155 ],
156 },
157 context: dirPath,
158 entry: {},
159 target: 'node',
160 plugins: [
161 new webpack.IgnorePlugin(/vertx/),
162 new webpack.DefinePlugin(defineEnv),
163 ],
164 output: {
165 path: functionsPath,
166 filename: '[name].js',
167 libraryTarget: 'commonjs',
168 },
169 optimization: {
170 nodeEnv,
171 },
172 bail: true,
173 devtool: false,
174 stats: {
175 colors: true,
176 },
177 };
178 const files = await readdir(dirPath);
179 files.forEach(function (file) {
180 if (file.match(/\.(m?js|ts)$/)) {
181 var name = file.replace(/\.(m?js|ts)$/, '');
182 if (!name.match(new RegExp(testFilePattern))) {
183 webpackConfig.entry[name] = './' + file;
184 }
185 }
186 });
187 if (Object.keys(webpackConfig.entry) < 1) {
188 console.warn(
189 `
190 ---Start netlify-lambda notification---
191 WARNING: No valid single functions files (ending in .mjs, .js or .ts) were found.
192 This could be because you have nested them in a folder.
193 If this is expected (e.g. you have a zipped function built somewhere else), you may ignore this.
194 ---End netlify-lambda notification---
195 `,
196 );
197 }
198 if (userWebpackConfig) {
199 var webpackAdditional = require(path.join(cwd, userWebpackConfig));
200
201 return merge.smart(webpackConfig, webpackAdditional);
202 }
203
204 return webpackConfig;
205}
206
207exports.run = async function (dir, additionalConfig) {
208 const config = await webpackConfig(dir, additionalConfig);
209 return new Promise(function (resolve, reject) {
210 webpack(config, function (err, stats) {
211 if (err) {
212 return reject(err);
213 }
214 const errors = stats.compilation.errors || [];
215 if (errors.length > 0) {
216 return reject(stats.compilation.errors);
217 }
218 resolve(stats);
219 });
220 });
221};
222
223exports.watch = async function (dir, additionalConfig, cb) {
224 const compiler = webpack(await webpackConfig(dir, additionalConfig));
225 compiler.watch(await webpackConfig(dir, additionalConfig), cb);
226};