List of jscodeshift transformers that I cooked to address some chore tasks
Last active
September 15, 2020 09:57
-
-
Save selvagsz/9ffb7e3ab19b0dccce0d3bd1fd7436a5 to your computer and use it in GitHub Desktop.
CodeMods that I'd used
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Changes | |
import TableRowComponent from 'tradegecko/components/table-row'; | |
import TableRowComponent from 'tradegecko/components/table-row'; | |
export default TableRowComponent.extend({}); | |
to | |
import TableRowComponent from 'tradegecko/components/table-row'; | |
import TableRowComponent from 'tradegecko/components/table-row'; | |
export default TableRowComponent.extend({}); | |
*/ | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.ImportDeclaration) | |
.at(-1) | |
.forEach((path) => { | |
path.insertAfter('\n'); | |
}) | |
.toSource() | |
.replace(/\n\s*\n\s*\n/, '\n\n'); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Adds the `module` & `test` qunit imports | |
import { module, test } from 'ember-qunit'; | |
|| | |
\/ | |
import { module, test } from 'qunit'; | |
import { module, test, moduleForHelper } from 'ember-qunit'; | |
|| | |
\/ | |
import { module, test } from 'qunit'; | |
import { moduleForHelper } from 'ember-qunit'; | |
*/ | |
const getQunitImportDeclaration = j => | |
j.importDeclaration( | |
[j.importSpecifier(j.identifier('module')), j.importSpecifier(j.identifier('test'))], | |
j.literal('qunit') | |
); | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
let qunitImports = j(file.source) | |
.find(j.ImportDeclaration) | |
.filter(path => { | |
return path.value.source.value === 'qunit'; | |
}); | |
if (qunitImports.length) { | |
return; | |
} | |
let emberQunitImports = j(file.source) | |
.find(j.ImportDeclaration) | |
.filter(path => { | |
return path.value.source.value === 'ember-qunit'; | |
}); | |
if (emberQunitImports.length) { | |
return emberQunitImports | |
.forEach(path => { | |
let otherImportSpecifiers = path.value.specifiers.filter(specifier => { | |
return !['module', 'test'].includes(specifier.imported.name); | |
}); | |
if (otherImportSpecifiers.length) { | |
j(path).replaceWith(j.importDeclaration(otherImportSpecifiers, j.literal('ember-qunit'))); | |
path.parentPath.value.unshift(getQunitImportDeclaration(j)); | |
} else { | |
j(path).replaceWith(getQunitImportDeclaration(j)); | |
} | |
}) | |
.toSource(); | |
} | |
return j(file.source) | |
.find(j.Program) | |
.forEach(path => { | |
path.value.body.unshift(getQunitImportDeclaration(j)); | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Adds `sinon` import if being using in the file | |
*/ | |
const getSinonImportDeclaration = j => | |
j.importDeclaration([j.importDefaultSpecifier(j.identifier('sinon'))], j.literal('sinon')); | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
let sinonUsages = j(file.source).find(j.Identifier, { name: 'sinon' }).length; | |
if (sinonUsages) { | |
return j(file.source) | |
.find(j.Program) | |
.forEach(path => { | |
path.value.body.unshift(getSinonImportDeclaration(j)); | |
}) | |
.toSource(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Super custom codemod that I never care to do any clean ups. But gets the job done | |
const computedExts = [ | |
'add', | |
'append', | |
'boundEqual', | |
'convert', | |
'defaultTo', | |
'divide', | |
'divideRounded', | |
'findBy', | |
'groupBy', | |
'humanize', | |
'includes', | |
'isEvery', | |
'isNone', | |
'isThis', | |
'lookup', | |
'multiply', | |
'multiplyRounded', | |
'nearestOfType', | |
'parseNumber', | |
'prefix', | |
'rejectBy', | |
'subtract', | |
'suffix', | |
'sumBy', | |
'uniqSumBy', | |
'getBy', | |
'slice', | |
]; | |
const EMBER_COMPUTED_IMPORT_PATH = '@ember/object/computed'; | |
const COMPUTED_EXT_IMPORT_PATH = 'tradegecko/utils/computed-ext'; | |
const findDefaultImportDeclarations = function(collection, j, importPath) { | |
return collection.find(j.ImportDeclaration).filter((path) => { | |
return path.value.source.value === importPath; | |
}); | |
}; | |
const contructDefaultImportDeclaration = (j, identifier, path) => | |
j.importDeclaration([j.importDefaultSpecifier(j.identifier(identifier))], j.literal(path)); | |
const addImportStmts = function(j, computedNamedImports) { | |
const extSpecifiers = []; | |
const defaultSpecifiers = []; | |
computedNamedImports.forEach((cp) => { | |
const isExtCP = computedExts.includes(cp); | |
let importPath = isExtCP ? COMPUTED_EXT_IMPORT_PATH : EMBER_COMPUTED_IMPORT_PATH; | |
const computedExtNode = findDefaultImportDeclarations(this, j, importPath); | |
if (computedExtNode.size()) { | |
let computedExtNodePath = computedExtNode.paths()[0]; | |
if ( | |
!j(computedExtNodePath.value.specifiers) | |
.find(j.Identifier, { name: cp }) | |
.size() | |
) { | |
computedExtNodePath.value.specifiers.push(j.importSpecifier(j.identifier(cp))); | |
} | |
} else { | |
isExtCP ? extSpecifiers.push(cp) : defaultSpecifiers.push(cp); | |
} | |
}); | |
if (extSpecifiers.length) { | |
this.find(j.Program).forEach((nodePath) => { | |
nodePath.value.body.splice( | |
this.find(j.ImportDeclaration).size(), | |
0, | |
contructDefaultImportDeclaration(j, `{ ${extSpecifiers.join(', ')} }`, COMPUTED_EXT_IMPORT_PATH) | |
); | |
}); | |
} | |
if (defaultSpecifiers.length) { | |
this.find(j.Program).forEach((nodePath) => { | |
nodePath.value.body.splice( | |
this.find(j.ImportDeclaration).size(), | |
0, | |
contructDefaultImportDeclaration(j, `{ ${defaultSpecifiers.join(', ')} }`, EMBER_COMPUTED_IMPORT_PATH) | |
); | |
}); | |
} | |
return this; | |
}; | |
module.exports = function transformer(file, api) { | |
const computedNamedImports = []; | |
const j = api.jscodeshift; | |
const ast = j(file.source); | |
j.registerMethods({ | |
addImportStmts: addImportStmts.bind(ast, j, computedNamedImports), | |
}); | |
const computedVariableDeclarations = (path) => path.value.declarations[0].init && path.value.declarations[0].init.name === 'computed'; | |
const getNamedComputeds = (path) => | |
j(path.value.declarations[0]) | |
.find(j.Property) | |
.forEach((path) => { | |
computedNamedImports.push(path.value.value.name); | |
}); | |
ast.find(j.MemberExpression, { object: { name: 'computed' } }).forEach((path) => { | |
let computedFnName = path.value.property.name; | |
computedNamedImports.push(computedFnName); | |
path.replace(j.identifier(computedFnName)); | |
}); | |
return ast | |
.find(j.VariableDeclaration) | |
.filter(computedVariableDeclarations) | |
.forEach(getNamedComputeds) | |
.remove() | |
.addImportStmts() | |
.toSource({ quote: 'single' }) | |
.replace(/\n\nimport/, '\nimport') | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Remove "decaffeinate" comments | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.Comment) | |
.forEach(path => { | |
if (path.value.value.includes('decaffeinate suggestions')) { | |
j(path).replaceWith(''); | |
} | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.CallExpression) | |
.filter(path => path.value.callee.name === 'test') | |
.find(j.BlockStatement) | |
.forEach(path => { | |
let returnStatement = path.value.body.pop() | |
path.value.body.push(j.expressionStatement( | |
returnStatement.argument | |
)); | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Adds `assert` params to the `test` callbacks & replaces global asserts | |
test('Foo Bar Baz', function() { | |
ok(someBlah, 'blha blah') | |
notOk(someBlah, 'blha blah') | |
equal(someBlah, 'blah', 'blha blah') | |
notEqual(someBlah, 'blah', 'blha blah') | |
}); | |
|| | |
\/ | |
test('Foo Bar Baz', function(assert) { | |
assert.ok(someBlah, 'blha blah') | |
assert.notOk(someBlah, 'blha blah') | |
assert.equal(someBlah, 'blah', 'blha blah') | |
assert.equal(someBlah, 'blah', 'blha blah') | |
}); | |
*/ | |
const QUNIT_ASSERTIONS = ['equal', 'ok', 'deepEqual', 'notOk', 'notEqual']; | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.CallExpression, { callee: { name: 'test' } }) | |
.find(j.FunctionExpression) | |
.filter(path => { | |
return path.parentPath.parentPath.value.callee.name === 'test'; | |
}) | |
.forEach(path => { | |
path.value.params = ['assert']; | |
}) | |
.find(j.BlockStatement) | |
.find(j.CallExpression, { callee: { type: 'Identifier' } }) | |
.find(j.Identifier) | |
.forEach(path => { | |
if (QUNIT_ASSERTIONS.includes(path.value.name)) { | |
path.value.name = 'assert.' + path.value.name.replace('assert.', ''); | |
} | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Removes `'use strict'` | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.ExpressionStatement) | |
.filter(path => path.value.expression.value === 'use strict') | |
.forEach(path => { | |
j(path).replaceWith(''); | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Migrates qunit's setup/teardown hooks to beforeEach/afterEach | |
module('foo', { | |
setup() { | |
}, | |
teardown() { | |
} | |
}) | |
|| | |
\/ | |
module('foo', { | |
beforeEach() { | |
}, | |
afterEach() { | |
} | |
}) | |
*/ | |
const QUNIT_HOOKS = ['setup', 'teardown']; | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
return j(file.source) | |
.find(j.ObjectExpression) | |
.find(j.Property, { key: j.Identifier }) | |
.filter(path => QUNIT_HOOKS.indexOf(path.value.key.name) !== -1) | |
.forEach(path => { | |
let newHookName = path.value.key.name === 'setup' ? 'beforeEach' : 'afterEach'; | |
path.value.key.name = newHookName; | |
}) | |
.toSource(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Replaces global ENV usage with module | |
export default Component.extend({ | |
init() { | |
this._super(...arguments) | |
if (Ember.ENV.testing) {} | |
} | |
}) | |
becomes | |
|| | |
\/ | |
import ENV from 'env'; | |
export default Component.extend({ | |
init() { | |
this._super(...arguments) | |
if (ENV.testing) {} | |
} | |
}) | |
*/ | |
const isEmberENV = (p) => p.value.name === 'Ember' && p.parent.value.property.name === 'ENV'; | |
const findDefaultImportDeclarations = function(j, importPath) { | |
return this.find(j.ImportDeclaration).filter((path) => { | |
return path.value.source.value === importPath; | |
}); | |
}; | |
const findIdentifiers = function(j, name) { | |
return this.find(j.Identifier, { name }); | |
}; | |
const contructDefaultImportDeclaration = ({ j, identifier, path }) => | |
j.importDeclaration([j.importDefaultSpecifier(j.identifier(identifier))], j.literal(path)); | |
const addDefaultImport = function(j, identifier, path) { | |
return this.find(j.Program).forEach((nodePath) => { | |
nodePath.value.body.unshift(contructDefaultImportDeclaration({ j, identifier, path })); | |
}); | |
}; | |
export default function transformer(file, api) { | |
const j = api.jscodeshift; | |
let ast = j(file.source); | |
j.registerMethods({ | |
findIdentifiers: findIdentifiers.bind(ast, j), | |
addDefaultImport: addDefaultImport.bind(ast, j), | |
findDefaultImportDeclarations: findDefaultImportDeclarations.bind(ast, j), | |
}); | |
const emberEnvIdentifers = ast.findIdentifiers('Ember').filter(isEmberENV); | |
if (emberEnvIdentifers.size() > 0 && !ast.findDefaultImportDeclarations('env').size()) { | |
ast.addDefaultImport('ENV', 'env'); | |
} | |
return emberEnvIdentifers | |
.forEach((path) => { | |
j(path.parent).replaceWith(j.identifier('ENV')); | |
}) | |
.toSource({ quote: 'single' }); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment