canvas-lms/packages/format-message-estree-util/jsx.js

260 lines
7.6 KiB
JavaScript

'use strict'
/*
* This object gets mixed into the main collection of helpers
*/
exports = module.exports = {
isTranslatableElement: function (node) {
return this.hasTranslateAttribute(node, 'yes')
},
hasTranslateAttribute: function (node, value) {
var translate = this.getAttribute(node, 'translate')
return (
translate &&
this.isStringLiteral(translate) &&
(!value || translate.value === value)
)
},
getElementTargetLocale: function (node) {
var lang = this.getAttribute(node, 'lang')
return (
lang &&
this.isStringLiteral(lang) &&
lang.value
)
},
getAttribute: function (node, name) {
if (node.type !== 'JSXElement') return
var attrs = (node.openingElement && node.openingElement.attributes) || []
var attrNode = attrs.filter(function (attribute) {
return (
attribute.name &&
attribute.name.type === 'JSXIdentifier' &&
attribute.name.name === name
)
})[0]
return attrNode && attrNode.value
},
getAttributes: function (node) {
if (node.type !== 'JSXElement') return
var attrs = node.openingElement.attributes || []
var map = {}
attrs.forEach(function (attribute) {
if (attribute.name && attribute.name.type === 'JSXIdentifier') {
map[attribute.name.name] = attribute.value
}
})
return map
},
getElementMessageDetails: function (node) {
var i = 0
var wrappers = []
function nextToken (node, options) {
var token = i++
wrappers[token] = {
node: node,
options: options || {}
}
return token
}
var parameters = {}
return {
default: this.getMessageText(node, nextToken, parameters),
wrappers: wrappers,
parameters: parameters
}
},
getMessageText: function (node, nextToken, parameters) {
var self = this
return node.children.reduce(function (message, child) {
if (child.type === 'JSXText' || self.isStringLiteral(child)) {
return message + self.cleanJSXText(String(child.value))
}
if (child.type === 'JSXExpressionContainer') {
return message + self.getParameterText(child.expression, nextToken, parameters)
}
if (child.type === 'JSXElement') {
return message + self.getChildMessageText(child, nextToken, parameters)
}
return message
}, '')
},
hasTranslatableText: function (node) {
return node.children.some(function (child) {
if (child.type === 'JSXText' || this.isStringLiteral(child)) {
return this.cleanJSXText(String(child.value)).length > 0
}
if (child.type === 'JSXExpressionContainer') {
if (child.expression.type === 'JSXEmptyExpression') return false
if (this.isStringLiteral(child.expression)) return child.expression.value.length > 0
return true
}
if (child.type === 'JSXElement') {
return this.hasTranslatableText(child)
}
return false
}, this)
},
cleanJSXText: function (text) {
var lines = text.split(/\r\n|\n|\r/)
var lastNonEmptyLine = 0
for (var l = 0; l < lines.length; ++l) {
if (lines[l].match(/[^ \t]/)) {
lastNonEmptyLine = l
}
}
var clean = ''
for (var i = 0; i < lines.length; ++i) {
var line = lines[i]
var isFirstLine = i === 0
var isLastLine = i === lines.length - 1
var isLastNonEmptyLine = i === lastNonEmptyLine
// replace rendered whitespace tabs with spaces
var trimmedLine = line.replace(/\t/g, ' ')
// trim whitespace touching a newline
if (!isFirstLine) {
trimmedLine = trimmedLine.replace(/^[ ]+/, '')
}
// trim whitespace touching an endline
if (!isLastLine) {
trimmedLine = trimmedLine.replace(/[ ]+$/, '')
}
if (trimmedLine) {
if (!isLastNonEmptyLine) {
trimmedLine += ' '
}
clean += trimmedLine
}
}
return clean
},
getParameterText: function (node, nextToken, parameters) {
if (node.type === 'Literal' || node.type === 'StringLiteral') {
return String(node.value)
}
if (node.type === 'JSXEmptyExpression') {
return ''
}
var parameterText = this.getParameterFromHelper(node, nextToken, parameters)
if (parameterText) {
return parameterText
}
var name = this.getCodeSlug(node)
parameters[name] = node
return '{ ' + name + ' }'
},
getCodeSlug: function (node) {
// adapted from https://github.com/jenseng/react-i18nliner
return this.getSource(node)
.replace(/<[^>]*>/, '') // remove jsx tags
.replace(/(this|state|props)\./g, '') // remove common objects
.replace(/([A-Z]+)?([A-Z])/g, '$1 $2') // add spaces for consective capitals
.toLowerCase()
.replace(/[^a-z0-9]/g, ' ') // remove non-ascii
.trim()
.replace(/\s+/g, '_')
},
getChildMessageText: function (node, nextToken, parameters) {
var token
var children = node.children
var hasSubContent = (
children && children.length > 0 &&
!this.hasTranslateAttribute(node) &&
this.hasTranslatableText(node)
)
if (!hasSubContent) {
token = nextToken(node, { selfClosing: true })
return '<' + token + '/>'
}
token = nextToken(node)
var innerText = this.getMessageText(node, nextToken, parameters)
return '<' + token + '>' + innerText + '</' + token + '>'
},
getParameterFromHelper: function (node, nextToken, parameters) {
if (node.type !== 'CallExpression') return
var name = this.getHelperFunctionName(node.callee)
if (!name) return
var args = node.arguments
if (args.length < 1) return
var id = this.getCodeSlug(args[0])
var parameter = id + ', ' + name
if (name === 'number' || name === 'date' || name === 'time') {
if (args[1] && this.isStringLiteral(args[1])) {
var style = args[1].value
parameter += ', ' + (
/[{}\s]/.test(style)
? '\'' + style.replace(/'/g, '\'\'') + '\''
: style.replace(/'/g, '\'\'')
)
}
parameters[id] = args[0]
return '{ ' + parameter + ' }'
}
var options
if (name === 'select') {
if (args.length < 2) return
options = this.getOptionsFromObjectExpression(args[1], nextToken, parameters)
if (!options) return
parameters[id] = args[0]
return '{ ' + parameter + ', ' + options + ' }'
}
if (name === 'plural' || name === 'selectordinal') {
if (args.length < 2) return
var hasOffset = this.isNumericLiteral(args[1])
options = this.getOptionsFromObjectExpression(args[hasOffset ? 2 : 1], nextToken, parameters)
if (!options) return
if (hasOffset) {
options = 'offset:' + args[1].value + options
}
parameters[id] = args[0]
return '{ ' + parameter + ', ' + options + ' }'
}
},
getOptionsFromObjectExpression: function (node, nextToken, parameters) {
if (node.type !== 'ObjectExpression') return
var options = ''
var properties = node.properties
for (var p = 0, pp = properties.length; p < pp; ++p) {
var property = properties[p]
if (property.computed || property.shorthand || property.method) return
var key = property.key.name || property.key.value
var valueNode = property.value
var value
if (valueNode.type === 'JSXElement') {
value = this.getChildMessageText(valueNode, nextToken, parameters)
} else if (this.isStringLiteral(valueNode)) {
value = String(valueNode.value)
}
if (value == null) return
options += '\n' + key + ' {' + value + '}'
}
return options
}
}