Skip to content

Commit

Permalink
feat: Added build --as-needed to watch and rebuild
Browse files Browse the repository at this point in the history
  • Loading branch information
kumar303 committed Apr 22, 2016
1 parent fe54ee6 commit d458dcc
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 105 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dependencies": {
"babel-polyfill": "6.7.4",
"bunyan": "1.8.0",
"debounce": "1.0.0",
"es6-error": "2.1.0",
"es6-promisify": "4.0.0",
"firefox-profile": "0.3.12",
Expand All @@ -47,6 +48,7 @@
"source-map-support": "0.4.0",
"stream-to-promise": "1.1.0",
"tmp": "0.0.28",
"watchpack": "1.0.1",
"yargs": "4.6.0",
"zip-dir": "1.0.2"
},
Expand Down
106 changes: 66 additions & 40 deletions src/cmd/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import minimatch from 'minimatch';
import {createWriteStream} from 'fs';
import streamToPromise from 'stream-to-promise';

import defaultSourceWatcher from '../watcher';
import {zipDir} from '../util/zip-dir';
import getValidatedManifest from '../util/manifest';
import {prepareArtifactsDir} from '../util/artifacts';
Expand All @@ -12,49 +13,74 @@ import {createLogger} from '../util/logger';
const log = createLogger(__filename);


export default function build(
{sourceDir, artifactsDir}: Object,
{manifestData, fileFilter}: Object = {}): Promise {
function defaultPackageCreator(
{manifestData, sourceDir, fileFilter, artifactsDir}) {

log.info(`Building web extension from ${sourceDir}`);
return new Promise(
(resolve) => {
if (manifestData) {
log.debug(`Using manifest id=${manifestData.applications.gecko.id}`);
resolve(manifestData);
} else {
resolve(getValidatedManifest(sourceDir));
}
})
.then((manifestData) => {
return zipDir(
sourceDir, {
filter: (...args) => fileFilter.wantFile(...args),
})
.then((buffer) => {
let packageName = safeFileName(
`${manifestData.name}-${manifestData.version}.xpi`);
let extensionPath = path.join(artifactsDir, packageName);
let stream = createWriteStream(extensionPath);
let promisedStream = streamToPromise(stream);

stream.write(buffer, () => stream.end());

return promisedStream
.then(() => {
log.info(`Your web extension is ready: ${extensionPath}`);
return {extensionPath};
});
});
});
}

let resolveManifest;
if (manifestData) {
log.debug(`Using manifest id=${manifestData.applications.gecko.id}`);
resolveManifest = Promise.resolve(manifestData);
} else {
resolveManifest = getValidatedManifest(sourceDir);
}

if (!fileFilter) {
fileFilter = new FileFilter();
}
export default function build(
{sourceDir, artifactsDir, asNeeded}: Object,
{manifestData, fileFilter=new FileFilter(),
onSourceChange=defaultSourceWatcher,
packageCreator=defaultPackageCreator}
: Object = {}): Promise {

return resolveManifest
.then((manifestData) =>
Promise.all([
prepareArtifactsDir(artifactsDir),
zipDir(sourceDir, {
filter: (...args) => fileFilter.wantFile(...args),
}),
])
.then((results) => {
let [artifactsDir, buffer] = results;
let packageName = safeFileName(
`${manifestData.name}-${manifestData.version}.xpi`);
let extensionPath = path.join(artifactsDir, packageName);
let stream = createWriteStream(extensionPath);
let promisedStream = streamToPromise(stream);

stream.write(buffer, () => stream.end());

return promisedStream
.then(() => {
log.info(`Your web extension is ready: ${extensionPath}`);
return {extensionPath};
});
})
);
const rebuildAsNeeded = asNeeded; // alias for `build --as-needed`
log.info(`Building web extension from ${sourceDir}`);

const createPackage = () => packageCreator({
manifestData, sourceDir, fileFilter, artifactsDir,
});

return prepareArtifactsDir(artifactsDir)
.then(() => createPackage())
.then((result) => {
if (rebuildAsNeeded) {
log.info('Rebuilding when files change...');
onSourceChange({
sourceDir, artifactsDir,
onChange: () => {
return createPackage().catch((error) => {
log.error(error.stack);
throw error;
});
},
shouldWatchFile: (...args) => fileFilter.wantFile(...args),
});
}
return result;
});
}


Expand Down Expand Up @@ -86,7 +112,7 @@ export class FileFilter {
wantFile(path: string): boolean {
for (const test of this.filesToIgnore) {
if (minimatch(path, test)) {
log.debug(`Not including file ${path} in ZIP archive`);
log.debug(`FileFilter: ignoring file ${path}`);
return false;
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/program.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ Example: $0 --help run.
program
.command('build',
'Create a web extension package from source',
commands.build, {})
commands.build, {
'as-needed': {
describe: 'Watch for file changes and re-build as needed',
type: 'boolean',
},
})
.command('sign',
'Sign the web extension so it can be installed in Firefox',
commands.sign, {
Expand Down
41 changes: 41 additions & 0 deletions src/watcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* @flow */
import Watchpack from 'watchpack';
import debounce from 'debounce';

import {createLogger} from './util/logger';

const log = createLogger(__filename);


export default function onSourceChange(
{sourceDir, artifactsDir, onChange, shouldWatchFile}: Object): Watchpack {
// TODO: For network disks, we would need to add {poll: true}.
const watcher = new Watchpack();

const onFileChange = (filePath) => {
proxyFileChanges({artifactsDir, onChange, filePath, shouldWatchFile});
};
const executeImmediately = true;
watcher.on('change', debounce(onFileChange, 1000, executeImmediately));

log.debug(`Watching for file changes in ${sourceDir}`);
watcher.watch([], [sourceDir], Date.now());

// TODO: support windows See:
// http://stackoverflow.com/questions/10021373/what-is-the-windows-equivalent-of-process-onsigint-in-node-js
process.on('SIGINT', () => watcher.close());
return watcher;
}


export function proxyFileChanges(
{artifactsDir, onChange, filePath, shouldWatchFile=() => true}
: Object) {
if (filePath.indexOf(artifactsDir) === 0 || !shouldWatchFile(filePath)) {
log.debug(`Ignoring change to: ${filePath}`);
} else {
log.info(`Changed: ${filePath}`);
log.debug(`Last change detection: ${(new Date()).toTimeString()}`);
onChange();
}
}
Loading

0 comments on commit d458dcc

Please sign in to comment.