parse-i18n-function.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. const parse5 = require('parse5')
  2. const acorn = require('acorn')
  3. const escodegen = require('escodegen')
  4. function generateHTML(rs) {
  5. try {
  6. let processed = escodegen.generate(rs, {
  7. format: {
  8. semicolons: false,
  9. quotes: 'single',
  10. compact: false,
  11. indent: {
  12. style: '',
  13. base: 0,
  14. adjustMultilineComment: false
  15. }
  16. }
  17. })
  18. processed = processed.replace(/;$/, '')
  19. .replace(/;\s/g, ' ')
  20. .replace('let VALUE = ', '')
  21. .replace(/;,/g, ',')
  22. .replace(/;]/g, ']')
  23. return processed
  24. } catch (e) {
  25. }
  26. }
  27. function parseExpression(list, key, map) {
  28. list.forEach((one, index) => {
  29. let value = one[key] || one
  30. if (value.type === 'BinaryExpression') {
  31. for (let i in value) {
  32. if (i === 'left' || i === 'right') {
  33. let expression = value[i]
  34. if (expression.type === 'CallExpression' && expression.callee && expression.callee.name === '$t' && expression.arguments.length === 1) {
  35. value[i] = buildStringExpression(map[expression.arguments[0].value] || expression.arguments[0].value)
  36. }
  37. }
  38. }
  39. } else if (value.type === 'CallExpression') {
  40. const rs = buildStringExpression(map[value.arguments[0].value] || value.arguments[0].value)
  41. if (key) {
  42. list[index][key] = rs
  43. } else {
  44. list[index] = rs
  45. }
  46. }
  47. })
  48. }
  49. function doParseExpression(code, map) {
  50. if (typeof code === 'string' && !/\$t/.test(code)) {
  51. return
  52. }
  53. let rs = code
  54. if (typeof code === 'string') {
  55. rs = acorn.parse(code, {
  56. sourceType: 'module'
  57. })
  58. }
  59. for (let i = 0; i < rs.body.length; i++) {
  60. const one = rs.body[i]
  61. if (one.type === 'ExpressionStatement' && one.expression.type === 'ArrayExpression') {
  62. const elements = one.expression.elements
  63. parseExpression(elements, '', map)
  64. } else if (one.type === 'ExpressionStatement' && one.expression && one.expression.callee && one.expression.callee.name === '$t' && one.expression.arguments.length === 1 && one.expression.arguments[0].type !== 'BinaryExpression') {
  65. one.expression = buildStringExpression(map[one.expression.arguments[0].value] || one.expression.arguments[0].value)
  66. } else if (one.type === 'ExpressionStatement') { // 表达式模式
  67. parseCallExpression(one.expression, map)
  68. } else if (one.type === 'VariableDeclaration') { // 对象类型
  69. var properties = one.declarations[0].init.properties
  70. parseExpression(properties, 'value', map)
  71. } else {
  72. }
  73. }
  74. return rs
  75. }
  76. function parseAttrs(nodes, map) {
  77. nodes.forEach(one => {
  78. if (one.nodeName === '#text' && /{{/.test(one.value) && /}}/.test(one.value)) {
  79. one.value = one.value.replace(/{{(.*?)}}/g, function (a, b) {
  80. if (b.indexOf('$t(') === -1) {
  81. return a
  82. }
  83. let rs = acorn.parse(b, {
  84. sourceType: 'module'
  85. })
  86. if (rs.body.length === 1 && rs.body[0].expression && rs.body[0].type === 'ExpressionStatement' && rs.body[0].expression.type === 'CallExpression' && rs.body[0].expression.arguments[0].type === 'Literal') {
  87. rs = doParseExpression(rs, map)
  88. if (!rs) {
  89. return a
  90. } else {
  91. let html = generateHTML(rs)
  92. if (html) {
  93. html = html.replace(/^'(.*?)'$/, '$1')
  94. return html
  95. }
  96. }
  97. } else {
  98. return a
  99. }
  100. })
  101. }
  102. if (one.attrs) {
  103. for (let i in one.attrs) {
  104. let attr = one.attrs[i]
  105. if (/\$t/.test(attr.value)) {
  106. if (/^:/.test(attr.name) && /^{/.test(attr.value)) {
  107. attr.value = 'let VALUE = ' + attr.value
  108. }
  109. let rs = doParseExpression(attr.value, map)
  110. let processed = generateHTML(rs)
  111. if (processed) {
  112. attr.value = processed
  113. } else {
  114. // stay unchanged
  115. }
  116. }
  117. }
  118. }
  119. if (one.childNodes && one.childNodes.length) {
  120. parseAttrs(one.childNodes, map)
  121. }
  122. })
  123. }
  124. function parse(code, map) {
  125. const documentFragment = parse5.parseFragment(code)
  126. parseAttrs(documentFragment.childNodes, map)
  127. const html = parse5.serialize(documentFragment)
  128. return html
  129. }
  130. exports.parse = parse
  131. function parseCallExpression (expression, map) {
  132. for (let i in expression) {
  133. if (i === 'left' || i === 'right') {
  134. let currentExpression = expression[i]
  135. if (currentExpression.type === 'CallExpression' && currentExpression.callee && currentExpression.callee.name === '$t' && currentExpression.arguments.length === 1) {
  136. expression[i] = buildStringExpression(map[currentExpression.arguments[0].value] || currentExpression.arguments[0].value)
  137. } else {
  138. if (currentExpression.type === 'BinaryExpression') {
  139. parseCallExpression(currentExpression, map)
  140. }
  141. }
  142. }
  143. }
  144. }
  145. function buildStringExpression(value) {
  146. return {
  147. "type": "ExpressionStatement",
  148. "expression": {
  149. "type": "Literal",
  150. "value": value,
  151. "raw": "'" + value + "'"
  152. }
  153. }
  154. }