Code Monkey home page Code Monkey logo

extension-boilerplate's Introduction

Extension Boilerplate

A foundation for creating browser extensions for Chrome, Opera & Firefox.

Now that Firefox supports WebExtensions, it has become a lot easier to build browser extensions/addons for multiple browsers without duplicating the codebase. This project serves as a sensible starting point to help you get started.

I have extracted this from the browser extensions that I built for my side-project, Email This.

Side note: Do check out Email This. It is a simpler alternative to bookmarking tools like Pocket, Readability & Instapaper. Email This will remove ads & distractions from an article and send you a nice email with just the text/images. No need to install any additional applications or login to another app just to access your bookmarks. The Chrome Extensions is available on the Chrome Web Store.

Features

Write once and deploy to Chrome, Opera & Firefox
Based on WebExtensions. It also includes a tiny polyfill to bring uniformity to the APIs exposed by different browsers.
Live-reload
Your changes to CSS, HTML & JS files will be relayed instantly without having to manually reload the extension. This ends up saving a lot of time and improving the developer experience.
Sensible starting point
This comes with a gulp based workflow that converts modern ES6 JavaScript and SCSS to JS/CSS. Scripts are transpiled using Babel and bundled using Browserify. Sourcemaps are available for both JS and SCSS.
Sketch (.sketch) assets for icons and promo images
A .sketch file is included in the resources directory. This has all the icons and promo images that will be needed while uploading the extensions to the app stores.
Easily configurable and extendable
The gulpfile is easily understandable and configurable. If you want to add additional tasks or remove un-used ones, you can easily tweak that file to suit your needs.
Platform specific & Environment specific variables.
You might need to specify different data variables based on your environment. For example, you might want to use a localhost API endpoint during development and a production API endpoint once the extension is submitted to the appstore. You can specify such data in the json files inside `config` directory.
You can also set custom data variables based on the platform (different variable for Chrome, FF, Opera).

Installation

  1. Clone the repository git clone https://github.com/EmailThis/extension-boilerplate.git
  2. Run npm install
  3. Run npm run build

Alternately, if you want to try out the sample extension, here are the download links. After you download it, unzip the file and load it in your browser using the steps mentioned below.

Load the extension in Chrome & Opera
  1. Open Chrome/Opera browser and navigate to chrome://extensions
  2. Select "Developer Mode" and then click "Load unpacked extension..."
  3. From the file browser, choose to extension-boilerplate/build/chrome or (extension-boilerplate/build/opera)
Load the extension in Firefox
  1. Open Firefox browser and navigate to about:debugging
  2. Click "Load Temporary Add-on" and from the file browser, choose extension-boilerplate/build/firefox

Developing

The following tasks can be used when you want to start developing the extension and want to enable live reload -

  • npm run chrome-watch
  • npm run opera-watch
  • npm run firefox-watch

Packaging

Run npm run dist to create a zipped, production-ready extension for each browser. You can then upload that to the appstore.

TODO

  • Add support for Safari
  • Add Firefox & Opera Promo images
  • Add sample screenshot templates
  • Write a guide for using config variables & JS preprocessor

This project is licensed under the MIT license.

If you have any questions or comments, please create a new issue. I'd be happy to hear your thoughts.

Bharani, Email This

extension-boilerplate's People

Contributors

benhodgson87 avatar bharani91 avatar cbioley avatar londeren avatar yfdyh000 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  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

extension-boilerplate's Issues

ext.contextMenus is null.

I have loaded the boilerplate on chrome and start watching it.
npm run chrome-watch

When I am trying to use contextMenus I am getting a null error

background.js
ext.runtime.onInstalled.addListener(() => {
var id = ext.contextMenus.create({"title": "test menu", "contexts": ["selection"],
"onclick": genericOnClick});
});

This gives me an error 'Cannot read property 'create' of null'
I am tracing the ext object and I see a bunch of null values , alarms, bookmarks etc.

How to use config variables and JS preprocessor

Not an issue, as much as it a tutorial for those wanting/needing to use config variables.

In my development.json I defined a variable "apiUrl": "https://api.test.com" In production it's "https://api.prod.com"

How do you use it?
In my popup.js file:

var api = '/* @echo api */';

I stumbled upon the solution through trial and error and reading the documentation at : https://www.npmjs.com/package/preprocess

That does var text = 'Hi, I am <!-- @echo USERNAME -->'; as an example. The syntax probably would have worked too, but I chose JS style block comment and it worked.

Unexpected token keyword «function», expected punc «,»

I am getting Unexpected token keyword «function», expected punc «,» when i run npm run build

I am using vueify.

This is gulpfile.babel.js

import fs from "fs";
import gulp from "gulp";
import { merge } from "event-stream";
import browserify from "browserify";
import source from "vinyl-source-stream";
import buffer from "vinyl-buffer";
import preprocessify from "preprocessify";
import gulpif from "gulp-if";

const vueify = require("vueify");

const $ = require("gulp-load-plugins")();

const uglify = require("gulp-uglify");
const gulpUtil = require("gulp-util");
const ignore = require("gulp-ignore");

var production = process.env.NODE_ENV === "production";
var target = process.env.TARGET || "chrome";
var environment = process.env.NODE_ENV || "development";

var generic = JSON.parse(fs.readFileSync(`./config/${environment}.json`));
var specific = JSON.parse(fs.readFileSync(`./config/${target}.json`));
var context = Object.assign({}, generic, specific);

var manifest = {
  dev: {
    background: {
      scripts: ["scripts/livereload.js", "scripts/background.js"]
    }
  },

  firefox: {
    applications: {
      gecko: {
        id: "[email protected]"
      }
    }
  }
};

// Tasks

gulp.task("clean", () => {
  return pipe(`./build/${target}`, $.clean());
});

gulp.task("build", cb => {
  $.runSequence("clean", "styles", "css", "fonts", "ext", cb);
});

gulp.task("watch", ["build"], () => {
  $.livereload.listen();

  gulp
    .watch(["./src/**/*", "./src/components/*", "./manifest.json"])
    .on("change", () => {
      $.runSequence("build", $.livereload.reload);
    });
});

gulp.task("default", ["build"]);

gulp.task("ext", ["manifest", "js"], () => {
  return mergeAll(target);
});

// -----------------
// COMMON
// -----------------
gulp.task("js", () => {
  return buildJS(target);
});

gulp.task("styles", () => {
  return gulp
    .src("src/styles/**/*.scss")
    .pipe($.plumber())
    .pipe(
      $.sass
        .sync({
          outputStyle: "expanded",
          precision: 10,
          includePaths: ["."]
        })
        .on("error", $.sass.logError)
    )
    .pipe(gulp.dest(`build/${target}/styles`));
});

gulp.task("css", () => {
  return gulp
    .src("src/styles/**/*.css")
    .pipe($.plumber())
    .pipe(gulp.dest(`build/${target}/styles`));
});

gulp.task("fonts", () => {
  return gulp
    .src("src/webfonts/**/*")
    .pipe($.plumber())
    .pipe(gulp.dest(`build/${target}/webfonts`));
});

gulp.task("manifest", () => {
  return gulp
    .src("./manifest.json")
    .pipe(
      gulpif(
        !production,
        $.mergeJson({
          fileName: "manifest.json",
          jsonSpace: " ".repeat(4),
          endObj: manifest.dev
        })
      )
    )
    .pipe(
      gulpif(
        target === "firefox",
        $.mergeJson({
          fileName: "manifest.json",
          jsonSpace: " ".repeat(4),
          endObj: manifest.firefox
        })
      )
    )
    .pipe(gulp.dest(`./build/${target}`));
});

// -----------------
// DIST
// -----------------
gulp.task("dist", cb => {
  $.runSequence("build", "zip", cb);
});

gulp.task("zip", () => {
  return pipe(`./build/${target}/**/*`, $.zip(`${target}.zip`), "./dist");
});

// Helpers
function pipe(src, ...transforms) {
  return transforms.reduce((stream, transform) => {
    const isDest = typeof transform === "string";
    return stream.pipe(isDest ? gulp.dest(transform) : transform);
  }, gulp.src(src));
}

function mergeAll(dest) {
  return merge(
    pipe("./src/icons/**/*", `./build/${dest}/icons`),
    pipe(["./src/_locales/**/*"], `./build/${dest}/_locales`),
    pipe([`./src/images/${target}/**/*`], `./build/${dest}/images`),
    pipe(["./src/images/shared/**/*"], `./build/${dest}/images`),
    pipe(["./src/**/*.html"], `./build/${dest}`)
  );
}

function buildJS(target) {
  const files = [
    "background.js",
    "contentscript.js",
    "options.js",
    "popup.js",
    "livereload.js",
    "app.js"
  ]; // "materialize.js",

  let tasks = files.map(file => {
    const dest = `build/${target}/scripts`;

    return browserify({ entries: "src/scripts/" + file, debug: true })
      .transform(vueify)
      .transform("babelify", {
        presets: [
          "es2015",
          [
            "env",
            {
              targets: {
                uglify: true
              }
            }
          ]
        ],
        plugins: [
          "transform-remove-console",
          "transform-es2015-shorthand-properties",
          "transform-class-properties",
          [
            "transform-runtime",
            {
              polyfill: false,
              regenerator: true
            }
          ]
        ]
      })
      .transform(preprocessify, {
        includeExtensions: [".js"],
        context: context
      })
      .bundle()
      .pipe(source(file))
      .pipe(buffer())
      .pipe(gulpif(!production, $.sourcemaps.init({ loadMaps: true })))
      .pipe(gulpif(!production, $.sourcemaps.write("./")))
      .pipe(ignore.exclude(["**/*.map"]))
      .pipe(
        gulpif(
          production,
          $.uglify({
            mangle: false,
            output: {
              ascii_only: true
            }
          })
        )
      )
      .pipe(gulp.dest(dest));
  });

  return merge.apply(null, tasks);
}

process.on("uncaughtException", function(err) {
  console.log(err);
});

Could not upload Firefox extension: no manifest found

I've build plugin for Firefox, zipped and uploaded to the addons.mozilla.org, the error is:

Your add-on failed validation with 2 errors.
No install.rdf or manifest.json found
Add-on missing manifest.

So why is that? manifest.json exists in the archive.

apply npm audit fix --force

Hi, i use node v10.6.0 and npm v6.4.1 when run install when finish say me run $npm audit fix --force
have 9 packages vuln, when finish command, and i run build have error, or run chrome-watch
the error say
when run chrome-watch
Failed at the [email protected] chrome-watch script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above

and run build say
verbose Darwin 17.7.0
17 verbose argv "/Users/user1/.nvm/versions/node/v10.6.0/bin/node" "/Users/user1/.nvm/versions/node/v10.6.0/bin/npm" "run" "build"
18 verbose node v10.6.0
19 verbose npm v6.4.1
20 error code ELIFECYCLE
21 error errno 1
22 error [email protected] build: cross-env NODE_ENV=production npm run chrome-build && cross-env NODE_ENV=production npm run opera-build && cross-env NODE_ENV=production npm run firefox-build
22 error Exit status 1
23 error Failed at the [email protected] build script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

Cheers

npm scripts on Windows

Hi,
I recommend to use cross-env npm package for a compatibility on all operating systems.

Supporting chrome.topSites api

I wanted to use the chrome.topSites api but it was not already in the scripts/utils/ext.js. Just wanted to leave a note here that it "topSites" api is not in the array by default.

Support for edge

Hey there,

I really like your idea and think about using the boilerplate for one of my projects. What is about supporting edge browser. It is based on web extensions as well. Just a few little changes compared to chrome.

Thanks
Jan

ext.webRequest is NULL

I don't believe the API's are loading properly. Trying to work in chrome and ext.webRequest is NULL.

pcss vs css and tailwind in library

What is the difference between pcss and CSS, in the way it is used in the code?
And you have tailwindcss, and I don't see it in the code. Can you give an example of using tailwind with content script, and shadow house + CSS modules, for components?

浏览器调试

如何在浏览器中调试,有类似 npm run dev,开启node服务调试页面的功能吗

Adding Eslint support

Eslint will help in keeping a fix pattern of code extension wide, it can be added to git-hooks too before committing any changes

Live-reloading doesn't work for extension that overrides the New Tab

I am making an extension that overrides the standard "New Tab" page. It seems like the live-reloading doesn't work on Chrome latest (v59.0).

When I run: npm run chrome-watch the page refreshes but shows an error in the console:
screen shot 2017-06-20 at 13 25 04

Then, page crashes and falls back to the original "New Tab" page. I need to close the current tab and open a new one. Then - the extension works (plus my changes are present).

It doesn't work on Firefox (v54.0) either. When I run: npm run firefox-watch and I do a change - the tab with the extension closes and I need to manually open it again.

Updating to New (Future) Releases

Hello @bharani91 ,

I was wondering what are the guidelines (the best practice approaches) to adopt (use) the boilerplate, in order to get future changes/updates/releases? Obviously, I can just clone the repo and start doing changes on top, but that doesn't sound to me very maintainable... Or is it? 👷

I'll appreciate if you please share your thoughts.

PS: Thanks for sharing your awesome work! ⭐️ I appreciate it a lot! 🤘

Use mozilla polyfill to enable promise-based API interop with Chrome

Having some trouble getting this into the build process for extension-boilerplate: https://github.com/mozilla/webextension-polyfill.

Tried manually adding the built polyfill file to extension-boilerplate src and referencing in the manifest.json, but its not making its way into the build/ when building.

Would be a great feature to have. Am currently dealing with a nightmare trying to use the cookies.get() API and support callbacks in chrome and promises in firefox.

How to use storage local?

Hi, i use storage.set but i need use local
i put this ext.storage.set not working
i put ext.storage.local.set not working

What is the best way used storage.local in this extension boilerplate??

thank any help!!

How to target specific browser

Hi there, nice project,

how to go about targeting single browser for apis not yet supported (like chrome's declarativeContent)?

also using yarn instead of npm provides better performance and reliability of deps

Thanks

V8 Utf8Value - npm install breaking

Node 12.3.1

error: no matching function for call to ‘v8::String::Utf8Value::Utf8Value(v8::Local<v8::Value>&)’
   17 |   v8::String::Utf8Value string(value);
In file included from ../src/create_string.cpp:1:
../../nan/nan.h: In function ‘void Nan::AsyncQueueWorker(Nan::AsyncWorker*)’:
../../nan/nan.h:2298:62: warning: cast between incompatible function types from ‘void (*)(uv_work_t*)’ {aka ‘void (*)(uv_work_s*)’} to ‘uv_after_work_cb’ {aka ‘void (*)(uv_work_s*, int)’} [-Wcast-function-type]
 2298 |     , reinterpret_cast<uv_after_work_cb>(AsyncExecuteComplete)
      |                                                              ^
../src/create_string.cpp: In function ‘char* create_string(Nan::MaybeLocal<v8::Value>)’:
../src/create_string.cpp:17:37: error: no matching function for call to ‘v8::String::Utf8Value::Utf8Value(v8::Local<v8::Value>&)’
   17 |   v8::String::Utf8Value string(value);
      |                                     ^
In file included from /home/jdunlap/.node-gyp/12.3.1/include/node/node.h:63,
                 from ../../nan/nan.h:54,
                 from ../src/create_string.cpp:1:
/home/jdunlap/.node-gyp/12.3.1/include/node/v8.h:2995:5: note: candidate: ‘v8::String::Utf8Value::Utf8Value(v8::Isolate*, v8::Local<v8::Value>)’
 2995 |     Utf8Value(Isolate* isolate, Local<v8::Value> obj);
      |     ^~~~~~~~~
/home/jdunlap/.node-gyp/12.3.1/include/node/v8.h:2995:5: note:   candidate expects 2 arguments, 1 provided
/home/jdunlap/.node-gyp/12.3.1/include/node/v8.h: In instantiation of ‘void v8::PersistentBase<T>::SetWeak(P*, typename v8::WeakCallbackInfo<P>::Callback, v8::WeakCallbackType) [with P = node::ObjectWrap; T = v8::Object; typename v8::WeakCallbackInfo<P>::Callback = void (*)(const v8::WeakCallbackInfo<node::ObjectWrap>&)]’:
/home/jdunlap/.node-gyp/12.3.1/include/node/node_object_wrap.h:84:78:   required from here
/home/jdunlap/.node-gyp/12.3.1/include/node/v8.h:9817:16: warning: cast between incompatible function types from ‘v8::WeakCallbackInfo<node::ObjectWrap>::Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo<node::ObjectWrap>&)’} to ‘Callback’ {aka ‘void (*)(const v8::WeakCallbackInfo<void>&)’} [-Wcast-function-type]
 9817 |                reinterpret_cast<Callback>(callback), type)

Error when making npm install

Error when making npm install

I solved this by changing in package.json:
"gulp-sass": "^2.3.2"
to
"gulp-sass": "3.0.0",

codecombat/codecombat#4430 (comment)

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: `node scripts/build.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\yrrod_000\AppData\Roaming\npm-cache\_logs\2019-10-18T09_03_25_525Z-debug.log

Fonts task in gulp

I see there's no gulp task for moving fonts in the dist/build folder.
I can easily add one and send a pr if someone's interested?

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.