admin管理员组

文章数量:1303374

This is a bit of an edge case but it would be helpful to know.

When developing an extension using webpack-dev-server to keep the extension code up to date, it would be useful to listen to "webpackHotUpdate"

Chrome extensions with content scripts often have two sides to the equation:

  1. Background
  2. Injected Content Script

When using webpack-dev-server with HMR the background page stays in sync just fine. However content scripts require a reload of the extension in order to reflect the changes. I can remedy this by listening to the "webpackHotUpdate" event from the hotEmmiter and then requesting a reload. At present I have this working in a terrible and very unreliably hacky way.

var hotEmitter = __webpack_require__(XX)

hotEmitter.on('webpackHotUpdate', function() {
    console.log('Reloading Extension')
    chrome.runtime.reload()
})

XX simply represents the number that is currently assigned to the emitter. As you can imagine this changed whenever the build changes so it's a very temporary proof of concept sort of thing.

I suppose I could set up my own socket but that seems like overkill, given the events are already being transferred and I simply want to listen.

I am just recently getting more familiar with the webpack ecosystem so any guidance is much appreciated.

This is a bit of an edge case but it would be helpful to know.

When developing an extension using webpack-dev-server to keep the extension code up to date, it would be useful to listen to "webpackHotUpdate"

Chrome extensions with content scripts often have two sides to the equation:

  1. Background
  2. Injected Content Script

When using webpack-dev-server with HMR the background page stays in sync just fine. However content scripts require a reload of the extension in order to reflect the changes. I can remedy this by listening to the "webpackHotUpdate" event from the hotEmmiter and then requesting a reload. At present I have this working in a terrible and very unreliably hacky way.

var hotEmitter = __webpack_require__(XX)

hotEmitter.on('webpackHotUpdate', function() {
    console.log('Reloading Extension')
    chrome.runtime.reload()
})

XX simply represents the number that is currently assigned to the emitter. As you can imagine this changed whenever the build changes so it's a very temporary proof of concept sort of thing.

I suppose I could set up my own socket but that seems like overkill, given the events are already being transferred and I simply want to listen.

I am just recently getting more familiar with the webpack ecosystem so any guidance is much appreciated.

Share Improve this question asked Mar 17, 2017 at 23:55 Joel KellyJoel Kelly 2174 silver badges12 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 7

Okay!

I worked this out by looking around here:

https://github./facebookincubator/create-react-app/blob/master/packages/react-dev-utils/webpackHotDevClient.js

Many thanks to the create-react-app team for their judicious use of ments.

I created a slimmed down version of this specifically for handling the reload condition for extension development.

var SockJS = require('sockjs-client')
var url = require('url')

// Connect to WebpackDevServer via a socket.
var connection = new SockJS(
    url.format({
        // Default values - Updated to your own
        protocol: 'http',
        hostname: 'localhost',
        port: '3000',
        // Hardcoded in WebpackDevServer
        pathname: '/sockjs-node',
    })
)

var isFirstCompilation = true
var mostRecentCompilationHash = null

connection.onmessage = function(e) {
    var message = JSON.parse(e.data)
    switch (message.type) {
        case 'hash':
            handleAvailableHash(message.data)
            break
        case 'still-ok':
        case 'ok':
        case 'content-changed':
            handleSuccess()
            break
        default:
        // Do nothing.
    }
}

// Is there a newer version of this code available?
function isUpdateAvailable() {
    /* globals __webpack_hash__ */
    // __webpack_hash__ is the hash of the current pilation.
    // It's a global variable injected by Webpack.
    return mostRecentCompilationHash !== __webpack_hash__
}

function handleAvailableHash(data){
    mostRecentCompilationHash = data
}

function handleSuccess() {
    var isHotUpdate     = !isFirstCompilation
    isFirstCompilation  = false

    if (isHotUpdate) { handleUpdates() }
}

function handleUpdates() {
    if (!isUpdateAvailable()) return
    console.log('%c Reloading Extension', 'color: #FF00FF')
    chrome.runtime.reload()
}

When you are ready to use it (during development only) you can simply add it to your background.js entry point

module.exports = {
    entry: {
        background: [
            path.resolve(__dirname, 'reloader.js'), 
            path.resolve(__dirname, 'background.js')
        ]
    }
}




For actually hooking into the event emitter as was originally asked you can just require it from webpack/hot/emitter since that file exports an instance of the EventEmitter that's used.

if(module.hot) {
    var lastHash

    var upToDate = function upToDate() {
        return lastHash.indexOf(__webpack_hash__) >= 0
    }

    var clientEmitter = require('webpack/hot/emitter')

    clientEmitter.on('webpackHotUpdate', function(currentHash) {
        lastHash = currentHash
        if(upToDate()) return

        console.log('%c Reloading Extension', 'color: #FF00FF')
        chrome.runtime.reload()
    })
}

This is just a stripped down version straight from the source:

https://github./webpack/webpack/blob/master/hot/dev-server.js

I've fine-tuned the core logic of the crx-hotreload package and e up with a build-tool agnostic solution (meaning it will work with Webpack but also with anything else).

It asks the extension for its directory (via chrome.runtime.getPackageDirectoryEntry) and then watches that directory for file changes. Once a file is added/removed/changed inside that directory, it calls chrome.runtime.reload().

If you'd need to also reload the active tab (when developing a content script), then you should run a tabs.query, get the first (active) tab from the results and call reload on it as well.

The whole logic is ~35 lines of code:

/* global chrome */

const filesInDirectory = dir => new Promise(resolve =>
  dir.createReader().readEntries(entries =>
    Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
      e.isDirectory
        ? filesInDirectory(e)
        : new Promise(resolve => e.file(resolve))
    ))
      .then(files => [].concat(...files))
      .then(resolve)
  )
)

const timestampForFilesInDirectory = dir => filesInDirectory(dir)
  .then(files => files.map(f => f.name + f.lastModifiedDate).join())

const watchChanges = (dir, lastTimestamp) => {
  timestampForFilesInDirectory(dir).then(timestamp => {
    if (!lastTimestamp || (lastTimestamp === timestamp)) {
      setTimeout(() => watchChanges(dir, timestamp), 1000)
    } else {
      console.log('%c 

本文标签: javascriptListen for hot update events on the client side with webpackdevderverStack Overflow