* Transforms ajv's generated code to make it usable in a bundle: * - Creates strings for error messages instead of an object. * - Removes exports (it expects to be included inline). * - Rescopes the current code to prevent collisions with taken names. * - Strips down included schemas so that
(code, taken, config)
| 170 | * @return {{result: babel.BabelFileResult | null, name: string}} |
| 171 | */ |
| 172 | function transformAjvCode(code, taken, config) { |
| 173 | const {template, types: t} = babel; |
| 174 | |
| 175 | // ajvCompile's generated name |
| 176 | let name = 'validate'; |
| 177 | |
| 178 | const isObjectProperty = (node, name) => |
| 179 | (t.isObjectProperty(node) && t.isIdentifier(node.key, {name})) || |
| 180 | t.isStringLiteral(node.key, {value: name}); |
| 181 | |
| 182 | const findProperty = (properties, name) => |
| 183 | properties.find((node) => isObjectProperty(node, name)); |
| 184 | |
| 185 | /** |
| 186 | * @param {babel.NodePath<babel.types.MemberExpression>} memberExpression |
| 187 | * @return {null | undefined | number | string} |
| 188 | */ |
| 189 | function evaluatePropertyKey(memberExpression) { |
| 190 | const property = memberExpression.get('property'); |
| 191 | if (property.isIdentifier()) { |
| 192 | return property.node.name; |
| 193 | } |
| 194 | if (memberExpression.node.computed) { |
| 195 | return property.evaluate().value; |
| 196 | } |
| 197 | return null; |
| 198 | } |
| 199 | |
| 200 | const schemaIdRegexp = /^schema[0-9]*$/; |
| 201 | |
| 202 | /** |
| 203 | * @param {babel.NodePath<any>} path |
| 204 | * @param {string[]} keys |
| 205 | * @return {boolean} |
| 206 | */ |
| 207 | function isMemberExpressionLeftwards(path, keys) { |
| 208 | let i = keys.length - 1; |
| 209 | while (true) { |
| 210 | if (path.isMemberExpression()) { |
| 211 | const key = evaluatePropertyKey(path); |
| 212 | if (key !== keys[i--]) { |
| 213 | return false; |
| 214 | } |
| 215 | path = path.get('object'); |
| 216 | } else if (path.isVariableDeclarator()) { |
| 217 | const init = path.get('init'); |
| 218 | if (!init) { |
| 219 | break; |
| 220 | } |
| 221 | path = init; |
| 222 | } else if (path.isIdentifier()) { |
| 223 | const binding = path.scope.getBinding(path.node.name); |
| 224 | if (!binding) { |
| 225 | break; |
| 226 | } |
| 227 | path = binding.path; |
| 228 | } else { |
| 229 | break; |