@airtasker/form-schema-compiler
Version:
a form schema compiler
343 lines (264 loc) • 13.4 kB
JavaScript
;
exports.__esModule = true;
exports.compileProps = undefined;
exports.compileComponents = compileComponents;
var _curryRight = require("lodash/curryRight");
var _curryRight2 = _interopRequireDefault(_curryRight);
var _curry = require("lodash/curry");
var _curry2 = _interopRequireDefault(_curry);
var _mapValues = require("lodash/mapValues");
var _mapValues2 = _interopRequireDefault(_mapValues);
var _mapKeys = require("lodash/mapKeys");
var _mapKeys2 = _interopRequireDefault(_mapKeys);
var _findKey = require("lodash/findKey");
var _findKey2 = _interopRequireDefault(_findKey);
var _flowRight = require("lodash/flowRight");
var _flowRight2 = _interopRequireDefault(_flowRight);
var _const = require("./const");
var _parsers = require("./parsers");
var _typeCompiler = require("./typeCompiler");
var _typeCompiler2 = _interopRequireDefault(_typeCompiler);
var _utils = require("./utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }
function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var mapValuesFp = (0, _curryRight2["default"])(_mapValues2["default"]);
var mapKeysFp = (0, _curryRight2["default"])(_mapKeys2["default"]);
/**
* convert json value
* "string" to {type: string, value: "string"}
* 1 to {type: numeric, value: 1}
* true to {type: boolean, value: 1}
* null to {type: Null, value: null}
* other types to {type: Raw, value}
* @param obj
* @returns {*}
*/
var toValueObject = function toValueObject(value) {
if (value === null) {
return {
type: _const.TYPES.Null,
value: value
};
}
switch (_typeof(value)) {
case "string":
return {
type: _const.TYPES.String,
value: value
};
case "number":
return {
type: _const.TYPES.Numeric,
value: value
};
case "boolean":
return {
type: _const.TYPES.Boolean,
value: value
};
default:
return {
type: _const.TYPES.Raw,
value: value
};
}
};
/**
* get annotation
* @param key string
*
* e.g.
* '[value]' return '[]'
* '<value>' return '<>'
* '#value#' return '##'
* '{value}' return '{}'
* '(value)' return '()'
*/
var getAnnotationType = function getAnnotationType(key) {
return (0, _findKey2["default"])(_const.ANNOTATIONS, {
0: key[0],
1: key.substr(-1)
});
};
/**
* strip annotation
* @param key string
*
* e.g.
* '[value]' return 'value'
* '<value>' return 'value'
* '#value#' return 'value'
* '{value}' return 'value'
* '(value)' return 'value'
*/
var stripAnnotation = function stripAnnotation(value, key) {
var strippedKey = key.substring(1, key.length - 1);
switch (getAnnotationType(key)) {
case _const.ANNOTATION_TYPES.TwoWayBinding:
case _const.ANNOTATION_TYPES.PropertyBinding:
case _const.ANNOTATION_TYPES.Template:
case _const.ANNOTATION_TYPES.Components:
// only strip annotation when there is one
// convert [value] to value
return strippedKey;
case _const.ANNOTATION_TYPES.EventBinding:
// convert (click) to onClick
return "on".concat(strippedKey[0].toUpperCase()).concat(strippedKey.substr(1));
default:
return key;
}
};
var ErrorPathTrace =
/*#__PURE__*/
function (_Error) {
_inherits(ErrorPathTrace, _Error);
function ErrorPathTrace(key, value, error) {
var _this;
_classCallCheck(this, ErrorPathTrace);
_this = _possibleConstructorReturn(this, _getPrototypeOf(ErrorPathTrace).call(this));
if (error.kind === 'ErrorPathTrace') {
_this.path = "".concat(key, ".").concat(error.path);
_this.originMessage = error.originMessage;
} else {
_this.path = "".concat(key, ":\"").concat(value, "\"");
_this.originMessage = error.message;
}
_this.kind = 'ErrorPathTrace';
_this.message = "Found an error in ".concat(_this.path, ". Error details: ").concat(_this.originMessage);
return _this;
}
return ErrorPathTrace;
}(_wrapNativeSuper(Error));
/**
* compile value to ast expression,
* examples see expression parsers and tests
* @param {*} options
* @param {*} value
* @param {*} key
*/
var compileValue = (0, _curry2["default"])(function (options, value, key) {
try {
switch (getAnnotationType(key)) {
case _const.ANNOTATION_TYPES.EventBinding:
return {
type: _const.ANNOTATION_TYPES.EventBinding,
value: (0, _parsers.parseExpressionString)(value)
};
case _const.ANNOTATION_TYPES.PropertyBinding:
if (_typeof(value) === "object") {
return {
type: _const.ANNOTATION_TYPES.PropertyBinding,
nested: true,
value: compileProps(value, options)
};
}
return {
type: _const.ANNOTATION_TYPES.PropertyBinding,
nested: false,
value: (0, _parsers.parseExpressionString)(value)
};
case _const.ANNOTATION_TYPES.TwoWayBinding:
throw new Error("Should use compile to convert TwoWayBinding to EventBinding and PropertyBinding");
case _const.ANNOTATION_TYPES.Template:
return {
type: _const.ANNOTATION_TYPES.PropertyBinding,
value: (0, _parsers.parseTemplateString)(value)
};
case _const.ANNOTATION_TYPES.Components:
// recursively compile nested component
// eslint-disable-next-line no-use-before-define
return compileComponents(value, options);
default:
return toValueObject(value);
}
} catch (error) {
throw new ErrorPathTrace(key, value, error);
}
});
/**
* compile values but not keys, compile values to ast expression.
* e.g.
* {'{value}': 'expression'} return {'{value}': 'compiled expression'}
*/
var createCompileValues = function createCompileValues(options) {
return mapValuesFp(compileValue(options));
};
/**
* compile keys but not value, compile keys to non-annotation key.
* e.g.
* {'{value}': 'expression', '<component>': 'expression' } return {'value': 'expression', 'component': 'expression'}
*/
var compileKeys = mapKeysFp(stripAnnotation);
/**
* compile keys and values.
*/
var compileProps = exports.compileProps = function compileProps(props, options) {
return (0, _flowRight2["default"])(compileKeys, createCompileValues(options))(props);
};
var compilePropsFp = (0, _curryRight2["default"])(compileProps);
/**
* Take component schema return AST,
* examples see expression parsers and tests
* @param {*} componentSchema
* @param {{typeCompilers: *}} options
* @returns {{type: *}}
*/
var compileComponent = function compileComponent(_ref) {
var type = _ref.type,
props = _objectWithoutProperties(_ref, ["type"]);
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (!type) {
throw new Error("type is a mandatory field in component");
}
var typeCompiler = (0, _typeCompiler2["default"])(type, options.typeCompilers);
var composed = (0, _flowRight2["default"])(typeCompiler.after, compilePropsFp(options), typeCompiler.before);
return _objectSpread({
type: type
}, composed(props));
};
/**
* compile components schema
* @param {*} components if components is an object convert to [components]
* @param {{typeCompilers: *}} options
* @returns {{type: string, components: Array}}
*/
function compileComponents(components, options) {
if (_typeof(components) !== "object") {
throw new Error("components must be a object or array");
}
var componentArray = Array.isArray(components) ? components : [components];
return {
type: _const.TYPES.Components,
components: componentArray.map(function (component) {
return compileComponent(component, options);
})
};
}
var compile = function compile(_ref2, options) {
var schemaVersion = _ref2.schemaVersion,
rest = _objectWithoutProperties(_ref2, ["schemaVersion"]);
if (!schemaVersion || !(0, _utils.isVersionCompatible)(schemaVersion)) {
throw new Error("incompatible version, you may use wrong version form-schema");
}
var component = rest["<component>"];
return _objectSpread({}, rest, {
schemaVersion: schemaVersion,
component: compileComponents(component, options)
});
};
exports["default"] = compile;