Skip to content

Commit 5d58f41

Browse files
chrisdarrochfkling
authored andcommitted
Can ignore paths before jscodeshift runs
1 parent add35fc commit 5d58f41

File tree

5 files changed

+182
-10
lines changed

5 files changed

+182
-10
lines changed

bin/__tests__/jscodeshift-test.js

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var child_process = require('child_process');
2020
var fs = require('fs');
2121
var path = require('path');
2222
var temp = require('temp');
23+
var mkdirp = require('mkdirp');
2324
require('es6-promise').polyfill();
2425

2526
function run(args, stdin, cwd) {
@@ -44,11 +45,23 @@ function run(args, stdin, cwd) {
4445
}
4546

4647
describe('jscodeshift CLI', () => {
47-
function createTempFileWith(content) {
48+
function createTempFileWith(content, filename) {
4849
var info = temp.openSync();
50+
var filePath = info.path;
4951
fs.writeSync(info.fd, content);
5052
fs.closeSync(info.fd);
51-
return info.path;
53+
if (filename) {
54+
filePath = renameFileTo(filePath, filename);
55+
}
56+
return filePath;
57+
}
58+
59+
function renameFileTo(oldPath, newFilename) {
60+
var projectPath = path.dirname(oldPath);
61+
var newPath = path.join(projectPath, newFilename);
62+
mkdirp.sync(path.dirname(newPath));
63+
fs.renameSync(oldPath, newPath);
64+
return newPath;
5265
}
5366

5467
function createTransformWith(content) {
@@ -120,13 +133,8 @@ describe('jscodeshift CLI', () => {
120133
var transform = createTransformWith(
121134
'return (function() { "use strict"; const a = 42; }).toString();'
122135
);
123-
var babelrc = createTempFileWith(`{"ignore": ["${transform}"]}`);
124-
//var babelrc = createTempFileWith('{}');
125-
var projectPath = path.dirname(babelrc);
126-
fs.renameSync(babelrc, path.join(projectPath, '.babelrc'));
127-
var source = createTempFileWith('a');
128-
fs.renameSync(source, path.join(projectPath, 'source.js'));
129-
source = path.join(projectPath, 'source.js');
136+
var babelrc = createTempFileWith(`{"ignore": ["${transform}"]}`, '.babelrc');
137+
var source = createTempFileWith('a', 'source.js');
130138

131139
return run(['-t', transform, source]).then(
132140
([stdout, stderr]) => {
@@ -174,6 +182,79 @@ describe('jscodeshift CLI', () => {
174182
);
175183
});
176184

185+
describe('ignoring', () => {
186+
var transform = createTransformWith(
187+
'return "transform" + fileInfo.source;'
188+
);
189+
var sources = [];
190+
191+
beforeEach(() => {
192+
sources = [];
193+
sources.push(createTempFileWith('a', 'a.js'));
194+
sources.push(createTempFileWith('a', 'a-test.js'));
195+
// sources.push(createTempFileWith('b', 'src/lib/b.js'));
196+
});
197+
198+
pit('supports basic glob', () => {
199+
var pattern = '*-test.js';
200+
return run(['-t', transform, '--ignore-pattern', pattern, ...sources]).then(
201+
([stdout, stderr]) => {
202+
expect(fs.readFileSync(sources[0]).toString()).toBe('transforma');
203+
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
204+
}
205+
);
206+
});
207+
208+
pit('supports filename match', () => {
209+
var pattern = 'a.js';
210+
return run(['-t', transform, '--ignore-pattern', pattern, ...sources]).then(
211+
([stdout, stderr]) => {
212+
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
213+
expect(fs.readFileSync(sources[1]).toString()).toBe('transforma');
214+
}
215+
);
216+
});
217+
218+
pit('accepts a list of patterns', () => {
219+
var patterns = ['--ignore-pattern', 'a.js', '--ignore-pattern', '*-test.js'];
220+
return run(['-t', transform, ...patterns, ...sources]).then(
221+
([stdout, stderr]) => {
222+
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
223+
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
224+
}
225+
);
226+
});
227+
228+
pit('sources ignore patterns from configuration file', () => {
229+
var patterns = ['sub/dir/', '*-test.js'];
230+
var gitignore = createTempFileWith(patterns.join('\n'), '.gitignore');
231+
sources.push(createTempFileWith('subfile', 'sub/dir/file.js'));
232+
233+
return run(['-t', transform, '--ignore-config', gitignore, ...sources]).then(
234+
([stdout, stderr]) => {
235+
expect(fs.readFileSync(sources[0]).toString()).toBe('transforma');
236+
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
237+
expect(fs.readFileSync(sources[2]).toString()).toBe('subfile');
238+
}
239+
);
240+
});
241+
242+
pit('accepts a list of configuration files', () => {
243+
var gitignore = createTempFileWith(['sub/dir/'].join('\n'), '.gitignore');
244+
var eslintignore = createTempFileWith(['**/*test.js', 'a.js'].join('\n'), '.eslintignore');
245+
var configs = ['--ignore-config', gitignore, '--ignore-config', eslintignore];
246+
sources.push(createTempFileWith('subfile', 'sub/dir/file.js'));
247+
248+
return run(['-t', transform, ...configs, ...sources]).then(
249+
([stdout, stderr]) => {
250+
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
251+
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
252+
expect(fs.readFileSync(sources[2]).toString()).toBe('subfile');
253+
}
254+
);
255+
});
256+
});
257+
177258
describe('output', () => {
178259
pit('shows workers info and stats at the end by default', () => {
179260
var source = createTempFileWith('a');

bin/jscodeshift.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ var opts = require('nomnom')
5959
default: 'js',
6060
help: 'File extensions the transform file should be applied to'
6161
},
62+
ignorePattern: {
63+
full: 'ignore-pattern',
64+
list: true,
65+
help: 'Ignore files that match a provided glob expression'
66+
},
67+
ignoreConfig: {
68+
full: 'ignore-config',
69+
list: true,
70+
help: 'Ignore files if they match patterns sourced from a configuration file (e.g., a .gitignore)',
71+
metavar: 'FILE'
72+
},
6273
runInBand: {
6374
flag: true,
6475
default: false,

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"colors": "^1.1.2",
3131
"es6-promise": "^3.0.0",
3232
"lodash": "^3.5.0",
33+
"micromatch": "^2.3.7",
3334
"node-dir": "0.1.8",
3435
"nomnom": "^1.8.1",
3536
"recast": "^0.11.0",
@@ -38,7 +39,8 @@
3839
"devDependencies": {
3940
"babel": "^5.6.14",
4041
"babel-jest": "^5.3.0",
41-
"jest-cli": "^0.9.0"
42+
"jest-cli": "^0.9.0",
43+
"mkdirp": "^0.5.1"
4244
},
4345
"jest": {
4446
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",

src/Runner.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const path = require('path');
1919
const http = require('http');
2020
const https = require('https');
2121
const temp = require('temp');
22+
const ignores = require('./ignoreFiles');
2223

2324
const availableCpus = require('os').cpus().length - 1;
2425
const CHUNK_SIZE = 50;
@@ -86,6 +87,9 @@ function dirFiles (dir, callback, acc) {
8687
'Skipping path "' + name + '" which does not exist.\n'
8788
);
8889
done();
90+
} else if (ignores.shouldIgnore(name)) {
91+
// ignore the path
92+
done();
8993
} else if (stats.isDirectory()) {
9094
dirFiles(name + '/', callback, acc);
9195
} else {
@@ -113,6 +117,9 @@ function getAllFiles(paths, filter) {
113117
file,
114118
list => resolve(list.filter(filter))
115119
);
120+
} else if (ignores.shouldIgnore(file)) {
121+
// ignoring the file
122+
resolve([]);
116123
} else {
117124
resolve([file]);
118125
}
@@ -130,6 +137,9 @@ function run(transformFile, paths, options) {
130137
const statsCounter = {};
131138
const startTime = process.hrtime();
132139

140+
ignores.add(options.ignorePattern);
141+
ignores.addFromFile(options.ignoreConfig);
142+
133143
if (/^http/.test(transformFile)) {
134144
usedRemoteScript = true;
135145
return new Promise((resolve, reject) => {

src/ignoreFiles.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const mm = require('micromatch');
5+
6+
const matchers = [];
7+
8+
/**
9+
* Add glob patterns to ignore matched files and folders.
10+
* Creates glob patterns to approximate gitignore patterns.
11+
* @param {String} val - the glob or gitignore-style pattern to ignore
12+
* @see {@linkplain https://git-scm.com/docs/gitignore#_pattern_format}
13+
*/
14+
function addIgnorePattern(val) {
15+
if (val && typeof val === 'string') {
16+
let pattern = val;
17+
if (pattern.indexOf('/') === -1) {
18+
matchers.push('**/' + pattern);
19+
} else if (pattern[pattern.length-1] === '/') {
20+
matchers.push('**/' + pattern + '**');
21+
matchers.push(pattern + '**');
22+
}
23+
matchers.push(pattern);
24+
}
25+
}
26+
27+
/**
28+
* Adds ignore patterns directly from function input
29+
* @param {String|Array<String>} input - the ignore patterns
30+
*/
31+
function addIgnoreFromInput(input) {
32+
let patterns = [];
33+
if (input) {
34+
patterns = patterns.concat(input);
35+
}
36+
patterns.forEach(addIgnorePattern);
37+
}
38+
39+
/**
40+
* Adds ignore patterns by reading files
41+
* @param {String|Array<String>} input - the paths to the ignore config files
42+
*/
43+
function addIgnoreFromFile(input) {
44+
let lines = [];
45+
let files = [];
46+
if (input) {
47+
files = files.concat(input);
48+
}
49+
50+
files.forEach(function(config) {
51+
var stats = fs.statSync(config);
52+
if (stats.isFile()) {
53+
var content = fs.readFileSync(config, 'utf8');
54+
lines = lines.concat(content.split('\n'));
55+
}
56+
});
57+
58+
lines.forEach(addIgnorePattern);
59+
}
60+
61+
function shouldIgnore(path) {
62+
var matched = matchers.length ? mm.any(path, matchers, { dot:true }) : false;
63+
return matched;
64+
}
65+
66+
exports.add = addIgnoreFromInput;
67+
exports.addFromFile = addIgnoreFromFile;
68+
exports.shouldIgnore = shouldIgnore;

0 commit comments

Comments
 (0)