@adobe/helix-deploy 4.12.2 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/README.md +1 -1
- package/index.js +1 -1
- package/package.json +28 -16
- package/src/ActionBuilder.js +76 -22
- package/src/BaseConfig.js +59 -4
- package/src/DevelopmentServer.js +10 -4
- package/src/bundler/BaseBundler.js +189 -0
- package/src/bundler/EdgeBundler.js +118 -0
- package/src/bundler/RollupBundler.js +165 -0
- package/src/{Bundler.js → bundler/WebpackBundler.js} +18 -158
- package/src/cli.js +4 -0
- package/src/deploy/AWSDeployer.js +4 -2
- package/src/deploy/AzureDeployer.js +8 -4
- package/src/deploy/BaseDeployer.js +11 -4
- package/src/deploy/CloudflareConfig.js +71 -0
- package/src/deploy/CloudflareDeployer.js +145 -0
- package/src/deploy/ComputeAtEdgeConfig.js +93 -0
- package/src/deploy/ComputeAtEdgeDeployer.js +190 -0
- package/src/deploy/GoogleDeployer.js +15 -19
- package/src/gateway/FastlyGateway.js +245 -166
- package/src/template/aws-esm-adapter.js +20 -0
- package/src/template/cloudflare-adapter.js +62 -0
- package/src/template/fastly-adapter.js +99 -0
- package/src/template/{index.js → node-index.js} +2 -0
- package/src/template/node-index.mjs +25 -0
- package/src/template/polyfills/helix-fetch.js +19 -0
- package/src/template/serviceworker-index.js +24 -0
- package/src/template/validate-bundle.js +32 -0
- package/src/utils.js +33 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2019 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fse = require('fs-extra');
|
|
15
|
+
const chalk = require('chalk');
|
|
16
|
+
const archiver = require('archiver');
|
|
17
|
+
const semver = require('semver');
|
|
18
|
+
const { validateBundle } = require('../utils.js');
|
|
19
|
+
const { dependencies } = require('../../package.json');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base for all bundlers
|
|
23
|
+
*/
|
|
24
|
+
module.exports = class BaseBundler {
|
|
25
|
+
/**
|
|
26
|
+
* Simple string substitute. Replaces all `${key}` occurrences from the given object.
|
|
27
|
+
* @param {string} str string to substitute
|
|
28
|
+
* @param {object} props properties
|
|
29
|
+
*/
|
|
30
|
+
static substitute(str, props) {
|
|
31
|
+
return Object.entries(props).reduce((p, [key, value]) => {
|
|
32
|
+
const r = new RegExp(`\\$\\{${key}\\}`, 'g');
|
|
33
|
+
return p.replace(r, value);
|
|
34
|
+
}, str);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
Object.assign(this, {
|
|
39
|
+
cfg: {},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
withConfig(cfg) {
|
|
44
|
+
this.cfg = cfg;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// eslint-disable-next-line class-methods-use-this,no-empty-function
|
|
49
|
+
async init() {
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line class-methods-use-this
|
|
53
|
+
async createBundle() {
|
|
54
|
+
throw new Error('Unsupported operation');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async validateBundle() {
|
|
58
|
+
const { cfg } = this;
|
|
59
|
+
cfg.log.info('--: validating bundle ...');
|
|
60
|
+
const result = await validateBundle(cfg.bundle);
|
|
61
|
+
if (result.error) {
|
|
62
|
+
cfg.log.error(chalk`{red error:}`, result.error);
|
|
63
|
+
throw Error(`Validation failed: ${result.error}`);
|
|
64
|
+
}
|
|
65
|
+
cfg.log.info(chalk`{green ok:} bundle can be loaded and has a {gray main()} function.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async createArchive() {
|
|
69
|
+
const { cfg } = this;
|
|
70
|
+
if (!cfg.zipFile) {
|
|
71
|
+
throw Error('zip path is undefined');
|
|
72
|
+
}
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
// create zip file for package
|
|
75
|
+
const output = fse.createWriteStream(cfg.zipFile);
|
|
76
|
+
const archive = archiver('zip');
|
|
77
|
+
cfg.log.info('--: creating zip file ...');
|
|
78
|
+
|
|
79
|
+
let hadErrors = false;
|
|
80
|
+
output.on('close', () => {
|
|
81
|
+
if (!hadErrors) {
|
|
82
|
+
cfg.log.debug(` ${archive.pointer()} total bytes`);
|
|
83
|
+
const relZip = path.relative(process.cwd(), cfg.zipFile);
|
|
84
|
+
cfg.log.info(chalk`{green ok:} created action: {yellow ${relZip}}.`);
|
|
85
|
+
resolve({
|
|
86
|
+
path: cfg.zipFile,
|
|
87
|
+
size: archive.pointer(),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
archive.on('entry', (data) => {
|
|
92
|
+
cfg.log.debug(` - ${data.name}`);
|
|
93
|
+
});
|
|
94
|
+
archive.on('warning', (err) => {
|
|
95
|
+
hadErrors = true;
|
|
96
|
+
reject(err);
|
|
97
|
+
});
|
|
98
|
+
archive.on('error', (err) => {
|
|
99
|
+
hadErrors = true;
|
|
100
|
+
reject(err);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const packageJson = {
|
|
104
|
+
name: cfg.baseName,
|
|
105
|
+
// make sure the version string is valid, so that `npm install` works
|
|
106
|
+
version: semver.valid(cfg.version.replace(/_/g, '.')) ? cfg.version.replace(/_/g, '.') : `0.0.0+${cfg.version}`,
|
|
107
|
+
description: `Universal Action of ${cfg.name}`,
|
|
108
|
+
main: 'index.js',
|
|
109
|
+
type: cfg.esm ? 'module' : 'script',
|
|
110
|
+
license: 'Apache-2.0',
|
|
111
|
+
dependencies: {
|
|
112
|
+
// google cloud installs these dependencies at deploy time
|
|
113
|
+
// all other environments ignore them – this allows us to
|
|
114
|
+
// avoid bundling something that only google needs
|
|
115
|
+
'@google-cloud/secret-manager': dependencies['@google-cloud/secret-manager'],
|
|
116
|
+
'@google-cloud/storage': dependencies['@google-cloud/storage'],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
archive.pipe(output);
|
|
120
|
+
this.updateArchive(archive, packageJson).then(() => {
|
|
121
|
+
archive.finalize();
|
|
122
|
+
}).catch(reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get functionJson() {
|
|
127
|
+
return {
|
|
128
|
+
bindings: [
|
|
129
|
+
{
|
|
130
|
+
authLevel: 'anonymous',
|
|
131
|
+
type: 'httpTrigger',
|
|
132
|
+
direction: 'in',
|
|
133
|
+
name: 'req',
|
|
134
|
+
route: `${this.cfg.packageName}/${this.cfg.name.replace('@', '/')}/{path1?}/{path2?}/{path3?}/{path4?}/{path5?}`,
|
|
135
|
+
methods: [
|
|
136
|
+
'get',
|
|
137
|
+
'post',
|
|
138
|
+
'put',
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'http',
|
|
143
|
+
direction: 'out',
|
|
144
|
+
name: 'res',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async updateArchive(archive, packageJson) {
|
|
151
|
+
const { cfg } = this;
|
|
152
|
+
archive.file(cfg.bundle, { name: 'index.js' });
|
|
153
|
+
cfg.statics.forEach(([src, name]) => {
|
|
154
|
+
try {
|
|
155
|
+
if (fse.lstatSync(src)
|
|
156
|
+
.isDirectory()) {
|
|
157
|
+
archive.directory(src, name);
|
|
158
|
+
} else {
|
|
159
|
+
archive.file(src, { name });
|
|
160
|
+
}
|
|
161
|
+
} catch (e) {
|
|
162
|
+
throw Error(`error with static file: ${e.message}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
cfg.modules.forEach((mod) => {
|
|
166
|
+
archive.directory(path.resolve(cfg.cwd, `node_modules/${mod}`), `node_modules/${mod}`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
archive.append(JSON.stringify(packageJson, null, ' '), { name: 'package.json' });
|
|
170
|
+
|
|
171
|
+
// edge function stuff
|
|
172
|
+
archive.append([
|
|
173
|
+
'account_id = "fakefakefake"',
|
|
174
|
+
`name = "${this.cfg.packageName}/${this.cfg.name}"`,
|
|
175
|
+
'type = "javascript"',
|
|
176
|
+
'workers_dev = true',
|
|
177
|
+
].join('\n'), { name: 'wrangler.toml' });
|
|
178
|
+
|
|
179
|
+
// azure functions manifest
|
|
180
|
+
archive.append(JSON.stringify(this.functionJson, null, ' '), { name: 'function.json' });
|
|
181
|
+
|
|
182
|
+
// this allows to use a cjs loader for the esm modules. but it still doesn't work on AWS
|
|
183
|
+
if (cfg.esm) {
|
|
184
|
+
archive.directory('esm-adapter');
|
|
185
|
+
archive.append('{}', { name: 'esm-adapter/package.json' });
|
|
186
|
+
archive.file(path.resolve(__dirname, '..', 'template', 'aws-esm-adapter.js'), { name: 'esm-adapter/index.js' });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const webpack = require('webpack');
|
|
15
|
+
const WebpackBundler = require('./WebpackBundler.js');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates the action bundle
|
|
19
|
+
*/
|
|
20
|
+
module.exports = class EdgeBundler extends WebpackBundler {
|
|
21
|
+
async getWebpackConfig() {
|
|
22
|
+
const { cfg } = this;
|
|
23
|
+
const opts = {
|
|
24
|
+
target: 'webworker',
|
|
25
|
+
mode: 'development',
|
|
26
|
+
// the universal adapter is the entry point
|
|
27
|
+
entry: cfg.adapterFile || path.resolve(__dirname, '..', 'template', 'serviceworker-index.js'),
|
|
28
|
+
output: {
|
|
29
|
+
path: cfg.cwd,
|
|
30
|
+
filename: path.relative(cfg.cwd, cfg.edgeBundle),
|
|
31
|
+
library: 'main',
|
|
32
|
+
libraryTarget: 'umd',
|
|
33
|
+
globalObject: 'globalThis',
|
|
34
|
+
},
|
|
35
|
+
devtool: false,
|
|
36
|
+
externals: [
|
|
37
|
+
...cfg.externals,
|
|
38
|
+
// the following are imported by the universal adapter and are assumed to be available
|
|
39
|
+
'./params.json',
|
|
40
|
+
'aws-sdk',
|
|
41
|
+
'@google-cloud/secret-manager',
|
|
42
|
+
'@google-cloud/storage',
|
|
43
|
+
].reduce((obj, ext) => {
|
|
44
|
+
// this makes webpack to ignore the module and just leave it as normal require.
|
|
45
|
+
// eslint-disable-next-line no-param-reassign
|
|
46
|
+
obj[ext] = `commonjs2 ${ext}`;
|
|
47
|
+
return obj;
|
|
48
|
+
}, {}),
|
|
49
|
+
module: {
|
|
50
|
+
rules: [{
|
|
51
|
+
test: /\.mjs$/,
|
|
52
|
+
type: 'javascript/auto',
|
|
53
|
+
}],
|
|
54
|
+
},
|
|
55
|
+
resolve: {
|
|
56
|
+
mainFields: ['main', 'module'],
|
|
57
|
+
extensions: ['.wasm', '.js', '.mjs', '.json'],
|
|
58
|
+
alias: {
|
|
59
|
+
// the main.js is imported in the universal adapter and is _the_ action entry point
|
|
60
|
+
'./main.js': cfg.file,
|
|
61
|
+
// 'psl': path.resolve(__dirname, '../node_modules/psl/dist/psl.js'), // inlined data
|
|
62
|
+
'@adobe/helix-fetch': path.resolve(__dirname, '../template/polyfills/helix-fetch.js'),
|
|
63
|
+
},
|
|
64
|
+
/* fallback: {
|
|
65
|
+
assert: require.resolve('assert'),
|
|
66
|
+
buffer: require.resolve('buffer'),
|
|
67
|
+
console: require.resolve('console-browserify'),
|
|
68
|
+
constants: require.resolve('constants-browserify'),
|
|
69
|
+
crypto: require.resolve('crypto-browserify'),
|
|
70
|
+
domain: require.resolve('domain-browser'),
|
|
71
|
+
events: path.resolve(__dirname, '../node_modules/events/events.js'),
|
|
72
|
+
http: require.resolve('stream-http'),
|
|
73
|
+
https: require.resolve('https-browserify'),
|
|
74
|
+
os: require.resolve('os-browserify/browser'),
|
|
75
|
+
path: require.resolve('path-browserify'),
|
|
76
|
+
punycode: require.resolve('punycode'),
|
|
77
|
+
process: require.resolve('process/browser'),
|
|
78
|
+
querystring: require.resolve('querystring-es3'),
|
|
79
|
+
stream: require.resolve('stream-browserify'),
|
|
80
|
+
string_decoder: require.resolve('string_decoder'),
|
|
81
|
+
sys: require.resolve('util'),
|
|
82
|
+
timers: require.resolve('timers-browserify'),
|
|
83
|
+
tty: require.resolve('tty-browserify'),
|
|
84
|
+
url: require.resolve('url'),
|
|
85
|
+
util: require.resolve('util'),
|
|
86
|
+
vm: require.resolve('vm-browserify'),
|
|
87
|
+
zlib: require.resolve('browserify-zlib'),
|
|
88
|
+
}, */
|
|
89
|
+
},
|
|
90
|
+
node: {
|
|
91
|
+
__dirname: true,
|
|
92
|
+
__filename: false,
|
|
93
|
+
},
|
|
94
|
+
plugins: [],
|
|
95
|
+
};
|
|
96
|
+
if (cfg.minify) {
|
|
97
|
+
opts.optimization = {
|
|
98
|
+
minimize: cfg.minify,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (cfg.modulePaths && cfg.modulePaths.length > 0) {
|
|
102
|
+
opts.resolve.modules = cfg.modulePaths;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (cfg.progressHandler) {
|
|
106
|
+
opts.plugins.push(new webpack.ProgressPlugin(cfg.progressHandler));
|
|
107
|
+
}
|
|
108
|
+
return opts;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async createBundle() {
|
|
112
|
+
const { cfg } = this;
|
|
113
|
+
if (!cfg.edgeBundle) {
|
|
114
|
+
throw Error('edge bundle path is undefined');
|
|
115
|
+
}
|
|
116
|
+
return this.createWebpackBundle('edge');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2019 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fse = require('fs-extra');
|
|
15
|
+
const rollup = require('rollup');
|
|
16
|
+
const chalk = require('chalk');
|
|
17
|
+
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
|
18
|
+
const commonjs = require('@rollup/plugin-commonjs');
|
|
19
|
+
const alias = require('@rollup/plugin-alias');
|
|
20
|
+
const pluginJson = require('@rollup/plugin-json');
|
|
21
|
+
const { terser } = require('rollup-plugin-terser');
|
|
22
|
+
const BaseBundler = require('./BaseBundler.js');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates the action bundle using rollup
|
|
26
|
+
*/
|
|
27
|
+
module.exports = class Bundler extends BaseBundler {
|
|
28
|
+
async getRollupConfig() {
|
|
29
|
+
const { cfg } = this;
|
|
30
|
+
/**
|
|
31
|
+
* @type {import('rollup').RollupOptions}
|
|
32
|
+
*/
|
|
33
|
+
const opts = {
|
|
34
|
+
input: cfg.adapterFile || path.resolve(__dirname, '..', 'template', cfg.esm ? 'node-index.mjs' : 'node-index.js'),
|
|
35
|
+
output: {
|
|
36
|
+
file: cfg.bundle,
|
|
37
|
+
name: 'main',
|
|
38
|
+
format: cfg.esm ? 'es' : 'cjs',
|
|
39
|
+
preferConst: true,
|
|
40
|
+
externalLiveBindings: false,
|
|
41
|
+
// inlineDynamicImports: true,
|
|
42
|
+
exports: 'default',
|
|
43
|
+
},
|
|
44
|
+
// shimMissingExports: false,
|
|
45
|
+
treeshake: false,
|
|
46
|
+
external: [
|
|
47
|
+
...cfg.externals,
|
|
48
|
+
// the following are imported by the universal adapter and are assumed to be available
|
|
49
|
+
'./params.json',
|
|
50
|
+
'aws-sdk',
|
|
51
|
+
'fs/promises',
|
|
52
|
+
'@google-cloud/secret-manager',
|
|
53
|
+
'@google-cloud/storage',
|
|
54
|
+
'@google-cloud/functions',
|
|
55
|
+
],
|
|
56
|
+
plugins: [
|
|
57
|
+
pluginJson({
|
|
58
|
+
preferConst: true,
|
|
59
|
+
}),
|
|
60
|
+
nodeResolve({
|
|
61
|
+
preferBuiltins: true,
|
|
62
|
+
}),
|
|
63
|
+
commonjs({
|
|
64
|
+
ignoreTryCatch: (id) => id !== './main.js',
|
|
65
|
+
}),
|
|
66
|
+
alias({
|
|
67
|
+
entries: [
|
|
68
|
+
{ find: './main.js', replacement: cfg.file },
|
|
69
|
+
],
|
|
70
|
+
}),
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
if (cfg.minify) {
|
|
74
|
+
opts.plugins.push(terser());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// if (cfg.modulePaths && cfg.modulePaths.length > 0) {
|
|
78
|
+
// opts.resolve.modules = cfg.modulePaths;
|
|
79
|
+
// }
|
|
80
|
+
|
|
81
|
+
return opts;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async createBundle() {
|
|
85
|
+
const { cfg } = this;
|
|
86
|
+
if (!cfg.bundle) {
|
|
87
|
+
throw Error('bundle path is undefined');
|
|
88
|
+
}
|
|
89
|
+
if (!cfg.depFile) {
|
|
90
|
+
throw Error('dependencies info path is undefined');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
cfg.log.info(`--: creating ${cfg.esm ? 'esm ' : ''}${cfg.minify ? 'minified ' : ''}bundle using rollup...`);
|
|
94
|
+
const config = await this.getRollupConfig();
|
|
95
|
+
const bundle = await rollup.rollup(config);
|
|
96
|
+
|
|
97
|
+
const { output } = await bundle.generate(config.output);
|
|
98
|
+
await this.resolveDependencyInfos(output);
|
|
99
|
+
await bundle.write(config.output);
|
|
100
|
+
await bundle.close();
|
|
101
|
+
cfg.log.info(chalk`{green ok:} created bundle {yellow ${config.output.file}}`);
|
|
102
|
+
return { };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resolves the dependencies by chunk. eg:
|
|
107
|
+
*
|
|
108
|
+
* {
|
|
109
|
+
* 'src/idx_json.bundle.js': [{
|
|
110
|
+
* id: '@adobe/helix-epsagon:1.2.0',
|
|
111
|
+
* name: '@adobe/helix-epsagon',
|
|
112
|
+
* version: '1.2.0' },
|
|
113
|
+
* ],
|
|
114
|
+
* ...
|
|
115
|
+
* }
|
|
116
|
+
*/
|
|
117
|
+
async resolveDependencyInfos(output) {
|
|
118
|
+
const depsByFile = {};
|
|
119
|
+
const resolved = {};
|
|
120
|
+
await Promise.all(output.filter((chunkOrAsset) => chunkOrAsset.type !== 'asset').map(async (chunk) => {
|
|
121
|
+
const deps = {};
|
|
122
|
+
depsByFile[chunk.name] = deps;
|
|
123
|
+
await Promise.all(Object.keys(chunk.modules).map(async (modulePath) => {
|
|
124
|
+
const segs = modulePath.split('/');
|
|
125
|
+
let idx = segs.lastIndexOf('node_modules');
|
|
126
|
+
if (idx >= 0) {
|
|
127
|
+
idx += 1;
|
|
128
|
+
if (segs[idx].charAt(0) === '@') {
|
|
129
|
+
idx += 1;
|
|
130
|
+
}
|
|
131
|
+
segs.splice(idx + 1);
|
|
132
|
+
const dir = path.resolve('/', ...segs);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
if (!resolved[dir]) {
|
|
136
|
+
const pkgJson = await fse.readJson(path.resolve(dir, 'package.json'));
|
|
137
|
+
const id = `${pkgJson.name}:${pkgJson.version}`;
|
|
138
|
+
resolved[dir] = {
|
|
139
|
+
id,
|
|
140
|
+
name: pkgJson.name,
|
|
141
|
+
version: pkgJson.version,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const dep = resolved[dir];
|
|
145
|
+
deps[dep.id] = dep;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
// ignore
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
// sort the deps
|
|
154
|
+
Object.entries(depsByFile)
|
|
155
|
+
.forEach(([scriptFile, deps]) => {
|
|
156
|
+
// map 'index' to 'main', in order to be compatible with rollup
|
|
157
|
+
if (scriptFile === 'node-index') {
|
|
158
|
+
// eslint-disable-next-line no-param-reassign
|
|
159
|
+
scriptFile = 'main';
|
|
160
|
+
}
|
|
161
|
+
this.cfg.dependencies[scriptFile] = Object.values(deps)
|
|
162
|
+
.sort((d0, d1) => d0.name.localeCompare(d1.name));
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
};
|
|
@@ -14,39 +14,16 @@ const path = require('path');
|
|
|
14
14
|
const fse = require('fs-extra');
|
|
15
15
|
const webpack = require('webpack');
|
|
16
16
|
const chalk = require('chalk');
|
|
17
|
-
const
|
|
18
|
-
const semver = require('semver');
|
|
19
|
-
const { dependencies } = require('../package.json');
|
|
17
|
+
const BaseBundler = require('./BaseBundler.js');
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
20
|
+
* Webpack based bundler
|
|
23
21
|
*/
|
|
24
|
-
module.exports = class
|
|
25
|
-
/**
|
|
26
|
-
* Simple string substitute. Replaces all `${key}` occurrences from the given object.
|
|
27
|
-
* @param {string} str string to substitute
|
|
28
|
-
* @param {object} props properties
|
|
29
|
-
*/
|
|
30
|
-
static substitute(str, props) {
|
|
31
|
-
return Object.entries(props).reduce((p, [key, value]) => {
|
|
32
|
-
const r = new RegExp(`\\$\\{${key}\\}`, 'g');
|
|
33
|
-
return p.replace(r, value);
|
|
34
|
-
}, str);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
constructor() {
|
|
38
|
-
Object.assign(this, {
|
|
39
|
-
cfg: {},
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
withConfig(cfg) {
|
|
44
|
-
this.cfg = cfg;
|
|
45
|
-
return this;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// eslint-disable-next-line class-methods-use-this,no-empty-function
|
|
22
|
+
module.exports = class WebpackBundler extends BaseBundler {
|
|
49
23
|
async init() {
|
|
24
|
+
if (this.cfg.esm) {
|
|
25
|
+
throw new Error('Webpack bundler does not support ESM builds.');
|
|
26
|
+
}
|
|
50
27
|
}
|
|
51
28
|
|
|
52
29
|
async getWebpackConfig() {
|
|
@@ -55,12 +32,13 @@ module.exports = class Bundler {
|
|
|
55
32
|
target: 'node',
|
|
56
33
|
mode: 'development',
|
|
57
34
|
// the universal adapter is the entry point
|
|
58
|
-
entry: cfg.adapterFile || path.resolve(__dirname, 'template', 'index.js'),
|
|
35
|
+
entry: cfg.adapterFile || path.resolve(__dirname, '..', 'template', 'node-index.js'),
|
|
59
36
|
output: {
|
|
60
37
|
path: cfg.cwd,
|
|
61
38
|
filename: path.relative(cfg.cwd, cfg.bundle),
|
|
62
39
|
library: 'main',
|
|
63
40
|
libraryTarget: 'umd',
|
|
41
|
+
globalObject: 'globalThis',
|
|
64
42
|
},
|
|
65
43
|
devtool: false,
|
|
66
44
|
externals: [
|
|
@@ -111,17 +89,14 @@ module.exports = class Bundler {
|
|
|
111
89
|
return opts;
|
|
112
90
|
}
|
|
113
91
|
|
|
114
|
-
async
|
|
92
|
+
async createWebpackBundle(arch) {
|
|
115
93
|
const { cfg } = this;
|
|
116
|
-
if (!cfg.bundle) {
|
|
117
|
-
throw Error('bundle path is undefined');
|
|
118
|
-
}
|
|
119
94
|
if (!cfg.depFile) {
|
|
120
95
|
throw Error('dependencies info path is undefined');
|
|
121
96
|
}
|
|
122
97
|
const m = cfg.minify ? 'minified ' : '';
|
|
123
98
|
if (!cfg.progressHandler) {
|
|
124
|
-
cfg.log.info(`--: creating ${m}bundle ...`);
|
|
99
|
+
cfg.log.info(`--: creating ${arch} ${m}bundle using webpack ...`);
|
|
125
100
|
}
|
|
126
101
|
const config = await this.getWebpackConfig();
|
|
127
102
|
const compiler = webpack(config);
|
|
@@ -143,11 +118,18 @@ module.exports = class Bundler {
|
|
|
143
118
|
// write dependencies info file
|
|
144
119
|
await fse.writeJson(cfg.depFile, cfg.dependencies, { spaces: 2 });
|
|
145
120
|
if (!cfg.progressHandler) {
|
|
146
|
-
cfg.log.info(chalk`{green ok:} created bundle {yellow ${config.output.filename}}`);
|
|
121
|
+
cfg.log.info(chalk`{green ok:} created ${arch} bundle {yellow ${config.output.filename}}`);
|
|
147
122
|
}
|
|
148
123
|
return stats;
|
|
149
124
|
}
|
|
150
125
|
|
|
126
|
+
async createBundle() {
|
|
127
|
+
if (!this.cfg.bundle) {
|
|
128
|
+
throw Error('bundle path is undefined');
|
|
129
|
+
}
|
|
130
|
+
return this.createWebpackBundle('node');
|
|
131
|
+
}
|
|
132
|
+
|
|
151
133
|
/**
|
|
152
134
|
* Resolves the dependencies by chunk. eg:
|
|
153
135
|
*
|
|
@@ -215,126 +197,4 @@ module.exports = class Bundler {
|
|
|
215
197
|
.sort((d0, d1) => d0.name.localeCompare(d1.name));
|
|
216
198
|
});
|
|
217
199
|
}
|
|
218
|
-
|
|
219
|
-
async validateBundle() {
|
|
220
|
-
const { cfg } = this;
|
|
221
|
-
cfg.log.info('--: validating bundle ...');
|
|
222
|
-
let module;
|
|
223
|
-
try {
|
|
224
|
-
// eslint-disable-next-line global-require,import/no-dynamic-require
|
|
225
|
-
module = require(cfg.bundle);
|
|
226
|
-
} catch (e) {
|
|
227
|
-
cfg.log.error(chalk`{red error:}`, e);
|
|
228
|
-
throw Error(`Validation failed: ${e}`);
|
|
229
|
-
}
|
|
230
|
-
if (!module.main && typeof module.main !== 'function') {
|
|
231
|
-
throw Error('Validation failed: Action has no main() function.');
|
|
232
|
-
}
|
|
233
|
-
cfg.log.info(chalk`{green ok:} bundle can be loaded and has a {gray main()} function.`);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async createArchive() {
|
|
237
|
-
const { cfg } = this;
|
|
238
|
-
if (!cfg.zipFile) {
|
|
239
|
-
throw Error('zip path is undefined');
|
|
240
|
-
}
|
|
241
|
-
return new Promise((resolve, reject) => {
|
|
242
|
-
// create zip file for package
|
|
243
|
-
const output = fse.createWriteStream(cfg.zipFile);
|
|
244
|
-
const archive = archiver('zip');
|
|
245
|
-
cfg.log.info('--: creating zip file ...');
|
|
246
|
-
|
|
247
|
-
let hadErrors = false;
|
|
248
|
-
output.on('close', () => {
|
|
249
|
-
if (!hadErrors) {
|
|
250
|
-
cfg.log.debug(` ${archive.pointer()} total bytes`);
|
|
251
|
-
const relZip = path.relative(process.cwd(), cfg.zipFile);
|
|
252
|
-
cfg.log.info(chalk`{green ok:} created action: {yellow ${relZip}}.`);
|
|
253
|
-
resolve({
|
|
254
|
-
path: cfg.zipFile,
|
|
255
|
-
size: archive.pointer(),
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
archive.on('entry', (data) => {
|
|
260
|
-
cfg.log.debug(` - ${data.name}`);
|
|
261
|
-
});
|
|
262
|
-
archive.on('warning', (err) => {
|
|
263
|
-
hadErrors = true;
|
|
264
|
-
reject(err);
|
|
265
|
-
});
|
|
266
|
-
archive.on('error', (err) => {
|
|
267
|
-
hadErrors = true;
|
|
268
|
-
reject(err);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const packageJson = {
|
|
272
|
-
name: cfg.baseName,
|
|
273
|
-
// make sure the version string is valid, so that `npm install` works
|
|
274
|
-
version: semver.valid(cfg.version.replace(/_/g, '.')) ? cfg.version.replace(/_/g, '.') : `0.0.0+${cfg.version}`,
|
|
275
|
-
description: `Universal Action of ${cfg.name}`,
|
|
276
|
-
main: 'index.js',
|
|
277
|
-
license: 'Apache-2.0',
|
|
278
|
-
dependencies: {
|
|
279
|
-
// google cloud installs these dependencies at deploy time
|
|
280
|
-
// all other environments ignore them – this allows us to
|
|
281
|
-
// avoid bundling something that only google needs
|
|
282
|
-
'@google-cloud/secret-manager': dependencies['@google-cloud/secret-manager'],
|
|
283
|
-
'@google-cloud/storage': dependencies['@google-cloud/storage'],
|
|
284
|
-
},
|
|
285
|
-
};
|
|
286
|
-
archive.pipe(output);
|
|
287
|
-
this.updateArchive(archive, packageJson).then(() => {
|
|
288
|
-
archive.finalize();
|
|
289
|
-
}).catch(reject);
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
get functionJson() {
|
|
294
|
-
return {
|
|
295
|
-
bindings: [
|
|
296
|
-
{
|
|
297
|
-
authLevel: 'anonymous',
|
|
298
|
-
type: 'httpTrigger',
|
|
299
|
-
direction: 'in',
|
|
300
|
-
name: 'req',
|
|
301
|
-
route: `${this.cfg.packageName}/${this.cfg.name.replace('@', '/')}/{path1?}/{path2?}/{path3?}/{path4?}/{path5?}`,
|
|
302
|
-
methods: [
|
|
303
|
-
'get',
|
|
304
|
-
'post',
|
|
305
|
-
'put',
|
|
306
|
-
],
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
type: 'http',
|
|
310
|
-
direction: 'out',
|
|
311
|
-
name: 'res',
|
|
312
|
-
},
|
|
313
|
-
],
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async updateArchive(archive, packageJson) {
|
|
318
|
-
const { cfg } = this;
|
|
319
|
-
archive.file(cfg.bundle, { name: 'index.js' });
|
|
320
|
-
cfg.statics.forEach(([src, name]) => {
|
|
321
|
-
try {
|
|
322
|
-
if (fse.lstatSync(src)
|
|
323
|
-
.isDirectory()) {
|
|
324
|
-
archive.directory(src, name);
|
|
325
|
-
} else {
|
|
326
|
-
archive.file(src, { name });
|
|
327
|
-
}
|
|
328
|
-
} catch (e) {
|
|
329
|
-
throw Error(`error with static file: ${e.message}`);
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
cfg.modules.forEach((mod) => {
|
|
333
|
-
archive.directory(path.resolve(cfg.cwd, `node_modules/${mod}`), `node_modules/${mod}`);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
archive.append(JSON.stringify(packageJson, null, ' '), { name: 'package.json' });
|
|
337
|
-
// azure functions manifest
|
|
338
|
-
archive.append(JSON.stringify(this.functionJson, null, ' '), { name: 'function.json' });
|
|
339
|
-
}
|
|
340
200
|
};
|