edge-functions 2.2.0 → 2.3.0-stage.1

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 CHANGED
@@ -1,3 +1,12 @@
1
+ ## [2.3.0-stage.1](https://github.com/aziontech/vulcan/compare/v2.2.0...v2.3.0-stage.1) (2024-01-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * added building with external api to local runtime ([52bcd13](https://github.com/aziontech/vulcan/commit/52bcd13451e3c3db7e73854dfe52845a8aeb691e))
7
+ * adding partial implementation of async_hooks to local dev ([3665359](https://github.com/aziontech/vulcan/commit/36653598f4c270c73c9375190c9da67a035965b8))
8
+ * resolve external APIs in the local build ([#211](https://github.com/aziontech/vulcan/issues/211)) ([b265ed6](https://github.com/aziontech/vulcan/commit/b265ed613ec9f2702df6f469e0bb6a63799e93de))
9
+
1
10
  ## [2.2.0](https://github.com/aziontech/vulcan/compare/v2.1.0...v2.2.0) (2023-12-14)
2
11
 
3
12
 
@@ -52,7 +52,7 @@ class Esbuild extends BundlerBase {
52
52
 
53
53
  if (useNodePolyfills) {
54
54
  if (!updatedConfig.plugins) updatedConfig.plugins = [];
55
- updatedConfig.plugins.push(ESBuildNodeModulePlugin());
55
+ updatedConfig.plugins.push(ESBuildNodeModulePlugin(globalThis.buildProd));
56
56
  }
57
57
 
58
58
  // inject content in worker initial code.
@@ -4,10 +4,11 @@ import path from 'path';
4
4
  import PolyfillsManager from '../../../polyfills/polyfills-manager.js';
5
5
 
6
6
  /**
7
+ * @param {boolean} buildProd Parameter to identify whether the build is dev or prod
7
8
  * ESBuild Node Module Plugin for polyfilling node modules.
8
9
  * @returns {object} - ESBuild plugin object.
9
10
  */
10
- const ESBuildNodeModulePlugin = () => {
11
+ const ESBuildNodeModulePlugin = (buildProd) => {
11
12
  const NAME = 'vulcan-node-modules-polyfills';
12
13
  const NAMESPACE = NAME;
13
14
 
@@ -43,6 +44,15 @@ const ESBuildNodeModulePlugin = () => {
43
44
  });
44
45
  }
45
46
 
47
+ // external
48
+ if (buildProd) {
49
+ options.external = options.external || [];
50
+ [...polyfillManager.external].forEach(([key]) => {
51
+ options.external.push(key);
52
+ options.external.push(`node:${key}`);
53
+ });
54
+ }
55
+
46
56
  /**
47
57
  * Resolve callback for ESBuild.
48
58
  * @param {object} args - Arguments object.
@@ -50,6 +60,14 @@ const ESBuildNodeModulePlugin = () => {
50
60
  */
51
61
  build.onResolve({ filter: /.*/ }, async (args) => {
52
62
  const argsPath = args.path.replace(/^node:/, '');
63
+
64
+ if (!buildProd && polyfillManager.external.has(argsPath)) {
65
+ return {
66
+ path: args.path,
67
+ namespace: NAMESPACE,
68
+ };
69
+ }
70
+
53
71
  if (!polyfillManager.libs.has(argsPath)) {
54
72
  return;
55
73
  }
@@ -98,7 +116,12 @@ const ESBuildNodeModulePlugin = () => {
98
116
  build.onLoad({ filter: /.*/, namespace: NAMESPACE }, async (args) => {
99
117
  const argsPath = args.path.replace(/^node:/, '');
100
118
 
101
- const resolved = polyfillManager.libs.get(argsPath);
119
+ let resolved = polyfillManager.libs.get(argsPath);
120
+
121
+ if (!buildProd && polyfillManager.external.has(argsPath)) {
122
+ resolved = polyfillManager.external.get(argsPath);
123
+ }
124
+
102
125
  const contents = await fs.promises.readFile(resolved, 'utf8');
103
126
  const resolveDir = path.dirname(resolved);
104
127
 
@@ -7,6 +7,11 @@ const polyfillsPath = `${libDirPath}/build/bundlers/polyfills`;
7
7
  const nextNodePresetPath = `${libDirPath}/presets/custom/next/compute/node`;
8
8
  const nodePolyfillsPath = `${polyfillsPath}/node`;
9
9
 
10
+ /**
11
+ * External polyfills are resolved in the build, but as they are for the local environment (Vulcan dev) they are located in #env/polyfills.
12
+ */
13
+ const externalPolyfillsPath = `${libDirPath}/env/polyfills`;
14
+
10
15
  /**
11
16
  * Manages and builds polyfills for Node and global browser environments.
12
17
  */
@@ -21,6 +26,8 @@ class PolyfillsManager {
21
26
  this.libs = new Map();
22
27
  /** @type {Map<string, string|boolean>} */
23
28
  this.alias = new Map();
29
+ /** @type {Map<string, string|boolean>} */
30
+ this.external = new Map();
24
31
  }
25
32
 
26
33
  /**
@@ -50,9 +57,18 @@ class PolyfillsManager {
50
57
  this.alias.set(name, path);
51
58
  }
52
59
 
60
+ /**
61
+ * Sets a external libs.
62
+ * @param {string} name - Name of the external.
63
+ * @param {string|boolean} path - Path to the polyfill or a boolean value.
64
+ */
65
+ setExternal(name, path) {
66
+ this.external.set(name, path);
67
+ }
68
+
53
69
  /**
54
70
  * Builds and retrieves the polyfills for Node and globals.
55
- * @returns {{ libs: Map<string, string|boolean>, globals: Map<string, string>, alias: Map<string, string> }} - Object containing libs and globals.
71
+ * @returns {{ libs: Map<string, string|boolean>, globals: Map<string, string>, alias: Map<string, string>, external: Map<string, string> }} - Object containing libs and globals.
56
72
  */
57
73
  buildPolyfills() {
58
74
  this.setGlobal('buffer', `${nodePolyfillsPath}/globals/buffer.js`);
@@ -70,7 +86,6 @@ class PolyfillsManager {
70
86
  );
71
87
 
72
88
  this.setLib('accepts', require.resolve('accepts'));
73
- this.setLib('async_hooks', `${nodePolyfillsPath}/_empty.js`);
74
89
  this.setLib('buffer', require.resolve('buffer/'));
75
90
  this.setLib('child_process', `${nodePolyfillsPath}/_empty.js`);
76
91
  this.setLib('cluster', `${nodePolyfillsPath}/_empty.js`);
@@ -131,7 +146,17 @@ class PolyfillsManager {
131
146
  this.setAlias('util', require.resolve('util/'));
132
147
  this.setAlias('process', `${nodePolyfillsPath}/globals/process.js`);
133
148
 
134
- return { libs: this.libs, globals: this.globals, alias: this.alias };
149
+ this.setExternal(
150
+ 'async_hooks',
151
+ `${externalPolyfillsPath}/async_hooks/index.js`,
152
+ );
153
+
154
+ return {
155
+ libs: this.libs,
156
+ globals: this.globals,
157
+ alias: this.alias,
158
+ external: this.external,
159
+ };
135
160
  }
136
161
  }
137
162
 
@@ -5,7 +5,6 @@ describe('Polyfills Manager', () => {
5
5
  it('Should return map of polyfills', () => {
6
6
  const expectedPolyfills = [
7
7
  'accepts',
8
- 'async_hooks',
9
8
  'buffer',
10
9
  'child_process',
11
10
  'cluster',
@@ -87,7 +87,7 @@ class Webpack extends BundlerBase {
87
87
  this.presetMode === 'compute';
88
88
 
89
89
  if (useNodePolyfills) {
90
- updatedConfig.plugins.push(new NodePolyfillPlugin());
90
+ updatedConfig.plugins.push(new NodePolyfillPlugin(globalThis.buildProd));
91
91
  }
92
92
  return updatedConfig;
93
93
  }
@@ -3,6 +3,10 @@ import { generateWebpackBanner } from '#utils';
3
3
  import PolyfillsManager from '../../../polyfills/polyfills-manager.js';
4
4
 
5
5
  class NodePolyfillPlugin {
6
+ constructor(buildProd) {
7
+ this.buildProd = buildProd;
8
+ }
9
+
6
10
  apply(compiler) {
7
11
  const polyfillsManager = PolyfillsManager.buildPolyfills();
8
12
 
@@ -46,6 +50,28 @@ class NodePolyfillPlugin {
46
50
  }),
47
51
  );
48
52
 
53
+ if (this.buildProd) {
54
+ compiler.options.externals = [
55
+ // eslint-disable-next-line
56
+ function ({ _, request }, callback) {
57
+ [...polyfillsManager.external].map(([key]) => {
58
+ const pattern = new RegExp(`${key}$`);
59
+ if (pattern.test(request)) {
60
+ return callback(null, `module ${request}`);
61
+ }
62
+ return callback();
63
+ });
64
+ },
65
+ ];
66
+ } else {
67
+ compiler.options.resolve.fallback = {
68
+ ...Object.fromEntries(
69
+ [...polyfillsManager.external].map(([key, value]) => [key, value]),
70
+ ),
71
+ ...compiler.options.resolve.fallback,
72
+ };
73
+ }
74
+
49
75
  compiler.options.resolve.alias = {
50
76
  ...Object.fromEntries(
51
77
  [...polyfillsManager.alias].map(([key, value]) => [key, value]),
@@ -14,7 +14,7 @@ import {
14
14
  injectFilesInMem,
15
15
  } from '#utils';
16
16
  import { Messages } from '#constants';
17
- import { vulcan } from '#env';
17
+ import vulcan from '../../env/vulcan.env.js';
18
18
  import {
19
19
  getAliasPath,
20
20
  createDotEnvFile,
@@ -378,6 +378,7 @@ class Dispatcher {
378
378
 
379
379
  await vulcan.createVulcanEnv(
380
380
  {
381
+ builder: builderSelected,
381
382
  entry: originalEntry, // original entry
382
383
  preset: buildConfig.preset.name,
383
384
  mode: buildConfig.preset.mode,
@@ -1,6 +1,6 @@
1
1
  import { Commands } from '#namespaces';
2
2
  import { feedback } from '#utils';
3
- import { vulcan } from '#env';
3
+ import vulcan from '../env/vulcan.env.js';
4
4
 
5
5
  /**
6
6
  * Retrieves a configuration value based on priority.
@@ -35,6 +35,7 @@ const env = {
35
35
  },
36
36
  errors: {
37
37
  load_worker_failed: (path) => `Failed load worker: ${path}`,
38
+ run_build_command: 'Run the build command before running your project',
38
39
  },
39
40
  },
40
41
  };
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ import * as async_hooks from 'async_hooks';
3
+
4
+ export class AsyncLocalStorage extends async_hooks.AsyncLocalStorage {}
5
+ export class AsyncResource extends async_hooks.AsyncResource {}
6
+
7
+ export default {
8
+ AsyncLocalStorage,
9
+ AsyncResource,
10
+ };
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * ASYNC_LOCAL_STORAGE is defined in runtime.env.js for use on the local server
4
+ */
5
+
6
+ export class AsyncLocalStorage extends ASYNC_LOCAL_STORAGE.AsyncLocalStorage {}
7
+ export class AsyncResource extends ASYNC_LOCAL_STORAGE.AsyncResource {}
8
+
9
+ export default {
10
+ AsyncLocalStorage,
11
+ AsyncResource,
12
+ };
@@ -1,4 +1,5 @@
1
- import fetchPolyfill from './fetch.polyfills.js';
2
- import FetchEventPolyfill from './FetchEvent.polyfills.js';
1
+ import fetchPolyfill from './fetch/fetch.polyfills.js';
2
+ import FetchEventPolyfill from './fetch/FetchEvent.polyfills.js';
3
+ import AsyncHooks from './async_hooks/context/index.js';
3
4
 
4
- export { fetchPolyfill, FetchEventPolyfill };
5
+ export { fetchPolyfill, FetchEventPolyfill, AsyncHooks };
@@ -1,6 +1,10 @@
1
1
  import { EdgeRuntime } from 'edge-runtime';
2
2
 
3
- import { fetchPolyfill, FetchEventPolyfill } from './polyfills/index.js';
3
+ import {
4
+ fetchPolyfill,
5
+ FetchEventPolyfill,
6
+ AsyncHooks,
7
+ } from './polyfills/index.js';
4
8
 
5
9
  /**
6
10
  * Executes the specified JavaScript code within a sandbox environment,
@@ -56,6 +60,9 @@ function runtime(code) {
56
60
  context.WebSocket = undefined;
57
61
  /* ========================================================== */
58
62
 
63
+ // Async Hooks
64
+ context.ASYNC_LOCAL_STORAGE = AsyncHooks;
65
+
59
66
  return context;
60
67
  };
61
68
 
@@ -1,9 +1,10 @@
1
- import { debug, readWorkerFile, feedback, exec } from '#utils';
1
+ import { debug, readWorkerFile, feedback } from '#utils';
2
2
  import { Messages } from '#constants';
3
3
  import { runServer, EdgeRuntime } from 'edge-runtime';
4
4
  import chokidar from 'chokidar';
5
5
  import runtime from './runtime.env.js';
6
6
  import vulcan from './vulcan.env.js';
7
+ import buildCommand from '../commands/build.commands.js';
7
8
 
8
9
  let currentServer;
9
10
  let isChangeHandlerRunning = false;
@@ -37,6 +38,21 @@ async function initializeServer(port, workerCode) {
37
38
  return runServer({ port, host: '0.0.0.0', runtime: execution });
38
39
  }
39
40
 
41
+ /**
42
+ * Build to Local Server with polyfill external
43
+ */
44
+ async function buildToLocalServer() {
45
+ const vulcanEnv = await vulcan.readVulcanEnv('local');
46
+
47
+ if (!vulcanEnv) {
48
+ const msg = Messages.env.server.errors.run_build_command;
49
+ feedback.server.error(msg);
50
+ throw new Error(msg);
51
+ }
52
+ globalThis.buildProd = false;
53
+ await buildCommand({});
54
+ }
55
+
40
56
  /**
41
57
  * Handle server operations: start, restart.
42
58
  * @param {string} workerPath - Path to the worker file.
@@ -44,6 +60,8 @@ async function initializeServer(port, workerCode) {
44
60
  */
45
61
  async function manageServer(workerPath, port) {
46
62
  try {
63
+ await buildToLocalServer();
64
+
47
65
  const workerCode = await readWorkerCode(workerPath);
48
66
 
49
67
  if (currentServer) {
@@ -90,23 +108,8 @@ async function handleFileChange(path, workerPath, port) {
90
108
  return;
91
109
  }
92
110
 
93
- const { entry, preset, mode, useNodePolyfills, useOwnWorker } =
94
- await vulcan.readVulcanEnv('local');
95
-
96
- let command = `vulcan build --entry ${entry} --preset ${preset} --mode ${mode}`;
97
-
98
- if (useNodePolyfills) {
99
- command += ` --useNodePolyfills ${useNodePolyfills}`;
100
- }
101
-
102
- if (useOwnWorker) {
103
- command += ` --useOwnWorker ${useOwnWorker}`;
104
- }
105
-
106
- feedback.build.info(Messages.build.info.rebuilding);
107
-
108
111
  try {
109
- await exec(command);
112
+ feedback.build.info(Messages.build.info.rebuilding);
110
113
  await manageServer(workerPath, port);
111
114
  } catch (error) {
112
115
  debug.error(`Build or server restart failed: ${error}`);
package/lib/main.js CHANGED
@@ -17,6 +17,9 @@ const vulcanVersion = vulcanPackageJSON.version;
17
17
 
18
18
  const debugEnabled = process.env.DEBUG === 'true';
19
19
 
20
+ // This global variable is to define whether the build is for prod or dev. This is to handle external modules.
21
+ globalThis.buildProd = true;
22
+
20
23
  const program = new Command();
21
24
 
22
25
  /**
@@ -88,6 +91,7 @@ function startVulcanProgram() {
88
91
  )
89
92
  .action(async (options) => {
90
93
  const { buildCommand } = await import('#commands');
94
+ globalThis.buildProd = true;
91
95
  await buildCommand(options);
92
96
  });
93
97
 
@@ -116,6 +116,11 @@ class Manifest {
116
116
  JSON.stringify(this.manifest, null, 2),
117
117
  'utf8',
118
118
  );
119
+ // clear manifest to reload server
120
+ this.manifest = {
121
+ routes: [],
122
+ fs: [],
123
+ };
119
124
  }
120
125
  }
121
126
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "edge-functions",
3
3
  "type": "module",
4
- "version": "2.2.0",
4
+ "version": "2.3.0-stage.1",
5
5
  "description": "Tool to launch and build JavaScript/Frameworks. This tool automates polyfills for Edge Computing and assists in creating Workers, notably for the Azion platform.",
6
6
  "main": "lib/main.js",
7
7
  "bin": {