gulpfile.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089
  1. /*******************
  2. * 后台管理系统、公司网站分为一组;
  3. * 代理商、经销商、测试系统分为一组(由于测试系统依赖app里面的公共文件,所以测试系统后续开发尽量不要和app内部文件耦合);
  4. * 终端用户分为一组,完全要做到:【其他系统发布不会造成对用户系统的影响】;
  5. *
  6. * 特殊的:公共组件,主要是手机网页用的多,后台管理系统用的少,但是经销商和终端用户又是分开的,导致经销商和终端用户的发布会相互影响,所以尽量不要修改!
  7. * ********************/
  8. //日志
  9. var log4js = require('log4js')
  10. var log4jsConfig = {
  11. appenders: {
  12. out: { type: 'stdout' }, //设置是否在控制台打印日志
  13. info: { type: 'file', filename: './var/logs/gulp-history.log' },
  14. },
  15. categories: {
  16. default: { appenders: ['out', 'info'], level: 'info' }, //去掉'out'。控制台不打印日志
  17. },
  18. }
  19. if (process.platform.indexOf('linux') > -1) {
  20. log4jsConfig.appenders.info.filename = '/var/log/gulp-history.log'
  21. }
  22. log4js.configure(log4jsConfig)
  23. var LogFile = log4js.getLogger('info')
  24. const { series, parallel } = require('gulp') //gulp主组件
  25. //引入gulp和gulp插件
  26. var gulp = require('gulp') //gulp主组件
  27. var babel = require('gulp-babel') //如果某些JS用了ES6语法,就需要转换
  28. var changed = require('gulp-changed')
  29. var htmlmin = require('gulp-htmlmin') //html压缩组件
  30. var jshint = require('gulp-jshint') //js语法检查
  31. var gulpAutoprefixer = require('gulp-autoprefixer')
  32. var sass = require('gulp-sass')(require('sass')) // 编译 sass
  33. var less = require('gulp-less')
  34. var minifyCss = require('gulp-minify-css') //压缩CSS为一行;
  35. var uglify = require('gulp-uglify') //js文件压缩
  36. var rev = require('gulp-rev') //对文件名加MD5后缀
  37. var revCollector = require('gulp-rev-collector') //路径替换
  38. var gulpRemoveHtml = require('gulp-remove-html') //标签清除,参考:https://www.npmjs.com/package/gulp-remove-html
  39. var removeEmptyLines = require('gulp-remove-empty-lines') //清除空白行,参考:https://www.npmjs.com/package/gulp-remove-empty-lines
  40. var rename = require('gulp-rename')
  41. var gulpSequence = series
  42. var clean = require('gulp-clean') //清除文件插件,参考:https://github.com/teambition/gulp-clean
  43. var modify = require('gulp-modify') //文件内容修改
  44. var inject = require('gulp-inject-string')
  45. /*************************************************开发环境**************************************************/
  46. var browserSync = require('browser-sync').create()
  47. // 文档 https://browsersync.io/docs/options
  48. //复制模拟数据接口
  49. gulp.task('copy-mock', function () {
  50. return gulp.src('static/mock/**/*').pipe(gulp.dest(buildBasePath + 'mock'))
  51. })
  52. // 开发时,拷贝文件到dev目录
  53. function devCopyChange() {
  54. // 拷贝时排除需要sass编译的文件、排除doc文档(因为已经不需要了)、排除babel
  55. return gulp
  56. .src([
  57. 'static/**/*',
  58. '!**/custom/css/*.css',
  59. '!**/app/css/*.css',
  60. '!**/pages/css/user-common.css',
  61. '!**/custom/css/*.css',
  62. '!**/custom/scss/*.*',
  63. '!**/app/doc/*.*',
  64. //排除babel编译的部分
  65. '!static/app/js/*.js',
  66. '!static/components/custom/js/*.js',
  67. ])
  68. .pipe(changed('dist_dev')) //只拷贝变动过的文件
  69. .pipe(gulp.dest('dist_dev'))
  70. }
  71. function sassCommonPipe(src) {
  72. return src
  73. .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError)) // sass只会解析scss格式的import文件,不会解析css
  74. .pipe(autoprefixer())
  75. .pipe(minifyCss()) //压缩css,minifyCss会先把所有的@import url()外部进入的css文件递归插入,如 iconfont.css
  76. }
  77. // 开发时,自动编译sass并更新到dev目录,不用压缩!
  78. function devCustomRevCss(done) {
  79. //公共样式
  80. sassCommonPipe(gulp.src('static/components/custom/scss/index.scss'))
  81. .pipe(
  82. rename(function (path) {
  83. path.basename = 'common' //必须重命名,common文件名全局独享,这样revCollector才能正确执行
  84. })
  85. )
  86. .pipe(gulp.dest('dist_dev/components/custom/css'))
  87. //经销商、代理商、测试的样式
  88. sassCommonPipe(gulp.src('static/components/custom/scss/dealer.scss'))
  89. .pipe(
  90. rename(function (path) {
  91. path.basename = 'xyf.common.min'
  92. })
  93. )
  94. .pipe(gulp.dest('dist_dev/app/css'))
  95. //终端用户的样式
  96. sassCommonPipe(gulp.src('static/components/custom/scss/user.scss'))
  97. .pipe(
  98. rename(function (path) {
  99. path.basename = 'user-common'
  100. })
  101. )
  102. .pipe(gulp.dest('dist_dev/pages/css'))
  103. //支付后广告样式
  104. sassCommonPipe(gulp.src('static/components/custom/scss/third-receipt.scss'))
  105. .pipe(
  106. rename(function (path) {
  107. path.basename = 'third-receipt'
  108. })
  109. )
  110. .pipe(gulp.dest('dist_dev/public/css'))
  111. done()
  112. }
  113. // 开发时,自动编译less并更新到dist_dev目录,不用压缩!
  114. function devManageLess() {
  115. console.log('less编译中')
  116. return gulp.src('static/1.0/css/less/app.less').pipe(less()).pipe(autoprefixer()).pipe(gulp.dest('dist_dev/1.0/css'))
  117. }
  118. function devEs6App() {
  119. return gulp
  120. .src('static/app/js/*.js')
  121. .pipe(babel()) //es6转es5
  122. .pipe(gulp.dest('dist_dev/app/js'))
  123. }
  124. function devEs6Custom() {
  125. return gulp
  126. .src('static/components/custom/js/*.js')
  127. .pipe(babel()) //es6转es5
  128. .pipe(gulp.dest('dist_dev/components/custom/js'))
  129. }
  130. // 监听变动文件,然后复制或是编译
  131. gulp.task(
  132. 'dev-watch',
  133. parallel([devCopyChange, devCustomRevCss, devManageLess, devEs6App, devEs6Custom], function (done) {
  134. console.log(
  135. '---------已监听大部分业务js和html,排除监听公共库、公司网站代码、图片,如果变动需要重启命令即可自动拷贝-------------'
  136. )
  137. // { interval: 1500 }设置 监听频率,可以减少CPU占用
  138. // 监听所有js html,以及1.0的css,无后缀名的接口文件;排除angular-mod (基本不改动),排除第三方库(如有新增库,重启命令即可拷贝过去),排除后可以减少cpu的占用(i5 4590 12%占用,全部监听占用30%左右)
  139. gulp.watch(
  140. [
  141. 'static/**/*.js',
  142. 'static/1.0/**/*.css',
  143. 'static/**/*.html',
  144. 'static/mock/**/*',
  145. 'static/wifi/*',
  146. //排除babel编译的部分
  147. '!static/app/js/*.js',
  148. '!static/components/custom/js/*.js',
  149. //排除公共库或是基本不改的代码
  150. '!static/1.0/vendor/**/*.*',
  151. '!static/components/lib/**/*.*',
  152. '!static/angular-mod/**/*.*',
  153. ],
  154. devCopyChange
  155. )
  156. // 监听sass文件,并编译
  157. gulp.watch(['static/components/custom/scss/**/*.scss', 'static/components/custom/css/*.css'], devCustomRevCss)
  158. // 监听less文件,并编译
  159. gulp.watch(['static/1.0/**/*.less'], devManageLess)
  160. // 监听某些JS文件,可能会有ES6,所以编译
  161. gulp.watch(['static/app/js/*.js'], devEs6App)
  162. gulp.watch(['static/components/custom/js/*.js'], devEs6Custom)
  163. done()
  164. })
  165. )
  166. //开发: 启动服务器
  167. gulp.task(
  168. 'run-dev',
  169. series(['dev-watch'], function (done) {
  170. browserSync.init({
  171. server: {
  172. baseDir: 'dist_dev',
  173. },
  174. ghostMode: false, //不需要同步页面行为,不然无法同时打开多个不同的页面
  175. notify: false, //禁用浏览器的通知元素
  176. open: false, // 不需要自动打开浏览器
  177. host: '127.0.0.1',
  178. port: 80,
  179. // 前端开发时,模拟POST 避免404
  180. middleware: function (req, res, next) {
  181. if (req.method.toUpperCase() == 'POST') {
  182. req.method = 'GET'
  183. }
  184. next()
  185. },
  186. })
  187. done()
  188. })
  189. )
  190. module.exports.dev = series('run-dev')
  191. /*************************************************生产环境**************************************************/
  192. // 启动服务器:主要是验证一下dist对不对,需要复制模拟数据
  193. gulp.task(
  194. 'preview',
  195. series(['copy-mock'], function () {
  196. console.log('---------即将验证dist目录,请确保代码已编译-------------')
  197. browserSync.init({
  198. server: {
  199. baseDir: 'dist',
  200. },
  201. open: 'external',
  202. host: '127.0.0.1',
  203. port: 80,
  204. //模拟POST 避免404
  205. middleware: function (req, res, next) {
  206. if (req.method.toUpperCase() == 'POST') {
  207. req.method = 'GET'
  208. }
  209. next()
  210. },
  211. })
  212. })
  213. )
  214. var buildBasePath = 'dist/' //构建输出的目录
  215. if (process.argv[4]) {
  216. buildBasePath = process.argv[4] + '/'
  217. }
  218. // css浏览器兼容修复,如加webkit
  219. function autoprefixer() {
  220. return gulpAutoprefixer({
  221. overrideBrowserslist: ['Chrome > 31', 'Android 4.1', 'iOS 7.1'],
  222. cascade: true, //是否美化属性值 默认:true 像这样:
  223. //-webkit-transform: rotate(45deg);
  224. // transform: rotate(45deg);
  225. remove: true, //是否去掉不必要的前缀 默认:true
  226. })
  227. }
  228. /**********旧的dist清除******************/
  229. //删除全部dist文件
  230. gulp.task('clean:Build', function (cb) {
  231. return gulp.src(buildBasePath, { read: false, allowEmpty: true }).pipe(clean())
  232. })
  233. //删除后台管理系统
  234. gulp.task('clean:manage', function (cb) {
  235. return gulp
  236. .src([buildBasePath + '1.0/', buildBasePath + 'admaster/', buildBasePath + 'administrator/'], {
  237. read: false,
  238. allowEmpty: true,
  239. })
  240. .pipe(clean())
  241. })
  242. //删除公共组件dist
  243. gulp.task('clean:common', function (cb) {
  244. return gulp.src([buildBasePath + 'components/'], { read: false, allowEmpty: true }).pipe(clean())
  245. })
  246. //删除经销商、代理商dist文件
  247. gulp.task('clean:dealer', function (cb) {
  248. return gulp.src([buildBasePath + 'app/', buildBasePath + 'agents/'], { read: false, allowEmpty: true }).pipe(clean())
  249. })
  250. //测试系统dist文件
  251. gulp.task('clean:test', function (cb) {
  252. return gulp.src([buildBasePath + 'test/'], { read: false, allowEmpty: true }).pipe(clean())
  253. })
  254. //weknow dist文件
  255. gulp.task('clean:weknow', function (cb) {
  256. return gulp.src([buildBasePath + 'weknow/'], { read: false, allowEmpty: true }).pipe(clean())
  257. })
  258. //public dist文件
  259. gulp.task('clean:public', function (cb) {
  260. return gulp.src([buildBasePath + 'public/'], { read: false, allowEmpty: true }).pipe(clean())
  261. })
  262. //help dist文件
  263. gulp.task('clean:help', function (cb) {
  264. return gulp.src([buildBasePath + 'help/'], { read: false, allowEmpty: true }).pipe(clean())
  265. })
  266. //删除终端用户dist文件
  267. gulp.task('clean:enduser', function (cb) {
  268. return gulp.src([buildBasePath + 'pages/'], { read: false, allowEmpty: true }).pipe(clean())
  269. })
  270. /****************不需要编译的文件拷贝*********************/
  271. //复制公共文件,如系统图标,公共组件,等不容易变更的文件,主要是经销商、代理商、终端用户用到,后台管理系统几乎不用
  272. gulp.task('copy_common_1', function () {
  273. //拷贝前台公共组件字体
  274. return gulp.src(['static/components/fonts/*.*']).pipe(gulp.dest(buildBasePath + 'components/fonts'))
  275. })
  276. gulp.task('copy_common_2', function () {
  277. //拷贝前台公共组件js和css
  278. return gulp.src(['static/components/lib/**/*.*']).pipe(gulp.dest(buildBasePath + 'components/lib'))
  279. })
  280. gulp.task('copy_common_3', function () {
  281. //拷贝前台公共组件媒体文件
  282. return gulp.src(['static/components/custom/voice/*.*']).pipe(gulp.dest(buildBasePath + 'components/custom/voice'))
  283. })
  284. gulp.task('copy_common_4', function () {
  285. //拷贝全局网页缺省logo
  286. return gulp.src(['static/favicon.ico', 'static/robots.txt']).pipe(gulp.dest(buildBasePath + ''))
  287. })
  288. gulp.task('copy-common', gulpSequence(['copy_common_1'], ['copy_common_2'], ['copy_common_3'], ['copy_common_4']))
  289. //复制管理系统文件
  290. gulp.task('copy_manage_1', function () {
  291. //拷贝后台管理系统全部文件---【使用base=static可以在拷贝文件时候保留路径】
  292. return gulp
  293. .src(['static/1.0/fonts/**/*', 'static/1.0/img/**/*', 'static/1.0/vendor/**/*', 'static/1.0/favicon.png'], {
  294. base: 'static/',
  295. })
  296. .pipe(gulp.dest(buildBasePath))
  297. })
  298. gulp.task('copy-manage', gulpSequence(['copy_manage_1']))
  299. //复制经销商、代理商文件、测试系统文件(暂无)
  300. gulp.task('copy_dealer_1', function () {
  301. //拷贝经销商说明文档
  302. return gulp.src('static/app/doc/**/*').pipe(gulp.dest(buildBasePath + 'app/doc'))
  303. })
  304. gulp.task('copy_dealer_2', function () {
  305. //拷贝经销商图片文件
  306. return gulp.src('static/app/img/**/*').pipe(gulp.dest(buildBasePath + 'app/img'))
  307. })
  308. gulp.task('copy-dealer', gulpSequence(['copy_dealer_1'], ['copy_dealer_2']))
  309. //复制终端用户文件
  310. gulp.task('copy-enduser', function () {
  311. //拷贝终端用户图片文件
  312. return gulp.src('static/pages/images/**/*').pipe(gulp.dest(buildBasePath + 'pages/images'))
  313. })
  314. gulp.task('copy-help', function () {
  315. return gulp.src(['static/help/**/*.*']).pipe(gulp.dest(buildBasePath + 'help'))
  316. })
  317. gulp.task('chaoben_html', function () {
  318. return gulp
  319. .src(['static/chaoben/**/*.html'])
  320. .pipe(gulpRemoveHtml()) //清除特定标签
  321. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  322. .pipe(
  323. htmlmin({
  324. removeComments: true, //清除HTML注释
  325. collapseWhitespace: true, //压缩HTML
  326. collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
  327. removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input />
  328. removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
  329. removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
  330. minifyJS: true, //压缩页面JS
  331. minifyCSS: true, //压缩页面CSS
  332. })
  333. )
  334. .pipe(gulp.dest(buildBasePath + 'chaoben'))
  335. })
  336. gulp.task('chaoben_css', function () {
  337. return gulp
  338. .src(['static/chaoben/**/*.css'])
  339. .pipe(autoprefixer())
  340. .pipe(minifyCss()) //压缩css
  341. .pipe(gulp.dest(buildBasePath + 'chaoben'))
  342. })
  343. gulp.task('chaoben_static', function () {
  344. return gulp
  345. .src(['static/chaoben/**/*.png', 'static/chaoben/**/*.jpg', 'static/chaoben/**/*.js'])
  346. .pipe(gulp.dest(buildBasePath + 'chaoben'))
  347. })
  348. gulp.task('chaoben', gulpSequence(['chaoben_html'], ['chaoben_css'], ['chaoben_static']))
  349. var commonRevDir = buildBasePath + 'components/custom' //自定义公共组件输出文件
  350. var dealerRevDir = buildBasePath + 'app' //经销商系统输出文件
  351. var agentsRevDir = buildBasePath + 'agents' //代理商系统输出文件
  352. var enduserRevDir = buildBasePath + 'pages' //终端用户输出文件
  353. var testRevDir = buildBasePath + 'test' //测试模块输出文件
  354. var weknowRevDir = buildBasePath + 'weknow' //导航模块输出文件
  355. var publicRevDir = buildBasePath + 'public'
  356. var helpRevDir = buildBasePath + 'help' //
  357. /******************自定义公共组件输出文件*****************/
  358. var commonSrc = 'static/components/custom' //定制化公共组件源
  359. gulp.task('customRevJs', function () {
  360. return gulp
  361. .src(commonSrc + '/js/*.js')
  362. .pipe(
  363. modify({
  364. fileModifier: function (file, contents) {
  365. //此处修改了多个文件,其实只需要修改commone.js,有待优化...
  366. return 'var APP_VERSION=' + new Date().getTime() + ';' + contents
  367. },
  368. })
  369. )
  370. .pipe(babel()) //es6转es5
  371. .pipe(rev())
  372. .pipe(uglify()) //压缩js
  373. .pipe(gulp.dest(commonRevDir + '/js')) //输出带后缀的文件
  374. .pipe(rev.manifest())
  375. .pipe(gulp.dest(commonRevDir + '/js')) //输出json对照表
  376. })
  377. //拷贝字体到dist目录
  378. gulp.task('customRevFont', function () {
  379. return gulp
  380. .src([commonSrc + '/css/*.eot', commonSrc + '/css/*.svg', commonSrc + '/css/*.ttf', commonSrc + '/css/*.woff'])
  381. .pipe(gulp.dest(commonRevDir + '/css')) //输出文件
  382. })
  383. gulp.task('customRevCss', function () {
  384. return (
  385. gulp
  386. .src(commonSrc + '/scss/index.scss')
  387. .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
  388. .pipe(autoprefixer())
  389. .pipe(minifyCss()) //压缩css
  390. //必须重命名,common文件名全局独享,这样revCollector才能正确执行
  391. .pipe(
  392. rename(function (path) {
  393. path.basename = 'common'
  394. })
  395. )
  396. .pipe(rev()) //文件加md5后缀
  397. .pipe(gulp.dest(commonRevDir + '/css')) //输出带后缀的文件
  398. .pipe(rev.manifest())
  399. .pipe(gulp.dest(commonRevDir + '/css'))
  400. ) //输出json对照表
  401. })
  402. gulp.task('customReceiptCss', function () {
  403. return gulp
  404. .src(commonSrc + '/scss/third-receipt.scss')
  405. .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
  406. .pipe(autoprefixer())
  407. .pipe(minifyCss()) //压缩css
  408. .pipe(
  409. rename(function (path) {
  410. path.basename = 'third-receipt'
  411. })
  412. )
  413. .pipe(rev()) //文件加md5后缀
  414. .pipe(gulp.dest(publicRevDir + '/css')) //输出带后缀的文件
  415. .pipe(rev.manifest())
  416. .pipe(gulp.dest(publicRevDir + '/css')) //输出json对照表
  417. })
  418. /**************经销商、代理商、测试系统用的公共CSS、JS文件编译以及加版本号***************/
  419. //定义经销商的css、js源文件路径
  420. var dealerJsSrc = 'static/app/js/*.js'
  421. //CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射
  422. gulp.task('appRevCss', function () {
  423. return gulp
  424. .src(commonSrc + '/scss/dealer.scss')
  425. .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
  426. .pipe(autoprefixer())
  427. .pipe(minifyCss()) //压缩css
  428. .pipe(
  429. rename(function (path) {
  430. path.basename = 'xyf.common.min'
  431. })
  432. )
  433. .pipe(rev()) //文件加md5后缀
  434. .pipe(gulp.dest(dealerRevDir + '/css')) //输出带后缀的文件
  435. .pipe(rev.manifest())
  436. .pipe(gulp.dest(dealerRevDir + '/css')) //输出json对照表
  437. })
  438. //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
  439. gulp.task('appRevJs', function () {
  440. return gulp
  441. .src(dealerJsSrc)
  442. .pipe(babel()) //es6转es5
  443. .pipe(rev()) //md5
  444. .pipe(uglify()) //压缩js
  445. .pipe(gulp.dest(dealerRevDir + '/js'))
  446. .pipe(rev.manifest())
  447. .pipe(gulp.dest(dealerRevDir + '/js')) //输出json对照表
  448. })
  449. /**************后台管理系统、广告商***************/
  450. // 编译后台管理系统的less,manageRevCss任务压缩即可,注意src路径细节
  451. gulp.task('manageLess', function () {
  452. return gulp
  453. .src(['static/1.0/css/less/app.less'])
  454. .pipe(less())
  455. .pipe(gulp.dest(buildBasePath + '1.0/css'))
  456. })
  457. //预先执行 manageLess , CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射,注意排除 app.css(因为已经单独编译),并包含dist下单app.css
  458. gulp.task(
  459. 'manageRevCss',
  460. series(['manageLess'], function () {
  461. return gulp
  462. .src(['static/1.0/css/*.css', buildBasePath + '1.0/css/app.css', '!static/1.0/css/app.css'])
  463. .pipe(rev())
  464. .pipe(autoprefixer())
  465. .pipe(minifyCss()) //压缩css
  466. .pipe(gulp.dest(buildBasePath + '1.0/css'))
  467. .pipe(rev.manifest())
  468. .pipe(gulp.dest(buildBasePath + '1.0/css')) //输出json对照表
  469. })
  470. )
  471. //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
  472. gulp.task('manageRevJs', function () {
  473. return gulp
  474. .src(['static/1.0/js/**/*.js'])
  475. .pipe(
  476. modify({
  477. fileModifier: function (file, contents) {
  478. // 目前app.js ,管理系统、广告商、厂商共用;
  479. if (file.path.indexOf('app.js') > -1) {
  480. return 'var APP_VERSION=' + new Date().getTime() + ';' + contents
  481. } else {
  482. return contents
  483. }
  484. },
  485. })
  486. )
  487. .pipe(babel()) //es6转es5
  488. .pipe(rev()) //md5
  489. .pipe(uglify()) //压缩js
  490. .on('error', function (err) {
  491. console.log('错误信息:' + err.toString())
  492. })
  493. .pipe(gulp.dest(buildBasePath + '1.0/js')) //输出压缩文件
  494. .pipe(rev.manifest())
  495. .pipe(gulp.dest(buildBasePath + '1.0/js')) //输出json对照表
  496. })
  497. gulp.task('admasterRevJs', function () {
  498. return gulp
  499. .src(['static/admaster/js/**/*.js'])
  500. .pipe(babel()) //es6转es5
  501. .pipe(rev()) //md5
  502. .pipe(uglify()) //压缩js
  503. .pipe(gulp.dest(buildBasePath + 'admaster/js')) //输出压缩文件
  504. .pipe(rev.manifest())
  505. .pipe(gulp.dest(buildBasePath + 'admaster/js')) //输出json对照表
  506. })
  507. gulp.task('administratorRevJs', function () {
  508. return gulp
  509. .src(['static/administrator/js/**/*.js'])
  510. .pipe(babel()) //es6转es5
  511. .pipe(rev()) //md5
  512. .pipe(uglify()) //压缩js
  513. .pipe(gulp.dest(buildBasePath + 'administrator/js')) //输出压缩文件
  514. .pipe(rev.manifest())
  515. .pipe(gulp.dest(buildBasePath + 'administrator/js')) //输出json对照表
  516. })
  517. /************终端用户CSS、JS编译以及加版本号*************/
  518. //定义经销商的css、js源文件路径
  519. var enduserJsSrc = 'static/pages/js/*.js'
  520. //CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射
  521. gulp.task('enduserRevCss', function () {
  522. return gulp
  523. .src(commonSrc + '/scss/user.scss')
  524. .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
  525. .pipe(autoprefixer())
  526. .pipe(minifyCss()) //压缩css
  527. .pipe(
  528. rename(function (path) {
  529. path.basename = 'user-common'
  530. })
  531. )
  532. .pipe(rev()) //文件加md5后缀
  533. .pipe(gulp.dest(enduserRevDir + '/css')) //输出带后缀的文件
  534. .pipe(rev.manifest())
  535. .pipe(gulp.dest(enduserRevDir + '/css')) //输出json对照表
  536. })
  537. //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
  538. gulp.task('enduserRevJs', function () {
  539. return gulp
  540. .src(enduserJsSrc)
  541. .pipe(rev()) //md5
  542. .pipe(uglify()) //压缩js
  543. .pipe(gulp.dest(enduserRevDir + '/js')) //输出文件
  544. .pipe(rev.manifest())
  545. .pipe(gulp.dest(enduserRevDir + '/js')) //输出json对照表
  546. })
  547. //html压缩配置项
  548. var htmlOptions = {
  549. removeComments: true, //清除HTML注释
  550. collapseWhitespace: false, //压缩HTML
  551. collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
  552. removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input />
  553. removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
  554. removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
  555. minifyJS: true, //压缩页面JS
  556. minifyCSS: true, //压缩页面CSS
  557. }
  558. //经销商html路径
  559. var dealerHtmlSrc = 'static/app/**/*.html'
  560. //代理商html路径
  561. var agentsHtmlSrc = 'static/agents/**/*.html'
  562. //终端用户html路径
  563. var enduserHtmlSrc = 'static/pages/**/*.html'
  564. //测试模块html路径
  565. var testHtmlSrc = 'static/test/**/*.html'
  566. // weknow html路径
  567. var weknowHtmlSrc = 'static/weknow/**/*.html'
  568. var publicHtmlSrc = 'static/public/**/*.html'
  569. var helpHtmlSrc = 'static/help/**/*.html'
  570. /************经销商、代理商加入带版本号的CSS、JS引用*************/
  571. gulp.task('html_dealer_1', function () {
  572. return gulp
  573. .src([dealerRevDir + '/**/*.json', commonRevDir + '/**/*.json', dealerHtmlSrc])
  574. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  575. .pipe(gulpRemoveHtml()) //清除特定标签
  576. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  577. .pipe(htmlmin(htmlOptions))
  578. .pipe(
  579. inject.beforeEach(
  580. '</head>',
  581. '\n<script>(function(){var b=function(c){var d=document.cookie.match(new RegExp("(^| )"+c+"=([^;]*)(;|$)"));return null==d?null:unescape(d[2])}("theme_name")||"theme-alipay";document.querySelectorAll("html")[0].classList.add(b)})();</script>\n'
  582. )
  583. )
  584. .pipe(gulp.dest(dealerRevDir))
  585. })
  586. gulp.task('html_dealer_2', function () {
  587. //由于引用了经销商的样式和js,压缩代理商的文件注意路径
  588. return gulp
  589. .src([dealerRevDir + '/**/*.json', commonRevDir + '/**/*.json', agentsHtmlSrc])
  590. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  591. .pipe(gulpRemoveHtml()) //清除特定标签
  592. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  593. .pipe(htmlmin(htmlOptions))
  594. .pipe(
  595. inject.beforeEach(
  596. '</head>',
  597. '\n<script>(function(){var b=function(c){var d=document.cookie.match(new RegExp("(^| )"+c+"=([^;]*)(;|$)"));return null==d?null:unescape(d[2])}("theme_name")||"theme-alipay";document.querySelectorAll("html")[0].classList.add(b)})();</script>\n'
  598. )
  599. )
  600. .pipe(gulp.dest(agentsRevDir))
  601. })
  602. gulp.task('html-dealer', gulpSequence(['html_dealer_1'], ['html_dealer_2']))
  603. /************测试系统加入带版本号的CSS、JS引用*************/
  604. gulp.task('html-test', function () {
  605. //测试模块html处理
  606. return gulp
  607. .src([dealerRevDir + '/**/*.json', commonRevDir + '/**/*.json', testHtmlSrc])
  608. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  609. .pipe(gulpRemoveHtml()) //清除特定标签
  610. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  611. .pipe(htmlmin(htmlOptions))
  612. .pipe(gulp.dest(testRevDir))
  613. })
  614. /************ weknow 加入带版本号的CSS、JS引用*************/
  615. gulp.task('html-weknow', function () {
  616. //测试模块html处理
  617. return gulp
  618. .src([dealerRevDir + '/**/*.json', commonRevDir + '/**/*.json', weknowHtmlSrc])
  619. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  620. .pipe(gulpRemoveHtml()) //清除特定标签
  621. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  622. .pipe(htmlmin(htmlOptions))
  623. .pipe(gulp.dest(weknowRevDir))
  624. })
  625. /************ public 加入带版本号的CSS、JS引用*************/
  626. gulp.task('html-public', function () {
  627. //测试模块html处理
  628. return gulp
  629. .src([commonRevDir + '/**/*.json', publicRevDir + '/**/*.json', publicHtmlSrc])
  630. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  631. .pipe(gulpRemoveHtml()) //清除特定标签
  632. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  633. .pipe(htmlmin(htmlOptions))
  634. .pipe(gulp.dest(publicRevDir))
  635. })
  636. /************ help 加入带版本号的CSS、JS引用*************/
  637. gulp.task('html-help', function () {
  638. //测试模块html处理
  639. return gulp
  640. .src([dealerRevDir + '/**/*.json', commonRevDir + '/**/*.json', helpHtmlSrc])
  641. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  642. .pipe(gulpRemoveHtml()) //清除特定标签
  643. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  644. .pipe(htmlmin(htmlOptions))
  645. .pipe(gulp.dest(helpRevDir))
  646. })
  647. /************终端用户加入带版本号的CSS、JS引用*************/
  648. gulp.task('html-enduser', function () {
  649. //终端用户html处理
  650. return gulp
  651. .src([enduserRevDir + '/**/*.json', commonRevDir + '/**/*.json', enduserHtmlSrc])
  652. .pipe(revCollector()) //修改html的引入资源成为带hash的文件路径
  653. .pipe(gulpRemoveHtml()) //清除特定标签
  654. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  655. .pipe(htmlmin(htmlOptions))
  656. .pipe(
  657. inject.beforeEach(
  658. '</head>',
  659. '\n<script>(function(){var b=function(c){var d=document.cookie.match(new RegExp("(^| )"+c+"=([^;]*)(;|$)"));return null==d?null:unescape(d[2])}("theme_name")||"theme-alipay";document.querySelectorAll("html")[0].classList.add(b)})();</script>\n'
  660. )
  661. )
  662. .pipe(gulp.dest(enduserRevDir))
  663. })
  664. /************管理系统加入带版本号的CSS、JS引用,开发时尽量不要有重名文件*************/
  665. //修改manage主页的js/css引用
  666. gulp.task('res-rev-manage', function () {
  667. return gulp
  668. .src([buildBasePath + '1.0/**/rev-manifest.json', 'static/1.0/**/*.html'])
  669. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  670. .pipe(gulpRemoveHtml()) //清除特定标签
  671. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  672. .pipe(htmlmin(htmlOptions))
  673. .pipe(gulp.dest(buildBasePath + '1.0'))
  674. })
  675. //修改admaster主页的js/css引用
  676. gulp.task('res-rev-admaster', function () {
  677. //----css、js 公用1.0的,【后面的json优先级高于前面,注意admaster往后放】---
  678. return gulp
  679. .src([
  680. buildBasePath + '1.0/js/rev-manifest.json',
  681. buildBasePath + 'admaster/js/rev-manifest.json',
  682. buildBasePath + '1.0/css/rev-manifest.json',
  683. 'static/admaster/**/*.html',
  684. ])
  685. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  686. .pipe(gulpRemoveHtml()) //清除特定标签
  687. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  688. .pipe(htmlmin(htmlOptions))
  689. .pipe(gulp.dest(buildBasePath + 'admaster'))
  690. })
  691. //修改administrator主页的js/css引用
  692. gulp.task('res-rev-administrator', function () {
  693. //----css、js 公用1.0的,【后面的json优先级高于前面,注意administrator往后放】---
  694. return gulp
  695. .src([
  696. buildBasePath + '1.0/js/rev-manifest.json',
  697. buildBasePath + 'administrator/js/rev-manifest.json',
  698. buildBasePath + '1.0/css/rev-manifest.json',
  699. 'static/administrator/**/*.html',
  700. ])
  701. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  702. .pipe(gulpRemoveHtml()) //清除特定标签
  703. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  704. .pipe(htmlmin(htmlOptions))
  705. .pipe(gulp.dest(buildBasePath + 'administrator'))
  706. })
  707. //【在manage路由配置中也有引用到js!】
  708. gulp.task('res-rev-manage-js', function () {
  709. return gulp
  710. .src([buildBasePath + '1.0/js/rev-manifest.json', buildBasePath + '1.0/js/*.router.js'])
  711. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  712. .pipe(gulp.dest(buildBasePath + '1.0/js'))
  713. })
  714. //【在admaster路由配置中也有引用到js!】
  715. gulp.task('res-rev-admaster-js', function () {
  716. //----指令、服务、过滤器等 公用1.0的,【后面的json优先级高于前面,注意admaster往后放】---
  717. return gulp
  718. .src([
  719. buildBasePath + '1.0/js/rev-manifest.json',
  720. buildBasePath + 'admaster/js/rev-manifest.json',
  721. buildBasePath + 'admaster/js/*.router.js',
  722. ])
  723. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  724. .pipe(gulp.dest(buildBasePath + 'admaster/js'))
  725. })
  726. //【在administrator路由配置中也有引用到js!】
  727. gulp.task('res-rev-administrator-js', function () {
  728. //----指令、服务、过滤器等 公用1.0的,【后面的json优先级高于前面,注意administrator往后放】---
  729. return gulp
  730. .src([
  731. buildBasePath + '1.0/js/rev-manifest.json',
  732. buildBasePath + 'administrator/js/rev-manifest.json',
  733. buildBasePath + 'administrator/js/*.router.js',
  734. ])
  735. .pipe(revCollector({ replaceReved: true })) //修改html的引入资源成为带hash的文件路径
  736. .pipe(gulp.dest(buildBasePath + 'administrator/js'))
  737. })
  738. gulp.task('moveAll', function (done) {
  739. // 暂时不清理
  740. done()
  741. })
  742. /********发布任务**************/
  743. //默认任务、全部产品发布
  744. gulp.task('default', function (done) {
  745. console.log('-------------------------请用以下命令发布产品-----------------------------')
  746. console.log(' \x1B[32m gulp all \x1B[0m --发布所有产品')
  747. console.log(' \x1B[32m gulp manage \x1B[0m --发布后台管理系统和公司网站')
  748. console.log(' \x1B[32m gulp dealer \x1B[0m --发布经销商、代理商')
  749. console.log(' \x1B[32m gulp test \x1B[0m --发布测试系统')
  750. console.log(' \x1B[32m gulp weknow \x1B[0m --发布weknow')
  751. console.log(' \x1B[32m gulp public \x1B[0m --发布public')
  752. console.log(' \x1B[32m gulp help \x1B[0m --发布whelp')
  753. console.log(' \x1B[32m gulp enduser \x1B[0m --发布终端用户')
  754. console.log(' \r\n')
  755. console.log(' \x1B[31m gulp all --dist new_dist_dir \x1B[0m --输出到new_dist_dir')
  756. console.log('--------------------------------------------------------------------------')
  757. })
  758. //默认任务、全部产品发布
  759. module.exports.all = gulpSequence(
  760. // ['clean:Build'],//删除所有旧的dist文件
  761. ['clean:common'], //清空公共组件 会导致经销商、代理商的公共文件丢失
  762. ['clean:manage'], //清空相关文件
  763. ['clean:dealer'], //清空相关文件
  764. ['clean:test'], //清空相关文件
  765. ['clean:weknow'], //清空相关文件
  766. ['clean:public'], //清空相关文件
  767. ['clean:help'], //清空相关文件
  768. ['clean:enduser'], //清空相关文件
  769. ['copy-manage'], //复制管理系统、广告系统 静态文件
  770. ['copy-common'], //复制公共组件、文件
  771. ['copy-dealer'], //复制经销商、代理商、测试系统文件
  772. ['copy-enduser'], //复制终端用户文件
  773. ['copy-help'], //copy help
  774. // ['chaoben'], //copy chaoben
  775. // 后台管理系统、广告系统
  776. ['manageRevCss'],
  777. ['manageRevJs'],
  778. ['admasterRevJs'],
  779. ['administratorRevJs'],
  780. ['res-rev-manage'],
  781. ['res-rev-admaster'],
  782. ['res-rev-administrator'],
  783. ['res-rev-manage-js'],
  784. ['res-rev-admaster-js'],
  785. ['res-rev-administrator-js'],
  786. //编译公共js、css并加入版本号
  787. ['customRevJs'],
  788. ['customRevCss'],
  789. ['customReceiptCss'],
  790. ['customRevFont'],
  791. //经销商、代理商、测试系统CSS、js文件编译并加入版本号
  792. ['appRevCss'],
  793. ['appRevJs'],
  794. //终端用户CSS、js文件编译并加入版本号
  795. ['enduserRevCss'],
  796. ['enduserRevJs'],
  797. //经销商、代理商、测试系统、终端用户的CSS、js引入修改
  798. ['html-dealer'],
  799. ['html-test'],
  800. ['html-weknow'],
  801. ['html-public'],
  802. ['html-enduser'],
  803. //移动到发布目录
  804. ['moveAll']
  805. )
  806. //后台管理系统、公司网站
  807. gulp.task(
  808. 'manage',
  809. gulpSequence(
  810. ['clean:manage'], //清空管理系统相关文件
  811. ['copy-manage'], //复制后台管理系统、网站文件
  812. /******后台管理系统公共组件vender、字体变更较少,不需要编译***********/
  813. /**!!!!依赖json minifest的一定要保证顺序执行,否则结果永远不正确!!!!**/
  814. ['manageRevCss'],
  815. ['manageRevJs'],
  816. ['admasterRevJs'],
  817. ['administratorRevJs'],
  818. ['res-rev-manage'],
  819. ['res-rev-admaster'],
  820. ['res-rev-administrator'],
  821. ['res-rev-manage-js'],
  822. ['res-rev-admaster-js'],
  823. ['res-rev-administrator-js'],
  824. //移动到发布目录
  825. ['moveAll']
  826. )
  827. )
  828. //经销商、代理商
  829. gulp.task(
  830. 'dealer',
  831. gulpSequence(
  832. ['clean:dealer'], //清空相关文件
  833. ['copy-common'], //复制公共组件、文件
  834. ['copy-dealer'], //复制经销商、代理商文件
  835. //编译公共js、css并加入版本号
  836. ['customRevJs'],
  837. ['customRevCss'],
  838. ['customRevFont'],
  839. //经销商、代理商、测试系统 公用的CSS、js文件编译并加入版本号
  840. ['appRevCss'],
  841. ['appRevJs'],
  842. //经销商、代理商的CSS、js引入修改
  843. ['html-dealer'],
  844. //移动到发布目录
  845. ['moveAll']
  846. )
  847. )
  848. // 测试系统
  849. gulp.task(
  850. 'test',
  851. gulpSequence(
  852. ['clean:test'], //清空相关文件
  853. // todo 其实和经销商的公共组件重复,但是为了可以单独部署,也需要公共文件;在和经销商一起部署时,为了不影响经销商,不要clean:common
  854. ['copy-common'], //复制公共组件、文件
  855. ['copy-dealer'], //复制经销商、代理商文件、测试系统就用到了几张图片
  856. //编译公共js、css并加入版本号:似乎代码不变的情况的下,编译的文件名居然不同。。。
  857. ['customRevJs'],
  858. ['customRevCss'],
  859. ['customRevFont'],
  860. //经销商、代理商、测试系统 公用的CSS、js文件编译并加入版本号
  861. ['appRevCss'],
  862. ['appRevJs'],
  863. //测试系统的CSS、js引入修改
  864. ['html-test'],
  865. //移动到发布目录
  866. ['moveAll']
  867. )
  868. )
  869. // weknow 发布
  870. gulp.task(
  871. 'weknow',
  872. gulpSequence(
  873. ['clean:weknow'], //清空相关文件
  874. ['copy-common'], //复制公共组件、文件
  875. ['copy-dealer'], //复制经销商、代理商文件、测试系统就用到了几张图片
  876. //编译公共js、css并加入版本号:似乎代码不变的情况的下,编译的文件名居然不同。。。
  877. ['customRevJs'],
  878. ['customRevCss'],
  879. ['customRevFont'],
  880. //经销商、代理商、测试系统 公用的CSS、js文件编译并加入版本号
  881. ['appRevCss'],
  882. ['appRevJs'],
  883. //测试系统的CSS、js引入修改
  884. ['html-weknow'],
  885. //移动到发布目录
  886. ['moveAll']
  887. )
  888. )
  889. // public 发布
  890. gulp.task(
  891. 'public',
  892. gulpSequence(
  893. ['clean:public'], //清空相关文件
  894. ['copy-common'], //复制公共组件、文件
  895. ['copy-dealer'], //复制经销商、代理商文件、测试系统就用到了几张图片
  896. //编译公共js、css并加入版本号:似乎代码不变的情况的下,编译的文件名居然不同。。。
  897. ['customRevJs'],
  898. ['customRevCss'],
  899. ['customRevFont'],
  900. //经销商、代理商、测试系统 公用的CSS、js文件编译并加入版本号
  901. ['appRevCss'],
  902. ['appRevJs'],
  903. ['html-public'],
  904. //移动到发布目录
  905. ['moveAll']
  906. )
  907. )
  908. // help 发布
  909. gulp.task(
  910. 'help',
  911. gulpSequence(
  912. ['clean:help'], //清空相关文件
  913. ['copy-help'], //copy help
  914. //移动到发布目录
  915. ['moveAll']
  916. )
  917. )
  918. //终端用户
  919. gulp.task(
  920. 'enduser',
  921. gulpSequence(
  922. ['clean:enduser'], //清空相关文件
  923. ['copy-common'], //复制公共组件、文件
  924. ['copy-enduser'], //复制终端用户文件
  925. //编译公共js、css并加入版本号
  926. ['customRevJs'],
  927. ['customRevCss'],
  928. ['customRevFont'],
  929. //终端用户CSS、js文件编译并加入版本号
  930. ['enduserRevCss'],
  931. ['enduserRevJs'],
  932. //终端用户的CSS、js引入修改
  933. ['html-enduser'],
  934. //移动到发布目录
  935. ['moveAll']
  936. )
  937. )
  938. //js语法检测 【开发使用】
  939. gulp.task('jshint', function () {
  940. return gulp.src('static/app/js/*.js').pipe(jshint()).pipe(jshint.reporter('default'))
  941. })
  942. /*************嵌入式设备页面压缩**********************/
  943. gulp.task('wifi-copy', function () {
  944. return gulp.src(['static/wifi/**/*.gz']).pipe(gulp.dest('dist/wifi'))
  945. })
  946. gulp.task(
  947. 'html-wifi',
  948. series('wifi-copy', function (done) {
  949. return gulp
  950. .src(['static/wifi/**/*.html'])
  951. .pipe(gulpRemoveHtml()) //清除特定标签
  952. .pipe(removeEmptyLines({ removeComments: true })) //清除空白行
  953. .pipe(htmlmin(htmlOptions))
  954. .pipe(gulp.dest('dist/wifi'))
  955. })
  956. )