├── test ├── files │ ├── filtering │ │ ├── file1.txt │ │ ├── file2.js │ │ ├── .dir-to-ignore │ │ │ └── file1.js │ │ ├── foo_file2.js │ │ ├── bar_file1.jsx │ │ ├── foo_file1.jsx │ │ └── file1.js │ ├── outside-cwd │ │ └── file1.js │ ├── html │ │ └── original │ │ │ ├── css │ │ │ └── file1.css │ │ │ ├── file1.html │ │ │ ├── level1 │ │ │ └── file2.js │ │ │ ├── file with spaces in name.js │ │ │ ├── images │ │ │ └── file1.png │ │ │ └── index.html │ ├── modules │ │ └── original │ │ │ ├── file1.conf │ │ │ ├── file1.html │ │ │ ├── file1.json │ │ │ ├── file1.txt │ │ │ ├── file2.js │ │ │ ├── file1.js.bak │ │ │ ├── rootfile1.js │ │ │ ├── rootfile3.js │ │ │ ├── rootfile4.js │ │ │ ├── level1a │ │ │ ├── file3.js │ │ │ ├── file3.jsx │ │ │ ├── file5.js │ │ │ ├── level2a │ │ │ │ ├── file6.js │ │ │ │ └── level3a │ │ │ │ │ ├── file2.txt │ │ │ │ │ └── file8.js │ │ │ └── file-with-references.jsx │ │ │ ├── level1b │ │ │ ├── file1.css │ │ │ ├── file4.js │ │ │ └── level2b │ │ │ │ ├── file2.jsx │ │ │ │ ├── file7.js │ │ │ │ └── file2.json │ │ │ ├── rootfile with spaces in name 2.js │ │ │ └── file-with-references.js │ ├── ref-outside-cwd │ │ └── referencing-file.js │ └── .reffixrc ├── fixtures │ ├── htmlFilesToMove.json │ └── moduleFilesToMove.json ├── OutsideWcdSpec.js ├── ReportSpec.js ├── helpers │ └── fileMover.js ├── FixedHtmlFileSpec.js ├── FixedModuleFileSpec.js ├── OriginalHtmlFilesSpec.js ├── MovedHTMLFileSpec.js ├── MovedModuleFileSpec.js ├── ConfigSpec.js ├── FilteringSpec.js └── OriginalModuleFilesSpec.js ├── img ├── example1.png └── example2.png ├── index.js ├── CHANGELOG.md ├── lib ├── qExtensions.js ├── gitParser.js ├── prompter.js ├── utils.js ├── updater.js ├── config.js ├── reporter.js ├── cli.js └── parser.js ├── LICENSE ├── config.json ├── package.json └── README.md /test/files/filtering/file1.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/filtering/file2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/outside-cwd/file1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/html/original/css/file1.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/html/original/file1.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file1.conf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file1.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file1.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file1.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/html/original/level1/file2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/file1.js.bak: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/rootfile1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/rootfile3.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/rootfile4.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/filtering/.dir-to-ignore/file1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1a/file3.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1a/file3.jsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1a/file5.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1b/file1.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1b/file4.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/html/original/file with spaces in name.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1a/level2a/file6.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1b/level2b/file2.jsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1b/level2b/file7.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1b/level2b/file2.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/filtering/foo_file2.js: -------------------------------------------------------------------------------- 1 | import module1 from './file1'; -------------------------------------------------------------------------------- /test/files/modules/original/level1a/level2a/level3a/file2.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/level1a/level2a/level3a/file8.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/modules/original/rootfile with spaces in name 2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/files/filtering/bar_file1.jsx: -------------------------------------------------------------------------------- 1 | import module1 from './file1'; -------------------------------------------------------------------------------- /test/files/filtering/foo_file1.jsx: -------------------------------------------------------------------------------- 1 | import module1 from './file1'; -------------------------------------------------------------------------------- /img/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkendall/reffix/HEAD/img/example1.png -------------------------------------------------------------------------------- /img/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkendall/reffix/HEAD/img/example2.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var cli = require('./lib/cli'); 4 | 5 | cli.start(); -------------------------------------------------------------------------------- /test/files/ref-outside-cwd/referencing-file.js: -------------------------------------------------------------------------------- 1 | import module1 from '../outside-cwd/file1.js'; -------------------------------------------------------------------------------- /test/files/modules/original/level1a/file-with-references.jsx: -------------------------------------------------------------------------------- 1 | import module from '../level1b/file4'; -------------------------------------------------------------------------------- /test/fixtures/htmlFilesToMove.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "index.html", 4 | "level1/index.html" 5 | ] 6 | ] -------------------------------------------------------------------------------- /test/files/filtering/file1.js: -------------------------------------------------------------------------------- 1 | import module1 from './foo_file1.jsx'; 2 | import module1 from './bar_file1.jsx'; -------------------------------------------------------------------------------- /test/files/html/original/images/file1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkendall/reffix/HEAD/test/files/html/original/images/file1.png -------------------------------------------------------------------------------- /test/files/.reffixrc: -------------------------------------------------------------------------------- 1 | { 2 | "test": { 3 | "referencingFileFilter": [ 4 | "test.*" 5 | ], 6 | "referencedFileFilter": [ 7 | "test_test.*" 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v0.1.1 (2016-07-27) 2 | 3 | - By default exclude comments from HTML files 4 | 5 | ### v0.1.2 (2016-07-29) 6 | 7 | - Improved accuracy of handling duplicate filenames when using git status. -------------------------------------------------------------------------------- /lib/qExtensions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | 5 | // Extend Q to handle errors in a consistent way 6 | Q.makePromise.prototype.handleError = function() { 7 | return this.catch(function(err) { 8 | console.error(err.stack); 9 | return err; 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/moduleFilesToMove.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "file-with-references.js", 4 | "level1c/level2c/file-with-references.js" 5 | ], 6 | [ 7 | "level1b/file4.js", 8 | "level1a/file4.js" 9 | ], 10 | [ 11 | "level1b/level2b/file7.js", 12 | "file7.js" 13 | ] 14 | ] -------------------------------------------------------------------------------- /test/files/html/original/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | link 13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/OutsideWcdSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var config = require('../lib/config');
5 | var parser = require('../lib/parser');
6 |
7 | describe('Outside CWD', function() {
8 |
9 | var testWorkingDir = 'test/files/ref-outside-cwd';
10 |
11 | describe('References', function() {
12 | var files;
13 | var options = {
14 | workingDirectory: testWorkingDir
15 | };
16 | beforeEach(function(done) {
17 | config.reset();
18 | config.forceSet(options);
19 | parser.getReferences().then(function(result) {
20 | files = result;
21 | done();
22 | });
23 | });
24 | it('Should find file outside of CWD', function() {
25 | expect(Object.keys(files.referencedFiles)).to.have.length(1);
26 | expect(files.referencedFiles).to.have.property(path.join(process.cwd(), 'test/files/outside-cwd/file1.js'));
27 | });
28 |
29 | });
30 |
31 | });
32 |
--------------------------------------------------------------------------------
/test/ReportSpec.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 |
3 | var config = require('../lib/config');
4 | var parser = require('../lib/parser');
5 | var reporter = require('../lib/reporter');
6 |
7 | describe('Reports', function() {
8 | var filesCheckedReport;
9 | var referencingFilesReport;
10 | var options = {
11 | workingDirectory: 'test/files/modules/original',
12 | referencingFileFilter: ['*.js'],
13 | referencedFileFilter: ['*.js']
14 | };
15 | beforeEach(function(done) {
16 | config.forceSet(options);
17 | parser.getAll()
18 | .then(function(fileData) {
19 | filesCheckedReport = reporter.reportFilesChecked(fileData);
20 | var reportOptions = {referencingFiles: true};
21 | referencingFilesReport = reporter.getReport(fileData, reportOptions);
22 | done();
23 | });
24 | });
25 | it('Should report correct number of files checked', function() {
26 | expect(filesCheckedReport).to.contain('12 files');
27 | });
28 |
29 | });
--------------------------------------------------------------------------------
/test/files/modules/original/file-with-references.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import module1 from './level1a/file-with-references.jsx';
4 | import $ from './file2.js';
5 |
6 | /*
7 | * import module1 from './file12.js';
8 | *
9 | */
10 |
11 | import { member1, member2 } from './level1a/level2a/file6';
12 | import * as name from "./rootfile1";
13 | import defaultMember, { member1, member2 }
14 | from "./rootfile with spaces in name 2";
15 | import defaultMember, * as name from "./rootfile3";
16 | import './rootfile4.js'
17 | // import module2a from './file12.js';
18 |
19 |
20 | var module3 = require('./level1a/file3.jsx');
21 | const module4 = require('./level1b/file4.js');
22 | const module5 = require ("./level1b/level2b/file2.jsx");
23 |
24 | let module6 = require( './level1b/level2b/file7' ).someMethod;
25 | var module7 = require("./level1a/file5.js")();
26 |
27 | // var module8 = require('./file10.js');
28 |
29 | /*
30 | * import module9 from './file11.js';
31 | *
32 | */
33 |
34 | let foo = bar;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 | Copyright (c) 2016 Robert Kendall
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": {
3 | "workingDirectory": ".",
4 | "directoriesToExclude": [
5 | ".*",
6 | "node_modules",
7 | "build"
8 | ],
9 | "referencingFileFilter": [
10 | "*.js",
11 | "*.jsx"
12 | ],
13 | "referencedFileFilter": [
14 | "*.*"
15 | ],
16 | "searchPatterns": [
17 | "(^|\\s)import\\s+([\\w\\-\\{\\}\\,\\*\\$\\s]+\\s+)?(['\"]){{valuePattern}}\\3",
18 | "(^|[^\\w-])require *\\( *(['\"]){{valuePattern}}\\2 *\\)"
19 | ],
20 | "valuePattern": "\\.+/[^'\"]+",
21 | "currentDirectoryPrefix": "./",
22 | "textToExclude": [
23 | "// *[^\\n]+\\n",
24 | "/\\*[\\s\\S]+?\\*/"
25 | ]
26 | },
27 | "html": {
28 | "referencingFileFilter": [
29 | "*.html",
30 | "*.js"
31 | ],
32 | "referencedFileFilter": [
33 | "*.*"
34 | ],
35 | "searchPatterns": [
36 | "]*href=(['\"]){{valuePattern}}\\1[^>]*>",
37 | "]*href=(['\"]){{valuePattern}}\\1[^>]*>",
38 | "');
45 | expect(content).to.contain('');
46 | expect(content).to.contain('link');
47 | expect(content).to.contain('
');
48 | done();
49 | });
50 | })
51 | });
52 |
53 | });
--------------------------------------------------------------------------------
/test/FixedModuleFileSpec.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 | var expect = require('chai').expect;
4 | var mv = require('mv');
5 |
6 | var fileMover = require('./helpers/fileMover');
7 | var config = require('../lib/config');
8 | var updater = require('../lib/updater');
9 |
10 | var filesToMove = require('./fixtures/moduleFilesToMove.json');
11 |
12 | describe('File Repair for Modules', function() {
13 | var testBaseDir = 'test/files/modules/moved';
14 | var testDir = 'modules';
15 | var options = {
16 | workingDirectory: testBaseDir
17 | };
18 | var baseDir = path.resolve(testBaseDir) + '/';
19 | before(function(done) {
20 | fileMover.moveFiles(testDir, filesToMove, done);
21 | });
22 | after(function(done) {
23 | fileMover.deleteMovedFiles(testDir, done);
24 | });
25 | describe('References', function() {
26 | var files;
27 | var pathOfFileWithReferences = path.join(baseDir, 'level1c/level2c/file-with-references.js');
28 | beforeEach(function(done) {
29 | config.forceSet(options);
30 | updater.fixReferences().then(function(result) {
31 | files = result;
32 | done();
33 | })
34 | });
35 | it('Should fix correct files', function() {
36 | expect(files).to.have.length(2);
37 | expect(files[0]).to.equal(path.join(baseDir, 'level1a/file-with-references.jsx'));
38 | expect(files[1]).to.equal(pathOfFileWithReferences);
39 | });
40 | it('Should correctly update file content', function(done) {
41 | fs.readFile(pathOfFileWithReferences, 'utf8', function(err, content) {
42 | expect(content).to.contain("import module1 from '../../level1a/file-with-references.jsx';");
43 | expect(content).to.contain("const module4 = require('../../level1a/file4.js');");
44 | expect(content).to.contain("let module6 = require( '../../file7.js' ).someMethod;");
45 | done();
46 | });
47 | })
48 | });
49 |
50 | });
--------------------------------------------------------------------------------
/test/OriginalHtmlFilesSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var config = require('../lib/config');
5 | var parser = require('../lib/parser');
6 |
7 | describe('Original HTML Files', function() {
8 |
9 | var testWorkingDir = 'test/files/html/original';
10 |
11 | describe('References', function() {
12 | var files;
13 | var options = {
14 | mode: 'html',
15 | workingDirectory: testWorkingDir
16 | };
17 | beforeEach(function(done) {
18 | config.reset();
19 | config.set(options);
20 | parser.getReferences().then(function(result) {
21 | files = result;
22 | done();
23 | });
24 | });
25 | afterEach(function() {
26 | config.reset();
27 | });
28 | it('Should get correct number of files', function() {
29 | expect(Object.keys(files.existingFiles)).to.have.length(6);
30 | });
31 | it('Should get correct number of referencing files', function() {
32 | expect(Object.keys(files.referencingFiles)).to.have.length(1);
33 | });
34 | it('Should get correct file paths', function() {
35 | expect(files.referencingFiles).to.have.property(path.resolve(options.workingDirectory, 'index.html'));
36 | });
37 | it('Should get correct number of references within files', function() {
38 | expect(Object.keys(files.referencedFiles)).to.have.length(5);
39 | });
40 | it('Should get correct file paths', function() {
41 | expect(files.referencedFiles).to.have.property(path.resolve(options.workingDirectory, 'level1/file2.js'));
42 | });
43 | });
44 |
45 | describe('Filtered References for CSS only', function() {
46 | var files;
47 | var options = {
48 | mode: 'html',
49 | workingDirectory: testWorkingDir,
50 | referencedFileFilter: ['*.css']
51 | };
52 | beforeEach(function(done) {
53 | config.set(options);
54 | parser.getReferences().then(function(result) {
55 | files = result;
56 | done();
57 | })
58 | });
59 | it('Should get correct number of references within files', function() {
60 | expect(Object.keys(files.referencedFiles)).to.have.length(1);
61 | })
62 | });
63 |
64 | describe('Broken References', function() {
65 | var brokenReferences;
66 | var options = {
67 | workingDirectory: testWorkingDir
68 | };
69 | beforeEach(function(done) {
70 | config.forceSet(options);
71 | parser.getBrokenReferences().then(function(result) {
72 | brokenReferences = result.brokenReferences;
73 | done();
74 | })
75 | });
76 | it('Should find no broken references', function() {
77 | expect(Object.keys(brokenReferences)).to.have.length(0);
78 | });
79 | });
80 |
81 | });
--------------------------------------------------------------------------------
/test/MovedHTMLFileSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var fileMover = require('./helpers/fileMover');
5 | var config = require('../lib/config');
6 | var parser = require('../lib/parser');
7 |
8 | var filesToMove = require('./fixtures/htmlFilesToMove.json');
9 |
10 | describe('Moved HMTL Files', function() {
11 |
12 | var testBaseDir = 'test/files/html/moved';
13 | var testDir = 'html';
14 | var fullBasePath = path.resolve(testBaseDir) + '/';
15 |
16 | before(function(done) {
17 | fileMover.moveFiles(testDir, filesToMove, done);
18 | });
19 | after(function(done) {
20 | fileMover.deleteMovedFiles(testDir, done);
21 | });
22 |
23 | describe('Broken References', function() {
24 | var brokenReferences;
25 | var options = {
26 | mode: 'html',
27 | workingDirectory: testBaseDir
28 | };
29 | beforeEach(function(done) {
30 | config.reset();
31 | config.set(options);
32 | parser.getBrokenReferences(options)
33 | .then(function(result) {
34 | brokenReferences = result.brokenReferences;
35 | done();
36 | })
37 | });
38 | afterEach(function() {
39 | config.reset();
40 | });
41 | it('Should find correct number of broken references', function() {
42 | expect(brokenReferences).to.have.length(5);
43 | });
44 |
45 | it('Should correctly identify broken references, referencers, and correct paths', function() {
46 |
47 | var result1 = brokenReferences.find(function(brokenReference) {
48 | if (brokenReference.referencedFile === fullBasePath + 'level1/css/file1.css') {
49 | return true;
50 | }
51 | });
52 | expect(result1).to.be.ok;
53 | expect(result1.referencingFiles).to.contain(fullBasePath + 'level1/index.html');
54 | expect(result1.correctPath).to.equal(fullBasePath + 'css/file1.css');
55 |
56 | var result2 = brokenReferences.find(function(brokenReference) {
57 | if (brokenReference.referencedFile.indexOf(fullBasePath + '' + 'level1/file1.html') !== -1) {
58 | return true;
59 | }
60 | });
61 | expect(result2).to.be.ok;
62 | expect(result2.referencingFiles).to.contain(fullBasePath + 'level1/index.html');
63 | expect(result2.correctPath).to.equal(fullBasePath + 'file1.html');
64 |
65 | var result3 = brokenReferences.find(function(brokenReference) {
66 | if (brokenReference.referencedFile.indexOf(fullBasePath + 'level1/file with spaces in name.js') !== -1) {
67 | return true;
68 | }
69 | });
70 | expect(result3).to.be.ok;
71 | expect(result3.referencingFiles).to.contain(fullBasePath + 'level1/index.html');
72 | expect(result3.correctPath).to.equal(fullBasePath + 'file with spaces in name.js');
73 |
74 | });
75 |
76 | });
77 |
78 | });
--------------------------------------------------------------------------------
/test/MovedModuleFileSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var fileMover = require('./helpers/fileMover');
5 | var config = require('../lib/config');
6 | var parser = require('../lib/parser');
7 |
8 | var filesToMove = require('./fixtures/moduleFilesToMove.json');
9 |
10 | describe('Moved Files', function() {
11 |
12 | var testBaseDir = 'test/files/modules/moved';
13 | var testDir = 'modules';
14 | var fullBasePath = path.resolve(testBaseDir) + '/';
15 |
16 | before(function(done) {
17 | fileMover.moveFiles(testDir, filesToMove, done);
18 | });
19 | after(function(done) {
20 | fileMover.deleteMovedFiles(testDir, done);
21 | });
22 |
23 | describe('Broken References', function() {
24 | var brokenReferences;
25 | var options = {
26 | workingDirectory: testBaseDir
27 | };
28 | beforeEach(function(done) {
29 | config.reset();
30 | config.forceSet(options);
31 | parser.getBrokenReferences(options)
32 | .then(function(result) {
33 | brokenReferences = result.brokenReferences;
34 | done();
35 | })
36 | });
37 | afterEach(function() {
38 | config.reset();
39 | });
40 | it('Should find correct number of broken references', function() {
41 | expect(brokenReferences).to.have.length(13);
42 | });
43 | it('Should correctly identify broken references, referencers, and correct paths', function() {
44 | var result1 = brokenReferences.find(function(brokenReference) {
45 | if (brokenReference.referencedFile === fullBasePath + 'level1c/level2c/level1a/file-with-references.jsx') {
46 | return true;
47 | }
48 | });
49 | expect(result1).to.be.ok;
50 | expect(result1.referencingFiles).to.contain(fullBasePath + 'level1c/level2c/file-with-references.js');
51 | expect(result1.correctPath).to.equal(fullBasePath + 'level1a/file-with-references.jsx');
52 |
53 | var result2 = brokenReferences.find(function(brokenReference) {
54 | if (brokenReference.referencedFile.indexOf(fullBasePath + 'level1c/level2c/level1b/file4.js') !== -1) {
55 | return true;
56 | }
57 | });
58 | expect(result2).to.be.ok;
59 | expect(result2.referencingFiles).to.contain(fullBasePath + 'level1c/level2c/file-with-references.js');
60 | expect(result2.correctPath).to.equal(fullBasePath + 'level1a/file4.js');
61 |
62 | var result3 = brokenReferences.find(function(brokenReference) {
63 | if (brokenReference.referencedFile.indexOf(fullBasePath + 'level1c/level2c/level1b/level2b/file7.js') !== -1) {
64 | return true;
65 | }
66 | });
67 | expect(result3).to.be.ok;
68 | expect(result3.referencingFiles).to.contain(fullBasePath + 'level1c/level2c/file-with-references.js');
69 | expect(result3.correctPath).to.equal(fullBasePath + 'file7.js');
70 | });
71 |
72 | });
73 |
74 | });
--------------------------------------------------------------------------------
/test/ConfigSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var config = require('../lib/config');
5 |
6 | describe('Configuration', function() {
7 |
8 | describe('Should accept options', function() {
9 |
10 | beforeEach(function() {
11 | config.reset();
12 | });
13 | afterEach(function() {
14 | config.reset();
15 | });
16 |
17 | it('Should load correct value for working dir', function() {
18 | var workingDir = path.join(process.cwd(), 'test/files/modules');
19 | var options = {
20 | workingDirectory: workingDir
21 | };
22 | config.set(options);
23 | var settings = config.get();
24 | expect(settings.workingDirectory).to.equal(workingDir);
25 | });
26 | it('Should change modes correctly', function() {
27 | config.setMode('html');
28 | var settings = config.get();
29 | expect(settings.referencingFileFilter).to.have.length(2);
30 | expect(settings.referencingFileFilter).to.contain('*.html');
31 | expect(settings.workingDirectory).to.equal(process.cwd());
32 | });
33 | it('Should accept new file filter', function() {
34 | var options = {
35 | referencingFileFilter: ['index.js']
36 | };
37 | config.set(options);
38 | var settings = config.get();
39 | expect(settings.referencingFileFilter).to.have.length(1);
40 | expect(settings.referencingFileFilter).to.contain('index.js');
41 | });
42 | it('Should change modes and accept new file filter', function() {
43 | config.setMode('html');
44 | var options = {
45 | referencingFileFilter: ['index.html']
46 | };
47 | config.set(options);
48 | var settings = config.get();
49 | expect(settings.referencingFileFilter).to.have.length(1);
50 | expect(settings.referencingFileFilter).to.contain('index.html');
51 | });
52 |
53 | });
54 |
55 | describe('Should accept custom rc file', function() {
56 |
57 | beforeEach(function() {
58 | config.reset();
59 | var pathOfRcFile = path.join(process.cwd(), 'test/files/.reffixrc');
60 | config.set(null, pathOfRcFile);
61 | });
62 | afterEach(function() {
63 | config.reset();
64 | });
65 |
66 | it('Should add test mode', function() {
67 | config.setMode('test');
68 | var settings = config.get();
69 | expect(settings.referencingFileFilter).to.have.length(1);
70 | expect(settings.referencingFileFilter).to.contain('test.*');
71 | expect(settings.referencedFileFilter).to.have.length(1);
72 | expect(settings.referencedFileFilter).to.contain('test_test.*');
73 | expect(settings.workingDirectory).to.equal(process.cwd());
74 | });
75 | it('Should not change default settings', function() {
76 | config.setMode('default');
77 | var settings = config.get();
78 | expect(settings.referencingFileFilter).to.have.length(2);
79 | expect(settings.referencingFileFilter).to.contain('*.js');
80 | expect(settings.referencingFileFilter).to.contain('*.jsx');
81 | expect(settings.workingDirectory).to.equal(process.cwd());
82 | });
83 |
84 | })
85 |
86 | });
--------------------------------------------------------------------------------
/test/FilteringSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var config = require('../lib/config');
5 | var parser = require('../lib/parser');
6 |
7 | describe('Filtering', function() {
8 |
9 | var testWorkingDir = 'test/files/filtering';
10 |
11 | describe('Should filter directories', function() {
12 | var files;
13 | var options = {
14 | workingDirectory: testWorkingDir,
15 | "directoriesToExclude": [
16 | ".*",
17 | "node_modules"
18 | ],
19 | "referencingFileFilter": [
20 | "*.js",
21 | "*.jsx"
22 | ],
23 | "referencedFileFilter": [
24 | "*.js",
25 | "*.jsx"
26 | ]
27 | };
28 | beforeEach(function(done) {
29 | config.forceSet(options);
30 | parser.getReferences().then(function(result) {
31 | files = result;
32 | done();
33 | });
34 | });
35 | afterEach(function() {
36 | config.reset();
37 | });
38 | it('Should get correct number of existing files by filtering directories', function() {
39 | expect(Object.keys(files.existingFiles)).to.have.length(5);
40 | });
41 | it('Should get correct number of referencing files', function() {
42 | expect(Object.keys(files.referencingFiles)).to.have.length(4);
43 | });
44 | });
45 |
46 | describe('Should filter files by name', function() {
47 | var files;
48 | var options = {
49 | workingDirectory: testWorkingDir,
50 | "directoriesToExclude": [
51 | ],
52 | "referencingFileFilter": [
53 | "*1.*"
54 | ],
55 | "referencedFileFilter": [
56 | "*.*",
57 | "!foo_file*.*"
58 | ]
59 | };
60 | beforeEach(function(done) {
61 | config.forceSet(options);
62 | parser.getReferences().then(function(result) {
63 | files = result;
64 | done();
65 | });
66 | });
67 | afterEach(function() {
68 | config.reset();
69 | });
70 | it('Should get correct number of referencing/referenced files', function() {
71 | expect(Object.keys(files.referencingFiles)).to.have.length(3);
72 | expect(Object.keys(files.referencedFiles)).to.have.length(2);
73 | expect(Object.keys(files.referencedFiles)).to.not.contain(path.join(process.cwd(), 'test/files/filtering/foo_file1.jsx'));
74 | });
75 | });
76 |
77 | describe('Should filter files by excluding sources', function() {
78 | var files;
79 | var options = {
80 | workingDirectory: testWorkingDir,
81 | "directoriesToExclude": [
82 | ],
83 | "referencingFileFilter": [
84 | "!foo*.*"
85 | ],
86 | "referencedFileFilter": [
87 | "*.js",
88 | "*.jsx"
89 | ]
90 | };
91 | beforeEach(function(done) {
92 | config.forceSet(options);
93 | parser.getReferences().then(function(result) {
94 | files = result;
95 | done();
96 | });
97 | });
98 | afterEach(function() {
99 | config.reset();
100 | });
101 | it('Should get correct number of referencing/referenced files', function() {
102 | expect(Object.keys(files.referencingFiles)).to.have.length(2);
103 | expect(Object.keys(files.referencedFiles)).to.have.length(3);
104 | });
105 | });
106 |
107 | });
108 |
--------------------------------------------------------------------------------
/test/OriginalModuleFilesSpec.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var expect = require('chai').expect;
3 |
4 | var config = require('../lib/config');
5 | var parser = require('../lib/parser');
6 |
7 | describe('Original Files', function() {
8 |
9 | var testWorkingDir = 'test/files/modules/original';
10 |
11 | describe('References', function() {
12 | var files;
13 | var options = {
14 | workingDirectory: testWorkingDir
15 | };
16 | beforeEach(function(done) {
17 | config.reset();
18 | config.set(options);
19 | parser.getReferences().then(function(result) {
20 | files = result;
21 | done();
22 | });
23 | });
24 | afterEach(function() {
25 | config.reset();
26 | });
27 | it('Should get correct number of files', function() {
28 | expect(Object.keys(files.existingFiles)).to.have.length(23);
29 | });
30 | it('Should get correct number of referencing files', function() {
31 | expect(Object.keys(files.referencingFiles)).to.have.length(2);
32 | });
33 | it('Should get correct file paths', function() {
34 | expect(files.existingFiles).to.have.property(path.join(process.cwd(), options.workingDirectory, 'file1.json'));
35 | expect(files.existingFiles).to.have.property(path.join(process.cwd(), options.workingDirectory, 'level1a/file-with-references.jsx'));
36 | expect(files.existingFiles).to.have.property(path.join(process.cwd(), options.workingDirectory, 'level1a/level2a/level3a/file8.js'));
37 | });
38 | it('Should get correct number of references within files', function() {
39 | expect(Object.keys(files.referencedFiles)).to.have.length(12);
40 | })
41 | });
42 |
43 | describe('Filtered References with one glob', function() {
44 | var files;
45 | var options = {
46 | workingDirectory: testWorkingDir,
47 | referencingFileFilter: ['*.js'],
48 | referencedFileFilter: ['*.js']
49 | };
50 | beforeEach(function(done) {
51 | config.forceSet(options);
52 | parser.getReferences().then(function(result) {
53 | files = result;
54 | done();
55 | })
56 | });
57 | it('Should get correct number of existing files', function() {
58 | expect(Object.keys(files.existingFiles)).to.have.length(12);
59 | });
60 | it('Should get correct number of references within files', function() {
61 | expect(Object.keys(files.referencedFiles)).to.have.length(9);
62 | })
63 | });
64 |
65 | describe('Filtered References with two globs', function() {
66 | var files;
67 | var options = {
68 | workingDirectory: testWorkingDir,
69 | referencingFileFilter: ['*.js', '*.jsx'],
70 | referencedFileFilter: ['*.js', '*.jsx']
71 | };
72 | beforeEach(function(done) {
73 | config.forceSet(options);
74 | parser.getReferences().then(function(result) {
75 | files = result;
76 | done();
77 | })
78 | });
79 | it('Should get correct number of existing files', function() {
80 | expect(Object.keys(files.existingFiles)).to.have.length(15);
81 | });
82 | it('Should get correct number of references within files', function() {
83 | expect(Object.keys(files.referencedFiles)).to.have.length(12);
84 | })
85 | });
86 |
87 | describe('Broken References', function() {
88 | var brokenReferences;
89 | var options = {
90 | workingDirectory: testWorkingDir
91 | };
92 | beforeEach(function(done) {
93 | config.forceSet(options);
94 | parser.getBrokenReferences().then(function(result) {
95 | brokenReferences = result.brokenReferences;
96 | done();
97 | })
98 | });
99 | it('Should find no broken references', function() {
100 | expect(Object.keys(brokenReferences)).to.have.length(0);
101 | });
102 | });
103 |
104 | });
--------------------------------------------------------------------------------
/lib/gitParser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var execa = require('execa');
4 | var path = require('path');
5 | // parser must be loaded at runtime because it calls gitParser,
6 | // which then calls parser; must ensure that parser has finished loading
7 | // before it is called by gitParser
8 | var parser;
9 |
10 | var utils = require('./utils');
11 | var config = require('./config');
12 |
13 | var gitParser = {
14 |
15 | renamedFiles: {},
16 |
17 | parse: function() {
18 | var self = this;
19 | this.loadParser();
20 | return this.getStatus().then(function(statusData) {
21 | if (statusData) {
22 | self.renamedFiles = self.parseStatus(statusData);
23 | return true;
24 | }
25 | return false;
26 | });
27 | },
28 |
29 | getCorrectedPathFromGit: function(brokenReference) {
30 | // If referencing file was moved
31 | var originalPathBeforeReferencingFileWasMoved = this.getCorrectedPathIfReferencingFileWasMoved(brokenReference);
32 | var pathToCheckToSeeIfReferencedFileWasMoved = originalPathBeforeReferencingFileWasMoved || brokenReference.referencedFile;
33 | // If referenced file was moved
34 | var originalPathBeforeReferencedFileWasMoved = this.getCorrectedPathIfReferencedFileWasMoved(pathToCheckToSeeIfReferencedFileWasMoved);
35 | var correctedPath = originalPathBeforeReferencedFileWasMoved || originalPathBeforeReferencingFileWasMoved || null;
36 | return correctedPath;
37 | },
38 |
39 | loadParser: function() {
40 | parser = require('./parser');
41 | },
42 |
43 | getStatus: function() {
44 | var workingDirectory = config.get().workingDirectory;
45 | var options = [
46 | '--git-dir=' + path.join(workingDirectory, '/.git'),
47 | '--work-tree=' + workingDirectory,
48 | 'status',
49 | '--porcelain'
50 | ];
51 | return execa('git', options)
52 | .then(function(result) {
53 | return result.stdout;
54 | })
55 | .catch(function() {
56 | return null;
57 | });
58 | },
59 |
60 | parseStatus: function(status) {
61 | var renamedFiles = {};
62 | var workingDirectory = config.get().workingDirectory;
63 | var modifiedFilePattern = /(?:^|\n)R[ A-Z] (.+?) -> ([^\n]+)/g;
64 | var match;
65 | while ((match = modifiedFilePattern.exec(status)) !== null) {
66 | var oldPath = path.resolve(workingDirectory, match[1].replace(/['"]/g, ''));
67 | var newPath = path.resolve(workingDirectory, match[2].replace(/['"]/g, ''));
68 | renamedFiles[oldPath] = newPath;
69 | }
70 | return renamedFiles;
71 | },
72 |
73 | getCorrectedPathIfReferencingFileWasMoved: function(brokenReference) {
74 | var pathOfFirstReferencingFile = brokenReference.referencingFiles[0];
75 | var originalPathOfReferencingFile = this.getOriginalPath(pathOfFirstReferencingFile);
76 | if (originalPathOfReferencingFile) {
77 | var referencedPath = brokenReference.referencedFile;
78 | var relativePathOfBrokenReference = parser.getRelativePathOfReference(pathOfFirstReferencingFile, referencedPath);
79 | var referencedPathBeforeFileWasMoved = utils.getAbsolutePathFromRelativePath(originalPathOfReferencingFile, relativePathOfBrokenReference);
80 | return referencedPathBeforeFileWasMoved;
81 | }
82 | return null;
83 | },
84 |
85 | getCorrectedPathIfReferencedFileWasMoved: function(referencedPath) {
86 | var pathOfRenamedFile = this.renamedFiles[referencedPath];
87 | return pathOfRenamedFile || null;
88 | },
89 |
90 | getOriginalPath: function(renamedPathToFind) {
91 | for (var originalPath in this.renamedFiles) {
92 | var renamedPath = this.renamedFiles[originalPath];
93 | if (renamedPath === renamedPathToFind) {
94 | return originalPath;
95 | }
96 | }
97 | return null;
98 | }
99 |
100 | };
101 |
102 | module.exports = {
103 | parse: gitParser.parse.bind(gitParser),
104 | getCorrectedPathFromGit: gitParser.getCorrectedPathFromGit.bind(gitParser)
105 | };
--------------------------------------------------------------------------------
/lib/prompter.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var inquirer = require('inquirer');
4 | var clc = require('cli-color');
5 | var Q = require('q');
6 |
7 | var config = require('./config').default;
8 | var parser = require('./parser');
9 | var updater = require('./updater');
10 | var reporter = require('./reporter');
11 |
12 | var prompter = {
13 |
14 | start: function(options) {
15 | var self = this;
16 | config.set(options);
17 | var settings = config.get();
18 | console.log('\n' + clc.blue.bold('Looking for broken references in ') + clc.blue(settings.workingDirectory));
19 | parser.getAll()
20 | .then(function(references) {
21 | console.log(clc.blue.bold('Searching ' + Object.keys(references.existingFiles).length + ' files\n'));
22 | console.log(parser.getReport(references.brokenReferences));
23 | self.promptToCorrect(references.brokenReferences);
24 | })
25 | .handleError();
26 | },
27 |
28 | promptToCorrect: function(brokenReferences) {
29 | var self = this;
30 | var numberOfFilesToFix = parser.getNumberOfFilesToFix(brokenReferences);
31 | var referencesWithMultiplePossibleCorrections = parser.getReferencesWithMultiplePossibleCorrections(brokenReferences);
32 | if (numberOfFilesToFix || Object.keys(referencesWithMultiplePossibleCorrections).length) {
33 | Q(inquirer.prompt({
34 | type: 'confirm',
35 | name: 'correct',
36 | message: 'Update files to correct references?'
37 | }))
38 | .then(function(confirmed) {
39 | if (confirmed.correct) {
40 | return self.correctAndReport(brokenReferences);
41 | }
42 | return false;
43 | })
44 | .then(function(promptToFix) {
45 | if (promptToFix) {
46 | self.promptToSelectCorrectPath(brokenReferences);
47 | }
48 | })
49 | .handleError();
50 | }
51 |
52 | },
53 |
54 | // PRIVATE METHODS
55 |
56 | correctAndReport: function(brokenReferences) {
57 | return updater.update(brokenReferences)
58 | .then(function(namesArrayOfFilesFixed) {
59 | reporter.showReport(reporter.reportFilesUpdated(namesArrayOfFilesFixed));
60 | return true;
61 | })
62 | .handleError();
63 | },
64 |
65 | promptToSelectCorrectPath: function(brokenReferences) {
66 | var self = this;
67 | var referencesWithMultiplePossibleCorrections = parser.getReferencesWithMultiplePossibleCorrections(brokenReferences);
68 | if (!referencesWithMultiplePossibleCorrections.length) {
69 | return;
70 | }
71 | var questions = [];
72 | referencesWithMultiplePossibleCorrections.forEach(function(referenceWithMultipleOptions) {
73 | var newQuestions = self.setPromptQuestionForEachFile(referenceWithMultipleOptions);
74 | questions = questions.concat(newQuestions);
75 | });
76 | console.log('\n');
77 | Q(inquirer.prompt(questions))
78 | .then(function(answers) {
79 | var fixedReferences = [];
80 | for (var referenceToCorrect in answers) {
81 | var brokenReference = brokenReferences.find(function(reference) {
82 | return reference.referencedFile === referenceToCorrect;
83 | });
84 | brokenReference.correctPath = answers[referenceToCorrect];
85 | fixedReferences.push(brokenReference);
86 | }
87 | self.correctAndReport(fixedReferences);
88 | })
89 | .handleError();
90 | },
91 |
92 | setPromptQuestionForEachFile: function(referenceWithMultipleOptions) {
93 | var questions = [];
94 | referenceWithMultipleOptions.referencingFiles.forEach(function(referencingFile) {
95 | questions.push({
96 | type: 'list',
97 | name: referenceWithMultipleOptions.referencedFile,
98 | message: clc.blue('\nChoose the correct path for this broken reference: ') + referenceWithMultipleOptions.referencedFile + clc.blue('\nwhich is contained in this file: ') + referencingFile + clc.blue('\nSelect one of the following paths:\n'),
99 | choices: referenceWithMultipleOptions.possibleCorrectPaths
100 | });
101 | });
102 | return questions;
103 | }
104 |
105 | };
106 |
107 | module.exports = {
108 | start: prompter.start.bind(prompter),
109 | promptToCorrect: prompter.promptToCorrect.bind(prompter)
110 | };
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var mime = require('mime');
6 | var Q = require('q');
7 | var minimatch = require('minimatch');
8 | var XRegExp = require('../vendor/xregexp-all');
9 |
10 | var utils = {
11 |
12 | getSearchPatterns: function(replacementString, options) {
13 | var valuePattern = replacementString
14 | ? XRegExp.escape(replacementString)
15 | : options.valuePattern;
16 | return options.searchPatterns.map(function(generalPattern) {
17 | return XRegExp.build(generalPattern, {
18 | valuePattern: XRegExp('(?