const { removeRecursiveSync, buildWasmLib } = require('./esbuild.js'); const child_process = require('child_process'); const assert = require('assert'); const path = require('path'); const fs = require('fs'); const tests = { serveTest({ testDir, esbuildPathWASM }) { try { child_process.execFileSync('node', [ esbuildPathWASM, '--servedir=.', '--log-level=warning', ], { stdio: 'pipe', cwd: testDir, }); throw new Error('Expected an error to be thrown'); } catch (err) { assert.strictEqual(err.stderr + '', `✘ [ERROR] The "serve" API is not supported when using WebAssembly\n\n`) } }, basicStdinTest({ testDir, esbuildPathWASM }) { const stdout = child_process.execFileSync('node', [ esbuildPathWASM, '--format=cjs', '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export default 1+2`, }).toString(); // Check that the bundle is valid const module = { exports: {} }; new Function('module', 'exports', stdout)(module, module.exports); assert.deepStrictEqual(module.exports.default, 3); }, stdinOutfileTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'out.js') child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export default 1+2`, }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, 3); }, stdinStdoutUnicodeTest({ testDir, esbuildPathWASM }) { const stdout = child_process.execFileSync('node', [ esbuildPathWASM, '--format=cjs', '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export default ['π', '🍕']`, }).toString(); // Check that the bundle is valid const module = { exports: {} }; new Function('module', 'exports', stdout)(module, module.exports); assert.deepStrictEqual(module.exports.default, ['π', '🍕']); }, stdinOutfileUnicodeTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'out.js') child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export default ['π', '🍕']`, }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, ['π', '🍕']); }, stdoutLargeTest({ testDir, esbuildPathNative, esbuildPathWASM }) { const entryPoint = path.join(__dirname, 'js-api-tests.js'); // Build with native const stdoutNative = child_process.execFileSync(esbuildPathNative, [ entryPoint, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, }).toString(); // Build with WASM const stdoutWASM = child_process.execFileSync('node', [ esbuildPathWASM, entryPoint, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, }).toString(); // Check that the output is equal assert.deepStrictEqual(stdoutNative.length, stdoutWASM.length); assert.deepStrictEqual(stdoutNative, stdoutWASM); }, outfileLargeTest({ testDir, esbuildPathNative, esbuildPathWASM }) { const entryPoint = path.join(__dirname, 'js-api-tests.js'); // Build with native const outfileNative = path.join(testDir, 'a.js'); const stdoutNative = child_process.execFileSync(esbuildPathNative, [ entryPoint, '--outfile=' + outfileNative, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, }).toString(); const jsNative = fs.readFileSync(outfileNative, 'utf8'); // Build with WASM const outfileWASM = path.join(testDir, 'b.js'); const stdoutWASM = child_process.execFileSync('node', [ esbuildPathWASM, entryPoint, '--outfile=' + outfileWASM, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, }).toString(); const jsWASM = fs.readFileSync(outfileWASM, 'utf8'); // Check that the output is equal assert.deepStrictEqual(jsNative.length, jsWASM.length); assert.deepStrictEqual(jsNative, jsWASM); }, outfileNestedTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'a', 'b', 'c', 'd', 'out.js'); child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export default 123`, }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, 123); }, metafileNestedTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'out.js'); const metafile = path.join(testDir, 'a', 'b', 'c', 'd', 'meta.json'); const cwd = path.join(testDir, 'a', 'b') fs.mkdirSync(cwd, { recursive: true }) child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--metafile=' + metafile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd, input: `export default 123`, }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, 123); const json = JSON.parse(fs.readFileSync(metafile, 'utf8')); assert.deepStrictEqual(json.outputs['../../out.js'].entryPoint, ''); }, importRelativeFileTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'out.js') const packageJSON = path.join(__dirname, '..', 'npm', 'esbuild-wasm', 'package.json'); child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export {default} from ` + JSON.stringify('./' + path.relative(testDir, packageJSON)), }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, require(packageJSON)); }, importAbsoluteFileTest({ testDir, esbuildPathWASM }) { const outfile = path.join(testDir, 'out.js') const packageJSON = path.join(__dirname, '..', 'npm', 'esbuild-wasm', 'package.json'); child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=cjs', '--outfile=' + outfile, '--log-level=warning', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `export {default} from ` + JSON.stringify(packageJSON), }).toString(); // Check that the bundle is valid const exports = require(outfile); assert.deepStrictEqual(exports.default, require(packageJSON)); }, zipFile({ testDir, esbuildPathWASM }) { const entry = path.join(testDir, 'entry.js') fs.writeFileSync(entry, ` import foo from './test.zip/foo.js' import bar from './test.zip/bar/bar.js' import __virtual__1 from './test.zip/__virtual__/ignored/0/foo.js' import __virtual__2 from './test.zip/ignored/__virtual__/ignored/1/foo.js' import __virtual__3 from './test.zip/__virtual__/ignored/1/test.zip/foo.js' import $$virtual1 from './test.zip/$$virtual/ignored/0/foo.js' import $$virtual2 from './test.zip/ignored/$$virtual/ignored/1/foo.js' import $$virtual3 from './test.zip/$$virtual/ignored/1/test.zip/foo.js' console.log({ foo, bar, __virtual__1, __virtual__2, __virtual__3, $$virtual1, $$virtual2, $$virtual3, }) `) // This uses the real file system instead of the mock file system so that // we can check that everything works as expected on Windows, which is not // a POSIX environment. fs.writeFileSync(path.join(testDir, 'test.zip'), Buffer.from( `UEsDBAoAAgAAAG1qCFUSAXosFQAAABUAAAAGABwAZm9vLmpzVVQJAAOeRfFioEXxYnV4C` + `wABBPUBAAAEFAAAAGV4cG9ydCBkZWZhdWx0ICdmb28nClBLAwQKAAIAAABzaghVwuDbLR` + `UAAAAVAAAACgAcAGJhci9iYXIuanNVVAkAA6lF8WKrRfFidXgLAAEE9QEAAAQUAAAAZXh` + `wb3J0IGRlZmF1bHQgJ2JhcicKUEsBAh4DCgACAAAAbWoIVRIBeiwVAAAAFQAAAAYAGAAA` + `AAAAAQAAAKSBAAAAAGZvby5qc1VUBQADnkXxYnV4CwABBPUBAAAEFAAAAFBLAQIeAwoAA` + `gAAAHNqCFXC4NstFQAAABUAAAAKABgAAAAAAAEAAACkgVUAAABiYXIvYmFyLmpzVVQFAA` + `OpRfFidXgLAAEE9QEAAAQUAAAAUEsFBgAAAAACAAIAnAAAAK4AAAAAAA==`, 'base64')) const stdout = child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', entry, ], { stdio: 'pipe', cwd: testDir, }).toString(); assert.strictEqual(stdout, `(() => { // test.zip/foo.js var foo_default = "foo"; // test.zip/bar/bar.js var bar_default = "bar"; // test.zip/__virtual__/ignored/0/foo.js var foo_default2 = "foo"; // test.zip/ignored/__virtual__/ignored/1/foo.js var foo_default3 = "foo"; // test.zip/__virtual__/ignored/1/test.zip/foo.js var foo_default4 = "foo"; // test.zip/$$virtual/ignored/0/foo.js var foo_default5 = "foo"; // test.zip/ignored/$$virtual/ignored/1/foo.js var foo_default6 = "foo"; // test.zip/$$virtual/ignored/1/test.zip/foo.js var foo_default7 = "foo"; // entry.js console.log({ foo: foo_default, bar: bar_default, __virtual__1: foo_default2, __virtual__2: foo_default3, __virtual__3: foo_default4, $$virtual1: foo_default5, $$virtual2: foo_default6, $$virtual3: foo_default7 }); })(); `) }, // https://github.com/evanw/esbuild/issues/3001 nodePathsReaddirEINVAL({ testDir, esbuildPathWASM }) { const libDir = path.join(testDir, 'lib'); const libFile = path.join(libDir, 'file.js'); fs.mkdirSync(libDir, { recursive: true }); fs.writeFileSync(libFile, 'foo()'); const stdout = child_process.execFileSync('node', [ esbuildPathWASM, '--bundle', '--format=esm', ], { stdio: ['pipe', 'pipe', 'inherit'], cwd: testDir, input: `import "file.js"`, env: { ...process.env, NODE_PATH: libDir }, }).toString(); assert.deepStrictEqual(stdout, '// lib/file.js\nfoo();\n'); }, }; function runTest({ testDir, esbuildPathNative, esbuildPathWASM, test }) { try { fs.mkdirSync(testDir, { recursive: true }) test({ testDir, esbuildPathNative, esbuildPathWASM }) return true } catch (e) { console.error(`❌ ${test.name} failed: ${e && e.message || e}`) return false } } async function main() { // Generate the WebAssembly module const esbuildPathNative = path.join(__dirname, '..', process.platform === 'win32' ? 'esbuild.exe' : 'esbuild'); await buildWasmLib(esbuildPathNative); const esbuildPathWASM = path.join(__dirname, '..', 'npm', 'esbuild-wasm', 'bin', 'esbuild'); const testDir = path.join(__dirname, '.wasm-tests') // Run all tests in serial because WebAssembly compilation is a CPU hog let allTestsPassed = true; for (const test in tests) { if (!runTest({ testDir: path.join(testDir, test), test: tests[test], esbuildPathNative, esbuildPathWASM, })) { allTestsPassed = false; } } if (!allTestsPassed) { console.error(`❌ wasm-tests failed`) process.exit(1) } else { console.log(`✅ wasm-tests passed`) removeRecursiveSync(testDir) } } main().catch(e => setTimeout(() => { throw e }))