edge-functions 2.2.0-stage.1 → 2.2.0-stage.2

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/jest.config.e2e.js +8 -0
  4. package/jest.config.unit.js +7 -0
  5. package/lib/build/bundlers/bundler-base.js +102 -0
  6. package/lib/build/bundlers/esbuild/esbuild.config.js +2 -0
  7. package/lib/build/bundlers/esbuild/index.js +46 -33
  8. package/lib/build/bundlers/esbuild/index.test.js +90 -0
  9. package/lib/build/bundlers/esbuild/plugins/node-polyfills/index.js +92 -131
  10. package/lib/build/bundlers/esbuild/plugins/node-polyfills/index.test.js +38 -0
  11. package/lib/build/bundlers/polyfills/node/_empty.js +0 -0
  12. package/lib/build/bundlers/polyfills/node/crypto.js +70 -0
  13. package/lib/build/bundlers/polyfills/node/globals/buffer.js +4 -0
  14. package/lib/build/bundlers/polyfills/node/globals/path-dirname.js +6 -0
  15. package/lib/build/{polyfills → bundlers/polyfills}/node/globals/process.js +76 -24
  16. package/lib/build/bundlers/polyfills/polyfills-manager.js +138 -0
  17. package/lib/build/bundlers/{esbuild/plugins/node-polyfills/node-pollyfills-paths.test.js → polyfills/polyfills-manager.test.js} +34 -21
  18. package/lib/build/bundlers/webpack/index.js +44 -28
  19. package/lib/build/bundlers/webpack/index.test.js +101 -0
  20. package/lib/build/bundlers/webpack/plugins/node-polyfills/index.js +65 -0
  21. package/lib/build/bundlers/webpack/plugins/node-polyfills/index.test.js +55 -0
  22. package/lib/build/bundlers/webpack/webpack.config.js +31 -2
  23. package/lib/build/dispatcher/dispatcher.js +203 -118
  24. package/lib/build/dispatcher/dispatcher.test.js +0 -1
  25. package/lib/constants/messages/build.messages.js +4 -0
  26. package/lib/presets/custom/next/compute/config.js +2 -87
  27. package/lib/presets/custom/next/compute/node/custom-server/12.3.1/server/require.js +3 -0
  28. package/package.json +12 -5
  29. package/lib/build/bundlers/esbuild/plugins/node-polyfills/node-polyfills-paths.js +0 -122
  30. /package/lib/build/{polyfills → bundlers/polyfills}/node/dns.js +0 -0
  31. /package/lib/build/{polyfills → bundlers/polyfills}/node/fs.js +0 -0
  32. /package/lib/build/{polyfills → bundlers/polyfills}/node/globals/navigator.js +0 -0
  33. /package/lib/build/{polyfills → bundlers/polyfills}/node/globals/performance.js +0 -0
  34. /package/lib/build/{polyfills → bundlers/polyfills}/node/http2.js +0 -0
  35. /package/lib/build/{polyfills → bundlers/polyfills}/node/module.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [2.2.0-stage.2](https://github.com/aziontech/vulcan/compare/v2.2.0-stage.1...v2.2.0-stage.2) (2023-12-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * esbuild bundler improvements and changes ([5e668c4](https://github.com/aziontech/vulcan/commit/5e668c4c8c725de37102b776708a5641980842f4))
7
+ * improvements bundlers ([d546697](https://github.com/aziontech/vulcan/commit/d546697154b61124b947fff347b494dc474b7c36))
8
+ * initial code for organizing polyfill plugins ([2212272](https://github.com/aziontech/vulcan/commit/22122729bbdf0155357142c7fcbebd612d0509e3))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * esbuild plugin initial options ([8c84baa](https://github.com/aziontech/vulcan/commit/8c84baa117ed84a67202284d34abb05857083ec2))
14
+ * prevent reference sharing in config ([98b8caa](https://github.com/aziontech/vulcan/commit/98b8caa526e554401aeec2afed2dd126b29994f0))
15
+
1
16
  ## [2.2.0-stage.1](https://github.com/aziontech/vulcan/compare/v2.1.0...v2.2.0-stage.1) (2023-12-05)
2
17
 
3
18
 
package/README.md CHANGED
@@ -108,7 +108,7 @@ Defines which build tool to use. The available options are `esbuild` and `webpac
108
108
  **Type:** Boolean
109
109
 
110
110
  **Description:**
111
- Determines if Node.js polyfills should be applied. This is useful for projects that leverage Node.js specific functionalities but are targeting environments without such built-in capabilities.
111
+ Determines whether Node.js polyfills should be applied. This is useful for projects that leverage specific Node.js functionality but target environments without these built-in features. The use of useNodePolyfills is ignored when used in mode `deliver` presets, as Node.js features must be resolved at build time by the framework process itself.
112
112
 
113
113
  ### UseOwnWorker
114
114
 
@@ -0,0 +1,8 @@
1
+ export default {
2
+ transform: {
3
+ '^.+\\.(js|jsx)?$': 'babel-jest',
4
+ },
5
+ testPathIgnorePatterns: ['/node_modules/', '/examples/'],
6
+ testEnvironment: 'node',
7
+ globalSetup: '<rootDir>/jest.global.setup.js',
8
+ };
@@ -0,0 +1,7 @@
1
+ export default {
2
+ transform: {
3
+ '^.+\\.(t|j)s?$': '@swc/jest',
4
+ },
5
+ testPathIgnorePatterns: ['/node_modules/', '/examples/'],
6
+ testEnvironment: 'node',
7
+ };
@@ -0,0 +1,102 @@
1
+ import { Messages } from '#constants';
2
+ import { feedback } from '#utils';
3
+ import merge from 'lodash.merge';
4
+
5
+ /**
6
+ * Base class for a bundler.
7
+ * @class
8
+ */
9
+ class BundlerBase {
10
+ /**
11
+ * Represents a configuration object.
12
+ * @typedef {object} BuilderConfig
13
+ * @property {string} entry - The entry point for the configuration
14
+ * @property {string} buildId - The build ID
15
+ * @property {boolean} useNodePolyfills - Indicates if Node polyfills are being used
16
+ * @property {boolean} useOwnWorker - Indicates if a custom worker is being used
17
+ * @property {object} custom - Custom configuration.
18
+ * @property {*} localCustom - Local custom data
19
+ * @property {*} preset - The preset configuration
20
+ * @property {string} contentToInject - Content to inject
21
+ */
22
+
23
+ /**
24
+ * @param {BuilderConfig} builderConfig - Constructor configuration.
25
+ */
26
+ constructor(builderConfig) {
27
+ /**
28
+ * Constructor configuration.
29
+ * @type {BuilderConfig}
30
+ */
31
+ this.builderConfig = builderConfig;
32
+
33
+ /**
34
+ * Custom configuration from the preset.
35
+ * @type {object}
36
+ */
37
+ this.customConfigPreset = builderConfig.custom;
38
+
39
+ /**
40
+ * Local custom configuration.
41
+ * @type {object}
42
+ */
43
+ this.customConfigLocal = builderConfig.localCustom;
44
+
45
+ /**
46
+ * Preset mode. e.g deliver | mode
47
+ * @type {object}
48
+ */
49
+ this.presetMode = builderConfig?.preset?.mode;
50
+ }
51
+
52
+ /**
53
+ * Executes the plugin modification logic.
54
+ * This method can be overridden by subclasses.
55
+ * @returns {Promise<void>}
56
+ * @abstract
57
+ */
58
+ // eslint-disable-next-line class-methods-use-this
59
+ async run() {
60
+ // Implement plugin modification logic here
61
+ // This method can be overridden by subclasses
62
+ // as it does not require the use of `this`
63
+ // eslint-disable-next-line class-methods-use-this
64
+ throw new Error('Running BundlerBase run method');
65
+ }
66
+
67
+ /**
68
+ * Applies the configuration.
69
+ * @param {object} config - Configuration to be applies.
70
+ * @returns {object} - The apply configuration.
71
+ * @abstract
72
+ */
73
+ // eslint-disable-next-line class-methods-use-this
74
+ applyConfig(config) {
75
+ // The ESLint error is being ignored for this method
76
+ // as it does not require the use of `this`
77
+ return config;
78
+ }
79
+
80
+ /**
81
+ * Merges the configuration with the current configuration.
82
+ * @param {object} config - Configuration to be merged.
83
+ * @returns {object} - The merged configuration.
84
+ */
85
+ mergeConfig(config = {}) {
86
+ const isDeliverMode = this.presetMode === 'deliver';
87
+ // waring mode deliver when useNodePolyfills
88
+ if (this.builderConfig.useNodePolyfills && isDeliverMode) {
89
+ feedback.build.warn(
90
+ Messages.build.warning.use_node_polyfill_mode_deliver,
91
+ );
92
+ }
93
+ const hasCustomConfig = Object.keys(this.customConfigPreset).length > 0;
94
+ const hasCustomConfigLocal = Object.keys(this.customConfigLocal).length > 0;
95
+ if (hasCustomConfig || hasCustomConfigLocal) {
96
+ return merge(config, this.customConfigPreset, this.customConfigLocal);
97
+ }
98
+ return config;
99
+ }
100
+ }
101
+
102
+ export default BundlerBase;
@@ -11,7 +11,9 @@ export default {
11
11
  bundle: true,
12
12
  minify: true,
13
13
  target: 'es2022',
14
+ format: 'esm',
14
15
  platform: 'browser',
16
+ mainFields: ['browser', 'main', 'module'],
15
17
  loader: {
16
18
  '.js': 'jsx',
17
19
  },
@@ -1,59 +1,72 @@
1
1
  import * as esbuild from 'esbuild';
2
- import merge from 'deepmerge';
2
+ import lodash from 'lodash';
3
3
 
4
- import { debug } from '#utils';
5
4
  import { Messages } from '#constants';
5
+ import { debug } from '#utils';
6
6
 
7
+ import BundlerBase from '../bundler-base.js';
7
8
  import AzionEsbuildConfig from './esbuild.config.js';
8
- import { NodeModulesPolyfillPlugin } from './plugins/node-polyfills/index.js';
9
+ import ESBuildNodeModulePlugin from './plugins/node-polyfills/index.js';
9
10
 
10
- class Esbuild {
11
- constructor(builderConfig) {
12
- this.builderConfig = builderConfig;
13
- this.customConfigPreset = builderConfig.custom;
14
- this.customConfigLocal = builderConfig.localCustom;
15
- }
16
-
17
- run = async () => {
18
- let config = AzionEsbuildConfig;
11
+ /**
12
+ * Class representing an ESBuild bundler, extending BundlerBase.
13
+ */
14
+ class Esbuild extends BundlerBase {
15
+ /**
16
+ * Asynchronous method to run the ESBuild bundler.
17
+ */
18
+ async run() {
19
+ let config = lodash.cloneDeep(AzionEsbuildConfig);
19
20
  config.entryPoints = [this.builderConfig.entry];
20
21
  config.define = {
21
22
  AZION_VERSION_ID: JSON.stringify(this.builderConfig.buildId),
22
23
  };
23
24
 
24
- const hasCustomConfig = Object.keys(this.customConfigPreset).length > 0;
25
- if (hasCustomConfig) {
26
- config = merge(this.customConfigPreset, this.customConfigLocal, config);
25
+ if (!config.plugins) config.plugins = [];
26
+
27
+ // merge config common
28
+ config = super.mergeConfig(config);
29
+ config = this.applyConfig(config);
30
+
31
+ try {
32
+ await esbuild.build(config);
33
+ } catch (error) {
34
+ debug.error(error);
35
+ throw Error(Messages.build.error.vulcan_build_failed);
27
36
  }
37
+ }
28
38
 
29
- if (
30
- this.builderConfig.useNodePolyfills ||
31
- this.customConfigPreset.useNodePolyfills ||
32
- this.customConfigLocal
33
- ) {
34
- if (!config.plugins) config.plugins = [];
39
+ /**
40
+ * Applies specific configurations to the ESBuild config.
41
+ * @param {object} config - ESBuild configuration object.
42
+ * @returns {object} - Updated ESBuild configuration object.
43
+ */
44
+ applyConfig(config) {
45
+ const updatedConfig = { ...config };
46
+ // use polyfill with useNodePolyfills and preset mode compute
47
+ const useNodePolyfills =
48
+ (this.builderConfig?.useNodePolyfills ||
49
+ this.customConfigPreset?.useNodePolyfills ||
50
+ this.customConfigLocal?.useNodePolyfills) &&
51
+ this.presetMode === 'compute';
35
52
 
36
- config.plugins = [NodeModulesPolyfillPlugin(), ...config.plugins];
53
+ if (useNodePolyfills) {
54
+ if (!updatedConfig.plugins) updatedConfig.plugins = [];
55
+ updatedConfig.plugins.push(ESBuildNodeModulePlugin());
37
56
  }
38
57
 
39
58
  // inject content in worker initial code.
40
59
  if (this.builderConfig.contentToInject) {
41
60
  const workerInitContent = this.builderConfig.contentToInject;
42
61
 
43
- if (config.banner?.js) {
44
- config.banner.js = `${config.banner.js} ${workerInitContent}`;
62
+ if (updatedConfig.banner?.js) {
63
+ updatedConfig.banner.js = `${updatedConfig.banner.js} ${workerInitContent}`;
45
64
  } else {
46
- config.banner = { js: workerInitContent };
65
+ updatedConfig.banner = { js: workerInitContent };
47
66
  }
48
67
  }
49
-
50
- try {
51
- await esbuild.build(config);
52
- } catch (error) {
53
- debug.error(error);
54
- throw Error(Messages.build.error.vulcan_build_failed);
55
- }
56
- };
68
+ return updatedConfig;
69
+ }
57
70
  }
58
71
 
59
72
  export default Esbuild;
@@ -0,0 +1,90 @@
1
+ import fs from 'fs';
2
+ import tmp from 'tmp';
3
+ import Esbuild from './index.js';
4
+ import AzionEsbuildConfig from './esbuild.config.js';
5
+
6
+ // IN MILESECONDS
7
+ const TIMEOUT = 20000;
8
+
9
+ describe('Esbuild Bundler', () => {
10
+ let tmpDir;
11
+ let tmpEntry;
12
+ let tmpOutput;
13
+
14
+ beforeEach(async () => {
15
+ tmpDir = tmp.dirSync();
16
+ tmpEntry = tmp.fileSync({
17
+ postfix: '.js',
18
+ dir: tmpDir.name,
19
+ name: 'entry.js',
20
+ });
21
+ tmpOutput = tmp.fileSync({
22
+ postfix: '.js',
23
+ dir: tmpDir.name,
24
+ name: 'output.js',
25
+ });
26
+ });
27
+
28
+ afterEach(async () => {
29
+ tmpEntry.removeCallback();
30
+ tmpOutput.removeCallback();
31
+ tmpDir.removeCallback();
32
+ });
33
+
34
+ it(
35
+ 'should execute the bundle successfully',
36
+ async () => {
37
+ const code = `import crypto from 'crypto';console.log(process.env.NODE_ENV);`;
38
+ await fs.promises.writeFile(tmpEntry.name, code);
39
+
40
+ const bundler = new Esbuild({
41
+ buildId: '12345',
42
+ custom: {},
43
+ entry: tmpEntry.name,
44
+ localCustom: {
45
+ outfile: tmpOutput.name,
46
+ minify: false,
47
+ },
48
+ useNodePolyfills: true,
49
+ useOwnWorker: false,
50
+ preset: {
51
+ name: 'javascript',
52
+ mode: 'compute',
53
+ },
54
+ });
55
+ await bundler.run();
56
+
57
+ const result = fs.readFileSync(tmpOutput.name, 'utf-8');
58
+
59
+ expect(result).toContain('vulcan-node-modules-polyfills:crypto');
60
+ },
61
+ TIMEOUT,
62
+ );
63
+
64
+ it(
65
+ 'should merge the config, in this case optimization.minimize false',
66
+ async () => {
67
+ const bundler = new Esbuild({
68
+ buildId: '12345',
69
+ custom: {
70
+ minify: true,
71
+ },
72
+ entry: tmpEntry.name,
73
+ localCustom: {
74
+ outfile: tmpOutput.name,
75
+ minify: false,
76
+ },
77
+ useNodePolyfills: false,
78
+ useOwnWorker: false,
79
+ preset: {
80
+ name: 'javascript',
81
+ mode: 'compute',
82
+ },
83
+ });
84
+ let config = AzionEsbuildConfig;
85
+ config = bundler.mergeConfig(config);
86
+ expect(config?.minify).toBeFalsy();
87
+ },
88
+ TIMEOUT,
89
+ );
90
+ });
@@ -1,154 +1,115 @@
1
- /* eslint-disable */
2
- // Based on https://github.com/remorses/esbuild-plugins/blob/master/node-modules-polyfill/src/index.ts
3
-
1
+ /* eslint-disable consistent-return */
4
2
  import fs from 'fs';
5
3
  import path from 'path';
6
-
7
- import builtinsPolyfills from './node-polyfills-paths.js';
8
-
9
- const NAME = 'node-modules-polyfills';
10
- const NAMESPACE = NAME;
11
-
12
- /**
13
- * Remove ending slash
14
- * @param {string} importee - the imported path
15
- * @returns {string} the new path without ending slash
16
- */
17
- function removeEndingSlash(importee) {
18
- if (importee && importee.slice(-1) === '/') {
19
- return importee.slice(0, -1);
20
- }
21
-
22
- return importee;
23
- }
24
-
25
- function escapeStringRegexp(string) {
26
- if (typeof string !== 'string') {
27
- throw new TypeError('Expected a string');
28
- }
29
-
30
- // Escape characters with special meaning either inside or outside character sets.
31
- // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
32
- return string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d');
33
- }
4
+ import PolyfillsManager from '../../../polyfills/polyfills-manager.js';
34
5
 
35
6
  /**
36
- * Generates a template for commonjs with module require
37
- * @param {string} importPath - the import path
38
- * @returns {string} the template
7
+ * ESBuild Node Module Plugin for polyfilling node modules.
8
+ * @returns {object} - ESBuild plugin object.
39
9
  */
40
- function commonJsTemplate({ importPath }) {
41
- return `
42
- const polyfill = require('${importPath}')
43
-
44
- if (polyfill && polyfill.default) {
45
- module.exports = polyfill.default
46
- for (let k in polyfill) {
47
- module.exports[k] = polyfill[k]
48
- }
49
- } else if (polyfill) {
50
- module.exports = polyfill
51
- }
52
-
53
-
54
- `;
55
- }
56
-
57
- /**
58
- * Plugin to use nodejs polyfills
59
- * @param {object} options - Options to use with the plugin
60
- * @returns {object} - the plugin
61
- */
62
- export function NodeModulesPolyfillPlugin(options = {}) {
63
- const { namespace = NAMESPACE, name = NAME } = options;
64
- if (namespace.endsWith('commonjs')) {
65
- throw new Error(`namespace ${namespace} must not end with commonjs`);
66
- }
67
- // this namespace is needed to make ES modules expose their default export to require:
68
- // require('assert') will give you import('assert').default
69
- const commonjsNamespace = `${namespace}-commonjs`;
70
- const polyfilledBuiltins = builtinsPolyfills();
71
- const polyfilledBuiltinsNames = [...polyfilledBuiltins.keys()];
10
+ const ESBuildNodeModulePlugin = () => {
11
+ const NAME = 'vulcan-node-modules-polyfills';
12
+ const NAMESPACE = NAME;
72
13
 
73
14
  return {
74
- name,
75
- setup: function setup({ onLoad, onResolve, initialOptions }) {
76
- // polyfills contain global keyword, it must be defined
77
- if (initialOptions?.define && !initialOptions.define?.global) {
78
- initialOptions.define.global = 'globalThis';
79
- } else if (!initialOptions?.define) {
80
- initialOptions.define = { global: 'globalThis' };
15
+ /**
16
+ * Name and setup of the ESBuild plugin.
17
+ * @param {object} build - ESBuild build object.
18
+ */
19
+ name: NAME,
20
+ setup: (build) => {
21
+ const polyfillManager = PolyfillsManager.buildPolyfills();
22
+
23
+ // build options
24
+ const options = build.initialOptions;
25
+ options.define = options.define || {};
26
+ if (!options.define?.global) {
27
+ options.define.global = 'globalThis';
28
+ }
29
+
30
+ // define env
31
+ options.define = {
32
+ ...options.define,
33
+ 'process.env.NODE_ENV': '"production"',
34
+ 'process.env.NEXT_RUNTIME': JSON.stringify('edge'),
35
+ 'process.env.NEXT_COMPUTE_JS': JSON.stringify(true),
36
+ };
37
+
38
+ // build inject
39
+ options.inject = options.inject || [];
40
+ if (polyfillManager.globals) {
41
+ [...polyfillManager.globals].forEach(([, value]) => {
42
+ options.inject.push(value);
43
+ });
81
44
  }
82
45
 
83
- // TODO these polyfill module cannot import anything, is that ok?
84
46
  /**
85
- *
86
- * @param args
47
+ * Resolve callback for ESBuild.
48
+ * @param {object} args - Arguments object.
49
+ * @returns {object|undefined} - Object with path and namespace or undefined.
87
50
  */
88
- async function loader(args) {
89
- try {
90
- const argsPath = args.path.replace(/^node:/, '');
91
- const isCommonjs = args.namespace.endsWith('commonjs');
92
-
93
- const resolved = polyfilledBuiltins.get(removeEndingSlash(argsPath));
94
- const contents = await (
95
- await fs.promises.readFile(resolved)
96
- ).toString();
97
- const resolveDir = path.dirname(resolved);
98
-
99
- if (isCommonjs) {
100
- return {
101
- loader: 'js',
102
- contents: commonJsTemplate({
103
- importPath: argsPath,
104
- }),
105
- resolveDir,
106
- };
107
- }
108
- return {
109
- loader: 'js',
110
- contents,
111
- resolveDir,
112
- };
113
- } catch (e) {
114
- console.error('node-modules-polyfill', e);
115
- return {
116
- contents: 'export {}',
117
- loader: 'js',
118
- };
51
+ build.onResolve({ filter: /.*/ }, async (args) => {
52
+ const argsPath = args.path.replace(/^node:/, '');
53
+ if (!polyfillManager.libs.has(argsPath)) {
54
+ return;
119
55
  }
120
- }
121
- onLoad({ filter: /.*/, namespace }, loader);
122
- onLoad({ filter: /.*/, namespace: commonjsNamespace }, loader);
123
- const filter = new RegExp(
124
- [
125
- ...polyfilledBuiltinsNames,
126
- ...polyfilledBuiltinsNames.map((n) => `node:${n}`),
127
- ]
128
- .map(escapeStringRegexp)
129
- .join('|'), // TODO builtins could end with slash, keep in mind in regex
130
- );
131
56
 
132
- async function resolver(args) {
133
- const argsPath = args.path.replace(/^node:/, '');
134
- const ignoreRequire = args.namespace === commonjsNamespace;
57
+ // alias bypass
58
+ options.alias = options.alias || {};
59
+ if (
60
+ Object.keys(options.alias)?.length > 0 &&
61
+ options?.alias[argsPath]
62
+ ) {
63
+ return;
64
+ }
135
65
 
136
- if (!polyfilledBuiltins.has(argsPath)) {
66
+ // external bypass
67
+ if (
68
+ options?.external?.length > 0 &&
69
+ options?.external?.includes(argsPath)
70
+ ) {
137
71
  return;
138
72
  }
139
73
 
140
- const isCommonjs = !ignoreRequire && args.kind === 'require-call';
74
+ return {
75
+ path: args.path,
76
+ namespace: NAMESPACE,
77
+ };
78
+ });
141
79
 
80
+ /**
81
+ * Load callback for assets.
82
+ * @param {object} args - Arguments object.
83
+ * @returns {object} - Object with contents and loader type.
84
+ */
85
+ build.onLoad({ filter: /\.(txt|html)/ }, async (args) => {
86
+ const contents = await fs.promises.readFile(args.path, 'utf8');
142
87
  return {
143
- namespace: isCommonjs ? commonjsNamespace : namespace,
144
- path: argsPath,
88
+ contents,
89
+ loader: 'text',
145
90
  };
146
- }
147
- onResolve({ filter }, resolver);
148
- // onResolve({ filter: /.*/, namespace }, resolver)
91
+ });
92
+
93
+ /**
94
+ * Load callback for node module files.
95
+ * @param {object} args - Arguments object.
96
+ * @returns {object} - Object with loader, contents, and resolve directory.
97
+ */
98
+ build.onLoad({ filter: /.*/, namespace: NAMESPACE }, async (args) => {
99
+ const argsPath = args.path.replace(/^node:/, '');
100
+
101
+ const resolved = polyfillManager.libs.get(argsPath);
102
+ const contents = await fs.promises.readFile(resolved, 'utf8');
103
+ const resolveDir = path.dirname(resolved);
104
+
105
+ return {
106
+ loader: 'js',
107
+ contents,
108
+ resolveDir,
109
+ };
110
+ });
149
111
  },
150
112
  };
151
- }
113
+ };
152
114
 
153
- export default NodeModulesPolyfillPlugin;
154
- /* eslint-enable */
115
+ export default ESBuildNodeModulePlugin;
@@ -0,0 +1,38 @@
1
+ import * as esbuild from 'esbuild';
2
+ import ESBuildNodeModulePlugin from './index.js';
3
+
4
+ describe('Esbuild Node Plugin', () => {
5
+ it('should check the env (NODE_ENV) in define options', async () => {
6
+ const results = await esbuild.build({
7
+ write: false,
8
+ stdin: {
9
+ contents: 'console.log(process.env.NODE_ENV)',
10
+ },
11
+ plugins: [ESBuildNodeModulePlugin()],
12
+ });
13
+
14
+ expect(results.outputFiles.at(0)?.text.trim()).toContain(
15
+ 'console.log("production");',
16
+ );
17
+ });
18
+
19
+ it('should resolve global crypto polyfill', async () => {
20
+ const results = await esbuild.build({
21
+ write: false,
22
+ target: 'es2022',
23
+ format: 'esm',
24
+ platform: 'browser',
25
+ mainFields: ['browser', 'main', 'module'],
26
+ bundle: true,
27
+ minify: false,
28
+ stdin: {
29
+ contents: 'import crypto from "crypto";',
30
+ },
31
+ plugins: [ESBuildNodeModulePlugin()],
32
+ });
33
+
34
+ expect(results.outputFiles.at(0)?.text.trim()).toContain(
35
+ 'vulcan-node-modules-polyfills:crypto',
36
+ );
37
+ });
38
+ });
File without changes