build.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. const path = require('path')
  2. const fs = require('fs')
  3. const gulp = require('gulp')
  4. const clean = require('gulp-clean')
  5. const sass = require('gulp-sass')(require('sass'));
  6. const less = require('gulp-less')
  7. const rename = require('gulp-rename')
  8. const gulpif = require('gulp-if')
  9. const sourcemaps = require('gulp-sourcemaps')
  10. const webpack = require('webpack')
  11. const gulpInstall = require('gulp-install')
  12. const gulpReplace = require('gulp-replace')
  13. const config = require('./config')
  14. const checkComponents = require('./checkcomponents')
  15. const checkWxss = require('./checkwxss')
  16. const _ = require('./utils')
  17. const jsConfig = config.js || {}
  18. const wxssConfig = config.wxss || {}
  19. const srcPath = config.srcPath
  20. const distPath = config.distPath
  21. const IGNORE_PATH = [
  22. './**/_*.scss',
  23. 'node_modules/**/*',
  24. 'miniprogram_npm/**/*'
  25. ]
  26. const sass2wxss = (PATH) => {
  27. return gulp
  28. .src(srcPath, {
  29. ignore: IGNORE_PATH,
  30. since: gulp.lastRun(sass2wxss)
  31. })
  32. .pipe(
  33. replace(/@import(.+?);/g, ($1, $2) =>
  34. /*! 这种注释不会被clean-css 处理 */
  35. $2.includes('./_') ? $1 : `/*! ${$1} */`
  36. )
  37. )
  38. .pipe(sass({ outputStyle: 'expanded' }).on('error', sass.logError))
  39. .pipe(replace(/\/\*!\s(@import\s+.+?;)\s\*\//g, ($1, $2) => $2))
  40. .pipe(rename({ extname: '.wxss' }))
  41. .pipe(gulp.dest('./'))
  42. }
  43. // gulp.task('transform sass to wxss', sass2wxss)
  44. /**
  45. * 获取 wxss sass流
  46. */
  47. function wxssSass(wxssFileList) {
  48. if (!wxssFileList.length) return false
  49. return gulp.src(wxssFileList, {cwd: srcPath, base: srcPath})
  50. // .pipe(gulpReplace(/@import\s*["|'][^"|']+["|'];?/gi, function (match) {
  51. // return `/**R**${match}**R**/`;
  52. // }))
  53. .pipe(sass())
  54. // 使@import引入样式不被转化
  55. // .pipe(gulpReplace(/\/\*{2}R\*{2}(@import\s*["|'][^"|']+["|'];?)\*{2}R\*{2}\//gi, function (match, p1) {
  56. // return p1.replace(/\.s+css/gi, '.wxss');
  57. // }))
  58. .pipe(rename({extname: '.wxss'}))
  59. .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.write('./')))
  60. .pipe(_.logger(wxssConfig.less ? 'generate' : undefined))
  61. .pipe(gulp.dest(distPath))
  62. }
  63. /**
  64. * 获取 wxss 流
  65. */
  66. function wxss(wxssFileList) {
  67. if (!wxssFileList.length) return false
  68. return gulp.src(wxssFileList, {cwd: srcPath, base: srcPath})
  69. .pipe(checkWxss.start()) // 开始处理 import
  70. .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.init()))
  71. .pipe(gulpif(wxssConfig.less, less({paths: [srcPath]})))
  72. .pipe(checkWxss.end()) // 结束处理 import
  73. .pipe(rename({extname: '.wxss'}))
  74. .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.write('./')))
  75. .pipe(_.logger(wxssConfig.less ? 'generate' : undefined))
  76. .pipe(gulp.dest(distPath))
  77. }
  78. /**
  79. * 获取 js 流
  80. */
  81. function js(jsFileMap, scope) {
  82. const webpackConfig = config.webpack
  83. const webpackCallback = (err, stats) => {
  84. if (!err) {
  85. // eslint-disable-next-line no-console
  86. console.log(stats.toString({
  87. assets: true,
  88. cached: false,
  89. colors: true,
  90. children: false,
  91. errors: true,
  92. warnings: true,
  93. version: true,
  94. modules: false,
  95. publicPath: true,
  96. }))
  97. } else {
  98. // eslint-disable-next-line no-console
  99. console.log(err)
  100. }
  101. }
  102. webpackConfig.entry = jsFileMap
  103. webpackConfig.output.path = distPath
  104. if (scope.webpackWatcher) {
  105. scope.webpackWatcher.close()
  106. scope.webpackWatcher = null
  107. }
  108. if (config.isWatch) {
  109. scope.webpackWatcher = webpack(webpackConfig).watch({
  110. ignored: /node_modules/,
  111. }, webpackCallback)
  112. } else {
  113. webpack(webpackConfig).run(webpackCallback)
  114. }
  115. }
  116. /**
  117. * 拷贝文件
  118. */
  119. function copy(copyFileList) {
  120. if (!copyFileList.length) return false
  121. return gulp.src(copyFileList, {cwd: srcPath, base: srcPath})
  122. .pipe(_.logger())
  123. .pipe(gulp.dest(distPath))
  124. }
  125. /**
  126. * 安装依赖包
  127. */
  128. function install() {
  129. return gulp.series(async () => {
  130. const demoDist = config.demoDist
  131. const demoPackageJsonPath = path.join(demoDist, 'package.json')
  132. const packageJson = _.readJson(path.resolve(__dirname, '../package.json'))
  133. const dependencies = packageJson.dependencies || {}
  134. await _.writeFile(demoPackageJsonPath, JSON.stringify({dependencies}, null, '\t')) // write dev demo's package.json
  135. }, () => {
  136. const demoDist = config.demoDist
  137. const demoPackageJsonPath = path.join(demoDist, 'package.json')
  138. return gulp.src(demoPackageJsonPath)
  139. .pipe(gulpInstall({production: true}))
  140. })
  141. }
  142. class BuildTask {
  143. constructor(id, entry) {
  144. if (!entry) return
  145. this.id = id
  146. this.entries = Array.isArray(config.entry) ? config.entry : [config.entry]
  147. this.copyList = Array.isArray(config.copy) ? config.copy : []
  148. this.componentListMap = {}
  149. this.cachedComponentListMap = {}
  150. this.init()
  151. }
  152. init() {
  153. const id = this.id
  154. /**
  155. * 清空目标目录
  156. */
  157. gulp.task(`${id}-clean-dist`, () => gulp.src(distPath, {read: false, allowEmpty: true}).pipe(clean()))
  158. /**
  159. * 拷贝 demo 到目标目录
  160. */
  161. let isDemoExists = false
  162. gulp.task(`${id}-demo`, gulp.series(async () => {
  163. const demoDist = config.demoDist
  164. isDemoExists = await _.checkFileExists(path.join(demoDist, 'project.config.json'))
  165. }, done => {
  166. if (!isDemoExists) {
  167. const demoSrc = config.demoSrc
  168. const demoDist = config.demoDist
  169. return gulp.src('**/*', {cwd: demoSrc, base: demoSrc})
  170. .pipe(gulp.dest(demoDist))
  171. }
  172. return done()
  173. }))
  174. /**
  175. * 安装依赖包
  176. */
  177. gulp.task(`${id}-install`, install())
  178. /**
  179. * 检查自定义组件
  180. */
  181. gulp.task(`${id}-component-check`, async () => {
  182. const entries = this.entries
  183. const mergeComponentListMap = {}
  184. for (let i = 0, len = entries.length; i < len; i++) {
  185. let entry = entries[i]
  186. entry = path.join(srcPath, `${entry}.json`)
  187. const newComponentListMap = await checkComponents(entry)
  188. _.merge(mergeComponentListMap, newComponentListMap)
  189. }
  190. this.cachedComponentListMap = this.componentListMap
  191. this.componentListMap = mergeComponentListMap
  192. })
  193. /**
  194. * 写 json 文件到目标目录
  195. */
  196. gulp.task(`${id}-component-json`, done => {
  197. const jsonFileList = this.componentListMap.jsonFileList
  198. if (jsonFileList && jsonFileList.length) return copy(jsonFileList)
  199. return done()
  200. })
  201. /**
  202. * 拷贝 wxml 文件到目标目录
  203. */
  204. gulp.task(`${id}-component-wxml`, done => {
  205. const wxmlFileList = this.componentListMap.wxmlFileList
  206. if (wxmlFileList &&
  207. wxmlFileList.length &&
  208. !_.compareArray(this.cachedComponentListMap.wxmlFileList, wxmlFileList)) {
  209. console.log('11')
  210. return copy(wxmlFileList)
  211. }
  212. return done()
  213. })
  214. /**
  215. * 生成 wxss 文件到目标目录
  216. */
  217. gulp.task(`${id}-component-wxss`, done => {
  218. const wxssFileList = this.componentListMap.wxssFileList
  219. if (wxssFileList &&
  220. wxssFileList.length &&
  221. !_.compareArray(this.cachedComponentListMap.wxssFileList, wxssFileList)) {
  222. return wxssSass(wxssFileList, srcPath, distPath)
  223. }
  224. return done()
  225. })
  226. /**
  227. * 拷贝 wxs 文件到目标目录
  228. */
  229. gulp.task(`${id}-component-wxs`, done => {
  230. const wxsFileList = this.componentListMap.wxsFileList
  231. if (wxsFileList &&
  232. wxsFileList.length &&
  233. !_.compareArray(this.cachedComponentListMap.wxsFileList, wxsFileList)) {
  234. console.log('22')
  235. return copy(wxsFileList)
  236. }
  237. return done()
  238. })
  239. /**
  240. * 生成 js 文件到目标目录
  241. */
  242. gulp.task(`${id}-component-js`, done => {
  243. const jsFileList = this.componentListMap.jsFileList
  244. if (jsFileList &&
  245. jsFileList.length &&
  246. !_.compareArray(this.cachedComponentListMap.jsFileList, jsFileList)) {
  247. if (jsConfig.webpack) {
  248. js(this.componentListMap.jsFileMap, this)
  249. } else {
  250. return copy(jsFileList)
  251. }
  252. }
  253. return done()
  254. })
  255. /**
  256. * 拷贝相关资源到目标目录
  257. */
  258. gulp.task(`${id}-copy`, gulp.parallel(done => {
  259. const copyList = this.copyList
  260. const copyFileList = copyList.map(copyFilePath => {
  261. try {
  262. if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
  263. return path.join(copyFilePath, '**/*.!(wxss)')
  264. } else {
  265. return copyFilePath
  266. }
  267. } catch (err) {
  268. // eslint-disable-next-line no-console
  269. console.error(err)
  270. return null
  271. }
  272. }).filter(copyFilePath => !!copyFilePath)
  273. if (copyFileList.length) return copy(copyFileList)
  274. return done()
  275. }, done => {
  276. const copyList = this.copyList
  277. const copyFileList = copyList.map(copyFilePath => {
  278. try {
  279. if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
  280. return path.join(copyFilePath, '**/*.wxss')
  281. } else if (copyFilePath.slice(-5) === '.wxss') {
  282. return copyFilePath
  283. } else {
  284. return null
  285. }
  286. } catch (err) {
  287. // eslint-disable-next-line no-console
  288. console.error(err)
  289. return null
  290. }
  291. }).filter(copyFilePath => !!copyFilePath)
  292. if (copyFileList.length) return wxss(copyFileList, srcPath, distPath)
  293. return done()
  294. }))
  295. /**
  296. * 监听 js 变化
  297. */
  298. gulp.task(`${id}-watch-js`, done => {
  299. if (!jsConfig.webpack) {
  300. return gulp.watch(this.componentListMap.jsFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-js`))
  301. }
  302. return done()
  303. })
  304. /**
  305. * 监听 json 变化
  306. */
  307. gulp.task(`${id}-watch-json`, () => gulp.watch(this.componentListMap.jsonFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-check`, gulp.parallel(`${id}-component-wxml`, `${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`))))
  308. /**
  309. * 监听 wxml 变化
  310. */
  311. gulp.task(`${id}-watch-wxml`, () => {
  312. this.cachedComponentListMap.wxmlFileList = null
  313. return gulp.watch(this.componentListMap.wxmlFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxml`))
  314. })
  315. /**
  316. * 监听 wxss 变化
  317. */
  318. gulp.task(`${id}-watch-wxss`, () => {
  319. this.cachedComponentListMap.wxssFileList = null
  320. return gulp.watch('**/*.wxss', {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxss`))
  321. })
  322. /**
  323. * 监听相关资源变化
  324. */
  325. gulp.task(`${id}-watch-copy`, () => {
  326. const copyList = this.copyList
  327. const copyFileList = copyList.map(copyFilePath => {
  328. try {
  329. if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
  330. return path.join(copyFilePath, '**/*')
  331. } else {
  332. return copyFilePath
  333. }
  334. } catch (err) {
  335. // eslint-disable-next-line no-console
  336. console.error(err)
  337. return null
  338. }
  339. }).filter(copyFilePath => !!copyFilePath)
  340. const watchCallback = filePath => copy([filePath])
  341. return gulp.watch(copyFileList, {cwd: srcPath, base: srcPath})
  342. .on('change', watchCallback)
  343. .on('add', watchCallback)
  344. .on('unlink', watchCallback)
  345. })
  346. /**
  347. * 监听 demo 变化
  348. */
  349. gulp.task(`${id}-watch-demo`, () => {
  350. const demoSrc = config.demoSrc
  351. const demoDist = config.demoDist
  352. const watchCallback = filePath => gulp.src(filePath, {cwd: demoSrc, base: demoSrc})
  353. .pipe(gulp.dest(demoDist))
  354. return gulp.watch('**/*', {cwd: demoSrc, base: demoSrc})
  355. .on('change', watchCallback)
  356. .on('add', watchCallback)
  357. .on('unlink', watchCallback)
  358. })
  359. /**
  360. * 监听安装包列表变化
  361. */
  362. gulp.task(`${id}-watch-install`, () => gulp.watch(path.resolve(__dirname, '../package.json'), install()))
  363. /**
  364. * 构建相关任务
  365. */
  366. gulp.task(`${id}-build`, gulp.series(`${id}-clean-dist`, `${id}-component-check`, gulp.parallel(`${id}-component-wxml`,`${id}-component-wxs`,`${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`, `${id}-copy`)))
  367. gulp.task(`${id}-watch`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`, gulp.parallel(`${id}-watch-wxml`, `${id}-watch-wxss`, `${id}-watch-js`, `${id}-watch-json`, `${id}-watch-copy`, `${id}-watch-install`, `${id}-watch-demo`)))
  368. gulp.task(`${id}-dev`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`))
  369. gulp.task(`${id}-default`, gulp.series(`${id}-build`))
  370. }
  371. }
  372. module.exports = BuildTask