flow-immutable-models
Version:
Generates model classes from Flow types using Immutable.js
162 lines (129 loc) • 6.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (file, api, options) {
var j = api.jscodeshift;
var printOptions = options.printOptions || defaultPrintOptions;
var root = j(file.source);
var program = root.get().value.program;
var body = program.body;
var classes = [];
function makeClass(className, type, defaultValues) {
var classNameIdentifier = j.identifier(className);
var staticMethods = [(0, _fromJS2.default)(j, className, defaultValues, type.properties)];
var instanceMethods = type.properties.reduce(function (methods, prop) {
methods.push((0, _getter2.default)(j, prop), (0, _setter2.default)(j, prop));
return methods;
}, [(0, _toJS2.default)(j, className, type.properties)]);
var classDeclaration = j.exportNamedDeclaration(j.classDeclaration(classNameIdentifier, j.classBody(staticMethods.concat(instanceMethods)), j.identifier('ImmutableModel')));
var comments = [' /////////////////////////////////////////////////////////////////////////////', '', ' NOTE: THIS CLASS IS GENERATED. DO NOT MAKE CHANGES HERE.', '', ' If you need to update this class, update the corresponding flow type above', ' and re-run the flow-immutable-models codemod', '', ' /////////////////////////////////////////////////////////////////////////////'];
classDeclaration.comments = comments.map(function (comment) {
return j.commentLine(comment);
});
return classDeclaration;
}
function parseType(td) {
if (typeof td === 'string') {
return td;
}
var typeDef = Object.assign({}, td);
delete typeDef.start;
delete typeDef.end;
delete typeDef.loc;
delete typeDef.extra;
if (typeDef.id) {
typeDef.id = parseType(typeDef.id);
}
if (typeDef.key) {
typeDef.key = parseType(typeDef.key);
}
if (typeDef.value) {
typeDef.value = parseType(typeDef.value);
}
if (typeDef.types) {
typeDef.types = typeDef.types.map(parseType);
}
if (typeDef.properties) {
typeDef.properties = typeDef.properties.map(parseType);
}
return typeDef;
}
root.find(j.ExportNamedDeclaration).filter(function (p) {
if (p.node.exportKind === 'type') {
var identifier = p.node.declaration.id.name;
return (0, _withoutModelTypeSuffix.endsWithModelType)(identifier);
}
return false;
}).forEach(function (p) {
var identifier = p.node.declaration.id.name;
var className = (0, _withoutModelTypeSuffix.withoutModelTypeSuffix)(identifier);
var parsedType = parseType(p.node.declaration.right);
if (parsedType.type !== 'ObjectTypeAnnotation') {
throw new Error('Expected ' + identifier + ' to be of type ObjectTypeAnnotation. Instead it was of type ' + parsedType.type + '.\n\nAll types ending with "ModelType" are expected to be defined as object literals with properties.\nPerhaps you didn\'t mean for ' + identifier + ' to be a ModelType.\n');
}
var defaultValuesName = 'default' + (0, _capitalize2.default)(className) + 'Values';
var defaultValues = null;
root.find(j.VariableDeclaration).filter(function (path) {
return path.node.declarations.forEach(function (dec) {
if (dec.id.name === defaultValuesName) {
defaultValues = dec;
}
});
});
classes.push({
className: className,
classDef: makeClass(className, parsedType, defaultValues),
optionalType: (0, _getDefaultValueTypeStatements.getOptionalTypeStatement)(j, className, defaultValues),
requiredType: (0, _getDefaultValueTypeStatements.getRequiredTypeStatement)(j, className, defaultValues, parsedType.properties),
fromJSType: (0, _getDefaultValueTypeStatements.getFromJSTypeStatement)(j, className, defaultValues),
fullType: (0, _getDefaultValueTypeStatements.getFullTypeStatement)(j, className, defaultValues)
});
});
var replaceOrAddStatement = function replaceOrAddStatement(tokenType, filterFn, statement) {
var existingStatement = root.find(tokenType).filter(filterFn);
if (existingStatement.size() === 1) {
existingStatement.replaceWith(statement);
} else {
body.push(statement);
}
};
classes.forEach(function (_ref) {
var className = _ref.className,
classDef = _ref.classDef,
fromJSType = _ref.fromJSType,
fullType = _ref.fullType,
optionalType = _ref.optionalType,
requiredType = _ref.requiredType;
replaceOrAddStatement(j.ExportNamedDeclaration, function (path) {
return path.node.declaration.type === 'ClassDeclaration' && path.node.declaration.id.name === className;
}, classDef);
replaceOrAddStatement(j.TypeAlias, function (path) {
return path.node.id.name === className + 'OptionalArguments';
}, optionalType);
replaceOrAddStatement(j.TypeAlias, function (path) {
return path.node.id.name === className + 'RequiredArguments';
}, requiredType);
replaceOrAddStatement(j.TypeAlias, function (path) {
return path.node.id.name === className + 'FullType';
}, fullType);
replaceOrAddStatement(j.TypeAlias, function (path) {
return path.node.id.name === className + 'FromJSType';
}, fromJSType);
});
return root.toSource(printOptions);
};
var _capitalize = require('./helpers/capitalize');
var _capitalize2 = _interopRequireDefault(_capitalize);
var _fromJS = require('./helpers/fromJS');
var _fromJS2 = _interopRequireDefault(_fromJS);
var _getDefaultValueTypeStatements = require('./helpers/getDefaultValueTypeStatements');
var _getter = require('./helpers/getter');
var _getter2 = _interopRequireDefault(_getter);
var _setter = require('./helpers/setter');
var _setter2 = _interopRequireDefault(_setter);
var _toJS = require('./helpers/toJS');
var _toJS2 = _interopRequireDefault(_toJS);
var _withoutModelTypeSuffix = require('./helpers/withoutModelTypeSuffix');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var defaultPrintOptions = { quote: 'single', trailingComma: true };