Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement .crxignore #51

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Expand Up @@ -115,6 +115,10 @@ Generate a 1,024-bit RSA private key within the directory. This is called automa

Use the `--force` option to overwrite an existing private key located in the same given folder.

### crx ignore <patterns...>

Add a space delimited array of patterns to a [.crxignore](#crxignore) file, or create [.crxignore](#crxignore) if it does not yet exist.

### crx -h

Show information about using this utility, generated by [commander](https://github.com/visionmedia/commander.js).
Expand Down Expand Up @@ -163,6 +167,20 @@ you can run this:

to sign your package without keeping the key in the directory.

## .crxignore

.crxignore specifies files to exclude from being packed into your Chrome Extension. The .crxignore file is similar to git's .gitignore file.

Given the following .crxignore file:

ignore-dir/
ignore.js

* all files in the `ignore-dir/` directory and its subdirectories and
* all files named `ignore.js`

would be excluded from the Chrome Extension.

Copyright
---------

Expand Down
14 changes: 14 additions & 0 deletions bin/crx.js
Expand Up @@ -33,6 +33,11 @@ program
.option("-b, --max-buffer <total>", "max amount of memory allowed to generate the crx, in byte")
.action(pack);

program
.command("ignore <patterns...>")
.description("add patterns to a .crxignore file")
.action(addPatterns);

program.parse(process.argv);

/**
Expand Down Expand Up @@ -147,3 +152,12 @@ function pack (dir, program) {
});

}

function addPatterns (patterns) {
console.log("Adding pattern(s) to .crxignore");
var crxignorePath = join(cwd, ".crxignore");
patterns.forEach(function (pattern) {
console.log("... adding " + pattern);
fs.appendFile(crxignorePath, pattern + "\n");
});
}
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -28,11 +28,13 @@
"archiver": "^0.8.0",
"commander": "^2.5.0",
"es6-promise": "^2.0.0",
"ignore": "^2.2.19",
"node-rsa": "^0.2.10",
"temp": "^0.8.1",
"wrench": "^1.5.0"
},
"devDependencies": {
"adm-zip": "^0.4.7",
"github-changes": "^1.0.0",
"sinon": "^1.12.1",
"tape": "^3.0.3"
Expand Down
83 changes: 49 additions & 34 deletions src/crx.js
Expand Up @@ -8,6 +8,7 @@ var crypto = require("crypto");
var RSA = require('node-rsa');
var wrench = require("wrench");
var archiver = require("archiver");
var ignore = require("ignore");
var Promise = require('es6-promise').Promise;
var temp = require('temp');

Expand Down Expand Up @@ -179,6 +180,18 @@ ChromeExtension.prototype = {
var archive = archiver("zip");
var selfie = this;

var getCrxignore = function () {
return new Promise(function (resolve) {
fs.readFile(selfie.path + '/.crxignore', 'utf8', function(err, data){
var ig = ignore({
ignore: data ? data.split(/\r?\n/) : [],
twoGlobstars: true
});
resolve(ig.createFilter());
});
});
};

return new Promise(function(resolve, reject){
var contents = new Buffer('');
var allFiles = [];
Expand All @@ -187,48 +200,50 @@ ChromeExtension.prototype = {
throw new Error('crx.load needs to be called first in order to prepare the workspace.');
}

// the callback is called many times
// when 'files' is null, it means we accumulated everything
// hence this weird setup
wrench.readdirRecursive(selfie.path, function(err, files){
if (err){
return reject(err);
}
getCrxignore().then(function(crxignore){
// the callback is called many times
// when 'files' is null, it means we accumulated everything
// hence this weird setup
wrench.readdirRecursive(selfie.path, function(err, files){
if (err){
return reject(err);
}

// stack unless 'files' is null
if (files){
allFiles = allFiles.concat(files);
return;
}
// stack unless 'files' is null
if (files){
allFiles = allFiles.concat(files);
return;
}

allFiles.forEach(function (file) {
var filePath = join(selfie.path, file);
var stat = fs.statSync(filePath);
allFiles.filter(crxignore).forEach(function (file) {
var filePath = join(selfie.path, file);
var stat = fs.statSync(filePath);

if (stat.isFile() && file !== "key.pem") {
archive.append(fs.createReadStream(filePath), { name: file });
}
});
if (stat.isFile() && file !== "key.pem") {
archive.append(fs.createReadStream(filePath), { name: file });
}
});

archive.finalize();
archive.finalize();

// Relates to the issue: "Event 'finished' no longer valid #18"
// https://github.com/jed/crx/issues/18
// TODO: Buffer concat could be a problem when building a big extension.
// So ideally only the 'finish' callback must be used.
archive.on('readable', function () {
var buf = archive.read();
// Relates to the issue: "Event 'finished' no longer valid #18"
// https://github.com/jed/crx/issues/18
// TODO: Buffer concat could be a problem when building a big extension.
// So ideally only the 'finish' callback must be used.
archive.on('readable', function () {
var buf = archive.read();

if (buf) {
contents = Buffer.concat([contents, buf]);
}
});
if (buf) {
contents = Buffer.concat([contents, buf]);
}
});

archive.on('finish', function () {
resolve(contents);
});
archive.on('finish', function () {
resolve(contents);
});

archive.on("error", reject);
archive.on("error", reject);
});
});
});
},
Expand Down
20 changes: 20 additions & 0 deletions test/index.js
Expand Up @@ -8,6 +8,7 @@ var join = require("path").join;
var privateKey = fs.readFileSync(join(__dirname, "key.pem"));
var sinon = require('sinon');
var sandbox = sinon.sandbox.create();
var AdmZip = require('adm-zip');

function newCrx(){
return new ChromeExtension({
Expand Down Expand Up @@ -71,3 +72,22 @@ test('it should fail if the public key was not set prior to trying to generate t
var crx = newCrx();
t.throws(function() { crx.generateAppId(); }, /Public key is neither set, nor given/);
});

test('it should not archive paths listed in .crxignore', function(t) {
var crx = newCrx();
var testIgnore = ["ignore.js", "ignore-dir/file.js"];

t.plan(testIgnore.length);

crx.load().then(function(){
return crx.loadContents();
})
.then(function(zipBuffer){
var zipOutputPath = join(__dirname, "output.zip");

fs.writeFile(zipOutputPath, zipBuffer, function(){
var zip = new AdmZip(zipOutputPath);
testIgnore.forEach(function(file){ t.ok(zip.getEntry(file) === null); });
});
});
});
2 changes: 2 additions & 0 deletions test/myFirstExtension/.crxignore
@@ -0,0 +1,2 @@
ignore-dir/
ignore.js
Empty file.
Empty file added test/myFirstExtension/ignore.js
Empty file.