Skip to content

Instantly share code, notes, and snippets.

@olvnikon
Last active October 6, 2017 11:30
Show Gist options
  • Save olvnikon/201bade96b53cdc7f5f787e8f2066b85 to your computer and use it in GitHub Desktop.
Save olvnikon/201bade96b53cdc7f5f787e8f2066b85 to your computer and use it in GitHub Desktop.
[AST + JSShiftCode] - AngularJS 1.x directive to component
const DIRECTIVE_TO_COMPONENT = {
scope: "bindings"
};
const DEPRECATED_PROPS = ["restrict", "replace", "bindToController"];
const DIRECTIVE_UNIQUE_PROPS = ["link", "compile", "multiElement", "priority", "templateNamespace", "terminal"];
function transform(j, path) {
const directiveDeclaration = path.value.arguments[1];
const directiveProperties = directiveDeclaration.body.body[0].argument.properties;
const newProps = directiveProperties.filter(prop => !DEPRECATED_PROPS.includes(prop.key.name));
const calleeObject = path.value.callee.object;
const args = path.value.arguments;
const controllerName = args[0].value;
const componentName = controllerName.charAt(0).toLowerCase() + controllerName.slice(1);
j(path).replaceWith(
j.callExpression(
j.memberExpression(j.callExpression(calleeObject.callee, calleeObject.arguments), j.identifier("component")),
[
j.literal(componentName),
j.objectExpression(
newProps.map(prop => {
const newIdentifier = DIRECTIVE_TO_COMPONENT[prop.key.name];
return !!newIdentifier ? j.property("init", j.identifier(newIdentifier), prop.value) : prop;
})
)
]
)
);
}
module.exports = function(fileInfo, api, options) {
const j = api.jscodeshift;
const ast = j(fileInfo.source);
ast
.find(j.CallExpression)
// Filter only directives
.filter(path => {
const calleeProperty = path.value.callee.property;
return calleeProperty && calleeProperty.type === j.Identifier.name && calleeProperty.name === "directive";
})
// Filter directives with only return statement and with restrict "E"
.filter(path => {
const directiveDeclaration = path.value.arguments[1];
const directiveBody = directiveDeclaration.body.body;
return directiveBody.length === 1 && directiveBody[0].type === j.ReturnStatement.name;
})
// Filter directives with restrict "E" and without directive unique props
.filter(path => {
const directiveDeclaration = path.value.arguments[1];
const directiveProperties = directiveDeclaration.body.body[0].argument.properties;
const rejectedProps = directiveProperties.filter(prop => DIRECTIVE_UNIQUE_PROPS.includes(prop.key.name));
if (rejectedProps.length > 0) {
return false;
}
const restrict = directiveProperties.filter(prop => prop.key.name === "restrict");
return restrict.length === 0 ? false : restrict[0].value.value === "E";
})
.forEach(path => {
transform(j, path);
});
return ast.toSource();
};
@olvnikon
Copy link
Author

olvnikon commented Oct 6, 2017

This script converts AngularJS 1.x directives to components if it is allowed to do this. The directive should be an element and shouldn't contain unique for it properties.
Before:

angular.module("App").directive("staticTextEditor", function () {
  return {
    restrict: "E",
    transclude: true,
    templateUrl: "statictext-editor.html",
    controller: "staticTextEditorController"
  };
});

After:

angular.module("App").component("staticTextEditor", {
  transclude: true,
  templateUrl: "statictext-editor.html",
  controller: "staticTextEditorController"
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment