feat: library wrapped as an UMD module

This addresses [issue 7] by making it possible for users to run:

```sh
npm install webextension-polyfill
```

and download a module that they can use with a bundler as follows:

```js
import browser from 'webextension-polyfill';
```

Also, add a [prepublish script] so that users who clone the repo don't
need to run `grunt` manually. In addition, specify [files] in
package.json so that this module can be published to npm without
including miscellanea. This can be verified by running:

```sh
npm pack && tar -tvf webextension-polyfill-0.1.0.tgz
```

[issue 7]: https://github.com/mozilla/webextension-polyfill/issues/7
[files]: https://docs.npmjs.com/files/package.json#files
[prepublish script]: https://docs.npmjs.com/misc/scripts
This commit is contained in:
Joseph Frazier 2017-01-25 16:38:32 -05:00 committed by Luca Greco
parent 52791c8633
commit f9248e62e7
7 changed files with 113 additions and 34 deletions

View File

@ -28,7 +28,7 @@ module.exports = function(grunt) {
options: { options: {
patterns: [ patterns: [
{ {
match: /\{\/\* include\("(.*?)"\) \*\/\}/, match: /require\("..\/(.*?)"\)/,
replacement: (match, filename) => { replacement: (match, filename) => {
return grunt.file.read(filename) return grunt.file.read(filename)
.replace(/\n$/, "") .replace(/\n$/, "")
@ -55,6 +55,15 @@ module.exports = function(grunt) {
}, },
}, },
umd: {
all: {
src: "dist/browser-polyfill.js",
template: "unit",
globalAlias: "browser",
amdModuleId: "webextension-polyfill",
},
},
"closure-compiler": { "closure-compiler": {
dist: { dist: {
files: { files: {
@ -88,8 +97,9 @@ module.exports = function(grunt) {
grunt.loadNpmTasks("gruntify-eslint"); grunt.loadNpmTasks("gruntify-eslint");
grunt.loadNpmTasks("grunt-replace"); grunt.loadNpmTasks("grunt-replace");
grunt.loadNpmTasks("grunt-umd");
grunt.loadNpmTasks("grunt-coveralls"); grunt.loadNpmTasks("grunt-coveralls");
require("google-closure-compiler").grunt(grunt); require("google-closure-compiler").grunt(grunt);
grunt.registerTask("default", ["eslint", "replace", "closure-compiler"]); grunt.registerTask("default", ["eslint", "replace", "umd", "closure-compiler"]);
}; };

View File

@ -14,7 +14,6 @@ simply run:
```sh ```sh
npm install npm install
grunt
``` ```
This will build both non-minified and minified versions of the final library, This will build both non-minified and minified versions of the final library,

View File

@ -2,6 +2,12 @@
"name": "webextension-polyfill", "name": "webextension-polyfill",
"version": "0.1.0", "version": "0.1.0",
"description": "A lightweight polyfill library for Promise-based WebExtension APIs in Chrome.", "description": "A lightweight polyfill library for Promise-based WebExtension APIs in Chrome.",
"main": "src/browser-polyfill.js",
"files": [
"api-metadata.json",
"src",
"dist"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/mozilla/webextension-polyfill.git" "url": "git+https://github.com/mozilla/webextension-polyfill.git"
@ -19,6 +25,7 @@
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-coveralls": "^1.0.1", "grunt-coveralls": "^1.0.1",
"grunt-replace": "*", "grunt-replace": "*",
"grunt-umd": "^2.4.0",
"gruntify-eslint": "*", "gruntify-eslint": "*",
"istanbul-lib-instrument": "^1.1.3", "istanbul-lib-instrument": "^1.1.3",
"jsdom": "^9.6.0", "jsdom": "^9.6.0",
@ -38,6 +45,7 @@
"build": "grunt", "build": "grunt",
"test": "mocha", "test": "mocha",
"test-coverage": "COVERAGE=y nyc mocha", "test-coverage": "COVERAGE=y nyc mocha",
"publish-coverage": "grunt coveralls" "publish-coverage": "grunt coveralls",
"prepublish": "npm run build"
} }
} }

View File

@ -13,7 +13,9 @@ if (typeof browser === "undefined") {
// never actually need to be called, this allows the polyfill to be included // never actually need to be called, this allows the polyfill to be included
// in Firefox nearly for free. // in Firefox nearly for free.
const wrapAPIs = () => { const wrapAPIs = () => {
const apiMetadata = {/* include("api-metadata.json") */}; // Note that `require` does NOT work in general. See discussion here:
// https://github.com/mozilla/webextension-polyfill/pull/17#discussion_r99170958
const apiMetadata = require("../api-metadata.json"); // eslint-disable-line no-undef
/** /**
* A WeakMap subclass which creates and stores a value for any key which does * A WeakMap subclass which creates and stores a value for any key which does
@ -336,5 +338,9 @@ if (typeof browser === "undefined") {
return wrapObject(chrome, staticWrappers, apiMetadata); return wrapObject(chrome, staticWrappers, apiMetadata);
}; };
this.browser = wrapAPIs(); // The build process adds a UMD wrapper around this file, which makes the
// `module` variable available.
module.exports = wrapAPIs(); // eslint-disable-line no-undef
} else {
module.exports = browser; // eslint-disable-line no-undef
} }

32
test/helpers.js Normal file
View File

@ -0,0 +1,32 @@
"use strict";
const {deepEqual, equal, ok} = require("chai").assert;
module.exports.testCustomProperties = window => {
Object.defineProperty(window.browser, "myns", {
enumerable: true,
configurable: true,
value: {mykey: true},
});
ok("myns" in window.browser, "The custom property exists");
ok("mykey" in window.browser.myns,
"The content of the custom property exists");
deepEqual(window.browser.myns, {mykey: true},
"The custom property has the expected content");
delete window.browser.myns;
ok(!("myns" in window.browser),
"The deleted custom defined property has been removed");
};
module.exports.testUndefinedProperties = window => {
equal(window.browser.myns.mykey, true,
"Got the expected result from a wrapped property");
equal(window.browser.myns.nonexistent, undefined,
"Got undefined for non existent property");
equal(window.browser.nonexistent, undefined,
"Got undefined for non existent namespaces");
};

View File

@ -1,9 +1,11 @@
"use strict"; "use strict";
const {deepEqual, equal, ok} = require("chai").assert; const {deepEqual, equal} = require("chai").assert;
const {setupTestDOMWindow} = require("./setup"); const {setupTestDOMWindow} = require("./setup");
const {testCustomProperties, testUndefinedProperties} = require("./helpers");
describe("browser-polyfill", () => { describe("browser-polyfill", () => {
it("wraps the global chrome namespace with a global browser namespace", () => { it("wraps the global chrome namespace with a global browser namespace", () => {
const fakeChrome = {}; const fakeChrome = {};
@ -29,37 +31,12 @@ describe("browser-polyfill", () => {
describe("browser wrapper", () => { describe("browser wrapper", () => {
it("supports custom properties defined using Object.defineProperty", () => { it("supports custom properties defined using Object.defineProperty", () => {
const fakeChrome = {}; const fakeChrome = {};
return setupTestDOMWindow(fakeChrome).then(window => { return setupTestDOMWindow(fakeChrome).then(testCustomProperties);
Object.defineProperty(window.browser, "myns", {
enumerable: true,
configurable: true,
value: {mykey: true},
});
ok("myns" in window.browser, "The custom property exists");
ok("mykey" in window.browser.myns,
"The content of the custom property exists");
deepEqual(window.browser.myns, {mykey: true},
"The custom property has the expected content");
delete window.browser.myns;
ok(!("myns" in window.browser),
"The deleted custom defined property has been removed");
});
}); });
it("returns undefined for property undefined in the target", () => { it("returns undefined for property undefined in the target", () => {
const fakeChrome = {myns: {mykey: true}}; const fakeChrome = {myns: {mykey: true}};
return setupTestDOMWindow(fakeChrome).then(window => { return setupTestDOMWindow(fakeChrome).then(testUndefinedProperties);
equal(window.browser.myns.mykey, true,
"Got the expected result from a wrapped property");
equal(window.browser.myns.nonexistent, undefined,
"Got undefined for non existent property");
equal(window.browser.nonexistent, undefined,
"Got undefined for non existent namespaces");
});
}); });
}); });
}); });

47
test/test-node-export.js Normal file
View File

@ -0,0 +1,47 @@
"use strict";
const {deepEqual, strictEqual, notStrictEqual, throws} = require("chai").assert;
const {testCustomProperties, testUndefinedProperties} = require("./helpers");
describe("node-export", () => {
beforeEach(() => {
delete global.browser;
delete global.chrome;
delete require.cache[require.resolve("../")];
});
it("exports the global browser namespace if it already exists", () => {
global.browser = {key: "value"};
const exported = require("../");
strictEqual(exported, browser);
});
it("exports a wrapper around the global chrome namespace", () => {
global.chrome = {key: "value"};
const exported = require("../");
deepEqual(exported, chrome);
notStrictEqual(exported, chrome);
});
it("throws an error if the global chrome namespace is missing", () => {
throws(() => require("../"), ReferenceError, /chrome is not defined/);
});
describe("browser wrapper", () => {
it("supports custom properties defined using Object.defineProperty", () => {
global.chrome = {};
global.browser = require("../");
testCustomProperties(global);
});
it("returns undefined for property undefined in the target", () => {
global.chrome = {myns: {mykey: true}};
global.browser = require("../");
testUndefinedProperties(global);
});
});
});