Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Converts the given angular injected parameter into an explicit require statement
/*
* Converts the given angular injected parameter into an explicit require statement
*
* Run this with jscodeshift
* @example
* jscodeshift . --specifier='Auth' --source='application/Auth'
*
* Live demo: https://astexplorer.net/#/gist/5492d2b9850a451d8e8d532bc64f21ce/latest
*
* Converts:
* app.controller('myController', function ($scope, Auth, $q) {
* });
*
* function test($scope, Auth, $q) {
* 'ngInject';
* }
*
* app.component('myComponent', {
* controller: function ($scope, Auth, $q) {
* }
* });
*
* app.directive('myDirective', function (Auth) {
* return {
* controller: function ($scope, Auth, $q) {
* }
* };
* });
*
* to:
* import Auth from 'application/Auth';
* app.controller('myController', function ($scope, $q) {
* });
*
* function test($scope, $q) {
* 'ngInject';
* }
*
* app.component('myComponent', {
* controller: function ($scope, $q) {
* }
* });
*
* app.directive('myDirective', function () {
* return {
* controller: function ($scope, $q) {
* }
* };
* });
*/
const angularMethods = [
'config',
'provider',
'factory',
'service',
'controller',
'filter',
'directive'
];
function isNgInject(functionExpression) {
if (!functionExpression.body.body.length) {
return false;
}
let firstBodyNode = functionExpression.body.body[0];
return (
firstBodyNode.type === 'ExpressionStatement' &&
firstBodyNode.expression.type === 'Literal' &&
firstBodyNode.expression.value === 'ngInject'
);
}
function isControllerOnObject(path) {
return (
path.name === 'value' &&
path.parentPath.parentPath.name === 'properties' &&
path.node.type === 'FunctionExpression' &&
path.parentPath.node.type === 'Property' &&
path.parentPath.parentPath.node.type === 'ObjectExpression'
);
}
function isParameterOfAngularMethod(path) {
return (
path.node.type === 'FunctionExpression' &&
path.parentPath.name === 'arguments' &&
path.parentPath.parentPath.node.type === 'CallExpression' &&
path.parentPath.parentPath.node.callee.type === 'MemberExpression' &&
path.parentPath.parentPath.node.callee.property.type === 'Identifier' &&
angularMethods.indexOf(path.parentPath.parentPath.node.callee.property.name) > -1
);
}
function isUseStrict(node) {
return (
node &&
node.type === 'ExpressionStatement' &&
node.expression.type === 'Literal' &&
node.expression.value === 'use strict'
);
}
module.exports = function transformer(file, api, options) {
const j = api.jscodeshift;
const {
specifier = 'Auth',
source = 'application/Auth'
} = options;
let addedImport = false;
let output = j(file.source)
.find(j.Identifier)
.filter((path) => {
return (
path.node.name === specifier &&
path.parentPath.name === 'params' && (
isNgInject(path.parentPath.node) ||
isControllerOnObject(path.parentPath.parentPath) ||
isParameterOfAngularMethod(path.parentPath.parentPath)
)
);
})
.closest(j.Program)
.forEach((path) => {
let importDeclaration = j.importDeclaration([j.importDefaultSpecifier(j.identifier(specifier))], j.literal(source));
if (isUseStrict(path.node.body[0])) {
path.node.body.splice(1, 0, importDeclaration);
}
else {
path.node.body.unshift(importDeclaration);
}
addedImport = true;
})
.toSource({
arrowParensAlways: true,
quote: 'single',
});
if (addedImport) {
// Remove injections using regex to retain code style
output = output.replace(new RegExp(`\\h*${specifier}\\s*,\\s*`, 'g'), '')
.replace(new RegExp(`\\s*,\\s*${specifier}\\h*(?=[,\\n\\)])`, 'g'), '')
.replace(new RegExp(`\\(\\s*${specifier}\\s*\\)`, 'g'), '()');
}
return output;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.