Skip to content

Commit 99a9ab8

Browse files
committed
Add --watch option
1 parent f0e2dfa commit 99a9ab8

File tree

8 files changed

+100
-34
lines changed

8 files changed

+100
-34
lines changed

bin/args.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = require('nomnom')
66
.help('Compile ecmarkup documents to html by passing your input file and output file.')
77
.options({
88
help: { abbr: 'h', flag: true, help: 'Display this help message' },
9+
watch: { abbr: 'w', flag: true, help: 'Rebuild when files change' },
910
biblio: { abbr: 'b', metavar: 'FILE', help: 'Write a biblio file to FILE' },
1011
css: { metavar: 'FILE', help: 'Write Emu CSS dependencies to FILE' },
1112
js: { metavar: 'FILE', help: 'Write Emu JS dependencies to FILE' },

bin/ecmarkup.js

+57-15
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,69 @@ const Promise = require('bluebird');
99
const Path = require('path');
1010
const fs = require('fs');
1111
const readFile = Promise.promisify(fs.readFile);
12+
const utils = require('../lib/utils');
13+
const debounce = require('promise-debounce');
1214

1315
function fetch(path) {
1416
return readFile(path, 'utf8');
1517
}
1618

17-
ecmarkup.build(args.infile, fetch, args).then(function (spec) {
18-
if (args.biblio) {
19-
fs.writeFileSync(args.biblio, JSON.stringify(spec.exportBiblio()), 'utf8');
20-
}
19+
const build = debounce(function build() {
20+
return ecmarkup.build(args.infile, fetch, args).then(function (spec) {
21+
if (args.biblio) {
22+
if (args.verbose) {
23+
utils.logVerbose('Writing biblio file to ' + args.biblio);
24+
}
25+
fs.writeFileSync(args.biblio, JSON.stringify(spec.exportBiblio()), 'utf8');
26+
}
2127

22-
if (args.outfile) {
23-
fs.writeFileSync(args.outfile, spec.toHTML(), 'utf8');
24-
} else {
25-
process.stdout.write(spec.toHTML());
26-
}
28+
if (args.outfile) {
29+
if (args.verbose) {
30+
utils.logVerbose('Writing output to ' + args.outfile);
31+
}
32+
fs.writeFileSync(args.outfile, spec.toHTML(), 'utf8');
33+
} else {
34+
process.stdout.write(spec.toHTML());
35+
}
2736

28-
if (args.css) {
29-
fs.writeFileSync(args.css, fs.readFileSync(Path.join(__dirname, '../css/elements.css')));
30-
}
37+
if (args.css) {
38+
if (args.verbose) {
39+
utils.logVerbose('Writing css file to ' + args.css);
40+
}
41+
fs.writeFileSync(args.css, fs.readFileSync(Path.join(__dirname, '../css/elements.css')));
42+
}
3143

32-
if (args.js) {
33-
fs.writeFileSync(args.js, fs.readFileSync(Path.join(__dirname, '../js/menu.js')));
34-
}
44+
if (args.js) {
45+
if (args.verbose) {
46+
utils.logVerbose('Writing css file to ' + args.css);
47+
}
48+
fs.writeFileSync(args.js, fs.readFileSync(Path.join(__dirname, '../js/menu.js')));
49+
}
50+
51+
return spec;
52+
});
3553
});
54+
55+
if (args.watch) {
56+
let watching = {};
57+
build().then(function(spec) {
58+
let toWatch = spec.imports.concat(args.infile);
59+
60+
// remove any files that we're no longer watching
61+
Object.keys(watching).forEach(function(file) {
62+
if (toWatch.indexOf(file) === -1) {
63+
watching[file].close();
64+
delete watching[file];
65+
}
66+
});
67+
68+
// watch any new files
69+
toWatch.forEach(function(file) {
70+
if (!watching[file]) {
71+
watching[file] = fs.watch(file, build);
72+
}
73+
});
74+
});
75+
} else {
76+
build();
77+
}

lib/Import.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = class Import extends Builder {
88
build(rootDir) {
99
const href = this.node.getAttribute('href');
1010
const importPath = Path.join(rootDir || this.spec.rootDir, href);
11+
this.spec.imports.push(importPath);
1112

1213
return this.spec.fetch(importPath)
1314
.then(utils.htmlToDoc)

lib/Spec.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module.exports = class Spec {
3838
this.doc = doc;
3939
this.fetch = fetch;
4040
this.subclauses = [];
41+
this.imports = [];
4142
this._numberer = ClauseNumbers.iterator();
4243
this._figureCounts = {
4344
table: 0,
@@ -77,10 +78,10 @@ module.exports = class Spec {
7778
buildAll(selector, Builder, opts) {
7879
opts = opts || {};
7980

80-
this._log('Building ' + selector + '...');
8181

8282
let elems;
8383
if (typeof selector === 'string') {
84+
this._log('Building ' + selector + '...');
8485
elems = this.doc.querySelectorAll(selector);
8586
} else {
8687
elems = selector;
@@ -157,7 +158,7 @@ module.exports = class Spec {
157158
try {
158159
data = yaml.safeLoad(block.textContent);
159160
} catch (e) {
160-
console.log('Warning: metadata block failed to parse');
161+
utils.logWarning('metadata block failed to parse');
161162
return;
162163
} finally {
163164
block.parentNode.removeChild(block);
@@ -194,7 +195,7 @@ module.exports = class Spec {
194195

195196
exportBiblio() {
196197
if (!this.opts.location) {
197-
console.log('Warning: no spec location specified. Biblio not generated. Try --location or setting the location in the document\'s metadata block.');
198+
utils.logWarning('no spec location specified. Biblio not generated. Try --location or setting the location in the document\'s metadata block.');
198199
return {};
199200
}
200201

@@ -277,9 +278,9 @@ module.exports = class Spec {
277278
return null;
278279
}
279280

280-
_log() {
281+
_log(str) {
281282
if (!this.opts.verbose) return;
282-
console.log.apply(console, arguments);
283+
utils.logVerbose(str);
283284
}
284285
};
285286

lib/Xref.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,26 @@ module.exports = class Xref extends Builder {
99
const aoid = xref.getAttribute('aoid');
1010

1111
if (href && aoid) {
12-
console.log('Warning: xref can\'t have both href and aoid.');
12+
utils.logWarning('xref can\'t have both href and aoid.');
1313
return;
1414
}
1515

1616
if (!href && !aoid) {
17-
console.log('Warning: xref has no href or aoid.');
17+
utils.logWarning('xref has no href or aoid.');
1818
return;
1919
}
2020

2121
if (href) {
2222
if (href[0] !== '#') {
23-
console.log('Warning: xref to anything other than a fragment id is not supported (is ' + href + '). Try href="#sec-id" instead.');
23+
utils.logWarning('xref to anything other than a fragment id is not supported (is ' + href + '). Try href="#sec-id" instead.');
2424
return;
2525
}
2626

2727
const id = href.slice(1);
2828

2929
const entry = this.spec.lookupBiblioEntryById(id);
3030
if (!entry) {
31-
console.log('Warning: can\'t find clause, production, note or example with id ' + href);
31+
utils.logWarning('can\'t find clause, production, note or example with id ' + href);
3232
return;
3333
}
3434

@@ -52,7 +52,7 @@ module.exports = class Xref extends Builder {
5252
buildFigureLink(this.spec, xref, entry.entry, 'Figure');
5353
break;
5454
default:
55-
console.log('Warning: found unknown biblio entry (this is a bug, please file it)');
55+
utils.logWarning('found unknown biblio entry (this is a bug, please file it)');
5656
}
5757
} else if (aoid) {
5858
const entry = this.spec.biblio.ops[aoid] || this.spec.externalBiblio.ops[aoid];
@@ -62,7 +62,7 @@ module.exports = class Xref extends Builder {
6262
return;
6363
}
6464

65-
console.log('Warning: can\'t find abstract op with aoid ' + aoid);
65+
utils.logWarning('can\'t find abstract op with aoid ' + aoid);
6666
}
6767

6868
}
@@ -102,7 +102,7 @@ function buildFigureLink(spec, xref, entry, type) {
102102
// first need to find the associated clause
103103
const clauseEntry = spec.lookupBiblioEntryById(entry.clauseId);
104104
if (clauseEntry.type !== 'clause') {
105-
console.log('Warning: could not find parent clause for ' + type + ' id ' + entry.id);
105+
utils.logWarning('could not find parent clause for ' + type + ' id ' + entry.id);
106106
}
107107

108108
const parentClause = utils.parent(xref, ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']);

lib/utils.js

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const jsdom = require('jsdom');
44
const Promise = require('bluebird');
5+
const chalk = require('chalk');
56

67
exports.htmlToDoc = function (html) {
78
return new Promise(function (res, rej) {
@@ -56,3 +57,13 @@ exports.parent = function parent(node, types) {
5657
return parent(node.parentNode, types);
5758
};
5859

60+
61+
exports.logVerbose = function(str) {
62+
let dateString = (new Date()).toISOString();
63+
console.log(chalk.gray('[' + dateString + '] ') + str);
64+
};
65+
66+
exports.logWarning = function(str) {
67+
let dateString = (new Date()).toISOString();
68+
console.log(chalk.gray('[' + dateString + '] ') + chalk.red('Warning: ' + str));
69+
};

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ecmarkup",
3-
"version": "2.0.3",
3+
"version": "2.1.0",
44
"description": "Custom element definitions and core utilities for markup that specifies ECMAScript and related technologies.",
55
"main": "lib/ecmarkup.js",
66
"scripts": {
@@ -33,13 +33,15 @@
3333
"homepage": "https://github.com/bterlson/ecmarkup",
3434
"dependencies": {
3535
"bluebird": "^2.6.4",
36+
"chalk": "^1.1.1",
3637
"ecmarkdown": "^3.0.1",
3738
"grammarkdown": "^0.1.3",
3839
"highlight.js": "^8.6.0",
3940
"html-escape": "^1.0.2",
4041
"js-yaml": "^3.3.1",
4142
"jsdom": "^5.0.1",
42-
"nomnom": "^1.8.1"
43+
"nomnom": "^1.8.1",
44+
"promise-debounce": "^1.0.1"
4345
},
4446
"devDependencies": {
4547
"diff": "^1.4.0",

spec/index.html

+13-5
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,26 @@ <h1>Getting Started</h1>
3535
ecmarkup spec.html out.html
3636
</pre>
3737

38+
<h2>Useful Options</h2>
39+
<table>
40+
<tr><th>Option</th><th>Description</th></tr>
41+
<tr><td>watch</td><td>Rebuild when files change</td></tr>
42+
<tr><td>verbose</td><td>Print verbose logging info</td></tr>
43+
<tr><td>biblio</td><td>Emit a biblio file to the specified location</td></tr>
44+
<tr><td>css</td><td>Emit the Ecmarkup CSS file to the specified location</td></tr>
45+
<tr><td>js</td><td>Emit the Ecmarkup JS file to the specified location</td></tr>
46+
<tr><td>toc</td><td>Emit table of contents. Boolean, default true.</td></tr>
47+
<tr><td>old-toc</td><td>Emit the old style of table of contents. Boolean, default false.</td></tr>
48+
</table>
49+
3850
<h2>Stylesheet</h2>
3951
<p>Styles are provided by `elements.css` under the `css` directory. The --css command line option will emit the CSS file to a location of your choosing. You may also link to https://bterlson.github.io/ecmarkup/elements.css which will contain the latest styles for the forseeable future (though I don't promise this won't break you if you're not tracking the latest version).</p>
4052
</emu-clause>
4153
<emu-clause id="metadata">
4254
<h1>Metadata</h1>
4355
<p>There are a number of settings that allow customizations or enable generation of boilerplate. Metadata can be included in the document and passed on the command line, for example `--no-toc --title "Document 1"`. Metadata given on the command line takes precedence.</p>
4456
<p>To add metadata to your document, use yaml syntax inside a `&lt;pre class=metadata>` element somewhere in the root of your document.</p>
45-
<p>The following table lists the currently supported metadata:</p>
46-
<table>
47-
<tr><th>Option</th><th>Description</th></tr>
48-
<tr><td>toc</td><td>Emit table of contents. Boolean, default true.</td></tr>
49-
</table>
57+
<p>All of the command line options can be provided in the metadata block. See the table above for a list (or consult `--help`).</p>
5058
<h2>Example document with metadata</h2>
5159
<pre><code class="language-html">
5260
&lt;pre class=metadata>

0 commit comments

Comments
 (0)