Skip to content

Commit 995fa5a

Browse files
authored
Tweaks to build configuration for webR node ESM module (#583)
* Tweaks to build configuration for node esm module Also adds testing for importing cjs and esm modules into various node project types, with and without bundling. * Launch node via npx * Don't eslint module test files
1 parent 4c7b964 commit 995fa5a

40 files changed

+4452
-34
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ check: ## Run unit tests and calculate coverage
4040

4141
.PHONY: check-pr
4242
check-pr: ## Run additional pull request tests, linter, and calculate coverage
43-
cd src && $(MAKE) lint && $(MAKE) check && $(MAKE) check-packages
43+
cd src && $(MAKE) lint && $(MAKE) check && $(MAKE) check-packages && $(MAKE) check-module
4444

4545
.PHONY: clean
4646
clean: ## Remove Wasm R build

src/.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ module.exports = {
2525
"jest.config.js",
2626
"tests/webr.config.js",
2727
"tests/scripts/proxy-worker.worker.js",
28-
"tests/packages.config.js"
28+
"tests/packages.config.js",
29+
"tests/module"
2930
],
3031
plugins: ["@typescript-eslint", "jest", "jsdoc", "react"],
3132
rules: {

src/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ HTML_TEMPLATES = repl.html
88
HTML_INDEX = repl.html
99

1010
TS_SOURCES = $(shell find $(ROOT) \
11-
-not \( -path '$(ROOT)/node_modules' -prune \) \
11+
-not \( -path '*/node_modules' -prune \) \
12+
-not \( -path '*/tests/module' -prune \) \
1213
-not \( -path '$(PKG_DIST)' -prune \) \
1314
-name '*.ts' -o -name '*.tsx')
1415

@@ -53,6 +54,10 @@ check: $(DIST)
5354
check-packages: $(DIST)
5455
npx node ./node_modules/jest/bin/jest.js --config tests/packages.config.js
5556

57+
.PHONY: check-module
58+
check-module: $(DIST)
59+
npx node tests/module/test.js
60+
5661
node_modules: package.json
5762
npm ci
5863
touch $@

src/esbuild.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,64 @@ import http from 'http';
44

55
let serve = false;
66
let prod = false;
7-
let pkg = true;
87

98
if (process.argv.some((x) => x === '--serve')) {
109
serve = true;
11-
pkg = false;
1210
}
1311

1412
if (process.argv.some((x) => x === '--prod')) {
1513
prod = true;
1614
}
1715

18-
function build(input: string, output: string, platform: esbuild.Platform, minify: boolean) {
16+
function build(input: string, options: any) {
1917
return esbuild.context({
2018
assetNames: 'assets/[name]-[hash]',
2119
bundle: true,
2220
entryPoints: [input],
23-
external: ['worker_threads', 'path', 'fs', 'ws'],
21+
external: ['worker_threads', 'path', 'fs', 'ws', 'url', 'child_process', 'http', 'https', 'crypto'],
2422
loader: {
2523
'.jpg': 'file',
2624
'.png': 'file',
2725
'.gif': 'file',
2826
},
2927
mainFields: ['main', 'module'],
30-
minify: minify,
31-
outfile: output,
32-
platform: platform,
3328
plugins: [
3429
cssModulesPlugin({
3530
inject: (cssContent, digest) => `console.log("${cssContent}", "${digest}")`,
3631
})
3732
],
3833
sourcemap: true,
39-
target: ['es2020', 'node12'],
4034
define: {
4135
'process.env.NODE_ENV': prod ? '"production"' : '"development"',
4236
},
37+
...options,
4338
});
4439
}
4540

46-
const outputs = {
47-
browser: [
48-
build('repl/App.tsx', '../dist/repl.mjs', 'browser', prod),
49-
build('webR/webr-worker.ts', '../dist/webr-worker.js', 'node', true),
50-
build('webR/webr-main.ts', '../dist/webr.mjs', 'neutral', prod),
51-
],
52-
npm: [
53-
build('webR/webr-worker.ts', './dist/webr-worker.js', 'node', true),
54-
build('webR/webr-main.ts', './dist/webr.cjs', 'node', prod),
55-
build('webR/webr-main.ts', './dist/webr.mjs', 'neutral', prod),
56-
]
57-
};
58-
const allOutputs = outputs.browser.concat(pkg ? outputs.npm : []);
41+
const outputs = [
42+
build('repl/App.tsx', { outfile: '../dist/repl.js', platform: 'browser', format: 'iife', target: ['es2022'], minify: prod }), // browser, script
43+
build('webR/webr-main.ts', { outfile: '../dist/webr.mjs', platform: 'neutral', format: 'esm', target: ['es2022'], minify: prod }), // browser, script, type="module"
44+
build('webR/webr-worker.ts', { outfile: '../dist/webr-worker.js', platform: 'neutral', format: 'iife', minify: prod }), // browser, worker
45+
build('webR/webr-main.ts', { outfile: './dist/webr.cjs', platform: 'node', format: 'cjs', minify: prod }), // node, cjs
46+
build('webR/webr-worker.ts', { outfile: './dist/webr-worker.js', platform: 'node', format: 'cjs', minify: prod }), // node, worker
47+
build('webR/webr-main.ts', { // node, esm
48+
outfile: './dist/webr.mjs',
49+
platform: 'node',
50+
format: 'esm',
51+
banner: {
52+
js: `import { createRequire } from 'module';
53+
import { fileURLToPath as urlESMPluginFileURLToPath } from "url";
54+
import { dirname as pathESMPluginDirname} from "path";
55+
const require = createRequire(import.meta.url);
56+
var __filename = urlESMPluginFileURLToPath(import.meta.url);
57+
var __dirname = pathESMPluginDirname(urlESMPluginFileURLToPath(import.meta.url));
58+
`
59+
},
60+
minify: prod
61+
}),
62+
];
5963

60-
allOutputs.forEach((build) => {
64+
outputs.forEach((build) => {
6165
build
6266
.then(async (context) => {
6367
await context.rebuild();
@@ -70,7 +74,7 @@ allOutputs.forEach((build) => {
7074
});
7175

7276
if (serve) {
73-
outputs.browser[0]
77+
outputs[0]
7478
.then(async (context) => {
7579
await context.serve({ servedir: '../dist', port: 8001 }).then(() => {
7680
http
@@ -101,7 +105,7 @@ if (serve) {
101105
throw new Error('A problem occurred serving webR distribution with esbuild');
102106
});
103107
} else {
104-
allOutputs.forEach(build => {
108+
outputs.forEach(build => {
105109
build
106110
.then(async (context) => {
107111
await context.dispose();

src/package.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,9 @@
4242
"exports": {
4343
".": {
4444
"types": "./dist/webR/webr-main.d.ts",
45-
"node": "./dist/webr.cjs",
45+
"require": "./dist/webr.cjs",
46+
"import": "./dist/webr.mjs",
4647
"default": "./dist/webr.mjs"
47-
},
48-
"./chan/serviceworker": {
49-
"types": "./dist/webR/chan/serviceworker.d.ts",
50-
"browser": "./dist/webr-serviceworker.js",
51-
"default": "./dist/webr-serviceworker.mjs"
5248
}
5349
},
5450
"engines": {

src/templates/repl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<noscript>
3535
You need to enable JavaScript to run this app.
3636
</noscript>
37-
<script type="module" src="repl.mjs"></script>
37+
<script src="repl.js"></script>
3838
</body>
3939

4040
</html>

src/tests/module/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/dist/
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Minimal test for importing webR using CommonJS with esbuild
2+
const { WebR } = require('webr');
3+
4+
async function main() {
5+
console.log('Attempting to import webR...');
6+
7+
if (typeof WebR === 'function') {
8+
console.log('✓ Successfully imported webR package using CommonJS with esbuild');
9+
10+
try {
11+
const webR = new WebR({ baseUrl: '../../../../dist/' });
12+
await webR.init();
13+
console.log('✓ WebR instance created successfully');
14+
const result = await webR.evalRNumber('123 + 456');
15+
console.log('✓ R evaluation result:', result); // Should log: 579
16+
webR.close();
17+
} catch (error) {
18+
console.error('WebR instantiation failed:', error.message);
19+
process.exit(1);
20+
}
21+
} else {
22+
console.error('✗ Failed to import WebR');
23+
process.exit(1);
24+
}
25+
}
26+
27+
main().catch(error => {
28+
console.error('Error:', error);
29+
process.exit(1);
30+
});

0 commit comments

Comments
 (0)