Skip to content

Commit

Permalink
Allow passing custom options to babylon or flow via CLI option
Browse files Browse the repository at this point in the history
It is not possible anymore to enable every possible option for babylon,
because some of them are incompatible. That also means that a single
configuration cannot fulfill everybody's needs.

While it has always been possible to pass a custom parser + options  by
using jscodeshift programmatically, this is probably too inconvenient
for many people.

I have been thinking about how to make different configuration sets
available, but every possible combinations of plugins is still
opinionated.

That's why I introduced the `--parse-config` option, which accepts a
path to a JSON file. If passed, the options there will be used instead.

A list of all available options for babylon can be found at
https://babeljs.io/docs/en/babel-parser#options .

To support this I needed to change how parser wrappers are defined. At
first I made `options` a second argument to `.parse`, with the default
options be set as default value (i.e. `.parse(code,
options=defaultOptions`).
However, this causes problems with when *no* custom options are passed.
Recast itself always passes options to `.parse`, in which case the default
options wouldn't be used and parsing would fail.
  • Loading branch information
fkling committed Dec 3, 2018
1 parent ee6242d commit 46d250f
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 117 deletions.
173 changes: 101 additions & 72 deletions bin/__tests__/jscodeshift-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ jest.autoMockOff();
// Increase default timeout (5000ms) for Travis
jasmine.DEFAULT_TIMEOUT_INTERVAL = 600000; // 10 minutes

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var temp = require('temp');
var mkdirp = require('mkdirp');
var testUtils = require('../../utils/testUtils');
const child_process = require('child_process');
const fs = require('fs');
const path = require('path');
const temp = require('temp');
const mkdirp = require('mkdirp');
const testUtils = require('../../utils/testUtils');

var createTransformWith = testUtils.createTransformWith;
var createTempFileWith = testUtils.createTempFileWith;
const createTransformWith = testUtils.createTransformWith;
const createTempFileWith = testUtils.createTempFileWith;

function readFile(path) {
return fs.readFileSync(path).toString();
}

function run(args, stdin, cwd) {
return new Promise(resolve => {
var jscodeshift = child_process.spawn(
const jscodeshift = child_process.spawn(
path.join(__dirname, '../jscodeshift.sh'),
args,
{
cwd,
}
);
var stdout = '';
var stderr = '';
let stdout = '';
let stderr = '';
jscodeshift.stdout.on('data', data => stdout += data);
jscodeshift.stderr.on('data', data => stderr += data);
jscodeshift.on('close', () => resolve([stdout, stderr]));
Expand All @@ -54,13 +54,13 @@ function run(args, stdin, cwd) {
describe('jscodeshift CLI', () => {

it('calls the transform and file information', () => {
var sourceA = createTempFileWith('a');
var sourceB = createTempFileWith('b\n');
var sourceC = createTempFileWith('c');
var transformA = createTransformWith(
const sourceA = createTempFileWith('a');
const sourceB = createTempFileWith('b\n');
const sourceC = createTempFileWith('c');
const transformA = createTransformWith(
'return "transform" + fileInfo.source;'
);
var transformB = createTransformWith(
const transformB = createTransformWith(
'return fileInfo.path;'
);

Expand All @@ -82,10 +82,10 @@ describe('jscodeshift CLI', () => {
});

it('takes file list from stdin if --stdin is set', () => {
var sourceA = createTempFileWith('a');
var sourceB = createTempFileWith('b\n');
var sourceC = createTempFileWith('c');
var transformA = createTransformWith(
const sourceA = createTempFileWith('a');
const sourceB = createTempFileWith('b\n');
const sourceC = createTempFileWith('c');
const transformA = createTransformWith(
'return "transform" + fileInfo.source;'
);

Expand All @@ -100,43 +100,43 @@ describe('jscodeshift CLI', () => {
});

it('does not transform files in a dry run', () => {
var source = createTempFileWith('a');
var transform = createTransformWith(
const source = createTempFileWith('a');
const transform = createTransformWith(
'return "transform" + fileInfo.source;'
);
return run(['-t', transform, '-d', source]).then(
() => {
expect(fs.readFileSync(source).toString()).toBe('a');
expect(readFile(source).toString()).toBe('a');
}
);
});

describe('Babel', () => {

it('loads transform files with Babel if not disabled', () => {
var source = createTempFileWith('a');
var transform = createTransformWith(
const source = createTempFileWith('a');
const transform = createTransformWith(
'return (function() { "use strict"; const a = 42; }).toString();'
);
return Promise.all([
run(['-t', transform, source]).then(
() => {
expect(fs.readFileSync(source).toString())
expect(readFile(source).toString())
.toMatch(/var\s*a\s*=\s*42/);
}
),
run(['-t', transform, '--no-babel', source]).then(
() => {
expect(fs.readFileSync(source).toString())
expect(readFile(source).toString())
.toMatch(/const\s*a\s*=\s*42/);
}
),
]);
});

it('supports proposals in transform files', () => {
var source = createTempFileWith('a');
var transform = createTransformWith(
const source = createTempFileWith('a');
const transform = createTransformWith(
'return (function() {' +
' "use strict"; ' +
' const spread = {}; ' +
Expand All @@ -146,38 +146,38 @@ describe('jscodeshift CLI', () => {
return Promise.all([
run(['-t', transform, source]).then(
() => {
expect(fs.readFileSync(source).toString())
expect(readFile(source).toString())
.toMatch(/\(\{\},\s*spread\)/);
}
),
]);
});

it('supports flow type annotations in transform files', () => {
var source = createTempFileWith('a');
var transform = createTransformWith(
const source = createTempFileWith('a');
const transform = createTransformWith(
'return (function() { "use strict"; const a: number = 42; }).toString();'
);
return Promise.all([
run(['-t', transform, source]).then(
() => {
expect(fs.readFileSync(source).toString())
expect(readFile(source).toString())
.toMatch(/var\s*a\s*=\s*42/);
}
),
]);
});

it('ignores .babelrc files in the directories of the source files', () => {
var transform = createTransformWith(
const transform = createTransformWith(
'return (function() { "use strict"; const a = 42; }).toString();'
);
var babelrc = createTempFileWith(`{"ignore": ["${transform}"]}`, '.babelrc');
var source = createTempFileWith('a', 'source.js');
const babelrc = createTempFileWith(`{"ignore": ["${transform}"]}`, '.babelrc');
const source = createTempFileWith('a', 'source.js');

return run(['-t', transform, source]).then(
() => {
expect(fs.readFileSync(source).toString())
expect(readFile(source).toString())
.toMatch(/var\s*a\s*=\s*42/);
}
);
Expand All @@ -186,36 +186,36 @@ describe('jscodeshift CLI', () => {
});

it('passes jscodeshift and stats the transform function', () => {
var source = createTempFileWith('a');
var transform = createTransformWith([
const source = createTempFileWith('a');
const transform = createTransformWith([
' return String(',
' typeof api.jscodeshift === "function" &&',
' typeof api.stats === "function"',
' );',
].join('\n'));
return run(['-t', transform, source]).then(
() => {
expect(fs.readFileSync(source).toString()).toBe('true');
expect(readFile(source).toString()).toBe('true');
}
);
});

it('passes options along to the transform', () => {
var source = createTempFileWith('a');
var transform = createTransformWith('return options.foo;');
const source = createTempFileWith('a');
const transform = createTransformWith('return options.foo;');
return run(['-t', transform, '--foo=42', source]).then(
() => {
expect(fs.readFileSync(source).toString()).toBe('42');
expect(readFile(source).toString()).toBe('42');
}
);
});

it('does not stall with too many files', () => {
var sources = [];
for (var i = 0; i < 100; i++) {
const sources = [];
for (let i = 0; i < 100; i++) {
sources.push(createTempFileWith('a'));
}
var transform = createTransformWith('');
const transform = createTransformWith('');
return run(['-t', transform, '--foo=42'].concat(sources)).then(
() => {
expect(true).toBe(true);
Expand All @@ -224,10 +224,10 @@ describe('jscodeshift CLI', () => {
});

describe('ignoring', () => {
var transform = createTransformWith(
const transform = createTransformWith(
'return "transform" + fileInfo.source;'
);
var sources = [];
let sources = [];

beforeEach(() => {
sources = [];
Expand All @@ -237,69 +237,69 @@ describe('jscodeshift CLI', () => {
});

it('supports basic glob', () => {
var pattern = '*-test.js';
const pattern = '*-test.js';
return run(['-t', transform, '--ignore-pattern', pattern].concat(sources)).then(
() => {
expect(fs.readFileSync(sources[0]).toString()).toBe('transforma');
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
expect(readFile(sources[0]).toString()).toBe('transforma');
expect(readFile(sources[1]).toString()).toBe('a');
}
);
});

it('supports filename match', () => {
var pattern = 'a.js';
const pattern = 'a.js';
return run(['-t', transform, '--ignore-pattern', pattern].concat(sources)).then(
() => {
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
expect(fs.readFileSync(sources[1]).toString()).toBe('transforma');
expect(readFile(sources[0]).toString()).toBe('a');
expect(readFile(sources[1]).toString()).toBe('transforma');
}
);
});

it('accepts a list of patterns', () => {
var patterns = ['--ignore-pattern', 'a.js', '--ignore-pattern', '*-test.js'];
const patterns = ['--ignore-pattern', 'a.js', '--ignore-pattern', '*-test.js'];
return run(['-t', transform].concat(patterns).concat(sources)).then(
() => {
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
expect(readFile(sources[0]).toString()).toBe('a');
expect(readFile(sources[1]).toString()).toBe('a');
}
);
});

it('sources ignore patterns from configuration file', () => {
var patterns = ['sub/dir/', '*-test.js'];
var gitignore = createTempFileWith(patterns.join('\n'), '.gitignore');
const patterns = ['sub/dir/', '*-test.js'];
const gitignore = createTempFileWith(patterns.join('\n'), '.gitignore');
sources.push(createTempFileWith('subfile', 'sub/dir/file.js'));

return run(['-t', transform, '--ignore-config', gitignore].concat(sources)).then(
() => {
expect(fs.readFileSync(sources[0]).toString()).toBe('transforma');
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
expect(fs.readFileSync(sources[2]).toString()).toBe('subfile');
expect(readFile(sources[0]).toString()).toBe('transforma');
expect(readFile(sources[1]).toString()).toBe('a');
expect(readFile(sources[2]).toString()).toBe('subfile');
}
);
});

it('accepts a list of configuration files', () => {
var gitignore = createTempFileWith(['sub/dir/'].join('\n'), '.gitignore');
var eslintignore = createTempFileWith(['**/*test.js', 'a.js'].join('\n'), '.eslintignore');
var configs = ['--ignore-config', gitignore, '--ignore-config', eslintignore];
const gitignore = createTempFileWith(['sub/dir/'].join('\n'), '.gitignore');
const eslintignore = createTempFileWith(['**/*test.js', 'a.js'].join('\n'), '.eslintignore');
const configs = ['--ignore-config', gitignore, '--ignore-config', eslintignore];
sources.push(createTempFileWith('subfile', 'sub/dir/file.js'));

return run(['-t', transform].concat(configs).concat(sources)).then(
() => {
expect(fs.readFileSync(sources[0]).toString()).toBe('a');
expect(fs.readFileSync(sources[1]).toString()).toBe('a');
expect(fs.readFileSync(sources[2]).toString()).toBe('subfile');
expect(readFile(sources[0]).toString()).toBe('a');
expect(readFile(sources[1]).toString()).toBe('a');
expect(readFile(sources[2]).toString()).toBe('subfile');
}
);
});
});

describe('output', () => {
it('shows workers info and stats at the end by default', () => {
var source = createTempFileWith('a');
var transform = createTransformWith('return null;');
const source = createTempFileWith('a');
const transform = createTransformWith('return null;');
return run(['-t', transform, source]).then(
out => {
expect(out[0]).toContain('Processing 1 files...');
Expand All @@ -313,8 +313,8 @@ describe('jscodeshift CLI', () => {
});

it('does not ouput anything in silent mode', () => {
var source = createTempFileWith('a');
var transform = createTransformWith('return null;');
const source = createTempFileWith('a');
const transform = createTransformWith('return null;');
return run(['-t', transform, '-s', source]).then(
out => {
expect(out[0]).toEqual('');
Expand All @@ -323,4 +323,33 @@ describe('jscodeshift CLI', () => {
});
})

describe('--parser-config', () => {
it('allows custom parser settings to be passed', () => {
// @decorators before export are not supported in the current default
// config
const source = createTempFileWith('@foo\nexport class Bar {}');
const parserConfig = createTempFileWith(JSON.stringify({
sourceType: 'module',
plugins: [
['decorators', {decoratorsBeforeExport: true}],
],
}));
const transform = createTransformWith(
'api.jscodeshift(fileInfo.source)\nreturn "changed";'
);
return run([
'-t', transform,
'--parser-config', parserConfig,
'--parser', 'babylon',
'--run-in-band',
source,
]).then(
out => {
expect(out[0]).not.toContain('Transformation error');
expect(readFile(source)).toEqual('changed');
}
);
});
});

});
Loading

0 comments on commit 46d250f

Please sign in to comment.