Skip to content

Commit

Permalink
Demos: Add Rollup and Webpack example mixing ESM import and CJS requi…
Browse files Browse the repository at this point in the history
…re()

Co-authored-by: Michał Gołębiowski-Owczarek <[email protected]>

Closes #1787.
  • Loading branch information
Krinkle authored Jul 30, 2024
1 parent 5bff13a commit a3327c3
Show file tree
Hide file tree
Showing 20 changed files with 483 additions and 22 deletions.
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"coverage/**",
"demos/*/qunit/**",
"demos/*/coverage/**",
"demos/*/tmp/**",
"demos/*/package-lock.json",
"docs/.jekyll-cache/**",
"docs/_site/**",
Expand Down Expand Up @@ -191,14 +192,14 @@
}
},
{
"files": ["demos/**/*.js"],
"files": ["demos/**/*.js", "demos/**/*.mjs"],
"env": {
"browser": true,
"es2017": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2018
"ecmaVersion": 2022
},
"rules": {
"compat/compat": "off"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/coverage/
/demos/*/package-lock.json
/demos/*/node_modules/
/demos/*/tmp/
/docs/.jekyll-cache/
/docs/_site/
/docs/Gemfile.lock
Expand Down
145 changes: 145 additions & 0 deletions demos/bundlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
const cp = require('child_process');
const path = require('path');
const DIR = path.join(__dirname, 'bundlers');

function normalize (str) {
return str
.replace(/^localhost:\d+/g, 'localhost:8000')
.replace(/\b\d+ms\b/g, '42ms');
}

QUnit.module('bundlers', {
before: async (assert) => {
assert.timeout(60_000);

cp.execSync('npm install --no-audit --update-notifier=false', { cwd: DIR, encoding: 'utf8' });

await import('./bundlers/build.mjs');
}
});

QUnit.test.each('test in Node.js [direct]', [
'./tmp/import-default.cjs.js',
'./tmp/import-named.cjs.js',
'./tmp/require-default.cjs.js'
], function (assert, fileName) {
const actual = cp.execFileSync(process.execPath,
[
'--input-type=module',
'-e',
`import ${JSON.stringify(fileName)}; QUnit.start();`
],
{ cwd: DIR, env: { qunit_config_reporters_tap: 'true' }, encoding: 'utf8' }
);
const expected = `
1..1
# pass 1
# skip 0
# todo 0
# fail 0`.trim();

assert.pushResult({ result: actual.includes(expected), actual, expected }, 'stdout');
});

QUnit.test.each('test in Node.js [indirect]', [
'./tmp/import-indirect.cjs.js',
'./tmp/require-indirect.cjs.js'
], function (assert, fileName) {
const actual = cp.execFileSync(process.execPath,
[
'--input-type=module',
'-e',
`import ${JSON.stringify(fileName)}; QUnit.start();`
],
{ cwd: DIR, env: { qunit_config_reporters_tap: 'true' }, encoding: 'utf8' }
);
const expected = `
1..4
# pass 4
# skip 0
# todo 0
# fail 0`.trim();

assert.pushResult({ result: actual.includes(expected), actual, expected }, 'stdout');
});

QUnit.test('test in browser', function (assert) {
const expected = `Running "connect:all" (connect) task
Started connect web server on http://localhost:8000
Running "qunit:all" (qunit) task
Testing http://localhost:8000/tmp/test-import-default.es.html .OK
>> passed test "import-default"
Testing http://localhost:8000/tmp/test-import-default.iife.html .OK
>> passed test "import-default"
Testing http://localhost:8000/tmp/test-import-default.umd.html .OK
>> passed test "import-default"
Testing http://localhost:8000/tmp/test-import-default.webpack.html .OK
>> passed test "import-default"
Testing http://localhost:8000/tmp/test-import-indirect.es.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "import-indirect"
Testing http://localhost:8000/tmp/test-import-indirect.iife.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "import-indirect"
Testing http://localhost:8000/tmp/test-import-indirect.umd.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "import-indirect"
Testing http://localhost:8000/tmp/test-import-indirect.webpack.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "import-indirect"
Testing http://localhost:8000/tmp/test-import-named.es.html .OK
>> passed test "import-named"
Testing http://localhost:8000/tmp/test-import-named.iife.html .OK
>> passed test "import-named"
Testing http://localhost:8000/tmp/test-import-named.umd.html .OK
>> passed test "import-named"
Testing http://localhost:8000/tmp/test-import-named.webpack.html .OK
>> passed test "import-named"
Testing http://localhost:8000/tmp/test-require-default.es.html .OK
>> passed test "require-default"
Testing http://localhost:8000/tmp/test-require-default.iife.html .OK
>> passed test "require-default"
Testing http://localhost:8000/tmp/test-require-default.umd.html .OK
>> passed test "require-default"
Testing http://localhost:8000/tmp/test-require-default.webpack.html .OK
>> passed test "require-default"
Testing http://localhost:8000/tmp/test-require-indirect.es.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "require-indirect"
Testing http://localhost:8000/tmp/test-require-indirect.iife.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "require-indirect"
Testing http://localhost:8000/tmp/test-require-indirect.umd.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "require-indirect"
Testing http://localhost:8000/tmp/test-require-indirect.webpack.html ....OK
>> passed test "import-default"
>> passed test "import-named"
>> passed test "require-default"
>> passed test "require-indirect"
>> 44 tests completed in 42ms, with 0 failed, 0 skipped, and 0 todo.
Done.`;

const actual = cp.execSync('node_modules/.bin/grunt test', {
cwd: DIR,
env: { PATH: process.env.PATH },
encoding: 'utf8'
});
assert.equal(normalize(actual).trim(), expected);
});
39 changes: 39 additions & 0 deletions demos/bundlers/Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-env node */
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-qunit');

grunt.initConfig({
connect: {
all: {
options: {
useAvailablePort: true,
base: '.'
}
}
},
qunit: {
options: {
},
all: ['tmp/test-*.html']
}
});

grunt.event.once('connect.all.listening', function (_host, port) {
grunt.config('qunit.options.httpBase', `http://localhost:${port}`);
// console.log(grunt.config()); // DEBUG
});

let results = [];
grunt.event.on('qunit.on.testEnd', function (test) {
results.push(
`>> ${test.status} test "${test.fullName.join(' > ')}"`
);
});
grunt.event.on('qunit.on.runEnd', function () {
grunt.log.writeln(results.join('\n'));
results = [];
});

grunt.registerTask('test', ['connect', 'qunit']);
};
17 changes: 17 additions & 0 deletions demos/bundlers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# QUnit ♥️ Rollup & Webpack

See also <https://rollupjs.org/> and <https://webpack.js.org/>.

```bash
npm run build
npm test
```

```
Running "qunit:all" (qunit) task
Testing http://localhost:8000/test.html .OK
>> passed test "example"
>> 1 test completed in 0ms, with 0 failed, 0 skipped, and 0 todo.
Done.
```
139 changes: 139 additions & 0 deletions demos/bundlers/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import fs from 'node:fs';
import path from 'node:path';
import url from 'node:url';

import { rollup } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import webpack from 'webpack';

const dirname = path.dirname(url.fileURLToPath(import.meta.url));
const tmpDir = path.join(dirname, 'tmp');

const inputs = [
`${dirname}/test/import-default.js`,
`${dirname}/test/import-named.js`,
`${dirname}/test/import-indirect.js`,
`${dirname}/test/require-default.cjs`,
`${dirname}/test/require-indirect.cjs`
];

const htmlTemplate = `<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<link rel="stylesheet" href="../node_modules/qunit/qunit/qunit.css">
{{scriptTag}}
</head>
<body>
<div id="qunit"></div>
</body>
</html>
`;

// Rollup configuration
const rollupOutputs = [
{
dir: tmpDir,
entryFileNames: '[name].[format].js',
format: 'es'
},
{
dir: tmpDir,
entryFileNames: '[name].[format].js',
format: 'cjs'
},
{
dir: tmpDir,
entryFileNames: '[name].[format].js',
format: 'iife'
},
{
dir: tmpDir,
entryFileNames: '[name].[format].js',
format: 'umd',
name: 'UNUSED'
}
];
async function * buildRollup () {
const plugins = [commonjs(), resolve()];

for (const input of inputs) {
const bundle = await rollup({
input,
plugins,
// Ignore "output.name" warning for require-default.iife.js
onwarn: () => {}
});

for (const outputOptions of rollupOutputs) {
const { output } = await bundle.write(outputOptions);
const fileName = output[0].fileName;
yield fileName;
}
}
}

// https://webpack.js.org/api/node/#webpack
async function * buildWebpack () {
for (const input of inputs) {
const config = {
entry: input,
output: {
filename: path.basename(input).replace(/\.(cjs|js)$/, '.webpack.js'),
path: tmpDir
}
};
await new Promise((resolve, reject) => {
webpack(config, (err, stats) => {
if (err || stats.hasErrors()) {
reject(err);
return;
}
resolve();
});
});
yield config.output.filename;
}
}

await (async function main () {
// Clean up
fs.rmSync(tmpDir, { force: true, recursive: true });

const gRollup = buildRollup();
const gWebpack = buildWebpack();

for await (const fileName of gRollup) {
console.log('... built ' + fileName);

if (!fileName.endsWith('.cjs.js')) {
const html = htmlTemplate
.replace('{{title}}', fileName)
.replace('{{scriptTag}}', (
fileName.endsWith('.es.js')
? `<script src="./${fileName}" type="module"></script>`
: `<script src="./${fileName}"></script>`
));

fs.writeFileSync(
`${tmpDir}/test-${fileName.replace('.js', '')}.html`,
html
);
}
}
for await (const fileName of gWebpack) {
console.log('... built ' + fileName);

const html = htmlTemplate
.replace('{{title}}', fileName)
.replace('{{scriptTag}}', (
`<script src="./${fileName}"></script>`
));

fs.writeFileSync(
`${tmpDir}/test-${fileName.replace('.js', '')}.html`,
html
);
}
}());
Empty file added demos/bundlers/favicon.ico
Empty file.
17 changes: 17 additions & 0 deletions demos/bundlers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"private": true,
"devDependencies": {
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"grunt": "1.6.1",
"grunt-contrib-connect": "^5.0.0",
"grunt-contrib-qunit": "10.1.1",
"qunit": "file:../..",
"rollup": "^4.18.0",
"webpack": "^5.92.0"
},
"scripts": {
"build": "node build.mjs",
"test": "grunt test"
}
}
Loading

0 comments on commit a3327c3

Please sign in to comment.