mirror of
https://github.com/zhigang1992/esbuild.git
synced 2026-01-12 22:46:54 +08:00
321 lines
9.4 KiB
JavaScript
321 lines
9.4 KiB
JavaScript
const puppeteer = require('puppeteer')
|
|
const http = require('http')
|
|
const path = require('path')
|
|
const url = require('url')
|
|
const fs = require('fs')
|
|
|
|
const js = fs.readFileSync(path.join(__dirname, '..', '..', 'npm', 'esbuild-wasm', 'lib', 'browser.js'))
|
|
const jsMin = fs.readFileSync(path.join(__dirname, '..', '..', 'npm', 'esbuild-wasm', 'lib', 'browser.min.js'))
|
|
const esm = fs.readFileSync(path.join(__dirname, '..', '..', 'npm', 'esbuild-wasm', 'esm', 'browser.js'))
|
|
const esmMin = fs.readFileSync(path.join(__dirname, '..', '..', 'npm', 'esbuild-wasm', 'esm', 'browser.min.js'))
|
|
const wasm = fs.readFileSync(path.join(__dirname, '..', '..', 'npm', 'esbuild-wasm', 'esbuild.wasm'))
|
|
|
|
// This is converted to a string and run inside the browser
|
|
async function runAllTests({ esbuild }) {
|
|
function setupForProblemCSS(prefix) {
|
|
// https://github.com/tailwindlabs/tailwindcss/issues/2889
|
|
const original = `
|
|
/* Variant 1 */
|
|
.${prefix}-v1 { --a: ; --b: ; max-width: var(--a) var(--b); }
|
|
.${prefix}-a { --a: 1px; }
|
|
.${prefix}-b { --b: 2px; }
|
|
|
|
/* Variant 2 */
|
|
.${prefix}-v2 { max-width: var(--a, ) var(--b, ); }
|
|
.${prefix}-a { --a: 1px; }
|
|
.${prefix}-b { --b: 2px; }
|
|
`
|
|
const style = document.createElement('style')
|
|
const test1a = document.createElement('div')
|
|
const test1b = document.createElement('div')
|
|
const test2a = document.createElement('div')
|
|
const test2b = document.createElement('div')
|
|
document.head.appendChild(style)
|
|
document.body.appendChild(test1a)
|
|
document.body.appendChild(test1b)
|
|
document.body.appendChild(test2a)
|
|
document.body.appendChild(test2b)
|
|
test1a.className = `${prefix}-v1 ${prefix}-a`
|
|
test1b.className = `${prefix}-v1 ${prefix}-b`
|
|
test2a.className = `${prefix}-v2 ${prefix}-a`
|
|
test2b.className = `${prefix}-v2 ${prefix}-b`
|
|
return [original, css => {
|
|
style.textContent = css
|
|
assertStrictEqual(getComputedStyle(test1a).maxWidth, `1px`)
|
|
assertStrictEqual(getComputedStyle(test1b).maxWidth, `2px`)
|
|
assertStrictEqual(getComputedStyle(test2a).maxWidth, `1px`)
|
|
assertStrictEqual(getComputedStyle(test2b).maxWidth, `2px`)
|
|
}]
|
|
}
|
|
|
|
const tests = {
|
|
async transformJS() {
|
|
const { code } = await esbuild.transform('1+2')
|
|
assertStrictEqual(code, '1 + 2;\n')
|
|
},
|
|
|
|
async transformTS() {
|
|
const { code } = await esbuild.transform('1 as any + <any>2', { loader: 'ts' })
|
|
assertStrictEqual(code, '1 + 2;\n')
|
|
},
|
|
|
|
async transformCSS() {
|
|
const { code } = await esbuild.transform('div { color: red }', { loader: 'css' })
|
|
assertStrictEqual(code, 'div {\n color: red;\n}\n')
|
|
},
|
|
|
|
async problemCSSOriginal() {
|
|
const [original, runAsserts] = setupForProblemCSS('original')
|
|
runAsserts(original)
|
|
},
|
|
|
|
async problemCSSPrettyPrinted() {
|
|
const [original, runAsserts] = setupForProblemCSS('pretty-print')
|
|
const { code: prettyPrinted } = await esbuild.transform(original, { loader: 'css' })
|
|
runAsserts(prettyPrinted)
|
|
},
|
|
|
|
async problemCSSMinified() {
|
|
const [original, runAsserts] = setupForProblemCSS('pretty-print')
|
|
const { code: minified } = await esbuild.transform(original, { loader: 'css', minify: true })
|
|
runAsserts(minified)
|
|
},
|
|
|
|
async buildFib() {
|
|
const fibonacciPlugin = {
|
|
name: 'fib',
|
|
setup(build) {
|
|
build.onResolve({ filter: /^fib\((\d+)\)/ }, args => {
|
|
return { path: args.path, namespace: 'fib' }
|
|
})
|
|
build.onLoad({ filter: /^fib\((\d+)\)/, namespace: 'fib' }, args => {
|
|
let match = /^fib\((\d+)\)/.exec(args.path), n = +match[1]
|
|
let contents = n < 2 ? `export default ${n}` : `
|
|
import n1 from 'fib(${n - 1}) ${args.path}'
|
|
import n2 from 'fib(${n - 2}) ${args.path}'
|
|
export default n1 + n2`
|
|
return { contents }
|
|
})
|
|
},
|
|
}
|
|
const result = await esbuild.build({
|
|
stdin: {
|
|
contents: `
|
|
import x from 'fib(10)'
|
|
module.exports = x
|
|
`,
|
|
},
|
|
format: 'cjs',
|
|
bundle: true,
|
|
plugins: [fibonacciPlugin],
|
|
})
|
|
assertStrictEqual(result.outputFiles.length, 1)
|
|
assertStrictEqual(result.outputFiles[0].path, '<stdout>')
|
|
const code = result.outputFiles[0].text
|
|
const answer = {}
|
|
new Function('module', code)(answer)
|
|
assertStrictEqual(answer.exports, 55)
|
|
},
|
|
|
|
async buildRelativeIssue693() {
|
|
const result = await esbuild.build({
|
|
stdin: {
|
|
contents: `const x=1`,
|
|
},
|
|
write: false,
|
|
outfile: 'esbuild.js',
|
|
});
|
|
assertStrictEqual(result.outputFiles.length, 1)
|
|
assertStrictEqual(result.outputFiles[0].path, '/esbuild.js')
|
|
assertStrictEqual(result.outputFiles[0].text, 'const x = 1;\n')
|
|
},
|
|
|
|
async serve() {
|
|
expectThrownError(esbuild.serve, 'The "serve" API only works in node')
|
|
},
|
|
|
|
async esbuildBuildSync() {
|
|
expectThrownError(esbuild.buildSync, 'The "buildSync" API only works in node')
|
|
},
|
|
|
|
async esbuildTransformSync() {
|
|
expectThrownError(esbuild.transformSync, 'The "transformSync" API only works in node')
|
|
},
|
|
}
|
|
|
|
function expectThrownError(fn, err) {
|
|
try {
|
|
fn()
|
|
throw new Error('Expected an error to be thrown')
|
|
} catch (e) {
|
|
assertStrictEqual(e.message, err)
|
|
}
|
|
}
|
|
|
|
function assertStrictEqual(a, b) {
|
|
if (a !== b) {
|
|
throw new Error(`Assertion failed:
|
|
Observed: ${JSON.stringify(a)}
|
|
Expected: ${JSON.stringify(b)}`);
|
|
}
|
|
}
|
|
|
|
async function runTest(test) {
|
|
try {
|
|
await tests[test]()
|
|
} catch (e) {
|
|
testFail(`[${test}] ` + (e && e.message || e))
|
|
}
|
|
}
|
|
|
|
const promises = []
|
|
for (const test in tests) {
|
|
promises.push(runTest(test))
|
|
}
|
|
await Promise.all(promises)
|
|
}
|
|
|
|
let pages = {};
|
|
|
|
for (let format of ['iife', 'esm']) {
|
|
for (let min of [false, true]) {
|
|
for (let async of [false, true]) {
|
|
let code = `
|
|
window.testStart = function() {
|
|
esbuild.initialize({
|
|
wasmURL: '/esbuild.wasm',
|
|
worker: ${async},
|
|
}).then(() => {
|
|
return (${runAllTests})({ esbuild })
|
|
}).then(() => {
|
|
testDone()
|
|
}).catch(e => {
|
|
testFail('' + (e && e.stack || e))
|
|
testDone()
|
|
})
|
|
}
|
|
`;
|
|
let page;
|
|
if (format === 'esm') {
|
|
page = `
|
|
<script type="module">
|
|
import * as esbuild from '/esm/browser${min ? '.min' : ''}.js'
|
|
${code}
|
|
</script>
|
|
`;
|
|
} else {
|
|
page = `
|
|
<script src="/lib/browser${min ? '.min' : ''}.js"></script>
|
|
<script>${code}</script>
|
|
`;
|
|
}
|
|
pages[format + (min ? 'Min' : '') + (async ? 'Async' : '')] = page;
|
|
}
|
|
}
|
|
}
|
|
|
|
const server = http.createServer((req, res) => {
|
|
if (req.method === 'GET' && req.url) {
|
|
if (req.url === '/lib/browser.js') {
|
|
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
res.end(js)
|
|
return
|
|
}
|
|
|
|
if (req.url === '/lib/browser.min.js') {
|
|
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
res.end(jsMin)
|
|
return
|
|
}
|
|
|
|
if (req.url === '/esm/browser.js') {
|
|
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
res.end(esm)
|
|
return
|
|
}
|
|
|
|
if (req.url === '/esm/browser.min.js') {
|
|
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
res.end(esmMin)
|
|
return
|
|
}
|
|
|
|
if (req.url === '/esbuild.wasm') {
|
|
res.writeHead(200, { 'Content-Type': 'application/wasm' })
|
|
res.end(wasm)
|
|
return
|
|
}
|
|
|
|
if (req.url.startsWith('/page/')) {
|
|
let key = req.url.slice('/page/'.length)
|
|
if (Object.prototype.hasOwnProperty.call(pages, key)) {
|
|
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
res.end(`
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf8">
|
|
</head>
|
|
<body>
|
|
${pages[key]}
|
|
</body>
|
|
</html>
|
|
`)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`[http] ${req.method} ${req.url}`)
|
|
res.writeHead(404)
|
|
res.end()
|
|
})
|
|
|
|
server.listen()
|
|
const { address, port } = server.address()
|
|
const serverURL = url.format({ protocol: 'http', hostname: address, port })
|
|
console.log(`[http] listening on ${serverURL}`)
|
|
|
|
async function main() {
|
|
const browser = await puppeteer.launch()
|
|
const promises = []
|
|
let allTestsPassed = true
|
|
|
|
async function runPage(key) {
|
|
try {
|
|
const page = await browser.newPage()
|
|
page.on('console', obj => console.log(`[console.${obj.type()}] ${obj.text()}`))
|
|
page.exposeFunction('testFail', error => {
|
|
console.log(`❌ ${error}`)
|
|
allTestsPassed = false
|
|
})
|
|
let testDone = new Promise(resolve => {
|
|
page.exposeFunction('testDone', resolve)
|
|
})
|
|
await page.goto(`${serverURL}/page/${key}`, { waitUntil: 'domcontentloaded' })
|
|
await page.evaluate('testStart()')
|
|
await testDone
|
|
await page.close()
|
|
} catch (e) {
|
|
allTestsPassed = false
|
|
console.log(`❌ ${key}: ${e && e.message || e}`)
|
|
}
|
|
}
|
|
|
|
for (let key in pages) {
|
|
await runPage(key)
|
|
}
|
|
|
|
await browser.close()
|
|
server.close()
|
|
|
|
if (!allTestsPassed) {
|
|
console.error(`❌ browser test failed`)
|
|
process.exit(1)
|
|
} else {
|
|
console.log(`✅ browser test passed`)
|
|
}
|
|
}
|
|
|
|
main().catch(error => setTimeout(() => { throw error }))
|