Skip to content

Commit

Permalink
feat(worker): Add sourcemap support for worker bundles (#5417)
Browse files Browse the repository at this point in the history
  • Loading branch information
KallynGowdy committed Mar 29, 2022
1 parent cb388e2 commit 465b6b9
Show file tree
Hide file tree
Showing 10 changed files with 481 additions and 17 deletions.
@@ -0,0 +1,129 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../../testUtils'
import { Page } from 'playwright-chromium'

if (isBuild) {
const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap-hidden/assets')
// assert correct files
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(25)
const index = files.find((f) => f.includes('main-module'))
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)
const possibleTsOutputWorker = files.find((f) =>
/^possible-ts-output-worker\.\w+\.js$/.test(f)
)
const possibleTsOutputWorkerContent = fs.readFileSync(
path.resolve(assetsDir, possibleTsOutputWorker),
'utf-8'
)
const possibleTsOutputWorkerSourcemap = getSourceMapUrl(
possibleTsOutputWorkerContent
)
const workerNestedWorker = files.find((f) =>
/^worker-nested-worker\.\w+\.js$/.test(f)
)
const workerNestedWorkerContent = fs.readFileSync(
path.resolve(assetsDir, workerNestedWorker),
'utf-8'
)
const workerNestedWorkerSourcemap = getSourceMapUrl(
workerNestedWorkerContent
)
const subWorker = files.find((f) => /^sub-worker\.\w+\.js$/.test(f))
const subWorkerContent = fs.readFileSync(
path.resolve(assetsDir, subWorker),
'utf-8'
)
const subWorkerSourcemap = getSourceMapUrl(subWorkerContent)

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$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^possible-ts-output-worker\.\w+\.js\.map$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^worker-nested-worker\.\w+\.js\.map$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^sub-worker\.\w+\.js\.map$/)
)

// sourcemap should exist and have a data URL
expect(indexSourcemap).toBe(null)
expect(workerSourcemap).toBe(null)
expect(sharedWorkerSourcemap).toBe(null)
expect(possibleTsOutputWorkerSourcemap).toBe(null)
expect(workerNestedWorkerSourcemap).toBe(null)
expect(subWorkerSourcemap).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("/iife-sourcemap-hidden/assets/my-worker`
)
expect(content).toMatch(`new Worker("data:application/javascript;base64`)
expect(content).toMatch(
`new Worker("/iife-sourcemap-hidden/assets/possible-ts-output-worker`
)
expect(content).toMatch(
`new Worker("/iife-sourcemap-hidden/assets/worker-nested-worker`
)
expect(content).toMatch(
`new SharedWorker("/iife-sourcemap-hidden/assets/my-shared-worker`
)

// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)

expect(workerNestedWorkerContent).toMatch(
`new Worker("/iife-sourcemap-hidden/assets/sub-worker`
)
})
} 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
}
@@ -0,0 +1 @@
module.exports = require('../../vite.config-sourcemap')('hidden')
@@ -0,0 +1,112 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../../testUtils'
import { Page } from 'playwright-chromium'

if (isBuild) {
const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap-inline/assets')
// assert correct files
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(13)
const index = files.find((f) => f.includes('main-module'))
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)
const possibleTsOutputWorker = files.find((f) =>
/^possible-ts-output-worker\.\w+\.js$/.test(f)
)
const possibleTsOutputWorkerContent = fs.readFileSync(
path.resolve(assetsDir, possibleTsOutputWorker),
'utf-8'
)
const possibleTsOutputWorkerSourcemap = getSourceMapUrl(
possibleTsOutputWorkerContent
)
const workerNestedWorker = files.find((f) =>
/^worker-nested-worker\.\w+\.js$/.test(f)
)
const workerNestedWorkerContent = fs.readFileSync(
path.resolve(assetsDir, workerNestedWorker),
'utf-8'
)
const workerNestedWorkerSourcemap = getSourceMapUrl(
workerNestedWorkerContent
)
const subWorker = files.find((f) => /^sub-worker\.\w+\.js$/.test(f))
const subWorkerContent = fs.readFileSync(
path.resolve(assetsDir, subWorker),
'utf-8'
)
const subWorkerSourcemap = getSourceMapUrl(subWorkerContent)

// sourcemap should exist and have a data URL
expect(indexSourcemap).toMatch(/^data:/)
expect(workerSourcemap).toMatch(/^data:/)
expect(sharedWorkerSourcemap).toMatch(/^data:/)
expect(possibleTsOutputWorkerSourcemap).toMatch(/^data:/)
expect(workerNestedWorkerSourcemap).toMatch(/^data:/)
expect(subWorkerSourcemap).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("/iife-sourcemap-inline/assets/my-worker`
)
expect(content).toMatch(`new Worker("data:application/javascript;base64`)
expect(content).toMatch(
`new Worker("/iife-sourcemap-inline/assets/possible-ts-output-worker`
)
expect(content).toMatch(
`new Worker("/iife-sourcemap-inline/assets/worker-nested-worker`
)
expect(content).toMatch(
`new SharedWorker("/iife-sourcemap-inline/assets/my-shared-worker`
)

// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)

expect(workerNestedWorkerContent).toMatch(
`new Worker("/iife-sourcemap-inline/assets/sub-worker`
)
})
} 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
}
@@ -0,0 +1 @@
module.exports = require('../../vite.config-sourcemap')('inline')
@@ -0,0 +1,131 @@
import fs from 'fs'
import path from 'path'
import { untilUpdated, isBuild, testDir } from '../../../testUtils'
import { Page } from 'playwright-chromium'

if (isBuild) {
const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap/assets')
// assert correct files
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(25)
const index = files.find((f) => f.includes('main-module'))
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)
const possibleTsOutputWorker = files.find((f) =>
/^possible-ts-output-worker\.\w+\.js$/.test(f)
)
const possibleTsOutputWorkerContent = fs.readFileSync(
path.resolve(assetsDir, possibleTsOutputWorker),
'utf-8'
)
const possibleTsOutputWorkerSourcemap = getSourceMapUrl(
possibleTsOutputWorkerContent
)
const workerNestedWorker = files.find((f) =>
/^worker-nested-worker\.\w+\.js$/.test(f)
)
const workerNestedWorkerContent = fs.readFileSync(
path.resolve(assetsDir, workerNestedWorker),
'utf-8'
)
const workerNestedWorkerSourcemap = getSourceMapUrl(
workerNestedWorkerContent
)
const subWorker = files.find((f) => /^sub-worker\.\w+\.js$/.test(f))
const subWorkerContent = fs.readFileSync(
path.resolve(assetsDir, subWorker),
'utf-8'
)
const subWorkerSourcemap = getSourceMapUrl(subWorkerContent)

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$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^possible-ts-output-worker\.\w+\.js\.map$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^worker-nested-worker\.\w+\.js\.map$/)
)
expect(files).toContainEqual(
expect.stringMatching(/^sub-worker\.\w+\.js\.map$/)
)

// sourcemap should exist and have a data URL
expect(indexSourcemap).toMatch(/^main-module\.\w+\.js\.map$/)
expect(workerSourcemap).toMatch(/^my-worker\.\w+\.js\.map$/)
expect(sharedWorkerSourcemap).toMatch(/^my-shared-worker\.\w+\.js\.map$/)
expect(possibleTsOutputWorkerSourcemap).toMatch(
/^possible-ts-output-worker\.\w+\.js\.map$/
)
expect(workerNestedWorkerSourcemap).toMatch(
/^worker-nested-worker\.\w+\.js\.map$/
)
expect(subWorkerSourcemap).toMatch(/^sub-worker\.\w+\.js\.map$/)

// 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("/iife-sourcemap/assets/my-worker`)
expect(content).toMatch(`new Worker("data:application/javascript;base64`)
expect(content).toMatch(
`new Worker("/iife-sourcemap/assets/possible-ts-output-worker`
)
expect(content).toMatch(
`new Worker("/iife-sourcemap/assets/worker-nested-worker`
)
expect(content).toMatch(
`new SharedWorker("/iife-sourcemap/assets/my-shared-worker`
)

// inlined
expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
expect(content).toMatch(`window.Blob`)

expect(workerNestedWorkerContent).toMatch(
`new Worker("/iife-sourcemap/assets/sub-worker`
)
})
} 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
}
@@ -0,0 +1 @@
module.exports = require('../../vite.config-sourcemap')(true)
10 changes: 10 additions & 0 deletions packages/playground/worker/__tests__/worker.spec.ts
Expand Up @@ -94,3 +94,13 @@ test('classic worker', async () => {
expect(await page.textContent('.classic-worker')).toMatch('A classic')
expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
})

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
}

0 comments on commit 465b6b9

Please sign in to comment.