Skip to content
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

feat(worker): Add sourcemap support for worker bundles #5417

Merged
merged 13 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../testUtils'
import { Page } from 'playwright-chromium'

// Workaround so that testing serve does not emit
// "Your test suite must contain at least one test"
test('true', () => {
expect(true).toBe(true)
})

if (isBuild) {
// assert correct files
test('hidden sourcemap generation for web workers', async () => {
const assetsDir = path.resolve(testDir, 'dist/assets')
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(6)
const index = files.find((f) => /^index\.\w+\.js$/.test(f))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const indexSourcemap = getSourceMapUrl(content)
const worker = files.find((f) => /^my-worker\.\w+\.js$/.test(f))
const workerContent = fs.readFileSync(
path.resolve(assetsDir, worker),
'utf-8'
)
const workerSourcemap = getSourceMapUrl(workerContent)
const sharedWorker = files.find((f) =>
/^my-shared-worker\.\w+\.js$/.test(f)
)
const sharedWorkerContent = fs.readFileSync(
path.resolve(assetsDir, sharedWorker),
'utf-8'
)
const sharedWorkerSourcemap = getSourceMapUrl(sharedWorkerContent)

expect(files).toContainEqual(expect.stringMatching(/^index\.\w+\.js\.map$/))
expect(files).toContainEqual(
expect.stringMatching(/^my-worker\.\w+\.js\.map$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^my-shared-worker\.\w+\.js\.map$/)
)

// sourcemap should exist and have a data URL
expect(indexSourcemap).toBe(null)
expect(workerSourcemap).toBe(null)
expect(sharedWorkerSourcemap).toBe(null)

// worker should have all imports resolved and no exports
expect(workerContent).not.toMatch(`import`)
expect(workerContent).not.toMatch(`export`)

// shared worker should have all imports resolved and no exports
expect(sharedWorkerContent).not.toMatch(`import`)
expect(sharedWorkerContent).not.toMatch(`export`)

// chunk
expect(content).toMatch(`new Worker("/assets`)
expect(content).toMatch(`new SharedWorker("/assets`)
// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)
})
} else {
// Workaround so that testing serve does not emit
// "Your test suite must contain at least one test"
test('true', () => {
expect(true).toBe(true)
})
}

function getSourceMapUrl(code: string): string {
const regex = /\/\/[#@]\s(?:source(?:Mapping)?URL)=\s*(\S+)/g
const results = regex.exec(code)

if (results && results.length >= 2) {
return results[1]
}
return null
}
57 changes: 57 additions & 0 deletions packages/playground/sourcemap-hidden/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<div>Expected values: <span class="mode-true"></span></div>
<button class="ping">Ping</button>
<div>
Response from worker: <span class="pong"></span><span class="mode"></span>
</div>

<button class="ping-inline">Ping Inline Worker</button>
<div>Response from inline worker: <span class="pong-inline"></span></div>

<button class="tick-shared">Tick Shared Worker</button>
<div>
Tick from shared worker, it syncs between pages:
<span class="tick-count">0</span>
</div>

<script type="module">
import Worker from './my-worker?worker'
import InlineWorker from './my-worker?worker&inline'
import SharedWorker from './my-shared-worker?sharedworker&name=shared'
import { mode } from './workerImport'

document.querySelector('.mode-true').textContent = mode

const worker = new Worker()
worker.addEventListener('message', (e) => {
text('.pong', e.data.msg)
text('.mode', e.data.mode)
})

document.querySelector('.ping').addEventListener('click', () => {
worker.postMessage('ping')
})

const inlineWorker = new InlineWorker()
inlineWorker.addEventListener('message', (e) => {
text('.pong-inline', e.data.msg)
})

document.querySelector('.ping-inline').addEventListener('click', () => {
inlineWorker.postMessage('ping')
})

const sharedWorker = new SharedWorker()
document.querySelector('.tick-shared').addEventListener('click', () => {
sharedWorker.port.postMessage('tick')
})

sharedWorker.port.addEventListener('message', (event) => {
text('.tick-count', event.data)
})

sharedWorker.port.start()

function text(el, text) {
document.querySelector(el).textContent = text
}
</script>
16 changes: 16 additions & 0 deletions packages/playground/sourcemap-hidden/my-shared-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
let count = 0
const ports = new Set()

onconnect = (event) => {
const port = event.ports[0]
ports.add(port)
port.postMessage(count)
port.onmessage = (message) => {
if (message.data === 'tick') {
count++
ports.forEach((p) => {
p.postMessage(count)
})
}
}
}
7 changes: 7 additions & 0 deletions packages/playground/sourcemap-hidden/my-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { msg, mode } from './workerImport'

self.onmessage = (e) => {
if (e.data === 'ping') {
self.postMessage({ msg, mode })
}
}
11 changes: 11 additions & 0 deletions packages/playground/sourcemap-hidden/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "test-sourcemap-hidden",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../vite/bin/vite",
"serve": "vite preview"
}
}
7 changes: 7 additions & 0 deletions packages/playground/sourcemap-hidden/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'

export default defineConfig({
build: {
sourcemap: 'hidden'
}
})
2 changes: 2 additions & 0 deletions packages/playground/sourcemap-hidden/workerImport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const msg = 'pong'
export const mode = process.env.NODE_ENV
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../testUtils'
import { Page } from 'playwright-chromium'

if (isBuild) {
// assert correct files
test('inlined sourcemap generation for web workers', async () => {
const assetsDir = path.resolve(testDir, 'dist/assets')
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(3)
const index = files.find((f) => f.includes('index'))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const indexSourcemap = getSourceMapUrl(content)
const worker = files.find((f) => f.includes('my-worker'))
const workerContent = fs.readFileSync(
path.resolve(assetsDir, worker),
'utf-8'
)
const workerSourcemap = getSourceMapUrl(workerContent)
const sharedWorker = files.find((f) => f.includes('my-shared-worker'))
const sharedWorkerContent = fs.readFileSync(
path.resolve(assetsDir, sharedWorker),
'utf-8'
)
const sharedWorkerSourcemap = getSourceMapUrl(sharedWorkerContent)

// sourcemap should exist and have a data URL
expect(indexSourcemap).toMatch(/^data:/)
expect(workerSourcemap).toMatch(/^data:/)
expect(sharedWorkerSourcemap).toMatch(/^data:/)

// worker should have all imports resolved and no exports
expect(workerContent).not.toMatch(`import`)
expect(workerContent).not.toMatch(`export`)

// shared worker should have all imports resolved and no exports
expect(sharedWorkerContent).not.toMatch(`import`)
expect(sharedWorkerContent).not.toMatch(`export`)

// chunk
expect(content).toMatch(`new Worker("/assets`)
expect(content).toMatch(`new SharedWorker("/assets`)
// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)
})
} else {
// Workaround so that testing serve does not emit
// "Your test suite must contain at least one test"
test('true', () => {
expect(true).toBe(true)
})
}

function getSourceMapUrl(code: string): string {
const regex = /\/\/[#@]\s(?:source(?:Mapping)?URL)=\s*(\S+)/g
const results = regex.exec(code)

if (results && results.length >= 2) {
return results[1]
}
return null
}
57 changes: 57 additions & 0 deletions packages/playground/sourcemap-inline/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<div>Expected values: <span class="mode-true"></span></div>
<button class="ping">Ping</button>
<div>
Response from worker: <span class="pong"></span><span class="mode"></span>
</div>

<button class="ping-inline">Ping Inline Worker</button>
<div>Response from inline worker: <span class="pong-inline"></span></div>

<button class="tick-shared">Tick Shared Worker</button>
<div>
Tick from shared worker, it syncs between pages:
<span class="tick-count">0</span>
</div>

<script type="module">
import Worker from './my-worker?worker'
import InlineWorker from './my-worker?worker&inline'
import SharedWorker from './my-shared-worker?sharedworker&name=shared'
import { mode } from './workerImport'

document.querySelector('.mode-true').textContent = mode

const worker = new Worker()
worker.addEventListener('message', (e) => {
text('.pong', e.data.msg)
text('.mode', e.data.mode)
})

document.querySelector('.ping').addEventListener('click', () => {
worker.postMessage('ping')
})

const inlineWorker = new InlineWorker()
inlineWorker.addEventListener('message', (e) => {
text('.pong-inline', e.data.msg)
})

document.querySelector('.ping-inline').addEventListener('click', () => {
inlineWorker.postMessage('ping')
})

const sharedWorker = new SharedWorker()
document.querySelector('.tick-shared').addEventListener('click', () => {
sharedWorker.port.postMessage('tick')
})

sharedWorker.port.addEventListener('message', (event) => {
text('.tick-count', event.data)
})

sharedWorker.port.start()

function text(el, text) {
document.querySelector(el).textContent = text
}
</script>
16 changes: 16 additions & 0 deletions packages/playground/sourcemap-inline/my-shared-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
let count = 0
const ports = new Set()

onconnect = (event) => {
const port = event.ports[0]
ports.add(port)
port.postMessage(count)
port.onmessage = (message) => {
if (message.data === 'tick') {
count++
ports.forEach((p) => {
p.postMessage(count)
})
}
}
}
7 changes: 7 additions & 0 deletions packages/playground/sourcemap-inline/my-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { msg, mode } from './workerImport'

self.onmessage = (e) => {
if (e.data === 'ping') {
self.postMessage({ msg, mode })
}
}
11 changes: 11 additions & 0 deletions packages/playground/sourcemap-inline/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "test-sourcemap-inline",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../vite/bin/vite",
"serve": "vite preview"
}
}
7 changes: 7 additions & 0 deletions packages/playground/sourcemap-inline/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'

export default defineConfig({
build: {
sourcemap: 'inline'
}
})
2 changes: 2 additions & 0 deletions packages/playground/sourcemap-inline/workerImport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const msg = 'pong'
export const mode = process.env.NODE_ENV