Skip to content

Commit 4b6cc2d

Browse files
committed
fix: removing the requirement to useNodePolyfill when api external azion
1 parent b01405b commit 4b6cc2d

File tree

8 files changed

+339
-18
lines changed

8 files changed

+339
-18
lines changed

lib/build/bundlers/esbuild/index.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { debug } from '#utils';
77
import BundlerBase from '../bundler-base.js';
88
import AzionEsbuildConfig from './esbuild.config.js';
99
import ESBuildNodeModulePlugin from './plugins/node-polyfills/index.js';
10+
import ESBuildAzionModulePlugin from './plugins/azion-polyfills/index.js';
1011

1112
/**
1213
* Class representing an ESBuild bundler, extending BundlerBase.
@@ -47,11 +48,14 @@ class Esbuild extends BundlerBase {
4748
this.customConfigLocal?.useNodePolyfills) &&
4849
this.presetMode === 'compute';
4950

51+
if (!updatedConfig.plugins) updatedConfig.plugins = [];
5052
if (useNodePolyfills) {
51-
if (!updatedConfig.plugins) updatedConfig.plugins = [];
5253
updatedConfig.plugins.push(ESBuildNodeModulePlugin(globalThis.buildProd));
5354
}
5455

56+
// plugin resolve azion:
57+
updatedConfig.plugins.push(ESBuildAzionModulePlugin(globalThis.buildProd));
58+
5559
// inject content in worker initial code.
5660
if (this.builderConfig.contentToInject) {
5761
const workerInitContent = this.builderConfig.contentToInject;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/* eslint-disable consistent-return */
2+
import fs from 'fs';
3+
import path from 'path';
4+
import PolyfillsManager from '../../../polyfills/polyfills-manager.js';
5+
6+
/**
7+
* ESBuild Azion Module Plugin for polyfilling node modules.
8+
* @param {boolean} buildProd Parameter to identify whether the build is dev or prod
9+
* @returns {object} - ESBuild plugin object.
10+
*/
11+
const ESBuildAzionModulePlugin = (buildProd) => {
12+
const NAME = 'vulcan-azion-modules-polyfills';
13+
const NAMESPACE = NAME;
14+
const filter = /^azion:/;
15+
16+
return {
17+
/**
18+
* Name and setup of the ESBuild plugin.
19+
* @param {object} build - ESBuild build object.
20+
*/
21+
name: NAME,
22+
setup: (build) => {
23+
const polyfillManager = PolyfillsManager.buildPolyfills();
24+
25+
const options = build.initialOptions;
26+
27+
// filter external no prefix
28+
const filteredExternal = new Map(
29+
[...polyfillManager.external].filter(([key]) => {
30+
const hasPrefix = /^[^:]+:/.test(key);
31+
return hasPrefix;
32+
}),
33+
);
34+
35+
// external
36+
if (buildProd) {
37+
options.external = options.external || [];
38+
[...filteredExternal].forEach(([key]) => {
39+
if (!options.external.includes(key)) {
40+
options.external.push(key);
41+
}
42+
});
43+
}
44+
45+
/**
46+
* Resolve callback for ESBuild.
47+
* @param {object} args - Arguments object.
48+
* @returns {object|undefined} - Object with path and namespace or undefined.
49+
*/
50+
build.onResolve({ filter }, async (args) => {
51+
if (!buildProd && polyfillManager.external.has(args.path)) {
52+
return {
53+
path: args.path,
54+
namespace: NAMESPACE,
55+
};
56+
}
57+
if (!polyfillManager.external.has(args.path)) {
58+
return;
59+
}
60+
61+
// external bypass
62+
if (
63+
options?.external?.length > 0 &&
64+
options?.external?.includes(args.path)
65+
) {
66+
return;
67+
}
68+
69+
return {
70+
path: args.path,
71+
namespace: NAMESPACE,
72+
};
73+
});
74+
75+
/**
76+
* Load callback for node module files.
77+
* @param {object} args - Arguments object.
78+
* @returns {object} - Object with loader, contents, and resolve directory.
79+
*/
80+
build.onLoad({ filter, namespace: NAMESPACE }, async (args) => {
81+
if (!polyfillManager.external.has(args.path)) {
82+
return;
83+
}
84+
const resolved = polyfillManager.external.get(args.path);
85+
const contents = await fs.promises.readFile(resolved, 'utf8');
86+
const resolveDir = path.dirname(resolved);
87+
88+
return {
89+
loader: 'js',
90+
contents,
91+
resolveDir,
92+
};
93+
});
94+
},
95+
};
96+
};
97+
98+
export default ESBuildAzionModulePlugin;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as esbuild from 'esbuild';
2+
import ESBuildAzionModulePlugin from './index.js';
3+
4+
describe('Esbuild Azion Plugin', () => {
5+
it('should resolve the azion module: as external', async () => {
6+
const results = await esbuild.build({
7+
write: false,
8+
target: 'es2022',
9+
format: 'esm',
10+
platform: 'browser',
11+
mainFields: ['browser', 'main', 'module'],
12+
bundle: true,
13+
minify: false,
14+
stdin: {
15+
contents: 'import storage from "azion:storage";',
16+
},
17+
plugins: [ESBuildAzionModulePlugin(true)],
18+
});
19+
20+
expect(results.outputFiles.at(0)?.text.trim()).toContain(
21+
'import storage from "azion:storage";',
22+
);
23+
});
24+
25+
it('should resolve the azion module: for local execution', async () => {
26+
const results = await esbuild.build({
27+
write: false,
28+
target: 'es2022',
29+
format: 'esm',
30+
platform: 'browser',
31+
mainFields: ['browser', 'main', 'module'],
32+
bundle: true,
33+
minify: false,
34+
stdin: {
35+
contents: 'import storage from "azion:storage";',
36+
},
37+
plugins: [ESBuildAzionModulePlugin(false)],
38+
});
39+
40+
expect(results.outputFiles.at(0)?.text.trim()).toContain(
41+
'vulcan-azion-modules-polyfills',
42+
);
43+
});
44+
});

lib/build/bundlers/esbuild/plugins/node-polyfills/index.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,20 @@ const ESBuildNodeModulePlugin = (buildProd) => {
4444
});
4545
}
4646

47+
// filter external no prefix
48+
const filteredExternal = new Map(
49+
[...polyfillManager.external].filter(([key]) => {
50+
const hasPrefix = /^[^:]+:/.test(key);
51+
return !hasPrefix;
52+
}),
53+
);
54+
4755
// external
4856
if (buildProd) {
4957
options.external = options.external || [];
50-
[...polyfillManager.external].forEach(([key]) => {
58+
[...filteredExternal].forEach(([key]) => {
5159
options.external.push(key);
52-
if (!key.includes('azion:')) {
53-
options.external.push(`node:${key}`);
54-
}
60+
options.external.push(`node:${key}`);
5561
});
5662
}
5763

lib/build/bundlers/webpack/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Messages } from '#constants';
99
import AzionWebpackConfig from './webpack.config.js';
1010
import NodePolyfillPlugin from './plugins/node-polyfills/index.js';
1111
import BundlerBase from '../bundler-base.js';
12+
import AzionPolyfillPlugin from './plugins/azion-polyfills/index.js';
1213

1314
class Webpack extends BundlerBase {
1415
// eslint-disable-next-line no-useless-constructor
@@ -84,6 +85,10 @@ class Webpack extends BundlerBase {
8485
if (useNodePolyfills) {
8586
updatedConfig.plugins.push(new NodePolyfillPlugin(globalThis.buildProd));
8687
}
88+
89+
// plugin resolve azion:
90+
updatedConfig.plugins.push(new AzionPolyfillPlugin(globalThis.buildProd));
91+
8792
return updatedConfig;
8893
}
8994
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* eslint-disable no-param-reassign,class-methods-use-this */
2+
import PolyfillsManager from '../../../polyfills/polyfills-manager.js';
3+
4+
class AzionPolyfillPlugin {
5+
constructor(buildProd) {
6+
this.buildProd = buildProd;
7+
this.prefix = 'azion:';
8+
}
9+
10+
apply(compiler) {
11+
const polyfillsManager = PolyfillsManager.buildPolyfills();
12+
13+
if (!compiler.options.plugins?.length) {
14+
compiler.options.plugins = [];
15+
}
16+
17+
const filteredExternal = new Map(
18+
[...polyfillsManager.external].filter(([key]) => {
19+
const hasPrefix = new RegExp(`^${this.prefix}`).test(key);
20+
return hasPrefix;
21+
}),
22+
);
23+
24+
if (this.buildProd) {
25+
compiler.options.externals = compiler.options.externals || [];
26+
compiler.options.externals.push(
27+
// eslint-disable-next-line
28+
({ request }, callback) => {
29+
const externalsToCheck = [...filteredExternal.keys()];
30+
31+
if (
32+
externalsToCheck.some((key) => new RegExp(`${key}$`).test(request))
33+
) {
34+
return callback(null, `module ${request}`);
35+
}
36+
37+
return callback();
38+
},
39+
);
40+
} else {
41+
compiler.options.plugins.push(
42+
new compiler.webpack.NormalModuleReplacementPlugin(
43+
new RegExp(`^${this.prefix}`),
44+
(resource) => {
45+
const mod = resource.request.replace(
46+
new RegExp(`^${this.prefix}`),
47+
'',
48+
);
49+
resource.request = mod;
50+
},
51+
),
52+
);
53+
54+
compiler.options.resolve.fallback = {
55+
...Object.fromEntries(
56+
[...filteredExternal].map(([key, value]) => [
57+
key.replace(new RegExp(`^${this.prefix}`), ''),
58+
value,
59+
]),
60+
),
61+
...compiler.options.resolve.fallback,
62+
};
63+
}
64+
}
65+
}
66+
67+
export default AzionPolyfillPlugin;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { promisify } from 'util';
2+
import webpack from 'webpack';
3+
import fs from 'fs';
4+
import tmp from 'tmp';
5+
import AzionPolyfillPlugin from './index.js';
6+
7+
describe('Webpack Azion Plugin', () => {
8+
let tmpDir;
9+
let tmpEntry;
10+
let tmpOutput;
11+
12+
beforeAll(async () => {
13+
tmpDir = tmp.dirSync();
14+
tmpEntry = tmp.fileSync({
15+
postfix: '.js',
16+
dir: tmpDir.name,
17+
name: 'entry.js',
18+
});
19+
tmpOutput = tmp.fileSync({
20+
postfix: '.js',
21+
dir: tmpDir.name,
22+
name: 'output.js',
23+
});
24+
});
25+
26+
afterAll(async () => {
27+
tmpEntry.removeCallback();
28+
tmpOutput.removeCallback();
29+
tmpDir.removeCallback();
30+
});
31+
32+
it('should resolve the azion module: for local execution', async () => {
33+
const code = `import storage from 'azion:storage';`;
34+
await fs.promises.writeFile(tmpEntry.name, code);
35+
36+
const runWebpack = promisify(webpack);
37+
38+
await runWebpack({
39+
entry: tmpEntry.name,
40+
mode: 'production',
41+
optimization: {
42+
minimize: false,
43+
},
44+
output: {
45+
path: tmpDir.name,
46+
filename: 'output.js',
47+
},
48+
target: ['webworker', 'es2022'],
49+
plugins: [new AzionPolyfillPlugin(false)],
50+
});
51+
const bundle = fs.readFileSync(tmpOutput.name, 'utf-8');
52+
expect(bundle).toContain(
53+
'./lib/env/polyfills/azion/storage/storage.polyfills.js',
54+
);
55+
}, 20000);
56+
57+
it('should resolve the azion module: as external', async () => {
58+
const code = `import storage from 'azion:storage';`;
59+
await fs.promises.writeFile(tmpEntry.name, code);
60+
61+
const runWebpack = promisify(webpack);
62+
63+
await runWebpack({
64+
entry: tmpEntry.name,
65+
mode: 'production',
66+
optimization: {
67+
minimize: false,
68+
},
69+
output: {
70+
path: tmpDir.name,
71+
filename: 'output.js',
72+
},
73+
target: ['webworker', 'es2022'],
74+
plugins: [new AzionPolyfillPlugin(true)],
75+
});
76+
const bundle = fs.readFileSync(tmpOutput.name, 'utf-8');
77+
expect(bundle).toContain('import("azion:storage")');
78+
}, 20000);
79+
});

0 commit comments

Comments
 (0)