Skip to content

Commit

Permalink
Improve all codemod related APIs to support returning a boolean, indi…
Browse files Browse the repository at this point in the history
…cating if a change was made

#40
  • Loading branch information
ganemone authored and generic-probot-app-workflow[bot] committed Nov 12, 2018
1 parent 4c2423c commit 4bf0a80
Show file tree
Hide file tree
Showing 10 changed files with 880 additions and 112 deletions.
1 change: 1 addition & 0 deletions src/core/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ test('StepperError', () => {
});

test('API', () => {
// $FlowFixMe
expect(Object.keys(API)).toMatchInlineSnapshot(`
Array [
"Stepper",
Expand Down
12 changes: 6 additions & 6 deletions src/utils/ensure-js-imports.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,39 @@ test('ensureJsImports', () => {
const path = parseJs('');
const vars = ensureJsImports(path, `import foo, {bar} from 'bar';`);
const code = generateJs(path);
expect(code.trim()).toEqual(`import foo, { bar } from "bar";`);
expect(code.trim()).toEqual(`import foo, { bar } from 'bar';`);
expect(vars).toEqual([{default: 'foo', bar: 'bar'}]);
});

test('ensureJsImports after', () => {
const path = parseJs(`import 'x';`);
const vars = ensureJsImports(path, `import foo, {bar} from 'bar';`);
const code = generateJs(path);
expect(code.trim()).toEqual(`import "x";\nimport foo, { bar } from "bar";`);
expect(code.trim()).toEqual(`import 'x';\nimport foo, { bar } from 'bar';`);
expect(vars).toEqual([{default: 'foo', bar: 'bar'}]);
});

test('ensureJsImports before', () => {
const path = parseJs(`const a = 1;`);
const vars = ensureJsImports(path, `import foo, {bar} from 'bar';`);
const code = generateJs(path);
expect(code.trim()).toEqual(`import foo, { bar } from "bar";\nconst a = 1;`);
expect(code.trim()).toEqual(`import foo, { bar } from 'bar';\nconst a = 1;`);
expect(vars).toEqual([{default: 'foo', bar: 'bar'}]);
});

test('merge', () => {
const path = parseJs(`import {x} from 'foo'`);
const vars = ensureJsImports(path, `import foo, {bar} from 'foo';`);
const code = generateJs(path);
expect(code.trim()).toEqual(`import foo, { x, bar } from "foo";`);
expect(code.trim()).toEqual(`import foo, { x, bar } from 'foo';`);
expect(vars).toEqual([{default: 'foo', x: 'x', bar: 'bar'}]);
});

test('retain default', () => {
const path = parseJs(`import foo from 'foo'`);
const vars = ensureJsImports(path, `import wildcard from 'foo';`);
const code = generateJs(path);
expect(code.trim()).toEqual(`import foo from "foo";`);
expect(code.trim()).toEqual(`import foo from 'foo';`);
expect(vars).toEqual([{default: 'foo'}]);
});

Expand All @@ -74,7 +74,7 @@ test('multiple', () => {
);
const code = generateJs(path);
expect(code.trim()).toEqual(
`import foo from "foo";\nimport bar, { x } from "bar";`,
`import foo from 'foo';\nimport bar, { x } from 'bar';`,
);
expect(vars).toEqual([{default: 'foo'}, {default: 'bar', x: 'x'}]);
});
5 changes: 3 additions & 2 deletions src/utils/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ export type GenerateJsOptions = ?{
};

export const generateJs = (path: NodePath, options: GenerateJsOptions) => {
const formatter = options ? options.formatter : 'prettier';
const formatter = options ? options.formatter : 'babel';
const formatterOptions = options ? options.formatterOptions : {};
switch (formatter) {
case 'babel':
return generate(path.node, formatterOptions);
const generated = generate(path.parent, formatterOptions);
return generated.code;
case 'prettier':
default:
// placeholder is needed to prevent prettier from short-circuiting before the parser runs
Expand Down
3 changes: 2 additions & 1 deletion src/utils/parse-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import traverse from '@babel/traverse';
import NodePath from '@babel/traverse/lib/path';
import {parse} from '@babel/parser';
import {readFile} from './read-file.js';
import generate from '@babel/generator';

export type ParserOptions = ?{mode: ?('typescript' | 'flow')};

Expand Down Expand Up @@ -65,7 +66,7 @@ export const parseJs = (code: string, options: ParserOptions) => {

// ensure `path` has correct type to keep flow.js happy
// we always override the dummy NodePath with the `enter` visitor
let path = new NodePath();
let path = null;
traverse(ast, {
enter(p) {
if (p.isProgram()) path = p;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/parse-js.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ test('parseJs', async () => {
const code = 'const a = 1;';
const path = parseJs(code);
const generated = generateJs(path);
expect(code).toEqual(generated.trim());
expect(generated.trim()).toEqual(code);
});
5 changes: 4 additions & 1 deletion src/utils/replace-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const replaceJs = (
source: string,
target: string,
wildcards: Array<string> = [],
) => {
): boolean => {
const sourcePath = parseJs(source);
const sourceNode = sourcePath.node.body[0];
const node =
Expand Down Expand Up @@ -58,15 +58,18 @@ export const replaceJs = (
}
},
});
let spreadReplaced = false;
path.traverse({
Identifier(identifierPath) {
const args = spreads[identifierPath.node.name];
const parent = identifierPath.parentPath;
if (args !== undefined && parent.node.type === 'SpreadElement') {
spreadReplaced = true;
parent.replaceWithMultiple(args);
}
},
});
return matched.size > 0 || spreadReplaced;
};

function match(a, b, interpolations = {}, spreads = {}) {
Expand Down
32 changes: 15 additions & 17 deletions src/utils/visit-js-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
export const visitJsImport = (
path: NodePath,
source: string,
handler: (path: NodePath, refPaths: Array<NodePath>) => void,
handler: (path: NodePath, refPaths: Array<NodePath>) => Boolean | void,
) => {
const sourcePath = parseJs(source);
const sourceNode = sourcePath.node.body[0];
Expand All @@ -50,6 +50,7 @@ export const visitJsImport = (
}
const sourceSpecifier = specifiers[0];
const localName = specifiers[0].local.name;
let hasChange = false;
path.traverse({
ImportDeclaration(ipath: NodePath) {
const sourceName = ipath.get('source').node.value;
Expand All @@ -61,25 +62,22 @@ export const visitJsImport = (
const binding = ipath.scope.bindings[targetName];
const refPaths = binding ? binding.referencePaths : [];
if (
isImportSpecifier(targetSpecifier) &&
isImportSpecifier(sourceSpecifier) &&
localName === targetName
// specifier case
(isImportSpecifier(targetSpecifier) &&
isImportSpecifier(sourceSpecifier) &&
localName === targetName) ||
// namespace import case
(isImportNamespaceSpecifier(targetSpecifier) &&
isImportNamespaceSpecifier(sourceSpecifier)) ||
// default import case
(isImportDefaultSpecifier(targetSpecifier) &&
isImportDefaultSpecifier(sourceSpecifier))
) {
return handler(ipath, refPaths);
}
if (
isImportNamespaceSpecifier(targetSpecifier) &&
isImportNamespaceSpecifier(sourceSpecifier)
) {
return handler(ipath, refPaths);
}
if (
isImportDefaultSpecifier(targetSpecifier) &&
isImportDefaultSpecifier(sourceSpecifier)
) {
return handler(ipath, refPaths);
hasChange = hasChange || handler(ipath, refPaths);
return;
}
});
},
});
return hasChange;
};
17 changes: 12 additions & 5 deletions src/utils/with-js-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ THE SOFTWARE.
@flow
*/

import traverse from '@babel/traverse';
import {readFile} from './read-file.js';
import {writeFile} from './write-file.js';
import {parseJs} from './parse-js.js';
Expand All @@ -32,8 +31,16 @@ export type JsFileMutation = NodePath => void;

export const withJsFile = async (file: string, transform: JsFileMutation) => {
const code = await readFile(file).catch(() => '');
const path = parseJs(code);
await transform(path);
const generated = generateJs(path);
await writeFile(file, generated);
try {
const program = parseJs(code);
const result = await transform(program);
if (result === false) {
return;
}
const generated = generateJs(program);
await writeFile(file, generated);
} catch (e) {
console.log(`Failed to handle file: ${file}`);
throw e;
}
};
6 changes: 5 additions & 1 deletion src/utils/with-json-file.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ test('withJsonFile defaults to empty object if it does not exist', async () => {
test('withJsonFile throws error w/ filename when invalid JSON', async () => {
const file = '__json_2__.json';
await writeFile(file, '');
await expect(withJsonFile(file, () => {})).rejects.toThrow(/__json_2__.json/);
await expect(
withJsonFile(file, () => {
return Promise.resolve();
}),
).rejects.toThrow(/__json_2__.json/);
await fse.remove(file);
});
Loading

0 comments on commit 4bf0a80

Please sign in to comment.