ifdef.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * @fileoverview 条件编译
  3. */
  4. const through2 = require('through2')
  5. /**
  6. * @description 条件编译
  7. * @param {string} platform 平台
  8. */
  9. module.exports = function (platform) {
  10. return through2.obj(function (file, _, callback) {
  11. if (file.isBuffer()) {
  12. // 文件夹级别的处理
  13. if (file.relative.includes('miniprogram')) {
  14. if (platform !== 'uni-app') {
  15. // 去掉这一层
  16. file.path = file.path.replace(/(.*)[/\\]miniprogram/, '$1')
  17. } else {
  18. // 不用于本平台的文件
  19. callback()
  20. return
  21. }
  22. } else if (file.relative.includes('uni-app')) {
  23. if (platform === 'uni-app') {
  24. file.path = file.path.replace(/(.*)[/\\]uni-app/, '$1')
  25. } else {
  26. callback()
  27. return
  28. }
  29. }
  30. // 小程序平台进行进一步处理
  31. if (platform !== 'uni-app') {
  32. let content = file.contents.toString()
  33. /**
  34. * 方式1:
  35. * 在注释 #if(n)def xxx 和 #endif 之间的内容会根据是否定义 xxx 决定是否保留
  36. */
  37. const commentReg = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|<!--[\s\S]*?-->/g // 提取所有注释
  38. const copy = content // 拷贝一个副本用于搜索
  39. let match = commentReg.exec(copy)
  40. const stack = []
  41. while (match) {
  42. if (match[0].includes('#if')) {
  43. stack.push([match[0], match.index])
  44. } else if (match[0].includes('#endif')) {
  45. const item = stack.pop()
  46. if (!item) {
  47. throw Error('条件编译错误:存在多余的 #endif,path:' + file.path + ',content: ' + content.substr(match.index, 100))
  48. }
  49. const def = item[0].match(/MP-[A-Z]+/gi) || [] // 取出定义条件
  50. let hit = false
  51. for (let i = 0; i < def.length; i++) {
  52. if (def[i] && platform === def[i].toLowerCase()) {
  53. hit = true // 命中
  54. break
  55. }
  56. }
  57. // 不匹配
  58. if ((item[0].includes('#ifdef') && !hit) || (item[0].includes('#ifndef') && hit)) {
  59. let fill = ''
  60. // 用空格填充
  61. for (let j = item[1] + item[0].length; j < match.index; j++) {
  62. if (content[j] === '\n') {
  63. fill += '\n'
  64. } else {
  65. fill += ' '
  66. }
  67. }
  68. content = content.substr(0, item[1] + item[0].length) + fill + content.substr(match.index)
  69. }
  70. }
  71. match = commentReg.exec(copy)
  72. }
  73. if (stack.length) {
  74. throw Error('条件编译错误:存在未闭合的 #ifdef,path:' + file.path + ',content: ' + content.substr(stack[0][1], 100))
  75. }
  76. /**
  77. * 方式2:
  78. * wxml 中属性前加平台名将仅编译到该平台,如 mp-weixin:attr
  79. */
  80. if (file.extname === '.wxml') {
  81. content = content.replace(/([^:\s]+:[^=\s]+)\s*=\s*"(.*?)"/g, ($, $1, $2) => {
  82. const platforms = $1.split(':')
  83. let name = platforms.pop()
  84. const last = platforms[platforms.length - 1]
  85. if (last && !last.includes('mp')) {
  86. name = platforms.pop() + ':' + name
  87. }
  88. if (!platforms.length) return $
  89. let i
  90. for (i = platforms.length; i--;) {
  91. if (platform === platforms[i].toLowerCase()) break
  92. }
  93. if (i >= 0) return `${name}="${$2}"`
  94. return ''
  95. })
  96. }
  97. file.contents = Buffer.from(content)
  98. }
  99. }
  100. this.push(file)
  101. callback()
  102. })
  103. }