Giter Site home page Giter Site logo

samuelsimoes / chrome-extension-webpack-boilerplate Goto Github PK

View Code? Open in Web Editor NEW
1.7K 31.0 348.0 926 KB

A basic foundation boilerplate for rich Chrome Extensions using Webpack to help you write modular and modern Javascript code, load CSS easily and automatic reload the browser on code changes.

License: MIT License

JavaScript 91.75% HTML 8.25%
chrome-extension webpack boilerplate

chrome-extension-webpack-boilerplate's Introduction

Chrome Extension Webpack Boilerplate

A basic foundation boilerplate for rich Chrome Extensions using Webpack to help you write modular and modern Javascript code, load CSS easily and automatic reload the browser on code changes.

Developing a new extension

I'll assume that you already read the Webpack docs and the Chrome Extension docs.

  1. Check if your Node.js version is >= 6.
  2. Clone the repository.
  3. Install yarn.
  4. Run yarn.
  5. Change the package's name and description on package.json.
  6. Change the name of your extension on src/manifest.json.
  7. Run yarn run start
  8. Load your extension on Chrome following:
    1. Access chrome://extensions/
    2. Check Developer mode
    3. Click on Load unpacked extension
    4. Select the build folder.
  9. Have fun.

Structure

All your extension's development code must be placed in src folder, including the extension manifest.

The boilerplate is already prepared to have a popup, a options page and a background page. You can easily customize this.

Each page has its own assets package defined. So, to code on popup you must start your code on src/js/popup.js, for example.

You must use the ES6 modules to a better code organization. The boilerplate is already prepared to that and here you have a little example.

Webpack auto-reload and HRM

To make your workflow much more efficient this boilerplate uses the webpack server to development (started with yarn run server) with auto reload feature that reloads the browser automatically every time that you save some file o your editor.

You can run the dev mode on other port if you want. Just specify the env var port like this:

$ PORT=6002 yarn run start

Content Scripts

Although this boilerplate uses the webpack dev server, it's also prepared to write all your bundles files on the disk at every code change, so you can point, on your extension manifest, to your bundles that you want to use as content scripts, but you need to exclude these entry points from hot reloading (why?). To do so you need to expose which entry points are content scripts on the webpack.config.js using the chromeExtensionBoilerplate -> notHotReload config. Look the example below.

Let's say that you want use the myContentScript entry point as content script, so on your webpack.config.js you will configure the entry point and exclude it from hot reloading, like this:

{
  
  entry: {
    myContentScript: "./src/js/myContentScript.js"
  },
  chromeExtensionBoilerplate: {
    notHotReload: ["myContentScript"]
  }
  
}

and on your src/manifest.json:

{
  "content_scripts": [
    {
      "matches": ["https://www.google.com/*"],
      "js": ["myContentScript.bundle.js"]
    }
  ]
}

Packing

After the development of your extension run the command

$ NODE_ENV=production yarn run build

Now, the content of build folder will be the extension ready to be submitted to the Chrome Web Store. Just take a look at the official guide to more infos about publishing.

Secrets

If you are developing an extension that talks with some API you probably are using different keys for testing and production. Is a good practice you not commit your secret keys and expose to anyone that have access to the repository.

To this task this boilerplate import the file ./secrets.<THE-NODE_ENV>.js on your modules through the module named as secrets, so you can do things like this:

./secrets.development.js

export default { key: "123" };

./src/popup.js

import secrets from "secrets";
ApiCall({ key: secrets.key });

👉 The files with name secrets.*.js already are ignored on the repository.

With React.js

💡 If you want use React.js with this boilerplate, check the react branch.

Contributing

  1. Please!! Do not create a pull request without an issue before discussing the problem.
  2. On your PR make sure that you are following the current codebase style.
  3. Your PR must be single purpose. Resolve just one problem on your PR.
  4. Make sure to commit in the same style that we are committing until now on the project.

Samuel Simões ~ @samuelsimoes ~ Blog

chrome-extension-webpack-boilerplate's People

Contributors

albertinator avatar dependabot[bot] avatar neaumusic avatar nicknish avatar samuelsimoes avatar vshih avatar whiteminds avatar xcv58 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chrome-extension-webpack-boilerplate's Issues

How to inject css in content scripts?

I have been trying to build an extension. I am able to add content script but css is not getting injected into content script.

Content script is expected to add dom elements and they need their style.

Auto hot reloading with HTML files?

The auto hot reloader works great with the JS files but not with HTML, is this an easy update?

Recreate:

  • Clone
  • Yarn
  • Yarn Start
  • Edit popup/example.js = reload and show changes
  • Edit popup.html = no reload

not work for content.js in chrome 66

this repo is awesome! and it works quite fine with popup.js, bg.js, but it may not work in content.js in chrome 66, I am not sure if it's an issue of chrome or this repo, when i include a content.js file, after the file modified, I should refresh the certain extension in the page chrome://extensions/, some days ago reloading the page chrome://extensions/ is enough

Add icons

Most extension are going to have some icons declared in the manifest for icon, and browser action. This should works out of the box no?

How to use "web_accessible_resources" option in manifest.json to inject CSS and JS?

I've done this sucessfully in my chrome extension project before changing it as per the boilerplate.

I have a CSS file and a JS file declared in the "web_accessible_resources" option.
This is how I inject it inside of a content script:

// js/content-script.js

//js
const s = document.createElement('script');
s.src = chrome.runtime.getURL('js/script.js');
document.body.appendChild(s);
s.onload = function() {
    this.remove();
};

//css
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = chrome.runtime.getURL('css/style.css');
(document.head || document.documentElement).appendChild(style);

Is this possible with the current state the project is in? I don't know Webpack at all, so maybe all I have to do is transfer the files I need to the /build folder somehow?

EDIT: Previously used File Manager Webpack plugin but this can be easily done with the currently used Copy Webpack Plugin.

new CopyWebpackPlugin([{
        from: 'src/manifest.json',
        transform: function (content, path) {
            // generates the manifest file using the package.json informations
            return Buffer.from(JSON.stringify({
                description: process.env.npm_package_description,
                version: process.env.npm_package_version,
                ...JSON.parse(content.toString())
            }));
        }
    },
    {
        from: path.join(__dirname, 'src', 'injected', 'script.js'),
    }
]),

However it would be great if this was automatically generated from the "web_accessible_resources" option in manifest.json. Additionally, I don't know how you'd apply Babel etc. to these files? Or something like SASS.

Moreover, hot reload on these files doesn't work.

For Firefox and Edge also?

Hi, firstly thank you for sharing this wonderful boilerplate.

I would just like to ask, whether it can be readily converted to be used in firefox and edge?

Vue dev tools

Did you find a way to activate the vue dev tools ?
I tried to force Vue.config.devtools = true; it with but no success so far.

webpack config error

The below piece of code with is in webpack.config.js gives me the error shown at the end

    new CopyWebpackPlugin([{
      from: "src/manifest.json",
      transform: function (content, path) {
        // generates the manifest file using the package.json informations
        return Buffer.from(JSON.stringify({
          description: process.env.npm_package_description,
          version: process.env.npm_package_version,
          ...JSON.parse(content.toString())
        }))
      }
    }]),

Not sure if i am missing some fundamental here but the spread operator doesnt have any property name in the last lines of the code, that is:
...JSON.parse(content.toString())

when i do npm start, the error shown in the image is what i get.

[email protected] start /Users/practice/WebstormProjects/chrome-extension-webpack-boilerplate
node utils/webserver.js

/Users/practice/WebstormProjects/chrome-extension-webpack-boilerplate/webpack.config.js:67
...JSON.parse(content.toString())
^^^

SyntaxError: Unexpected token ...
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:373:25)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object. (/Users/practice/WebstormProjects/chrome-extension-webpack-boilerplate/utils/webserver.js:3:14)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: node utils/webserver.js
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

How to use multiple content scripts?

How do I tell it to bundle all my different content scripts into myContentScript.bundle.js?

"content_scripts": [
    {
      "matches": ["https://www.google.com/*"],
      "js": ["myContentScript.bundle.js"]
    }
  ]

i.e, put multiple scripts into this entry point in webback config

  entry: {
    myContentScript: "./src/js/myContentScript.js"
  }

Console.log isn't being printed in background file

I'm having trouble loading the background.js file. I feel like it's not loading the file at all, so I tried adding a console log but that isn't working either. Am I using background.js incorrectly?

src/js/background.js

chrome.tabs.onUpdated.addListener((_tabId, changeInfo, tab) => {
  const { status } = changeInfo;
  const { url, title } = tab;

  if (status === "complete" && url && url.startsWith("http")) {
    initStore();
    store.update(STORE_KEY, history => {
      history.sites = [...history.sites, siteGenerator(url, title)];
    });
    console.log("HISTORY: ", store.get(STORE_KEY));
  }
  console.log("ZZzHISTORY: ", store.get(STORE_KEY));
});

console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX");


src/manifest.json

{
  "name": "Prototype",
  "options_page": "options.html",
  "background": {
    "scripts": ["background.bundle.js"],
    "persistent": true
  },
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": ["contentscript.bundle.js"]
    }
  ],
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": "icon-34.png"
  },
  "icons": {
    "128": "icon-128.png"
  },
  "manifest_version": 2,
  "content_security_policy":
    "script-src 'self' 'unsafe-eval'; object-src 'self'"
}

webpack.config.js

var options = {
  entry: {
    popup: path.join(__dirname, "src", "js", "popup.js"),
    options: path.join(__dirname, "src", "js", "options.js"),
    background: path.join(__dirname, "src", "js", "background.js"),
    contentscript: "./src/js/contentscript.js"
  },
  chromeExtensionBoilerplate: {
    notHotReload: ["contentscript", "background"]
  },

Debugging a Chrome Extension in development mode

I followed the instructions to start the development server in debug mode:
NODE_ENV=development npm run start

However, when I look at the source code in Chrome DevTools, everything seems to be bundled and optimized, and I don't even find some bundles:
screen shot 2019-02-20 at 00 14 01

From the image you can tell that the code is optimized, and that some bundles (like popup.bundle.js and options.bundle.js) don't even show up.

Any idea if there's a way around this to allow dynamic debugging?

Thanks :)

Serious merging issues

This boilerplate works fine, but recently I have found out that the webpack configuration is somewhat incorrect and because of that, my background script is also reflected in my popup.js code.

(In my case, I am making an api call in background, but it is triggering the api call again when I open the popup)

I've rechecked my code in all possible ways and after that only I came to conclusion that the webpack configuration is merging all the js files.

Is there any option for NOT COMBINING the scripts at compile time?

Decouple webpack config for development and production build

This should add some space for production build optimizations (uglify, code minimization, dead code elimination) and not to optimize development build.

Connected with #39 that will bundle css in single file, should be used for production build. Next we could add css tree shaking to remove unused css from bundle and minify it.

Webpack 2 issues

I wanted to report these but have not had time to investigate reasons/fixes.

  1. Webpack 2 throws an error when attempting to use the chromeExtensionBoilerplate -> notHotReload option. I upgrade to webpack 2.3.0 to see if that fixed it but instead I received a more helpful error stating that chromeExtensionBoilerplate is not a valid config option, therefore the build/server process comes to a halt in utils/webserver.js. As I mentioned, I haven't looked into this at all on the webpack side of things. What I did in the meantime was to just define my content script file names in the utils/webserver.js file directly.

  2. Another issue I noticed was that babel was transpiling all of the node_modules and therefore upon start up it would take a very long time (30-60 seconds) to start the dev server. My fix for this was to add the exclude property to the babel loader in the webpack config.
    { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },

If you'd like, I could option a PR for that fix.

Thanks for your work on this repo 👍

webpack config broken

Hi, this line "...JSON.parse(content.toString())" in webpack.config that was recently added, breaks the build.

How to access the chrome object?

How would I access the chrome object and run functions like this?

chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.executeScript({
    code: 'document.body.style.backgroundColor="red"'
  });
});

Using with Vue.js?

I saw that there are some issues which have attempted to use Vue.js. Is it documented or blogged somewhere how can we use Vue in this boilerplate?
If you can give me some hints, I would love to create a vue branch for others to use. :)

How to properly include dependencies?

I hope this is not a stupid question, but I feel this aspect is missing in the documentation.

I have created an browser extension, which I now would like to port to a proper build process with webpack and dependency management. I think your project is a great template which could be used for this purpose.

The browser action is opening a full page "popup" page, its including a few libraries and css (see example below).
Now I already included the thirdparty libraries in the yarn packages.json.

But I do not know, how is the proper method to specify these deps in the webpack config for this template project? Could someone maybe give an example of an elegant way?

  <link rel="stylesheet" type="text/css" href="thirdparty/jquery-ui.min.css"/>
  <link rel="stylesheet" type="text/css" href="thirdparty/dataTables.jqueryui.min.css"/>
  <link rel="stylesheet" type="text/css" href="css/myproject.css"/>
  <script src="thirdparty/browser-polyfill.min.js"></script>
  <script src="thirdparty/jquery-3.4.1.min.js"></script>
  <script src="thirdparty/datatables.min.js"></script>
  <script src="thirdparty/dataTables.responsive.min.js"></script>
  <script src="thirdparty/dataTables.jqueryui.min.js"></script>
  <script src="thirdparty/dataTables.ellipsis.js"></script>
  <script src="thirdparty/moment-with-locales.min.js"></script>
  <script src="js/myproject.js"></script>

How to use sass

I'm using sass in my plugin. Not sure how I should make it works. (I'm 100% new to webpack)

Enabling HMR for content scripts

I have a change to enable Hot Module Reloading for Chrome extension content scripts:

https://github.com/arseneyr/chrome-extension-webpack-boilerplate/tree/content-hmr

This change includes a new webpack plugin that does the following:

  • Modifies the HMR runtime in content scripts to use fetch() and eval() instead of JSONP for downloading hot update chunks. JSONP does not work for content scripts because adding a script tag to the DOM executes that script in the context of the host site, not the content script.
  • Modifies the webpack/hot/dev-server script to send a message to the background script in case a full reload is needed. The background bundle includes a small script to listen for the message and call chrome.runtime.reload() to reload the entire extension.

There are also some changes to the webpack-dev-server parameters so that the websocket client connects inside content scripts:

  • Use the native WS client instead of sock-js because sock-js has trouble connecting when the host site is HTTPS.
  • Use 127.0.0.1 as the websocket target domain in order to prevent the WS client from using a wss:// protocol.
  • Set the webpack public path to http://127.0.0.1:3000 so any requests for HMR manifests, hot chunks, etc. go to the webpack-dev-server instead of the host site.
  • Modify the manifest to include http://127.0.0.1:3000 in the content_security_policy.

I'm happy to open a PR if you think this change is valuable.

How to access background script public function

In my popup.js i used to do: bg = chrome.extension.getBackgroundPage() to get access to the background page. Then I could do bg.xxx() ...

However, with this setup, i am getting:

Uncaught (in promise) TypeError: bg.xxx is not a function

Any clue?

Extension Reload?

Hi isnt the point of hot module reload to not just re complile on changes but to also reload the extension, without the need to reload it on the extension page?

How do I get this working, or do I need to install something like browser-sync?

Can't update component state while running the dev server

Trying to call React.Component.setState() while running the dev servers results in the following error

(unknown) Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component.

when the component is definitely mounted. The same code works fine when doing a production build.

How to use background script?

In manifest.json I created

"background":
 {
    "scripts": ["background.bundle.js"],
    "persistent": true
  },

And in webpack.config.js:

entry: {
    popup: path.join(__dirname, "src", "js", "popup.js"),
    content: path.join(__dirname, "src", "js", "content.js"),
    background: path.join(__dirname, "src", "js", "content.js")
  },
  output: {
    path: path.join(__dirname, "build"),
    filename: "[name].bundle.js"
  },

After that I started project, background script added to extension, I can open generated background.html, but code in background.js don't execute. thank you

Meaningless escape

In webpack.config.js line 39
test: new RegExp('\.(' + fileExtensions.join('|') + ')$'),

setState can only update a mounted or mounting component warning

Hi,

I'm trying to use this boilerplate with react (react branch) but when I try to set a state (this.setState) I keep getting this warning in the console:

warning.js?8a56:33 Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the ReactComponent component.

The component isn't unmounted, even with the easiest state change I get the same warning. Maybe this has something to do with the fact that it's a chrome extension?

I hope someone can help me out here.
Thanks in advance,

Giel.

Looking for contributors

Hi people, there's a while that I don't do any kind of Chrome Extension and I am very busy in the last months because my other projects, but I see that you people are still interested in updates and some tweaks, so if you want to help me to keep this project, please send an email to [email protected] that I will talk with you.

Obs.: of course, it will be a volunteer job.

Thank you for your attention.

Autoreload JS file alone

First of all, your boilerplate looks really good! 👍

However, in a specific case I am required to load some JavaScript even before the page is rendered. Thus, making background.html unsuitable.

  "content_scripts": [{
    "js": ["page-load.bundle.js"],
    "matches": ["<all_urls>"],
    "run_at": "document_start",
    "all_frames": true
  }],

According to the chrome extension documentation I can use nothing but JS and CSS files

How would you do it in this case?

Webpack compilation was simply about adding a new entry "page-load": path.join(__dirname, "src", "js", "page-load.js") but I don't see how to make the extension get this latest version of the .js file without having to reload the extension (or if there are better ways to do this).

Logs and errors location

Currently when I console.log something in chrome dev tools the browser locates the execute instruction line number in the whole bundle. (eg. background.bundle.js:11383). I would like to do something to make it locate the executed instruction in the original file that was copied in the bundle. (eg. logger.js:16)
Same problem with error line location.

What needs to be changed to make this happen?

Reloading manifest.json file

Can you please confirm that you have to manually restart the extension from chrome://extensions/ when you update the manifest.json file?

Relative Path in popup.js not found?

I'm trying to use this (awesome) project with Vue JS. I'm trying

import RootComponent from "root.vue"; in popup.js, where root.vue is in src/js/ as well.

Also tried is as ... from "./root" and ... from "/root" but no dice. Error after running npm run start is always "Module not found: Error: Cannot resolve module..."

Do you have any idea why this would be happening?

Content Script not working on https websites when using dev server

When using dev server from npm start, it can't load content scripts on websites running https because WDS tries to get updates from https://localhost:3000/sockjs-node/info?t=... but the request fails as the dev server is running on http. I tried running the dev server on https but then its giving some certificate error.

My manifest:

{
  "manifest_version": 2,
  "name": "Name",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "browser_action": {
    "default_icon": "icon19.png",
    "default_title": "description"
  },
  "permissions": [
    "contextMenus",
    "tabs",
	"activeTab",
"*://github.com/*?tab=repositories",
    "*://github.com/*"
  ],
  "background": {
    "scripts": ["background.bundle.js"]
  },
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
  "content_scripts": [
	{
		"matches": ["*://github.com/*"],
		"js": ["content.bundle.js"]
	}
  ]
}

Works perfectly for a website running http.

How would this work for content scripts?

Thank you for boilerplate! I'm currently working on a Chrome extension that needs content scripts. So far I have been able to get it working by proxying to iFrame. That's not ideal. How would you go about making content scripts work?

Relaxed content security policy

A line in the manifest file may pose security issue due to relaxed content security policy.

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"

However, we strongly recommend against doing this. These functions are notorious XSS attack vectors.

CSP discussed in Chrome Developer Documentation

I understand it is done to allow the eval script to be run in webpack dev server. But this should be removed in the production build. One way to do it is adding some reduction here:

transform: function (content, path) {

images

If I add images in the img folder, webpack adds only few of them in the build folder when I run either npm start or NODE_ENV=production npm run build.

In the console I get:

[12:25:20] [write-file-webpack-plugin] stats.compilation.errors.length is "0".
[12:25:20] [write-file-webpack-plugin] asset: ./img.png; destination: ./build/img.png [written] (4 KB)
[12:25:20] [write-file-webpack-plugin] asset: ./icon-128.png; destination: ./build/icon-128.png [written] (10.04 KB)
[12:25:20] [write-file-webpack-plugin] asset: ./icon-34.png; destination: ./build/icon-34.png [written] (2.07 KB)

Other images have some extension png. I do not understand the reason.
Any idea?

Thank you

manifest.json: Set filenames based on hash from file-loader

Thanks for a good boilerplate.

I used file-loader to load my images into dist. They all get a random hash, based on contents (from caching purposes).

Is there any way to transform manifest.json, so its icons refer to the image files with the hash applied by file-loader?

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.