diff --git a/.gitignore b/.gitignore index ac77a32ac..1f0198238 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,10 @@ node_modules # Windows *Thumbs.db + +# Ignore subtrees + +# git@github.com:jsantell/jetpack-id.git +lib/jetpack-id/* +!lib/jetpack-id/index.js +!lib/jetpack-id/package.json diff --git a/lib/jetpack-id/index.js b/lib/jetpack-id/index.js new file mode 100644 index 000000000..bee5419ae --- /dev/null +++ b/lib/jetpack-id/index.js @@ -0,0 +1,49 @@ +/** + * Takes parsed `package.json` manifest and returns + * valid add-on id for it. + */ +function getID(manifest) { + manifest = manifest || {}; + + if (manifest.id) { + + if (typeof manifest.id !== "string") { + return null; + } + + // If manifest.id is already valid (as domain or GUID), use it + if (isValidAOMName(manifest.id)) { + return manifest.id; + } + // Otherwise, this ID is invalid so return `null` + return null; + } + + // If no `id` defined, turn `name` into a domain ID, + // as we transition to `name` being an id, similar to node/npm, but + // append a '@' to make it compatible with Firefox requirements + if (manifest.name) { + + if (typeof manifest.name !== "string") { + return null; + } + + var modifiedName = "@" + manifest.name; + return isValidAOMName(modifiedName) ? modifiedName : null; + } + + // If no `id` or `name` property, return null as this manifest + // is invalid + return null; +} + +module.exports = getID; + +/** + * Regex taken from XPIProvider.jsm in the Addon Manager to validate proper + * IDs that are able to be used. + * http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm#209 + */ +function isValidAOMName (s) { + return /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i.test(s || ""); +} diff --git a/lib/jetpack-id/package.json b/lib/jetpack-id/package.json new file mode 100644 index 000000000..de1bfa4c0 --- /dev/null +++ b/lib/jetpack-id/package.json @@ -0,0 +1,28 @@ +{ + "name": "jetpack-id", + "version": "1.0.0", + "description": "Creates an ID from a Firefox Jetpack manifest", + "main": "index.js", + "repository": { + "type": "git", + "url": "http://github.com/jsantell/jetpack-id" + }, + "author": { + "name": "Jordan Santell", + "url": "http://github.com/jsantell" + }, + "license": "MPL 2.0", + "scripts": { + "test": "./node_modules/.bin/mocha --reporter spec --ui bdd" + }, + "keywords": [ + "jetpack", + "addon", + "mozilla", + "firefox" + ], + "devDependencies": { + "mocha": "*", + "chai": "*" + } +} diff --git a/test/test-jetpack-id.js b/test/test-jetpack-id.js new file mode 100644 index 000000000..99479e32d --- /dev/null +++ b/test/test-jetpack-id.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +var getID = require("jetpack-id/index"); + +exports["test Returns GUID when `id` GUID"] = assert => { + var guid = "{8490ae4f-93bc-13af-80b3-39adf9e7b243}"; + assert.equal(getID({ id: guid }), guid); +}; + +exports["test Returns domain id when `id` domain id"] = assert => { + var id = "my-addon@jetpack"; + assert.equal(getID({ id: id }), id); +}; + +exports["test allows underscores in name"] = assert => { + var name = "my_addon"; + assert.equal(getID({ name: name }), `@${name}`); +}; + +exports["test allows underscores in id"] = assert => { + var id = "my_addon@jetpack"; + assert.equal(getID({ id: id }), id); +}; + +exports["test Returns valid name when `name` exists"] = assert => { + var id = "my-addon"; + assert.equal(getID({ name: id }), `@${id}`); +}; + + +exports["test Returns null when `id` and `name` do not exist"] = assert => { + assert.equal(getID({}), null) +} + +exports["test Returns null when no object passed in"] = assert => { + assert.equal(getID(), null) +} + +exports["test Returns null when `id` exists but not GUID/domain"] = assert => { + var id = "my-addon"; + assert.equal(getID({ id: id }), null); +} + +exports["test Returns null when `id` contains multiple @"] = assert => { + assert.equal(getID({ id: "my@addon@yeah" }), null); +}; + +exports["test Returns null when `id` or `name` specified in domain format but has invalid characters"] = assert => { + [" ", "!", "/", "$", " ", "~", "("].forEach(sym => { + assert.equal(getID({ id: "my" + sym + "addon@domain" }), null); + assert.equal(getID({ name: "my" + sym + "addon" }), null); + }); +}; + +exports["test Returns null, does not crash, when providing non-string properties for `name` and `id`"] = assert => { + assert.equal(getID({ id: 5 }), null); + assert.equal(getID({ name: 5 }), null); + assert.equal(getID({ name: {} }), null); +}; + +require("sdk/test").run(exports);