@quasar/app-webpack
Version:
Quasar Framework App CLI with Webpack
150 lines (118 loc) • 3.94 kB
JavaScript
import { BexBridge } from './private/bex-bridge.mjs'
function interceptRequests (devServerPort) {
/**
* We intercept all fetch requests from the extension page and redirect them to the dev server
* for HMR purposes.
*/
const bexOrigin = `chrome-extension://${ chrome.runtime.id }`
const hrefRE = /=$|=(?=&)/g
async function getDevServerResponse (url) {
// point it to the dev server
url.protocol = 'http:'
url.host = 'localhost'
url.port = devServerPort
// ensure we have a fresh version of the response
url.searchParams.set('t', Date.now())
// fetch the requested resource
const request = await fetch(
url.href.replace(hrefRE, '')
)
// and return it wrapped as if it comes from the extension
return new Response(request.body, {
headers: {
'Content-Type': request.headers.get('Content-Type') || 'text/javascript',
'Cache-Control': request.headers.get('Cache-Control') || ''
}
})
}
self.addEventListener('fetch', evt => {
const url = new URL(evt.request.url)
if (url.origin === bexOrigin) {
evt.respondWith(
getDevServerResponse(url)
)
}
})
}
function connectToDevServer (devServerPort) {
const pingUrl = `http://localhost:${ devServerPort }/`
const socket = new WebSocket(`ws://localhost:${ devServerPort }/ws`)
const contentScriptPortList = new Set()
const contentScriptPortNameRE = /^quasar@hmr\/content-script\//
function reloadExtension () {
const len = contentScriptPortList.size
const suffix = len !== 0
? ` along with ${ len } content script${ len > 1 ? 's' : '' }`
: ''
console.log(`[QBex|HMR] Reloading extension${ suffix }...`)
for (const port of contentScriptPortList) {
port.postMessage('qbex:hmr:reload-content')
}
chrome.runtime.reload()
}
socket.addEventListener('open', () => {
console.log('[QBex|HMR] Connected')
// send a ping every 30s to keep the connection alive
const interval = setInterval(() => socket.send('ping'), 30000)
socket.addEventListener('close', () => clearInterval(interval))
})
// Listen for messages
socket.addEventListener('message', ({ data }) => {
const { type, event } = JSON.parse(data)
if (type === 'custom' && event === 'qbex:hmr:reload') {
reloadExtension()
}
})
socket.addEventListener('close', async ({ wasClean }) => {
if (wasClean) return
console.log('[QBex|HMR] Lost connection. Reconnecting...')
let tries = 1
while (true) {
try {
if (tries > 2000) {
console.log('[QBex|HMR] Aborting re-connect after 2000 failed attempts. Please manually reload the extension.')
return
}
await fetch(pingUrl)
break
}
catch (_) {
console.log('[QBex|HMR] Could not re-connect. Retrying...')
await new Promise((resolve) => setTimeout(resolve, 1000))
tries++
}
}
reloadExtension()
})
chrome.runtime.onConnect.addListener(port => {
const { name } = port
if (contentScriptPortNameRE.test(name) === true) {
contentScriptPortList.add(port)
port.onDisconnect.addListener(() => {
contentScriptPortList.delete(port)
})
port.postMessage('qbex:hmr:hello')
}
})
}
/**
* Only run these in development mode and in a background service worker.
* Currently only Chrome supports this.
*/
if (process.env.DEV === true && process.env.TARGET === 'chrome') {
const devServerPort = process.env.__QUASAR_BEX_SERVER_PORT__
interceptRequests(devServerPort)
connectToDevServer(devServerPort)
}
let scriptHasBridge = false
export function createBridge ({ debug } = {}) {
if (scriptHasBridge === true) {
console.error('Background Quasar Bridge has already been created.')
return
}
scriptHasBridge = true
return new BexBridge({
type: 'background',
debug
})
}