-
Notifications
You must be signed in to change notification settings - Fork 781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Demos: Add Rollup and Webpack examples #1787
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
}); |
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']); | ||
}; |
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. | ||
``` |
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')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain this condition? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cjs outputs (not to confuse with cjs inputs) are not meant for browser context, as they depend on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment? |
||
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 | ||
); | ||
} | ||
}()); |
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" | ||
mgol marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
"scripts": { | ||
"build": "node build.mjs", | ||
"test": "grunt test" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When reading a file as ESM, Rollup differentiates between a file that has no exports (i.e. app code), and a file that has exports. When reading a file as CJS, Rollup doesn't understand this, and so it insists on exporting. When using
umd
, one of the branches assigns a global to the overallexports
which requires a name. Without the build will fail.The exports is an empty object, but Rollup wants to put it somewhere, so gave it the name
window.
UNUSED
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment in code could be useful here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. Done in #1791.