minify-code.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* eslint-disable @lwc/lwc/no-async-await */
  2. const path = require('path')
  3. const fs = require('fs')
  4. const recursive = require('recursive-readdir')
  5. const compiler = require('vue/compiler-sfc')
  6. const { minify } = require('terser')
  7. const babel = require('@babel/core')
  8. const presetJsx = require('@vue/babel-preset-jsx')
  9. const terserOptions = {
  10. compress: {
  11. defaults: false,
  12. },
  13. format: {
  14. comments: (obj, token) => {
  15. const comment = token.value
  16. return comment.startsWith(' webpackChunkName')
  17. },
  18. },
  19. }
  20. const srcDir = path.join(process.cwd(), 'src')
  21. recursive(
  22. srcDir,
  23. [
  24. (file, stats) => {
  25. if (stats.isDirectory()) {
  26. // 忽略自动导入目录
  27. return (
  28. path.basename(file) === '_import-components' || path.basename(file) === 'sd-icon-library'
  29. )
  30. }
  31. if (path.extname(file) !== '.vue' && path.extname(file) !== '.js') {
  32. // 非vue、js文件不处理
  33. return true
  34. }
  35. // if (path.basename(file) !== 'ext-layout.vue') {
  36. // // ext-layout.vue sd-data-table.vue sd-advance-search-btn.vue code-export.js sd-collapse-layout-render.vue sd-quill-reader.vue config-common.js sd-user-item.vue sd-webflow.vue sd-child-table-render.vue
  37. // return true
  38. // }
  39. return false
  40. },
  41. ],
  42. function(_err, files) {
  43. files.forEach((file) => {
  44. if (file.endsWith('.vue')) {
  45. processVue(file)
  46. } else {
  47. processJS(file)
  48. }
  49. })
  50. }
  51. )
  52. function logError(path, error) {
  53. console.log('file:', path)
  54. }
  55. async function compileAndMinifyJS(code, path) {
  56. try {
  57. const compiled = babel.transformSync(code, {
  58. configFile: false,
  59. presets: [presetJsx],
  60. }).code
  61. // 压缩 js
  62. const minifyResult = await minify(compiled, terserOptions)
  63. return '\n/* eslint-disable */\n// sd-skip-auto-import\n' + minifyResult.code
  64. } catch (error) {
  65. logError(path, error)
  66. throw error
  67. }
  68. }
  69. async function processJS(path) {
  70. const content = fs.readFileSync(path).toString()
  71. writeFile(path, await compileAndMinifyJS(content, path))
  72. }
  73. async function processVue(path) {
  74. const content = fs.readFileSync(path).toString()
  75. const parseResult = compiler.parseComponent(content)
  76. let script = parseResult.script.content
  77. let result
  78. if (parseResult.template) {
  79. // 有模板
  80. // functional 组件处理不了
  81. if (parseResult.template.attrs.functional) return
  82. let render
  83. const template = parseResult.template.content
  84. render = compiler.compileTemplate({
  85. source: template,
  86. filename: path,
  87. compilerOptions: { whitespace: 'condense' },
  88. }).code
  89. // render这个变量可能会冲突,改掉
  90. render = render
  91. .replace('render = function render', '_render = function _render')
  92. .replace('render._withStripped = true', '_render._withStripped = true')
  93. const beforeTemplate = content.substring(0, parseResult.template.start)
  94. const afterTemplate = content.substring(parseResult.template.end, parseResult.script.start)
  95. const afterScript = content.substring(parseResult.script.end)
  96. const isExportDefault = script.indexOf('export default {') !== -1
  97. let defaultExportName
  98. if (isExportDefault) {
  99. script = script.replace(/}[\s;]*$/, 'render:_render,staticRenderFns,}')
  100. } else {
  101. const match = script.match(/^export default (.*?)$/m)
  102. defaultExportName = match[1]
  103. }
  104. // 加上 render
  105. if (isExportDefault) {
  106. script = script.replace(/export default ?{/, render + '\nexport default {\n')
  107. } else {
  108. script = script.replace(
  109. 'export default ' + defaultExportName,
  110. render +
  111. '\n' +
  112. defaultExportName +
  113. '.render = _render;' +
  114. defaultExportName +
  115. '.staticRenderFns = staticRenderFns;export default ' +
  116. defaultExportName
  117. )
  118. }
  119. result =
  120. (beforeTemplate + afterTemplate).replace(/^<template.*?><\/template>$/gm, '') +
  121. (await compileAndMinifyJS(script, path)) +
  122. afterScript
  123. } else {
  124. // 没有模板
  125. const beforeScript = content.substring(0, parseResult.script.start)
  126. const afterScript = content.substring(parseResult.script.end)
  127. result = beforeScript + (await compileAndMinifyJS(script, path)) + afterScript
  128. }
  129. writeFile(path, result)
  130. }
  131. function writeFile(jsFile, jsContent) {
  132. // 生成最终的js文件
  133. fs.writeFile(jsFile, jsContent, (error) => {
  134. if (error) {
  135. // eslint-disable-next-line no-console
  136. console.error('Error while creating ' + jsFile)
  137. throw error
  138. }
  139. })
  140. }