diff --git a/Gruntfile.js b/Gruntfile.js index 7f6e503..b5f2582 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,7 +28,7 @@ module.exports = function(grunt) { options: { patterns: [ { - match: /\{\/\* include\("(.*?)"\) \*\/\}/, + match: /require\("..\/(.*?)"\)/, replacement: (match, filename) => { return grunt.file.read(filename) .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": { dist: { files: { @@ -88,8 +97,9 @@ module.exports = function(grunt) { grunt.loadNpmTasks("gruntify-eslint"); grunt.loadNpmTasks("grunt-replace"); + grunt.loadNpmTasks("grunt-umd"); grunt.loadNpmTasks("grunt-coveralls"); require("google-closure-compiler").grunt(grunt); - grunt.registerTask("default", ["eslint", "replace", "closure-compiler"]); + grunt.registerTask("default", ["eslint", "replace", "umd", "closure-compiler"]); }; diff --git a/README.md b/README.md index 1b6d8ac..503a22a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ simply run: ```sh npm install -grunt ``` This will build both non-minified and minified versions of the final library, diff --git a/package.json b/package.json index 4b389b0..521d9ee 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,12 @@ "name": "webextension-polyfill", "version": "0.1.0", "description": "A lightweight polyfill library for Promise-based WebExtension APIs in Chrome.", + "main": "src/browser-polyfill.js", + "files": [ + "api-metadata.json", + "src", + "dist" + ], "repository": { "type": "git", "url": "git+https://github.com/mozilla/webextension-polyfill.git" @@ -19,6 +25,7 @@ "grunt": "^1.0.1", "grunt-coveralls": "^1.0.1", "grunt-replace": "*", + "grunt-umd": "^2.4.0", "gruntify-eslint": "*", "istanbul-lib-instrument": "^1.1.3", "jsdom": "^9.6.0", @@ -38,6 +45,7 @@ "build": "grunt", "test": "mocha", "test-coverage": "COVERAGE=y nyc mocha", - "publish-coverage": "grunt coveralls" + "publish-coverage": "grunt coveralls", + "prepublish": "npm run build" } } diff --git a/src/browser-polyfill.js b/src/browser-polyfill.js index b7041e7..be7f52e 100644 --- a/src/browser-polyfill.js +++ b/src/browser-polyfill.js @@ -13,7 +13,9 @@ if (typeof browser === "undefined") { // never actually need to be called, this allows the polyfill to be included // in Firefox nearly for free. 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 @@ -336,5 +338,9 @@ if (typeof browser === "undefined") { 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 } diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..bf4f958 --- /dev/null +++ b/test/helpers.js @@ -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"); +}; diff --git a/test/test-browser-global.js b/test/test-browser-global.js index cde9bf5..70e796c 100644 --- a/test/test-browser-global.js +++ b/test/test-browser-global.js @@ -1,9 +1,11 @@ "use strict"; -const {deepEqual, equal, ok} = require("chai").assert; +const {deepEqual, equal} = require("chai").assert; const {setupTestDOMWindow} = require("./setup"); +const {testCustomProperties, testUndefinedProperties} = require("./helpers"); + describe("browser-polyfill", () => { it("wraps the global chrome namespace with a global browser namespace", () => { const fakeChrome = {}; @@ -29,37 +31,12 @@ describe("browser-polyfill", () => { describe("browser wrapper", () => { it("supports custom properties defined using Object.defineProperty", () => { const fakeChrome = {}; - return setupTestDOMWindow(fakeChrome).then(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"); - }); + return setupTestDOMWindow(fakeChrome).then(testCustomProperties); }); it("returns undefined for property undefined in the target", () => { const fakeChrome = {myns: {mykey: true}}; - return setupTestDOMWindow(fakeChrome).then(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"); - }); + return setupTestDOMWindow(fakeChrome).then(testUndefinedProperties); }); }); }); diff --git a/test/test-node-export.js b/test/test-node-export.js new file mode 100644 index 0000000..ded9795 --- /dev/null +++ b/test/test-node-export.js @@ -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); + }); + }); +});