diff --git a/doc/options.md b/doc/options.md index f856046..f0a28dc 100644 --- a/doc/options.md +++ b/doc/options.md @@ -18,6 +18,7 @@ * [options.tree](#optionstree) * [options.treeIn](#optionstreein) * [options.treeOut](#optionstreeout) +* [options.inspect](#optionsinspect) * [options.rcName](#optionsrcname) * [options.packageField](#optionspackagefield) * [options.detectConfig](#optionsdetectconfig) @@ -341,7 +342,8 @@ Whether to write successfully processed files and where to. Note that if [`treeIn`][tree-in] is turned on, generated files get the first defined [`extensions`][extensions]. If [`treeOut`][tree-out] is turned on, -generated files receive the `'json'` extension. +generated files receive the `'json'` extension. If [`inspect`][inspect] is +turned on, generated files receive the `'txt'` extension. @@ -538,6 +540,54 @@ Yields: } ``` +## `options.inspect` + +Skip the [compilation phase][unified-description] and output a syntax tree +formatted with [`unist-util-inspect`][unist-util-inspect]. + +Sets the extension of processed files to `txt` if possible. + +Uses ANSI colour sequences in the formatted syntax tree if `color` is turned on. + +* Type: `boolean`, optional +* Default: `false` + +###### Example + +The following example shows a script which reads and parses `doc.md`, then +[`remark-unlink`][remark-unlink] transforms the syntax tree, the tree is +formatted with [`unist-util-inspect`][unist-util-inspect], and finally written +to **stdout**(4). + +```js +var engine = require('unified-engine'); +var remark = require('remark'); +var unlink = require('remark-unlink'); + +engine({ + processor: remark(), + plugins: [unlink], + files: ['doc.md'], + inspect: true +}, function (err) { + if (err) throw err; +}); +``` + +Where `doc.md` looks as follows: + +```md +[foo](https://example.com) +``` + +Yields: + +```txt +root[1] (1:1-2:1, 0-27) +└─ paragraph[1] (1:1-1:27, 0-26) + └─ text: "foo" (1:2-1:5, 1-4) +``` + ## `options.rcName` Name of [configuration][configure] file to load. If given and @@ -970,7 +1020,7 @@ See [`options.reporter`][reporter] for an example. ## `options.color` -Whether to [report][reporter] with ANSI colour sequences. +Whether to [report][reporter] or [inspect][] with ANSI colour sequences. * Type: `boolean`, default: `false` @@ -1147,6 +1197,8 @@ engine({ [tree-out]: #optionstreeout +[inspect]: #optionsinspect + [detect-config]: #optionsdetectconfig [rc-name]: #optionsrcname @@ -1176,3 +1228,5 @@ engine({ [reporters]: https://github.com/vfile/vfile#reporters [json]: https://github.com/vfile/vfile-reporter-json + +[unist-util-inspect]: https://github.com/syntax-tree/unist-util-inspect diff --git a/lib/file-pipeline/stringify.js b/lib/file-pipeline/stringify.js index e90a52d..54647ba 100644 --- a/lib/file-pipeline/stringify.js +++ b/lib/file-pipeline/stringify.js @@ -2,6 +2,7 @@ var debug = require('debug')('unified-engine:file-pipeline:stringify'); var stats = require('vfile-statistics'); +var inspect = require('unist-util-inspect'); module.exports = stringify; @@ -23,7 +24,14 @@ function stringify(context, file) { debug('Compiling `%s`', file.path); - if (context.treeOut) { + if (context.inspect) { + /* Add a `txt` extension if there’s a path. */ + if (file.path) { + file.extname = '.txt'; + } + + value = inspect[context.color ? 'color' : 'noColor'](tree) + '\n'; + } else if (context.treeOut) { /* Add a `json` extension to ensure the file is correctly seen as JSON. * Only add it if there’s a path — not if the file is for example stdin. */ if (file.path) { diff --git a/lib/file-set-pipeline/transform.js b/lib/file-set-pipeline/transform.js index f60be8c..48b61fd 100644 --- a/lib/file-set-pipeline/transform.js +++ b/lib/file-set-pipeline/transform.js @@ -28,6 +28,8 @@ function transform(context, settings, next) { pluginPrefix: settings.pluginPrefix, treeIn: settings.treeIn, treeOut: settings.treeOut, + inspect: settings.inspect, + color: settings.color, out: settings.out, output: settings.output, streamOut: settings.streamOut, diff --git a/lib/index.js b/lib/index.js index 1a4fc8b..87715da 100644 --- a/lib/index.js +++ b/lib/index.js @@ -72,6 +72,7 @@ function run(options, callback) { settings.treeIn = options.treeIn; settings.treeOut = options.treeOut; + settings.inspect = options.inspect; if (settings.treeIn === null || settings.treeIn === undefined) { settings.treeIn = tree; diff --git a/package.json b/package.json index 859e2d0..c73ad9c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "parse-json": "^4.0.0", "to-vfile": "^2.0.0", "trough": "^1.0.0", + "unist-util-inspect": "^4.1.2", "vfile-reporter": "^4.0.0", "vfile-statistics": "^1.1.0", "x-is-function": "^1.0.4", diff --git a/readme.md b/readme.md index 761d9f3..97944b0 100644 --- a/readme.md +++ b/readme.md @@ -91,6 +91,8 @@ done. — Whether to treat input as a syntax tree * [`treeOut`][tree-out] (`boolean`, default: `tree`) — Whether to treat output as a syntax tree +* [`inspect`][inspect] (`boolean`, default: `false`) + — Whether to output a formatted syntax tree * [`rcName`][rc-name] (`string`, optional) — Name of configuration files to load * [`packageField`][package-field] (`string`, optional) @@ -228,6 +230,8 @@ repository, organisation, or community you agree to abide by its terms. [tree-out]: doc/options.md#optionstreeout +[inspect]: doc/options.md#optionsinspect + [detect-config]: doc/options.md#optionsdetectconfig [rc-name]: doc/options.md#optionsrcname diff --git a/test/index.js b/test/index.js index 17311c7..e701d88 100644 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ require('./configuration-default'); require('./stdin'); require('./output'); require('./tree'); +require('./inspect'); require('./file-path'); require('./color'); require('./reporting'); diff --git a/test/inspect.js b/test/inspect.js new file mode 100644 index 0000000..ed7a0c6 --- /dev/null +++ b/test/inspect.js @@ -0,0 +1,123 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var PassThrough = require('stream').PassThrough; +var test = require('tape'); +var noop = require('./util/noop-processor'); +var spy = require('./util/spy'); +var engine = require('..'); + +var join = path.join; +var read = fs.readFileSync; +var unlink = fs.unlinkSync; + +var fixtures = join(__dirname, 'fixtures'); + +test('inspect', function (t) { + t.plan(3); + + t.test('should write text when `inspect` is given', function (st) { + var cwd = join(fixtures, 'one-file'); + var stderr = spy(); + + st.plan(4); + + engine({ + processor: noop(), + cwd: cwd, + streamError: stderr.stream, + output: 'formatted.txt', + inspect: true, + files: ['.'], + extensions: ['txt'] + }, function (err, code) { + var doc = read(join(cwd, 'formatted.txt'), 'utf8'); + + /* Remove the file. */ + unlink(join(cwd, 'formatted.txt')); + + st.error(err, 'should not fail fatally'); + st.equal(code, 0, 'should exit with `0`'); + + st.equal( + stderr(), + 'one.txt > formatted.txt: written\n', + 'should report' + ); + + st.equal( + doc, + 'text: ""\n', + 'should write the transformed doc as a formatted syntax tree' + ); + }); + }); + + t.test('should support `inspect` for stdin', function (st) { + var stdin = new PassThrough(); + var stdout = spy(); + var stderr = spy(); + + setTimeout(send, 50); + + function send() { + stdin.end('\n'); + } + + st.plan(1); + + engine({ + processor: noop, + streamIn: stdin, + streamOut: stdout.stream, + streamError: stderr.stream, + inspect: true + }, function (err, code) { + st.deepEqual( + [err, code, stderr(), stdout()], + [ + null, + 0, + ': no issues found\n', + 'text: "\\n"\n' + ], + 'should work' + ); + }); + }); + + t.test('should support `inspect` with colour', function (st) { + var stdin = new PassThrough(); + var stdout = spy(); + var stderr = spy(); + + setTimeout(send, 50); + + function send() { + stdin.end('\n'); + } + + st.plan(1); + + engine({ + processor: noop, + streamIn: stdin, + streamOut: stdout.stream, + streamError: stderr.stream, + inspect: true, + color: true + }, function (err, code) { + st.deepEqual( + [err, code, stderr(), stdout()], + [ + null, + 0, + '\x1b[4m\x1b[32m\x1b[39m\x1b[24m: no issues found\n', + 'text\x1b[2m: \x1b[22m\x1b[32m"\\n"\x1b[39m\n' + ], + 'should work' + ); + }); + }); +});