├── .gitattributes
├── .npmrc
├── .prettierignore
├── .gitignore
├── TODO.md
├── tests
├── expected
│ └── empty.md
├── fixtures
│ ├── coffee.coffee
│ ├── line.styl
│ ├── block.scss
│ ├── jsdoc.js
│ ├── comments.jade
│ ├── typescript.ts
│ ├── block.styl
│ ├── block.sass
│ └── handlebars.hbs
└── stream-spec.js
├── .prettierrc.yml
├── .travis.yml
├── .editorconfig
├── .jshintrc
├── LICENSE
├── package.json
├── lib
└── reporter.js
├── gulpfile.js
├── index.js
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | tests/fixtures/
2 | tests/expected/
3 | .idea/
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .idea/
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | ### TODOs
2 | | Filename | line # | TODO
3 | |:------|:------:|:------
--------------------------------------------------------------------------------
/tests/expected/empty.md:
--------------------------------------------------------------------------------
1 | ### TODOs
2 | | Filename | line # | TODO
3 | |:------|:------:|:------
4 |
--------------------------------------------------------------------------------
/tests/fixtures/coffee.coffee:
--------------------------------------------------------------------------------
1 | # TODO: Do something
2 | callFunction 'hello', true
3 | # FIXME:Fix something
4 |
--------------------------------------------------------------------------------
/tests/fixtures/line.styl:
--------------------------------------------------------------------------------
1 | body
2 | // This is a comment without a todo
3 | color white
4 | // FIXME: use fixmes as well
5 | form
6 | color yellow
7 |
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | arrowParens: avoid
2 | bracketSpacing: true
3 | printWidth: 120
4 | semi: true
5 | singleQuote: true
6 | tabWidth: 4
7 | trailingComma: es5
8 | useTabs: false
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 6
5 | - 8
6 | - 9
7 | - 10
8 | - 11
9 | cache:
10 | directories:
11 | - node_modules
12 | notifications:
13 | email: false
14 |
--------------------------------------------------------------------------------
/tests/fixtures/block.scss:
--------------------------------------------------------------------------------
1 | .button {
2 | color: #000;
3 | display: block;
4 | // TODO: add another class
5 | }
6 |
7 | .test {
8 | .foo,
9 | .bar {
10 | margin: 10px;
11 | padding: 10px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/fixtures/jsdoc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var fun = function() {
3 | return null;
4 | };
5 | /**
6 | * Controller
7 | *
8 | *
9 | * @description: Yay
10 | *
11 | *
12 | * @author: SC
13 | ***/
14 | // TODO Show my TODO please
15 | var a = true;
16 |
17 | if (a) {
18 | fun();
19 | }
20 |
--------------------------------------------------------------------------------
/tests/fixtures/comments.jade:
--------------------------------------------------------------------------------
1 | !!!
2 | head
3 |
4 | body
5 | //this is a a comment
6 | .cluster class
7 | //- this is also a comment
8 | #oh-no yes
9 | //TODO: this is a todo
10 | //TODOTHERE this isnt a todo
11 | //FIXME also should be caught
12 | - var string = "//this isnt a todo as well"
13 |
--------------------------------------------------------------------------------
/tests/fixtures/typescript.ts:
--------------------------------------------------------------------------------
1 | // TODO: change to public
2 | class Greeter {
3 | constructor(public greeting: string) { }
4 | greet() {
5 | return "
" + this.greeting + "
";
6 | }
7 | };
8 | var greeter = new Greeter("Hello, world!");
9 | var str = greeter.greet();
10 | /*
11 | * FIXME: use jquery
12 | */
13 | document.body.innerHTML = str;
14 |
--------------------------------------------------------------------------------
/tests/fixtures/block.styl:
--------------------------------------------------------------------------------
1 | body
2 | // This is a comment without a todo
3 | color white
4 | /* single line comment without todo */
5 | /* TODO: single line comment with a todo */
6 | /*! FIXME: single line comment with a todo */
7 | /*
8 | multiple line comment without todo
9 | yes another line
10 | */
11 | form
12 | color yellow
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 |
8 | indent_style = space
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 |
14 | [*.{js,json,html,jade,md}]
15 | indent_size = 4
16 |
17 | [*.js]
18 | jslint_happy = true
19 |
--------------------------------------------------------------------------------
/tests/fixtures/block.sass:
--------------------------------------------------------------------------------
1 | /* This comment is
2 | * TODO: it will appear in the CSS output.
3 | * FIXME: this is a block comment too
4 | * several lines long.
5 | * since it uses the CSS comment syntax,
6 | * */
7 | body { color: black; }
8 |
9 | // These comments are only one line long each.
10 | // FIXME: They won't appear in the CSS output,
11 | // since they use the single-line comment syntax.
12 | a { color: green; }
13 |
14 | //TODO: improve this syntax
15 |
--------------------------------------------------------------------------------
/tests/fixtures/handlebars.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{!-- TODO: only output this author names if an author exists --}}
3 | {{#if author}}
4 |
{{firstName}} {{lastName}}
5 | {{/if}}
6 |
7 |
8 | {{! FIXME: This comment will not be in the output }}
9 |
10 |
11 |
12 |
13 | {{! TODO: Multiple line comment}} {{! TODO: and again}}
14 |
15 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "dojo": true
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Gilad Peleg (https://www.giladpeleg.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-todo",
3 | "version": "7.1.1",
4 | "description": "Generate a TODO.md file from comments of files in stream",
5 | "repository": "pgilad/gulp-todo",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Gilad Peleg",
9 | "email": "giladp007@gmail.com",
10 | "url": "http://giladpeleg.com"
11 | },
12 | "main": "index.js",
13 | "files": [
14 | "index.js",
15 | "lib"
16 | ],
17 | "engines": {
18 | "node": ">=6.0.0"
19 | },
20 | "scripts": {
21 | "test": "mocha -R spec tests/*.js",
22 | "prettier": "prettier --write '**/*.js'",
23 | "watchTest": "mocha --watch -R spec tests/*.js"
24 | },
25 | "keywords": [
26 | "gulpplugin",
27 | "gulp",
28 | "js",
29 | "fixme",
30 | "comments",
31 | "todo",
32 | "list",
33 | "parse",
34 | "generator",
35 | "ci"
36 | ],
37 | "dependencies": {
38 | "ansi-colors": "^3.2.3",
39 | "fancy-log": "^1.3.3",
40 | "leasot": "^7.3.1",
41 | "lodash.defaults": "^4.2.0",
42 | "plugin-error": "^1.0.1",
43 | "through2": "^3.0.0",
44 | "vinyl": "^2.2.0"
45 | },
46 | "devDependencies": {
47 | "gulp": "^4.0.0",
48 | "gulp-header": "^2.0.7",
49 | "gulp-wrap": "^0.14.0",
50 | "mocha": "^5.2.0",
51 | "prettier": "^1.16.4",
52 | "should": "^13.2.3"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/reporter.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const leasot = require('leasot');
4 | const path = require('path');
5 | const PluginError = require('plugin-error');
6 | const through = require('through2');
7 |
8 | const pluginName = 'gulp-todo';
9 |
10 | /**
11 | *
12 | * @param {string|function} [reporter] The reporter to use. See https://pgilad.github.io/leasot/enums/builtinreporters.html
13 | * @param {Object} [reportOptions={}] Passed directly to `leasot.report` - See https://pgilad.github.io/leasot/index.html#report
14 | * @param {string} [fileName] Pass along options to the reporter, and also if you pass a `fileName` - it will rename the filename in stream
15 | * @returns {*}
16 | */
17 | module.exports = function(reporter, { reportOptions = {}, fileName } = {}) {
18 | if (!reporter) {
19 | throw new PluginError('Reporter is required');
20 | }
21 | return through.obj(function(file, enc, cb) {
22 | if (file.isNull()) {
23 | return cb(null, file);
24 | }
25 |
26 | if (file.isStream()) {
27 | return cb(new PluginError(pluginName, 'Streaming not supported'));
28 | }
29 |
30 | // replace contents with requested reporter contents
31 | if (file.todos && file.todos.length) {
32 | const newContents = leasot.report(file.todos, reporter, reportOptions);
33 |
34 | if (fileName) {
35 | file.path = path.join(file.base, fileName);
36 | }
37 | file.contents = Buffer.from(newContents);
38 | }
39 |
40 | cb(null, file);
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var gulp = require('gulp');
3 | var todo = require('./index');
4 | var wrap = require('gulp-wrap');
5 | var header = require('gulp-header');
6 |
7 | gulp.task('xml', function() {
8 | gulp.src('./**/*.js', {
9 | base: './',
10 | })
11 | .pipe(
12 | todo({
13 | fileName: 'todo.xml',
14 | reporter: 'custom',
15 | transformComment: function(file, line, text, kind) {
16 | return [
17 | '',
18 | text,
19 | '',
20 | ];
21 | },
22 | transformHeader: function() {
23 | return '';
24 | },
25 | })
26 | )
27 | .pipe(wrap(' <%= contents %>'))
28 | .pipe(header(''))
29 | .pipe(gulp.dest('./'));
30 | });
31 |
32 | gulp.task('json', function() {
33 | gulp.src('./**/*.js', {
34 | base: './',
35 | })
36 | .pipe(
37 | todo({
38 | fileName: 'todo.json',
39 | reporter: 'json',
40 | absolute: true,
41 | })
42 | )
43 | .pipe(gulp.dest('./'));
44 | });
45 |
46 | gulp.task('multiple', function() {
47 | gulp.src('./**/*.js', {
48 | base: './',
49 | })
50 | .pipe(todo())
51 | .pipe(gulp.dest('./'))
52 | .pipe(
53 | todo.reporter('xml', {
54 | fileName: 'todo.xml',
55 | })
56 | )
57 | .pipe(gulp.dest('./'));
58 | });
59 |
60 | gulp.task('default', function() {
61 | gulp.src(['./index.js', './lib/**.*.js'], {
62 | base: './',
63 | })
64 | .pipe(todo())
65 | .pipe(gulp.dest('./'));
66 | });
67 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const colors = require('ansi-colors');
4 | const fancyLog = require('fancy-log');
5 | const leasot = require('leasot');
6 | const path = require('path');
7 | const PluginError = require('plugin-error');
8 | const through = require('through2');
9 | const Vinyl = require('vinyl');
10 |
11 | const pluginName = 'gulp-todo';
12 |
13 | function logCommentsToConsole(comments) {
14 | comments.forEach(function(comment) {
15 | const isTodo = /todo/i.test(comment.tag);
16 | const commentType = isTodo ? colors.cyan(comment.tag) : colors.magenta(comment.tag);
17 | const commentLocation = '@' + colors.gray(comment.file + ':' + comment.line);
18 | fancyLog(commentType, comment.text, commentLocation);
19 | });
20 | }
21 |
22 | /**
23 | *
24 | * @param {boolean} [absolute=false] Output absolute paths of files (as available via `file.path`)
25 | * @param {string} [fileName=TODO.md] Specify the output filename
26 | * @param {Object} [parseOptions={}] Passed directly to `leasot.parse` - See [ParseConfig](https://pgilad.github.io/leasot/interfaces/parseconfig.html)
27 | * @param {string|function} [reporter=markdown] The reporter to use. See https://pgilad.github.io/leasot/enums/builtinreporters.html
28 | * @param {Object} [reportOptions={}] Passed directly to `leasot.report` - See https://pgilad.github.io/leasot/index.html#report
29 | * @param {boolean} [skipUnsupported=false] Whether to skip unsupported files or not
30 | * @param {boolean} [verbose=false] Output comments to console as well
31 | * @returns {*}
32 | */
33 | module.exports = function({
34 | absolute = false,
35 | fileName = 'TODO.md',
36 | parseOptions = {},
37 | reporter = 'markdown',
38 | reportOptions = {},
39 | skipUnsupported = false,
40 | verbose = false,
41 | } = {}) {
42 | let firstFile;
43 | const comments = [];
44 |
45 | return through.obj(
46 | function collectTodos(file, enc, cb) {
47 | if (file.isNull()) {
48 | cb(null, file);
49 | return;
50 | }
51 |
52 | if (file.isStream()) {
53 | cb(new PluginError(pluginName, 'Streaming not supported'));
54 | return;
55 | }
56 | firstFile = firstFile || file;
57 |
58 | //get extension - assume .js as default
59 | const ext = path.extname(file.path) || '.js';
60 |
61 | //check if parser for filetype exists
62 | if (!leasot.isExtensionSupported(ext)) {
63 | if (!skipUnsupported) {
64 | const msg = `File: ${file.path} with extension ${colors.red(ext)} is not supported`;
65 | return cb(new PluginError(pluginName, msg));
66 | }
67 | if (verbose) {
68 | const msg = `Skipping file ${file.path} with extension ${colors.red(ext)} as it is unsupported`;
69 | fancyLog(msg);
70 | }
71 | return cb();
72 | }
73 | const filePath = absolute ? file.path : (file.path && file.relative) || file.path;
74 |
75 | const parsedComments = leasot.parse(file.contents.toString(), {
76 | associateParser: parseOptions.associateParser,
77 | customParsers: parseOptions.customParsers,
78 | customTags: parseOptions.customTags,
79 | extension: ext,
80 | filename: filePath,
81 | withInlineFiles: parseOptions.withInlineFiles,
82 | });
83 | if (verbose) {
84 | logCommentsToConsole(parsedComments);
85 | }
86 | comments.push(...parsedComments);
87 | cb();
88 | },
89 | function reportTodos(cb) {
90 | if (!firstFile) {
91 | return cb();
92 | }
93 | const reporterContents = leasot.report(comments, reporter, reportOptions);
94 |
95 | const todoFile = new Vinyl({
96 | base: firstFile.base,
97 | contents: Buffer.from(reporterContents),
98 | cwd: firstFile.cwd,
99 | path: path.join(firstFile.base, fileName),
100 | });
101 |
102 | // also pass along comments object for future reporters
103 | todoFile.todos = comments;
104 | cb(null, todoFile);
105 | }
106 | );
107 | };
108 |
109 | const reporter = require('./lib/reporter');
110 | module.exports.reporter = reporter;
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [gulp](https://github.com/wearefractal/gulp)-todo
2 | > Parse and output TODOs and FIXMEs from comments in your file in a stream
3 |
4 | [](https://npmjs.org/package/gulp-todo)
5 | [](https://npmjs.org/package/gulp-todo)
6 | [](https://travis-ci.org/pgilad/gulp-todo)
7 |
8 | Parse your files in a gulp-stream, extracting todos/fixmes from comments and reporting them
9 | in a reporter to your choosing using [leasot](https://github.com/pgilad/leasot).
10 |
11 | Issues with the output should be reported on the [leasot issue tracker](https://github.com/pgilad/leasot/issues)
12 |
13 | **Supports latest `leasot` version `7.0.0`.**
14 |
15 | **Please upgrade carefully to version `7.0.0`, there were breaking changes in the `gulp-todo` API**
16 |
17 | ## Install
18 |
19 | Install with [npm](https://npmjs.org/package/gulp-todo)
20 |
21 | ```sh
22 | $ npm install --save-dev gulp-todo
23 | ```
24 |
25 | ## Usage
26 |
27 | ```js
28 | const gulp = require('gulp');
29 | const todo = require('gulp-todo');
30 |
31 | // generate a todo.md from your javascript files
32 | gulp.task('todo', function() {
33 | gulp.src('js/**/*.js')
34 | .pipe(todo())
35 | .pipe(gulp.dest('./'));
36 | // -> Will output a TODO.md with your todos
37 | });
38 |
39 | // generate todo from your jade files
40 | gulp.task('todo-jade', function() {
41 | gulp.src('partials/**/*.jade')
42 | .pipe(todo({ fileName: 'jade-todo.md' }))
43 | .pipe(gulp.dest('./'));
44 | // -> Will output a jade-todo.md with your todos
45 | });
46 |
47 | // get filenames relative to project root (where your gulpfile is)
48 | gulp.task('todo-absolute', function() {
49 | gulp.src('js/**/*.js')
50 | .pipe(todo({
51 | absolute: true
52 | }))
53 | .pipe(gulp.dest('./'));
54 | });
55 |
56 | // get relative path filenames
57 | gulp.task('todo-absolute', function() {
58 | gulp.src('js/**/*.js', { base: '/' })
59 | .pipe(todo())
60 | .pipe(gulp.dest('./'));
61 | });
62 |
63 | // create a json output of the comments (useful for CI such as jenkins)
64 | gulp.task('todo-json', function () {
65 | gulp.src('./**/*.js', {
66 | base: './'
67 | })
68 | .pipe(todo({
69 | fileName: 'todo.json',
70 | reporter: 'json'
71 | }))
72 | .pipe(gulp.dest('./'));
73 | });
74 |
75 | // output once in markdown and then output a json file as well
76 | gulp.task('todo-reporters', function() {
77 | gulp.src('js/**/*.js')
78 | .pipe(todo())
79 | .pipe(gulp.dest('./')) //output todo.md as markdown
80 | .pipe(todo.reporter('json', {fileName: 'todo.json'}))
81 | .pipe(gulp.dest('./')) //output todo.json as json
82 | });
83 |
84 |
85 | // Delete the todo.md file if no todos were found
86 | const gulpIf = require('gulp-if');
87 | const del = require('del');
88 | const vinylPaths = require('vinyl-paths');
89 |
90 | gulp.task('todo-delete', function() {
91 | gulp.src('js/**/*.js')
92 | .pipe(todo())
93 | .pipe(gulpIf(function (file) {
94 | return file.todos && Boolean(file.todos.length);
95 | }, gulp.dest('./'), vinylPaths(del)));
96 | });
97 | ```
98 |
99 | #### Injecting the todo generated file into another template
100 |
101 | If you want to inject the generated todo stream into another file (say a `readme.md.template`)
102 | you can do the following:
103 |
104 | - Create `readme.md.template` file that contains the following marker, marking where you want to inject the generated todo file:
105 |
106 | ```md
107 | ### some previous content
108 | <%= marker %>
109 | ```
110 |
111 | - Use the following code to inject into that markdown, creating a markdown file with the generated todo:
112 |
113 | ```js
114 | const fs = require('fs');
115 | const path = require('path');
116 | const gulp = require('gulp');
117 | const todo = require('gulp-todo');
118 | const template = require('lodash.template');
119 | const through = require('through2');
120 |
121 | gulp.task('default', function () {
122 | gulp.src('./js/**/*.js')
123 | .pipe(todo())
124 | .pipe(through.obj(function (file, enc, cb) {
125 | //read and interpolate template
126 | const tmpl = fs.readFileSync('./readme.md.template', 'utf8');
127 | const compiledTpl = template(tmpl);
128 | const newContents = compiledTpl({
129 | 'marker': file.contents.toString()
130 | });
131 | //change file name
132 | file.path = path.join(file.base, 'readme-new.md');
133 | //replace old contents
134 | file.contents = Buffer.from(newContents);
135 | //push new file
136 | this.push(file);
137 | cb();
138 | }))
139 | .pipe(gulp.dest('./'));
140 | });
141 | ```
142 |
143 | ## Supported Filetypes
144 |
145 | See https://github.com/pgilad/leasot#supported-languages
146 |
147 | ## API
148 |
149 | ### todo(options)
150 |
151 | `options` is an optional configuration object, see https://github.com/pgilad/gulp-todo/blob/master/index.js#L22-L32
152 |
153 | ### todo.reporter(reporter, options)
154 |
155 | `options` is an optional configuration object, see https://github.com/pgilad/gulp-todo/blob/master/lib/reporter.js#L10-L16
156 |
157 | Use another reporter in stream, will replace the contents of the output file.
158 | Must be used after `todo()`, since it uses the `file.todos` that are passed along.
159 |
160 | See the example in the [usage](#usage)
161 |
162 | ## License
163 |
164 | MIT © [Gilad Peleg](https://www.giladpeleg.com)
165 |
--------------------------------------------------------------------------------
/tests/stream-spec.js:
--------------------------------------------------------------------------------
1 | /* global describe,it */
2 | 'use strict';
3 |
4 | const assert = require('assert');
5 | const fs = require('fs');
6 | const path = require('path');
7 | const Vinyl = require('vinyl');
8 | const todo = require('../index');
9 |
10 | const streamFile = function(filename, stream) {
11 | const file = fs.readFileSync(filename);
12 | stream.write(
13 | new Vinyl({
14 | path: filename,
15 | contents: Buffer.from(file.toString()),
16 | })
17 | );
18 |
19 | stream.end();
20 | };
21 |
22 | describe('gulp-todo streaming', function() {
23 | it('should output empty file when getting file with no comments', function(cb) {
24 | const stream = todo();
25 | const files = [];
26 |
27 | const expected = fs.readFileSync('./tests/expected/empty.md', 'utf8').trim();
28 | stream
29 | .on('data', function(file) {
30 | assert.equal(file.contents.toString(), expected);
31 | files.push(file);
32 | })
33 | .on('end', function() {
34 | assert.equal(files.length, 1, 'Make sure only 1 file was outputted');
35 | cb();
36 | });
37 |
38 | streamFile('./tests/stream-spec.js', stream);
39 | });
40 |
41 | it('should parse a file with comments correctly', function(cb) {
42 | const stream = todo();
43 |
44 | stream
45 | .on('data', function(file) {
46 | const _filename = path.basename(file.path);
47 | assert.equal(_filename, 'TODO.md');
48 | assert.ok(/Do something/.test(file._contents.toString()));
49 | assert.ok(/coffee.coffee/.test(file._contents.toString()));
50 | })
51 | .on('end', cb);
52 |
53 | streamFile('./tests/fixtures/coffee.coffee', stream);
54 | });
55 |
56 | it('should output to the correct filename', function(cb) {
57 | const name = 'magic.md';
58 | const stream = todo({
59 | fileName: name,
60 | });
61 |
62 | stream
63 | .on('data', function(file) {
64 | assert.equal(path.basename(file.path), name);
65 | })
66 | .on('end', cb);
67 |
68 | streamFile('./index.js', stream);
69 | });
70 |
71 | it('should work with verbose output', function(cb) {
72 | const stream = todo({
73 | verbose: true,
74 | });
75 | const output = [];
76 |
77 | const write = process.stdout.write;
78 | process.stdout.write = (function(stub) {
79 | return function(string) {
80 | stub.apply(process.stdout, arguments);
81 | output.push(string);
82 | };
83 | })(process.stdout.write);
84 |
85 | stream
86 | .on('data', function(file) {
87 | const _filename = path.basename(file.path);
88 | assert.equal(_filename, 'TODO.md');
89 | assert.ok(/Do something/.test(file._contents.toString()));
90 | assert.ok(/coffee.coffee/.test(file._contents.toString()));
91 | })
92 | .on('end', function() {
93 | //restore write
94 | process.stdout.write = write;
95 | let result = output.join('\n');
96 | assert(/TODO/.test(result));
97 | assert(/coffee.coffee/.test(result));
98 | cb();
99 | });
100 |
101 | streamFile('./tests/fixtures/coffee.coffee', stream);
102 | });
103 |
104 | it('should use custom transformation for header', function(cb) {
105 | const stream = todo({
106 | reportOptions: {
107 | transformHeader: function(kind) {
108 | return ['### //' + kind];
109 | },
110 | },
111 | });
112 |
113 | stream
114 | .on('data', function(file) {
115 | const contents = file._contents.toString();
116 | assert(/### \/\/TODO/.test(contents));
117 | })
118 | .on('end', cb);
119 |
120 | streamFile('./index.js', stream);
121 | });
122 |
123 | it('should use custom transformation for comment', function(cb) {
124 | const stream = todo({
125 | reportOptions: {
126 | transformComment: function(file, line, text) {
127 | return ['* ' + text + ' (at ' + file + ':' + line + ')'];
128 | },
129 | },
130 | });
131 |
132 | stream
133 | .on('data', function(file) {
134 | const contents = file._contents.toString();
135 | assert(/\*\s*(\w+\s*)+\s*\(at.*coffee.coffee:[0-9]+\)/.test(contents));
136 | })
137 | .on('end', cb);
138 |
139 | streamFile('./tests/fixtures/coffee.coffee', stream);
140 | });
141 |
142 | it('should throw if got an unsupported file extension', function(cb) {
143 | const stream = todo();
144 |
145 | stream
146 | .on('error', function(err) {
147 | assert(err);
148 | assert(/is not supported/.test(err.message));
149 | cb();
150 | })
151 | .on('end', cb);
152 |
153 | const file = './index.js';
154 | const testFile = fs.readFileSync(file);
155 |
156 | stream.write(
157 | new Vinyl({
158 | path: './index.unsupported',
159 | contents: Buffer.from(testFile.toString()),
160 | })
161 | );
162 |
163 | stream.end();
164 | });
165 |
166 | it('should skip on unsupported files when skip is true', function(cb) {
167 | const stream = todo({
168 | skipUnsupported: true,
169 | });
170 |
171 | const files = [];
172 |
173 | const expected = fs.readFileSync('./tests/expected/empty.md', 'utf8').trim();
174 | stream
175 | .on('data', function(file) {
176 | assert.equal(file.contents.toString(), expected);
177 | files.push(file);
178 | })
179 | .on('end', function() {
180 | assert.equal(files.length, 1, 'Make sure only 1 file was outputted');
181 | cb();
182 | });
183 |
184 | const file = './index.js';
185 | const testFile = fs.readFileSync(file);
186 |
187 | stream.write(
188 | new Vinyl({
189 | path: './index.unsupported',
190 | contents: Buffer.from(testFile.toString()),
191 | })
192 | );
193 |
194 | stream.end();
195 | });
196 |
197 | it('should show a message about skipping unsupported files if verbose and skip unsupported is true', function(cb) {
198 | const stream = todo({
199 | skipUnsupported: true,
200 | verbose: true,
201 | });
202 |
203 | const files = [];
204 |
205 | const expected = fs.readFileSync('./tests/expected/empty.md', 'utf8').trim();
206 | stream
207 | .on('data', function(file) {
208 | assert.equal(file.contents.toString(), expected);
209 | files.push(file);
210 | })
211 | .on('end', function() {
212 | assert.equal(files.length, 1, 'Make sure only 1 file was outputted');
213 | cb();
214 | });
215 |
216 | const file = './index.js';
217 | const testFile = fs.readFileSync(file);
218 |
219 | stream.write(
220 | new Vinyl({
221 | path: './index.unsupported',
222 | contents: Buffer.from(testFile.toString()),
223 | })
224 | );
225 |
226 | stream.end();
227 | });
228 |
229 | it('should parse a jade file', function(cb) {
230 | const stream = todo();
231 |
232 | stream
233 | .on('data', function(file) {
234 | const _filename = path.basename(file.path);
235 | assert.equal(_filename, 'TODO.md');
236 | const contents = file._contents.toString();
237 | assert.ok(/this is a todo/.test(contents));
238 | assert.ok(!/THERE this isnt a todo/.test(contents));
239 | assert.ok(/also should be caught/.test(contents));
240 | })
241 | .on('end', cb);
242 |
243 | streamFile('./tests/fixtures/comments.jade', stream);
244 | });
245 | });
246 |
--------------------------------------------------------------------------------