crx2ff
Version:
Check the compatibility of a Chrome extension with the Firefox WebExtensions API.
128 lines (104 loc) • 3.26 kB
JavaScript
var fs = require('fs');
var path = require('path');
var glob = require('glob');
var async = require('async');
var loadExtension = require('./ext-loader');
var findExpressions = require('./expr-finder');
var matcher = require('./support-matcher');
var checkManifest = require('./manifest-checker');
var unknownApis = {};
var usedApis = {};
function processScripts (extensionPath, cb) {
glob(extensionPath + "/**/*.js", function (error, scripts) {
if (error) {
return cb(error);
}
scripts.forEach(processScript.bind(this, extensionPath));
return cb(null, {
used: usedApis,
unknown: unknownApis
});
});
}
function processManifest (extensionPath, cb) {
fs.readFile(extensionPath + "/manifest.json", function (error, manifest) {
if (error) {
return cb(error);
}
return cb(null, checkManifest(JSON.parse(manifest)));
});
}
function processExtensionFiles (extensionPath, cb) {
async.series([
processScripts.bind(this, extensionPath),
processManifest.bind(this, extensionPath)
], function (error, results) {
if (error) {
return cb(error);
}
return cb(null, {
scriptsReport: results[0],
manifestReport: results[1]
});
});
}
function processScript (extensionPath, scriptPath) {
// Returns chrome.* expressions found in file at scriptPath
var chromeExprs = findExpressions(scriptPath);
if (!chromeExprs) {
return;
}
Object.keys(chromeExprs).forEach(function (expr) {
var relPath = path.relative(extensionPath, scriptPath);
treatExpression(expr, chromeExprs[expr], relPath);
});
}
function treatExpression (expr, locations, scriptPath) {
var parsedExpr = matcher(expr);
// List unrecognized API expressions, so users can easily report issues
// with the matcher
if (!parsedExpr.api) {
if (!unknownApis[expr]) {
unknownApis[expr] = [];
}
return unknownApis[expr].push({
file: scriptPath,
locations: locations
});
}
// Don't deal with feature detection expressions (top level api without,
// any property specified)
if (!parsedExpr.property) {
return;
}
// Build a dict of format
// - topLevelAPI
// - apiExpr
// - locations within files
if (!usedApis[parsedExpr.api]) {
usedApis[parsedExpr.api] = {};
}
if (!usedApis[parsedExpr.api][parsedExpr.property]) {
usedApis[parsedExpr.api][parsedExpr.property] = {
status: parsedExpr.status,
filesLocations: []
};
}
usedApis[parsedExpr.api][parsedExpr.property].filesLocations.push({
file: scriptPath,
locations: locations
});
}
function checker (pathOrId, excludes, cb) {
if (typeof excludes === 'function') {
cb = excludes;
excludes = null;
}
return loadExtension(pathOrId, true, excludes, function (error, extensionPath) {
if (error) {
return cb(error);
}
return processExtensionFiles(extensionPath, cb);
});
}
module.exports = checker;