| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- 'use strict'
- var allowedModifiers = ['trim', 'number', 'lazy']
- var RANGE_TOKEN = '__r'
- var CHECKBOX_RADIO_TOKEN = '__c'
- var htmlTags = require('html-tags')
- var svgTags = require('svg-tags')
- var isReservedTag = function isReservedTag(tag) {
- return ~htmlTags.indexOf(tag) || ~svgTags.indexOf(tag)
- }
- var getExpression = function getExpression(t, path) {
- return t.isStringLiteral(path.node.value) ? path.node.value : path.node.value.expression
- }
- var genValue = function genValue(t, model) {
- return t.jSXAttribute(t.jSXIdentifier('domPropsValue'), t.jSXExpressionContainer(model))
- }
- var genAssignmentCode = function genAssignmentCode(t, model, assignment) {
- if (model.computed) {
- var object = model.object,
- property = model.property
- return t.ExpressionStatement(
- t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('$set')), [object, property, assignment])
- )
- } else {
- return t.ExpressionStatement(t.AssignmentExpression('=', model, assignment))
- }
- }
- var genListener = function genListener(t, event, body) {
- return t.jSXAttribute(
- t.jSXIdentifier('on' + event),
- t.jSXExpressionContainer(t.ArrowFunctionExpression([t.Identifier('$event')], t.BlockStatement(body)))
- )
- }
- var genSelectModel = function genSelectModel(t, modelAttribute, model, modifier) {
- if (modifier && modifier !== 'number') {
- throw modelAttribute.get('name').get('name').buildCodeFrameError('you can only use number modifier with <select/ >')
- }
- var number = modifier === 'number'
- var valueGetter = t.ConditionalExpression(
- t.BinaryExpression('in', t.StringLiteral('_value'), t.Identifier('o')),
- t.MemberExpression(t.Identifier('o'), t.Identifier('_value')),
- t.MemberExpression(t.Identifier('o'), t.Identifier('value'))
- )
- var selectedVal = t.CallExpression(
- t.MemberExpression(
- t.CallExpression(
- t.MemberExpression(
- t.MemberExpression(
- t.MemberExpression(t.Identifier('Array'), t.Identifier('prototype')),
- t.Identifier('filter')
- ),
- t.Identifier('call')
- ),
- [
- t.MemberExpression(
- t.MemberExpression(t.Identifier('$event'), t.Identifier('target')),
- t.Identifier('options')
- ),
- t.ArrowFunctionExpression(
- [t.Identifier('o')],
- t.MemberExpression(t.Identifier('o'), t.Identifier('selected'))
- )
- ]
- ),
- t.Identifier('map')
- ),
- [
- t.ArrowFunctionExpression(
- [t.Identifier('o')],
- number
- ? t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_n')), [valueGetter])
- : valueGetter
- )
- ]
- )
- var assignment = t.ConditionalExpression(
- t.MemberExpression(t.MemberExpression(t.Identifier('$event'), t.Identifier('target')), t.Identifier('multiple')),
- t.Identifier('$$selectedVal'),
- t.MemberExpression(t.Identifier('$$selectedVal'), t.NumericLiteral(0), true)
- )
- var code = t.VariableDeclaration('const', [t.VariableDeclarator(t.Identifier('$$selectedVal'), selectedVal)])
- return [genValue(t, model), genListener(t, 'Change', [code, genAssignmentCode(t, model, assignment)])]
- }
- var genCheckboxModel = function genCheckboxModel(t, modelAttribute, model, modifier, path) {
- if (modifier && modifier !== 'number') {
- throw modelAttribute
- .get('name')
- .get('name')
- .buildCodeFrameError('you can only use number modifier with input[type="checkbox"]')
- }
- var value = t.NullLiteral()
- var trueValue = t.BooleanLiteral(true)
- var falseValue = t.BooleanLiteral(false)
- path.get('attributes').forEach(function(path) {
- if (!path.node.name) {
- return
- }
- if (path.node.name.name === 'value') {
- value = getExpression(t, path)
- path.remove()
- } else if (path.node.name.name === 'true-value') {
- trueValue = getExpression(t, path)
- path.remove()
- } else if (path.node.name.name === 'false-value') {
- falseValue = getExpression(t, path)
- path.remove()
- }
- })
- var checkedProp = t.JSXAttribute(
- t.JSXIdentifier('checked'),
- t.JSXExpressionContainer(
- t.ConditionalExpression(
- t.CallExpression(t.MemberExpression(t.Identifier('Array'), t.Identifier('isArray')), [model]),
- t.BinaryExpression(
- '>',
- t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_i')), [model, value]),
- t.UnaryExpression('-', t.NumericLiteral(1))
- ),
- t.isBooleanLiteral(trueValue) && trueValue.value === true
- ? model
- : t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_q')), [model, trueValue])
- )
- )
- )
- var handlerProp = t.JSXAttribute(
- t.JSXIdentifier('on' + CHECKBOX_RADIO_TOKEN),
- t.JSXExpressionContainer(
- t.ArrowFunctionExpression(
- [t.Identifier('$event')],
- t.BlockStatement([
- t.VariableDeclaration('const', [
- t.VariableDeclarator(t.Identifier('$$a'), model),
- t.VariableDeclarator(
- t.Identifier('$$el'),
- t.MemberExpression(t.Identifier('$event'), t.Identifier('target'))
- ),
- t.VariableDeclarator(
- t.Identifier('$$c'),
- t.ConditionalExpression(
- t.MemberExpression(t.Identifier('$$el'), t.Identifier('checked')),
- trueValue,
- falseValue
- )
- )
- ]),
- t.IfStatement(
- t.CallExpression(t.MemberExpression(t.Identifier('Array'), t.Identifier('isArray')), [t.Identifier('$$a')]),
- t.BlockStatement([
- t.VariableDeclaration('const', [
- t.VariableDeclarator(
- t.Identifier('$$v'),
- modifier === 'number'
- ? t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_n')), [value])
- : value
- ),
- t.VariableDeclarator(
- t.Identifier('$$i'),
- t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_i')), [
- t.Identifier('$$a'),
- t.Identifier('$$v')
- ])
- )
- ]),
- t.IfStatement(
- t.MemberExpression(t.Identifier('$$el'), t.Identifier('checked')),
- t.BlockStatement([
- t.ExpressionStatement(
- t.LogicalExpression(
- '&&',
- t.BinaryExpression('<', t.Identifier('$$i'), t.NumericLiteral(0)),
- t.AssignmentExpression(
- '=',
- model,
- t.CallExpression(t.MemberExpression(t.Identifier('$$a'), t.Identifier('concat')), [
- t.Identifier('$$v')
- ])
- )
- )
- )
- ]),
- t.BlockStatement([
- t.ExpressionStatement(
- t.LogicalExpression(
- '&&',
- t.BinaryExpression('>', t.Identifier('$$i'), t.UnaryExpression('-', t.NumericLiteral(1))),
- t.AssignmentExpression(
- '=',
- model,
- t.CallExpression(
- t.MemberExpression(
- t.CallExpression(t.MemberExpression(t.Identifier('$$a'), t.Identifier('slice')), [
- t.NumericLiteral(0),
- t.Identifier('$$i')
- ]),
- t.Identifier('concat')
- ),
- [
- t.CallExpression(t.MemberExpression(t.Identifier('$$a'), t.Identifier('slice')), [
- t.BinaryExpression('+', t.Identifier('$$i'), t.NumericLiteral(1))
- ])
- ]
- )
- )
- )
- )
- ])
- )
- ]),
- t.BlockStatement([genAssignmentCode(t, model, t.Identifier('$$c'))])
- )
- ])
- )
- )
- )
- return [checkedProp, handlerProp]
- }
- var genRadioModel = function genRadioModel(t, modelAttribute, model, modifier, path) {
- if (modifier && modifier !== 'number') {
- throw modelAttribute
- .get('name')
- .get('name')
- .buildCodeFrameError('you can only use number modifier with input[type="radio"]')
- }
- var value = t.BooleanLiteral(true)
- path.get('attributes').forEach(function(path) {
- if (!path.node.name) {
- return
- }
- if (path.node.name.name === 'value') {
- value = getExpression(t, path)
- path.remove()
- }
- })
- var checkedProp = t.JSXAttribute(
- t.JSXIdentifier('checked'),
- t.JSXExpressionContainer(
- t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_q')), [model, value])
- )
- )
- var handlerProp = t.JSXAttribute(
- t.JSXIdentifier('on' + CHECKBOX_RADIO_TOKEN),
- t.JSXExpressionContainer(
- t.ArrowFunctionExpression(
- [t.Identifier('$event')],
- t.BlockStatement([
- genAssignmentCode(
- t,
- model,
- modifier === 'number'
- ? t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_n')), [value])
- : value
- )
- ])
- )
- )
- )
- return [checkedProp, handlerProp]
- }
- var genDefaultModel = function genDefaultModel(t, modelAttribute, model, modifier, path, type) {
- var needCompositionGuard = modifier !== 'lazy' && type !== 'range'
- var event = modifier === 'lazy' ? 'Change' : type === 'range' ? RANGE_TOKEN : 'Input'
- var valueExpression = t.MemberExpression(
- t.MemberExpression(t.Identifier('$event'), t.Identifier('target')),
- t.Identifier('value')
- )
- if (modifier === 'trim') {
- valueExpression = t.CallExpression(t.MemberExpression(valueExpression, t.Identifier('trim')), [])
- } else if (modifier === 'number') {
- valueExpression = t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_n')), [valueExpression])
- }
- var code = genAssignmentCode(t, model, valueExpression)
- if (needCompositionGuard) {
- code = t.BlockStatement([
- t.IfStatement(
- t.MemberExpression(
- t.MemberExpression(t.Identifier('$event'), t.Identifier('target')),
- t.Identifier('composing')
- ),
- t.ReturnStatement(null)
- ),
- code
- ])
- } else {
- code = t.BlockStatement([code])
- }
- var valueProp = t.JSXAttribute(t.jSXIdentifier('domPropsValue'), t.JSXExpressionContainer(model))
- var handlerProp = t.JSXAttribute(
- t.JSXIdentifier('on' + event),
- t.JSXExpressionContainer(t.ArrowFunctionExpression([t.Identifier('$event')], code))
- )
- var props = [valueProp, handlerProp]
- if (modifier === 'trim' || modifier === 'number') {
- props.push(
- t.JSXAttribute(
- t.JSXIdentifier('onBlur'),
- t.JSXExpressionContainer(
- t.ArrowFunctionExpression(
- [],
- t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('$forceUpdate')), [])
- )
- )
- )
- )
- }
- return props
- }
- var genComponentModel = function genComponentModel(t, modelAttribute, model, modifier, path, type) {
- var baseValueExpression = t.Identifier('$$v')
- var valueExpression = baseValueExpression
- if (modifier === 'trim') {
- valueExpression = t.CallExpression(t.MemberExpression(valueExpression, t.Identifier('trim')), [])
- } else if (modifier === 'number') {
- valueExpression = t.CallExpression(t.MemberExpression(t.ThisExpression(), t.Identifier('_n')), [valueExpression])
- }
- var assignment = genAssignmentCode(t, model, valueExpression)
- var valueProp = t.JSXAttribute(t.jSXIdentifier('value'), t.JSXExpressionContainer(model))
- var handlerProp = t.JSXAttribute(
- t.JSXIdentifier('onInput'),
- t.JSXExpressionContainer(t.ArrowFunctionExpression([baseValueExpression], t.BlockStatement([assignment])))
- )
- return [valueProp, handlerProp]
- }
- module.exports = function(babel) {
- var t = babel.types
- return {
- inherits: require('babel-plugin-syntax-jsx'),
- visitor: {
- JSXOpeningElement: function JSXOpeningElement(path) {
- var model = null
- var modifier = null
- var modelAttribute = null
- var type = null
- var typePath = null
- path.get('attributes').forEach(function(path) {
- if (!path.node.name) {
- return
- }
- if (path.node.name.name === 'type') {
- type = path.node.value.value
- typePath = path.get('value')
- }
- /* istanbul ignore else */
- if (t.isJSXIdentifier(path.node.name)) {
- if (path.node.name.name !== 'v-model') {
- return
- }
- } else if (t.isJSXNamespacedName(path.node.name)) {
- if (path.node.name.namespace.name !== 'v-model') {
- return
- }
- if (!~allowedModifiers.indexOf(path.node.name.name.name)) {
- throw path
- .get('name')
- .get('name')
- .buildCodeFrameError('v-model does not support "' + path.node.name.name.name + '" modifier')
- }
- modifier = path.node.name.name.name
- } else {
- return
- }
- modelAttribute = path
- if (model) {
- throw path.buildCodeFrameError('you can not have multiple v-model directives on a single element')
- }
- if (!t.isJSXExpressionContainer(path.node.value)) {
- throw path.get('value').buildCodeFrameError('you should use JSX Expression with v-model')
- }
- if (!t.isMemberExpression(path.node.value.expression)) {
- throw path
- .get('value')
- .get('expression')
- .buildCodeFrameError('you should use MemberExpression with v-model')
- }
- model = path.node.value.expression
- })
- if (!model) {
- return
- }
- var tag = path.node.name.name
- if (tag === 'input' && typePath && t.isJSXExpressionContainer(typePath.node)) {
- throw typePath.buildCodeFrameError('you can not use dynamic type with v-model')
- }
- if (tag === 'input' && type === 'file') {
- throw typePath.buildCodeFrameError('you can not use "file" type with v-model')
- }
- var replacement = null
- if (tag === 'select') {
- replacement = genSelectModel(t, modelAttribute, model, modifier)
- } else if (tag === 'input' && type === 'checkbox') {
- replacement = genCheckboxModel(t, modelAttribute, model, modifier, path)
- } else if (tag === 'input' && type === 'radio') {
- replacement = genRadioModel(t, modelAttribute, model, modifier, path)
- } else if (tag === 'input' || tag === 'textarea') {
- replacement = genDefaultModel(t, modelAttribute, model, modifier, path, type)
- } else if (!isReservedTag(tag)) {
- replacement = genComponentModel(t, modelAttribute, model, modifier, path)
- } else {
- throw path.buildCodeFrameError('you can not use "' + tag + '" with v-model')
- }
- modelAttribute.replaceWithMultiple([
- ...replacement,
- t.JSXSpreadAttribute(
- t.ObjectExpression([
- t.ObjectProperty(
- t.Identifier('directives'),
- t.ArrayExpression([
- t.ObjectExpression([
- t.ObjectProperty(t.Identifier('name'), t.StringLiteral('model')),
- t.ObjectProperty(t.Identifier('value'), model)
- ])
- ])
- )
- ])
- )
- ])
- }
- }
- }
- }
|