├── .gitignore ├── .npmignore ├── README.md ├── examples ├── crx-hmr-mv3-advance │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.js │ │ ├── background.js │ │ ├── content.js │ │ └── manifest.json │ └── webpack.config.js └── crx-hmr-mv3-basic │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app.js │ ├── background.js │ ├── content.js │ └── manifest.json │ └── webpack.config.js ├── index.js ├── lib ├── LoadScriptRuntimeModule.js ├── constants.js └── loadScript.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /examples 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crx-load-script-webpack-plugin 2 | 3 | This Webpack plugin overrides the default load script mechanism of Webpack runtime. Useful for Chrome Extension developers that are trying to lazyload scripts or using HMR when working with the Webpack dev server and the manifest V3. 4 | 5 | Check out the article on how this plugin was developed: https://medium.com/coobyhq/hot-module-replacement-for-chrome-extension-1096cb480edd 6 | 7 | ## Demo 8 | 9 | Checkout the [examples](./examples/) folder for demo. 10 | 11 | ## Getting Started 12 | 13 | ### Intall plugin 14 | 15 | ``` 16 | npm install @cooby/crx-load-script-webpack-plugin --save-dev 17 | ``` 18 | 19 | or 20 | 21 | ``` 22 | yarn add -D @cooby/crx-load-script-webpack-plugin 23 | ``` 24 | 25 | ### Webpack config 26 | 27 | Config your webpack, here's an example for Hot Module Replacement and React Refresh to work. 28 | 29 | ```js 30 | // webpack.config.js 31 | 32 | const CrxLoadScriptWebpackPlugin = require('@cooby/crx-load-script-webpack-plugin'); 33 | 34 | module.exports = { 35 | mode: 'development', 36 | devServer: { 37 | /** 38 | * We need devServer write files to disk, 39 | * But don't want it reload whole page because of the output file changes. 40 | */ 41 | static: { watch: false }, 42 | /** 43 | * Set WebSocket url to dev-server, instead of the default `${publicPath}/ws` 44 | */ 45 | client: { 46 | webSocketURL: 'ws://localhost:8080/ws', 47 | }, 48 | /** 49 | * The host of the page of your script extension runs on. 50 | * You'll see `[webpack-dev-server] Invalid Host/Origin header` if this is not set. 51 | */ 52 | allowedHosts: ['web.whatsapp.com'], 53 | devMiddleware: { 54 | /** 55 | * Write file to output folder /build, so we can execute it later. 56 | */ 57 | writeToDisk: true, 58 | }, 59 | }, 60 | plugins: [ 61 | /** 62 | * Enable HMR related plugins. 63 | */ 64 | new CrxLoadScriptWebpackPlugin(), 65 | new ReactRefreshWebpackPlugin({ 66 | overlay: false, 67 | }), 68 | ], 69 | } 70 | ``` 71 | 72 | ### Add permission to `manifest.json` 73 | 74 | `scripting` and `host_permissions` is for `excuteScript`. 75 | And `*.hot-update.json` should be added to `web_accessible_resources`, it's for runtime to fetch the updated manifest. 76 | 77 | Caution: You may not want some of these permissions in produciton build. 78 | 79 | ```json 80 | { 81 | "manifest_version": 3, 82 | "permissions": [ 83 | "scripting" 84 | ], 85 | "web_accessible_resources": [ 86 | { 87 | "resources": [ 88 | "*.hot-update.json", 89 | ], 90 | "matches": [ 91 | "https://web.whatsapp.com/*" 92 | ] 93 | } 94 | ], 95 | "host_permissions": [ 96 | "https://web.whatsapp.com/*" 97 | ] 98 | } 99 | 100 | ``` 101 | 102 | ### Import `loadScript` handler from the background script 103 | 104 | ```js 105 | // background.js 106 | import '@cooby/crx-load-script-webpack-plugin/lib/loadScript' 107 | ``` 108 | 109 | If you haven't a background script yet, you need to add it to webpack entries and manifest.json. 110 | 111 | ```json 112 | "background": { 113 | "service_worker": "background.bundle.js" 114 | }, 115 | 116 | ``` 117 | 118 | ### Setup content script 119 | 120 | ```js 121 | // content.js 122 | 123 | /** 124 | * This will change `publicPath` to `chrome-extension:///`. 125 | * It for runtime to get script chunks from the output folder 126 | * and for asset modules like file-loader to work. 127 | */ 128 | __webpack_public_path__ = chrome.runtime.getURL(''); 129 | ``` 130 | 131 | 132 | ## Trouble shooting 133 | 134 | After modifying the manifest.json, you must click the reload button from chrome://extensions to reload it. You cannot reload the manifest.json by clicking the reload button of other reload extension extensions. For example, if you run `npm run build` and then run `npm run dev`, the manifest.json is modified by inserting the HMR related permissions. Therefore, you need to click the reload button from chrome://extensions. 135 | 136 | For some reasons HMR may fail and Webpack will attempt to fully reload the page. For example, you modify the content.js instead of app.js's React code. However, this reloading will not bring you the latest update. You need to click the reload button from chrome://extensions, then reload the page. Alternatively, you can click the reload button of other reload extension extensions. 137 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/README.md: -------------------------------------------------------------------------------- 1 | # Chrome extension HMR advance example 2 | 3 | - Run `npm run dev` and navigate to https://web.whatsapp.com. 4 | - Modify the `app.js` to see the HMR. 5 | 6 | This is an example of a Chrome extension manifest v3 that showcases real-world use. The configuration only enables HMR for the content script, and it's only activated when using webpack-dev-server. The HMR related permission is only inserted when it is enabled. 7 | 8 | ## Trouble shooting 9 | 10 | See [troubleshooting](../../README.md#trouble-shooting) -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crx-hmr-mv3-advance-example", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "webpack", 6 | "dev": "webpack-dev-server" 7 | }, 8 | "devDependencies": { 9 | "@babel/preset-env": "^7.20.2", 10 | "@babel/preset-react": "^7.18.6", 11 | "@cooby/crx-load-script-webpack-plugin": "^1.1.0", 12 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", 13 | "babel-core": "^6.26.3", 14 | "babel-loader": "^9.1.2", 15 | "babel-preset-env": "^1.7.0", 16 | "clean-webpack-plugin": "^4.0.0", 17 | "copy-webpack-plugin": "^11.0.0", 18 | "webpack": "^5.76.1", 19 | "webpack-cli": "^5.1.4", 20 | "webpack-dev-server": "^4.15.1" 21 | }, 22 | "dependencies": { 23 | "react": "^18.2.0", 24 | "react-dom": "^18.2.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const App = () => { 4 | return ( 5 |
15 | TEST {/* Modify this text to see HMR working */} 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/src/background.js: -------------------------------------------------------------------------------- 1 | import '@cooby/crx-load-script-webpack-plugin/lib/loadScript'; -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/src/content.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { App } from './app' 4 | 5 | /** 6 | * This will change `publicPath` to `chrome-extension:///`. 7 | * It for runtime to get script chunks from the output folder 8 | * and for asset modules like file-loader to work. 9 | */ 10 | __webpack_public_path__ = chrome.runtime.getURL('') 11 | 12 | if (module.hot) { 13 | require('webpack/hot/dev-server'); 14 | /** 15 | * Set WebSocket url to dev-server, instead of the default `${publicPath}/ws` 16 | */ 17 | require('webpack-dev-server/client?hot=true&protocol=ws&hostname=localhost&port=8080'); 18 | } 19 | 20 | const div = document.createElement('div') 21 | document.body.append(div) 22 | createRoot(div).render() 23 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crx-hmr-mv3-advance-example", 3 | "version": "1.0.0", 4 | "manifest_version": 3, 5 | "permissions": [], 6 | "web_accessible_resources": [ 7 | { 8 | "resources": [], 9 | "matches": ["https://web.whatsapp.com/*"] 10 | } 11 | ], 12 | "host_permissions": [], 13 | "background": { 14 | "service_worker": "background.js" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "js": ["./content.js"], 19 | "matches": ["https://web.whatsapp.com/*"] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-advance/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const CrxLoadScriptWebpackPlugin = require("@cooby/crx-load-script-webpack-plugin"); 4 | const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); 5 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 6 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 7 | 8 | module.exports = { 9 | mode: "development", 10 | devServer: { 11 | /** 12 | * Stop injecting 13 | * `hot:webpack/hot/dev-server`, 14 | * `client:webpack-dev-server/client` 15 | * these two modules to into injection and background scripts. 16 | * We'll import it from content script manually. 17 | */ 18 | hot: false, 19 | client: false, 20 | /** 21 | * We need devServer write files to disk, 22 | * But don't want it reload whole page because of the output file changes. 23 | */ 24 | static: { watch: false }, 25 | /** 26 | * The host of the page of your script extension runs on. Should not include `https://` or `http://`. 27 | * You'll see `[webpack-dev-server] Invalid Host/Origin header` if this is not set. 28 | */ 29 | allowedHosts: ["web.whatsapp.com"], 30 | devMiddleware: { 31 | /** 32 | * Write file to output folder /build, so we can execute it later. 33 | */ 34 | writeToDisk: true, 35 | }, 36 | }, 37 | devtool: "inline-source-map", 38 | entry: { 39 | background: path.resolve(__dirname, "src", "background.js"), 40 | content: path.resolve(__dirname, "src", "content.js"), 41 | }, 42 | output: { 43 | filename: "[name].js", 44 | path: path.resolve(__dirname, "build"), 45 | }, 46 | plugins: [ 47 | ...(process.env.WEBPACK_SERVE 48 | ? [ 49 | new webpack.HotModuleReplacementPlugin(), 50 | new ReactRefreshWebpackPlugin({ 51 | overlay: false, 52 | }), 53 | new CrxLoadScriptWebpackPlugin(), 54 | ] 55 | : []), 56 | new CopyWebpackPlugin({ 57 | patterns: [ 58 | { 59 | from: "src/manifest.json", 60 | to: () => `${path.join(__dirname, "build")}/[name][ext]`, 61 | force: true, 62 | transform(content){ 63 | const contentObject = JSON.parse(content.toString()); 64 | 65 | /** 66 | * Only insert HMR related permissions and resources in HMR mode. 67 | */ 68 | if (process.env.WEBPACK_SERVE) { 69 | contentObject.permissions.push('scripting'); 70 | contentObject.host_permissions.push('https://web.whatsapp.com/*'); 71 | contentObject.web_accessible_resources[0].resources.push( 72 | '*.hot-update.json' 73 | ); 74 | } 75 | 76 | return Buffer.from(JSON.stringify(contentObject)); 77 | } 78 | }, 79 | ], 80 | }), 81 | new CleanWebpackPlugin({ 82 | verbose: true, 83 | cleanStaleWebpackAssets: true, 84 | }), 85 | ], 86 | module: { 87 | rules: [ 88 | { 89 | test: /\.(js|jsx)$/, 90 | exclude: /node_modules/, 91 | use: [ 92 | { 93 | loader: "babel-loader", 94 | options: { 95 | plugins: [ 96 | process.env.WEBPACK_SERVE && 97 | require.resolve('react-refresh/babel'), 98 | ].filter(Boolean), 99 | }, 100 | }, 101 | ], 102 | }, 103 | ], 104 | }, 105 | }; 106 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/README.md: -------------------------------------------------------------------------------- 1 | # Chrome extension HMR basic example 2 | 3 | - Run `npm run dev` and navigate to https://web.whatsapp.com. 4 | - Modify the `app.js` to see the HMR. 5 | 6 | Here is a simple demonstration of the Chrome extension manifest v3. While you may encounter some error or warning messages in the background script, they won't impact the HMR. If you'd like to avoid these warnings or explore a more advanced project configuration, check out the [crx-hmr-mv3-advance](../crx-hmr-mv3-advance). 7 | 8 | ## Trouble shooting 9 | 10 | See [troubleshooting](../../README.md#trouble-shooting) -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crx-hmr-mv3-basic-example", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "webpack-dev-server" 6 | }, 7 | "devDependencies": { 8 | "@babel/preset-env": "^7.20.2", 9 | "@babel/preset-react": "^7.18.6", 10 | "@cooby/crx-load-script-webpack-plugin": "^1.1.0", 11 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", 12 | "babel-core": "^6.26.3", 13 | "babel-loader": "^9.1.2", 14 | "babel-preset-env": "^1.7.0", 15 | "clean-webpack-plugin": "^4.0.0", 16 | "copy-webpack-plugin": "^11.0.0", 17 | "webpack": "^5.76.1", 18 | "webpack-cli": "^5.1.4", 19 | "webpack-dev-server": "^4.15.1" 20 | }, 21 | "dependencies": { 22 | "react": "^18.2.0", 23 | "react-dom": "^18.2.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const App = () => { 4 | return ( 5 |
15 | TEST {/* Modify this text to see HMR working */} 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/src/background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Only import the `loadScript` function in HMR mode. 3 | */ 4 | if (module.hot) { 5 | require('@cooby/crx-load-script-webpack-plugin/lib/loadScript'); 6 | } 7 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/src/content.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { App } from './app' 4 | 5 | /** 6 | * This will change `publicPath` to `chrome-extension:///`. 7 | * It for runtime to get script chunks from the output folder 8 | * and for asset modules like file-loader to work. 9 | */ 10 | __webpack_public_path__ = chrome.runtime.getURL('') 11 | 12 | const div = document.createElement('div') 13 | document.body.append(div) 14 | createRoot(div).render() 15 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crx-hmr-mv3-basic-example", 3 | "version": "1.0.0", 4 | "manifest_version": 3, 5 | "permissions": ["scripting"], 6 | "web_accessible_resources": [ 7 | { 8 | "resources": ["*.hot-update.json"], 9 | "matches": ["https://web.whatsapp.com/*"] 10 | } 11 | ], 12 | "host_permissions": ["https://web.whatsapp.com/*"], 13 | "background": { 14 | "service_worker": "background.js" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "js": ["./content.js"], 19 | "matches": ["https://web.whatsapp.com/*"] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/crx-hmr-mv3-basic/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const CrxLoadScriptWebpackPlugin = require("@cooby/crx-load-script-webpack-plugin"); 4 | const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); 5 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 6 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 7 | 8 | module.exports = { 9 | mode: "development", 10 | devServer: { 11 | hot: true, 12 | /** 13 | * We need devServer write files to disk, 14 | * But don't want it reload whole page because of the output file changes. 15 | */ 16 | static: { watch: false }, 17 | /** 18 | * Set WebSocket url to dev-server, instead of the default `${publicPath}/ws` 19 | */ 20 | client: { 21 | webSocketURL: "ws://localhost:8080/ws", 22 | }, 23 | /** 24 | * The host of the page of your script extension runs on. Should not include `https://` or `http://`. 25 | * You'll see `[webpack-dev-server] Invalid Host/Origin header` if this is not set. 26 | */ 27 | allowedHosts: ["web.whatsapp.com"], 28 | devMiddleware: { 29 | /** 30 | * Write file to output folder /build, so we can execute it later. 31 | */ 32 | writeToDisk: true, 33 | }, 34 | }, 35 | devtool: "inline-source-map", 36 | entry: { 37 | background: path.resolve(__dirname, "src", "background.js"), 38 | content: path.resolve(__dirname, "src", "content.js"), 39 | }, 40 | output: { 41 | filename: "[name].js", 42 | path: path.resolve(__dirname, "build"), 43 | }, 44 | plugins: [ 45 | /** 46 | * Enable HMR related plugins. 47 | */ 48 | new ReactRefreshWebpackPlugin({ 49 | overlay: false, 50 | }), 51 | new CrxLoadScriptWebpackPlugin(), 52 | new CopyWebpackPlugin({ 53 | patterns: [ 54 | { 55 | from: "src/manifest.json", 56 | to: () => `${path.join(__dirname, "build")}/[name][ext]`, 57 | force: true, 58 | }, 59 | ], 60 | }), 61 | new CleanWebpackPlugin({ 62 | verbose: true, 63 | cleanStaleWebpackAssets: true, 64 | }), 65 | ], 66 | module: { 67 | rules: [ 68 | { 69 | test: /\.(js|jsx)$/, 70 | exclude: /node_modules/, 71 | use: [ 72 | { 73 | loader: "babel-loader", 74 | options: { 75 | plugins: [require.resolve("react-refresh/babel")], 76 | }, 77 | }, 78 | ], 79 | }, 80 | ], 81 | }, 82 | }; 83 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const LoadScriptRuntimeModule = require('./lib/LoadScriptRuntimeModule'); 2 | 3 | class CrxLoadScriptWebpackPlugin { 4 | apply(compiler) { 5 | compiler.hooks.compilation.tap( 6 | 'CrxLoadScriptWebpackPlugin', 7 | (compilation) => { 8 | const { RuntimeGlobals } = compiler.webpack; 9 | compilation.hooks.runtimeRequirementInTree 10 | .for(RuntimeGlobals.loadScript) 11 | .tap('CrxLoadScriptWebpackPlugin', (chunk, set) => { 12 | compilation.addRuntimeModule(chunk, new LoadScriptRuntimeModule()); 13 | return true; 14 | }); 15 | } 16 | ); 17 | } 18 | } 19 | 20 | module.exports = CrxLoadScriptWebpackPlugin; 21 | -------------------------------------------------------------------------------- /lib/LoadScriptRuntimeModule.js: -------------------------------------------------------------------------------- 1 | const { MESSAGE_TYPES } = require('./constants') 2 | const RuntimeGlobals = require('webpack/lib/RuntimeGlobals'); 3 | const Template = require('webpack/lib/Template'); 4 | const HelperRuntimeModule = require('webpack/lib/runtime/HelperRuntimeModule'); 5 | 6 | class LoadScriptRuntimeModule extends HelperRuntimeModule { 7 | constructor() { 8 | super('load script'); 9 | } 10 | 11 | generate() { 12 | const { compilation } = this; 13 | const { runtimeTemplate, outputOptions } = compilation; 14 | const { uniqueName } = outputOptions; 15 | const fn = RuntimeGlobals.loadScript; 16 | 17 | return Template.asString([ 18 | 'var loadedScripts = {};', 19 | 'var inProgress = {};', 20 | uniqueName 21 | ? `var dataWebpackPrefix = ${JSON.stringify(uniqueName + ':')};` 22 | : '// data-webpack is not used as build has no uniqueName', 23 | '// loadScript function to load a script by asking background script run chrome.runtime.executeScript', 24 | `${fn} = ${runtimeTemplate.basicFunction('url, done, key, chunkId', [ 25 | 'if(inProgress[url]) { inProgress[url].push(done); return; }', 26 | 'inProgress[url] = [done];', 27 | 28 | 'if(loadedScripts[url]) return;', 29 | 'loadedScripts[url] = true;', 30 | 31 | 'chrome.runtime.sendMessage({', 32 | Template.indent([ 33 | `type: '${MESSAGE_TYPES.LOAD_SCRIPT}',`, 34 | 'payload: {', 35 | Template.indent([ 36 | `file: url.replace(${RuntimeGlobals.publicPath},'')`, 37 | ]), 38 | '}', 39 | ]), 40 | '}, () => {', 41 | Template.indent([ 42 | 'var doneFns = inProgress[url];', 43 | 'delete inProgress[url];', 44 | `doneFns && doneFns.forEach(${runtimeTemplate.returningFunction( 45 | 'fn(event)', 46 | 'fn' 47 | )});`, 48 | ]), 49 | '})', 50 | ])};`, 51 | ]); 52 | } 53 | } 54 | 55 | module.exports = LoadScriptRuntimeModule; 56 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | const MESSAGE_TYPES = { 2 | LOAD_SCRIPT: 'crx_load_script_plugin.load_script' 3 | } 4 | 5 | module.exports = { 6 | MESSAGE_TYPES 7 | } -------------------------------------------------------------------------------- /lib/loadScript.js: -------------------------------------------------------------------------------- 1 | const { MESSAGE_TYPES } = require('./constants') 2 | 3 | const DEBUG = __resourceQuery.includes('debug=true'); 4 | 5 | const handleMessage = (request, sender, sendResponse) => { 6 | if(request.type !== MESSAGE_TYPES.LOAD_SCRIPT) return 7 | 8 | const { file } = request.payload; 9 | 10 | DEBUG && console.log('executeScript:', file); 11 | 12 | if (chrome.runtime.getManifest().manifest_version === 2){ 13 | chrome.tabs.executeScript(sender.tab.id, { file }); 14 | } else { 15 | chrome.scripting.executeScript({ 16 | target: { tabId: sender.tab.id }, 17 | injectImmediately: true, 18 | files: [file], 19 | }); 20 | } 21 | 22 | // Return true to indicate you want to send a response asynchronously. 23 | return true; 24 | }; 25 | 26 | chrome.runtime.onMessage.addListener(handleMessage); 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cooby/crx-load-script-webpack-plugin", 3 | "version": "1.1.0", 4 | "description": "A webpack plugin overrides the default load script mechanism for Chrome Extension. Useful for Chrome Extension developers that are trying to lazyload scripts or using HMR when working with the Webpack dev server.", 5 | "homepage": "https://github.com/cooby-inc/crx-load-script-webpack-plugin", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/cooby-inc/crx-load-script-webpack-plugin.git" 9 | }, 10 | "main": "index.js", 11 | "keywords": [ 12 | "webpack", 13 | "hmr", 14 | "hot-module-replacement", 15 | "chrome-extension", 16 | "crx", 17 | "manifest-v3", 18 | "lazyload", 19 | "dynamic-import" 20 | ], 21 | "author": "Pinkie Wen", 22 | "license": "MIT" 23 | } 24 | --------------------------------------------------------------------------------