Gruntfile.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. var webpack = require("webpack"),
  2. ExtractTextPlugin = require("extract-text-webpack-plugin");
  3. module.exports = function(grunt) {
  4. grunt.file.defaultEncoding = "utf8";
  5. grunt.file.preserveBOM = false;
  6. // Tasks
  7. grunt.registerTask("dev",
  8. "A persistent task which creates a development build whenever source files are modified.",
  9. ["clean:dev", "webpack:web", "copy:htmlDev", "copy:staticDev", "chmod:build", "watch"]);
  10. grunt.registerTask("node",
  11. "Compiles CyberChef into a single NodeJS module.",
  12. ["clean:node", "webpack:node", "chmod:build"]);
  13. grunt.registerTask("test",
  14. "A task which runs all the tests in test/tests.",
  15. ["clean:test", "webpack:tests", "chmod:build", "execute:test"]);
  16. grunt.registerTask("prod",
  17. "Creates a production-ready build. Use the --msg flag to add a compile message.",
  18. ["eslint", "test", "exec:stats", "clean", "jsdoc", "webpack:web", "copy:htmlDev", "copy:htmlProd", "copy:htmlInline",
  19. "copy:staticDev", "copy:staticProd", "cssmin", "uglify:prod", "inline", "htmlmin", "docs", "chmod"]);
  20. grunt.registerTask("docs",
  21. "Compiles documentation in the /docs directory.",
  22. ["clean:docs", "jsdoc", "chmod:docs"]);
  23. grunt.registerTask("stats",
  24. "Provides statistics about the code base such as how many lines there are as well as details of file sizes before and after compression.",
  25. ["webpack:web", "uglify:prod", "exec:stats", "exec:repoSize", "exec:displayStats"]);
  26. grunt.registerTask("release",
  27. "Prepares and deploys a production version of CyberChef to the gh-pages branch.",
  28. ["copy:ghPages", "exec:deployGhPages"]);
  29. grunt.registerTask("default",
  30. "Lints the code base and shows stats",
  31. ["eslint", "exec:stats", "exec:displayStats"]);
  32. grunt.registerTask("doc", "docs");
  33. grunt.registerTask("tests", "test");
  34. grunt.registerTask("lint", "eslint");
  35. // Load tasks provided by each plugin
  36. grunt.loadNpmTasks("grunt-eslint");
  37. grunt.loadNpmTasks("grunt-jsdoc");
  38. grunt.loadNpmTasks("grunt-contrib-clean");
  39. grunt.loadNpmTasks("grunt-webpack");
  40. grunt.loadNpmTasks("grunt-contrib-copy");
  41. grunt.loadNpmTasks("grunt-contrib-uglify");
  42. grunt.loadNpmTasks("grunt-contrib-cssmin");
  43. grunt.loadNpmTasks("grunt-contrib-htmlmin");
  44. grunt.loadNpmTasks("grunt-inline-alt");
  45. grunt.loadNpmTasks("grunt-chmod");
  46. grunt.loadNpmTasks("grunt-exec");
  47. grunt.loadNpmTasks("grunt-execute");
  48. grunt.loadNpmTasks("grunt-contrib-watch");
  49. var compileTime = grunt.template.today("dd/mm/yyyy HH:MM:ss") + " UTC",
  50. banner = '/**\n\
  51. * CyberChef - The Cyber Swiss Army Knife\n\
  52. *\n\
  53. * @copyright Crown Copyright 2016\n\
  54. * @license Apache-2.0\n\
  55. *\n\
  56. * Copyright 2016 Crown Copyright\n\
  57. *\n\
  58. * Licensed under the Apache License, Version 2.0 (the "License");\n\
  59. * you may not use this file except in compliance with the License.\n\
  60. * You may obtain a copy of the License at\n\
  61. *\n\
  62. * http://www.apache.org/licenses/LICENSE-2.0\n\
  63. *\n\
  64. * Unless required by applicable law or agreed to in writing, software\n\
  65. * distributed under the License is distributed on an "AS IS" BASIS,\n\
  66. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\
  67. * See the License for the specific language governing permissions and\n\
  68. * limitations under the License.\n\
  69. */\n';
  70. var templateOptions = {
  71. data: {
  72. compileTime: compileTime,
  73. compileMsg: grunt.option("compile-msg") || grunt.option("msg") || "",
  74. codebaseStats: grunt.file.read("src/static/stats.txt").split("\n").join("<br>")
  75. }
  76. };
  77. // Project configuration
  78. grunt.initConfig({
  79. eslint: {
  80. options: {
  81. configFile: "src/js/.eslintrc.json"
  82. },
  83. gruntfile: ["Gruntfile.js"],
  84. core: ["src/js/core/**/*.js"],
  85. config: ["src/js/config/**/*.js"],
  86. views: ["src/js/views/**/*.js"],
  87. operations: ["src/js/operations/**/*.js"],
  88. tests: ["test/**/*.js"],
  89. },
  90. jsdoc: {
  91. options: {
  92. destination: "docs",
  93. template: "node_modules/ink-docstrap/template",
  94. recurse: true,
  95. readme: "./README.md",
  96. configure: "docs/jsdoc.conf.json"
  97. },
  98. all: {
  99. src: [
  100. "src/js/**/*.js",
  101. "!src/js/lib/**/*",
  102. ],
  103. }
  104. },
  105. clean: {
  106. dev: ["build/dev/*"],
  107. prod: ["build/prod/*"],
  108. test: ["build/test/*"],
  109. node: ["build/node/*"],
  110. docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"],
  111. },
  112. webpack: {
  113. options: {
  114. plugins: [
  115. new webpack.ProvidePlugin({
  116. $: "jquery",
  117. jQuery: "jquery",
  118. moment: "moment-timezone"
  119. }),
  120. new webpack.BannerPlugin({
  121. "banner": banner,
  122. "raw": true,
  123. "entryOnly": true
  124. }),
  125. new webpack.DefinePlugin({
  126. COMPILE_TIME: JSON.stringify(compileTime),
  127. COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || "")
  128. }),
  129. ],
  130. resolve: {
  131. alias: {
  132. jquery: "jquery/src/jquery"
  133. }
  134. },
  135. module: {
  136. loaders: [
  137. {
  138. test: /\.js$/,
  139. exclude: /node_modules/,
  140. loader: "babel-loader?compact=false"
  141. }
  142. ]
  143. }
  144. },
  145. web: {
  146. target: "web",
  147. entry: [
  148. "babel-polyfill",
  149. "bootstrap",
  150. "bootstrap-switch",
  151. "bootstrap-colorpicker",
  152. "./src/css/index.js",
  153. "./src/js/views/html/main.js"
  154. ],
  155. output: {
  156. filename: "scripts.js",
  157. path: "build/dev"
  158. },
  159. module: {
  160. rules: [
  161. {
  162. test: /\.css$/,
  163. use: ExtractTextPlugin.extract({
  164. use: "css-loader"
  165. })
  166. },
  167. {
  168. test: /\.less$/,
  169. use: ExtractTextPlugin.extract({
  170. use: [
  171. { loader: "css-loader" },
  172. { loader: "less-loader" }
  173. ]
  174. })
  175. },
  176. {
  177. test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
  178. loader: "url-loader",
  179. options: {
  180. limit: 10000
  181. }
  182. }
  183. ]
  184. },
  185. plugins: [
  186. new ExtractTextPlugin("styles.css"),
  187. ]
  188. },
  189. tests: {
  190. target: "node",
  191. entry: ["babel-polyfill", "./test/TestRunner.js"],
  192. output: {
  193. filename: "index.js",
  194. path: "build/test"
  195. },
  196. module: {
  197. loaders: [{
  198. test: /prettify\.min\.js$/,
  199. use: "imports-loader?window=>global"
  200. }]
  201. }
  202. },
  203. node: {
  204. target: "node",
  205. entry: ["babel-polyfill", "./src/js/views/node/index.js"],
  206. output: {
  207. filename: "CyberChef.js",
  208. path: "build/node",
  209. library: "CyberChef",
  210. libraryTarget: "commonjs2"
  211. },
  212. module: {
  213. loaders: [{
  214. test: /prettify\.min\.js$/,
  215. use: "imports-loader?window=>global"
  216. }]
  217. }
  218. }
  219. },
  220. copy: {
  221. htmlDev: {
  222. options: {
  223. process: function(content, srcpath) {
  224. return grunt.template.process(content, templateOptions);
  225. }
  226. },
  227. src: "src/html/index.html",
  228. dest: "build/dev/index.html"
  229. },
  230. htmlProd: {
  231. options: {
  232. process: function(content, srcpath) {
  233. return grunt.template.process(content, templateOptions);
  234. }
  235. },
  236. src: "src/html/index.html",
  237. dest: "build/prod/index.html"
  238. },
  239. htmlInline: {
  240. options: {
  241. process: function(content, srcpath) {
  242. // TODO: Do all this in Jade
  243. content = content.replace(
  244. '<a href="cyberchef.htm" style="float: left; margin-left: 10px; margin-right: 80px;" download>Download CyberChef<img src="images/download-24x24.png" /></a>',
  245. '<span style="float: left; margin-left: 10px;">Compile time: ' + grunt.template.today("dd/mm/yyyy HH:MM:ss") + " UTC</span>");
  246. return grunt.template.process(content, templateOptions);
  247. }
  248. },
  249. src: "src/html/index.html",
  250. dest: "build/prod/cyberchef.htm"
  251. },
  252. staticDev: {
  253. files: [
  254. {
  255. expand: true,
  256. cwd: "src/static/",
  257. src: [
  258. "**/*",
  259. "**/.*",
  260. "!stats.txt",
  261. "!ga.html"
  262. ],
  263. dest: "build/dev/"
  264. }
  265. ]
  266. },
  267. staticProd: {
  268. files: [
  269. {
  270. expand: true,
  271. cwd: "src/static/",
  272. src: [
  273. "**/*",
  274. "**/.*",
  275. "!stats.txt",
  276. "!ga.html"
  277. ],
  278. dest: "build/prod/"
  279. }
  280. ]
  281. },
  282. ghPages: {
  283. options: {
  284. process: function(content, srcpath) {
  285. // Add Google Analytics code to index.html
  286. content = content.replace("</body></html>",
  287. grunt.file.read("src/static/ga.html") + "</body></html>");
  288. return grunt.template.process(content, templateOptions);
  289. }
  290. },
  291. src: "build/prod/index.html",
  292. dest: "build/prod/index.html"
  293. }
  294. },
  295. uglify: {
  296. options: {
  297. preserveComments: function(node, comment) {
  298. if (comment.value.indexOf("* @license") === 0) return true;
  299. return false;
  300. },
  301. screwIE8: true,
  302. ASCIIOnly: true,
  303. beautify: {
  304. beautify: false,
  305. inline_script: true, // eslint-disable-line camelcase
  306. ascii_only: true, // eslint-disable-line camelcase
  307. screw_ie8: true // eslint-disable-line camelcase
  308. },
  309. compress: {
  310. screw_ie8: true // eslint-disable-line camelcase
  311. },
  312. banner: banner
  313. },
  314. prod: {
  315. src: "build/dev/scripts.js",
  316. dest: "build/prod/scripts.js"
  317. }
  318. },
  319. cssmin: {
  320. prod: {
  321. src: "build/dev/styles.css",
  322. dest: "build/prod/styles.css"
  323. }
  324. },
  325. htmlmin: {
  326. prod: {
  327. options: {
  328. removeComments: true,
  329. collapseWhitespace: true,
  330. minifyJS: true,
  331. minifyCSS: true
  332. },
  333. src: "build/prod/index.html",
  334. dest: "build/prod/index.html"
  335. },
  336. inline: {
  337. options: {
  338. removeComments: true,
  339. collapseWhitespace: true,
  340. minifyJS: false,
  341. minifyCSS: false
  342. },
  343. src: "build/prod/cyberchef.htm",
  344. dest: "build/prod/cyberchef.htm"
  345. }
  346. },
  347. inline: {
  348. options: {
  349. tag: "",
  350. inlineTagAttributes: {
  351. js: "type='application/javascript'",
  352. css: "type='text/css'"
  353. }
  354. },
  355. compiled: {
  356. src: "build/prod/cyberchef.htm",
  357. dest: "build/prod/cyberchef.htm"
  358. },
  359. prod: {
  360. options: {
  361. tag: "__inline"
  362. },
  363. src: "build/prod/index.html",
  364. dest: "build/prod/index.html"
  365. }
  366. },
  367. chmod: {
  368. build: {
  369. options: {
  370. mode: "755",
  371. },
  372. src: ["build/**/*", "build/**/.htaccess", "build/"]
  373. },
  374. docs: {
  375. options: {
  376. mode: "755",
  377. },
  378. src: ["docs/**/*", "docs/"]
  379. }
  380. },
  381. exec: {
  382. repoSize: {
  383. command: [
  384. "git ls-files | wc -l | xargs printf '\n%b\ttracked files\n'",
  385. "du -hs | egrep -o '^[^\t]*' | xargs printf '%b\trepository size\n'"
  386. ].join(";"),
  387. stderr: false
  388. },
  389. stats: {
  390. command: "rm src/static/stats.txt;" +
  391. [
  392. "ls src/ -R1 | grep '^$' -v | grep ':$' -v | wc -l | xargs printf '%b\tsource files\n'",
  393. "find src/ -regex '.*\..*' -print | xargs cat | wc -l | xargs printf '%b\tlines\n'",
  394. "du -hs src/ | pcregrep -o '^[^\t]*' | xargs printf '%b\tsize\n'",
  395. "ls src/js/ -R1 | grep '\.js$' | wc -l | xargs printf '\n%b\tJavaScript source files\n'",
  396. "find src/js/ -regex '.*\.js' -print | xargs cat | wc -l | xargs printf '%b\tlines\n'",
  397. "find src/js/ -regex '.*\.js' -exec du -hcs {} \+ | tail -n1 | egrep -o '^[^\t]*' | xargs printf '%b\tsize\n'",
  398. "find src/js/ -regex '.*/lib/.*\.js' -print | wc -l | xargs printf '\n%b\tthird party JavaScript source files\n'",
  399. "find src/js/ -regex '.*/lib/.*\.js' -print | xargs cat | wc -l | xargs printf '%b\tlines\n'",
  400. "find src/js/ -regex '.*/lib/.*\.js' -exec du -hcs {} \+ | tail -n1 | egrep -o '^[^\t]*' | xargs printf '%b\tsize\n'",
  401. "find src/js/ -regex '.*\.js' -not -regex '.*/lib/.*' -print | wc -l | xargs printf '\n%b\tfirst party JavaScript source files\n'",
  402. "find src/js/ -regex '.*\.js' -not -regex '.*/lib/.*' -print | xargs cat | wc -l | xargs printf '%b\tlines\n'",
  403. "find src/js/ -regex '.*\.js' -not -regex '.*/lib/.*' -exec du -hcs {} \+ | tail -n1 | egrep -o '^[^\t]*' | xargs printf '%b\tsize\n'",
  404. "du build/dev/scripts.js -h | egrep -o '^[^\t]*' | xargs printf '\n%b\tuncompressed JavaScript size\n'",
  405. "du build/prod/scripts.js -h | egrep -o '^[^\t]*' | xargs printf '%b\tcompressed JavaScript size\n'",
  406. "grep -E '^\\s+name: ' src/js/config/Categories.js | wc -l | xargs printf '\n%b\tcategories\n'",
  407. "grep -E '^\\s+\"[A-Za-z0-9 \\-]+\": {' src/js/config/OperationConfig.js | wc -l | xargs printf '%b\toperations\n'",
  408. ].join(" >> src/static/stats.txt;") + " >> src/static/stats.txt;",
  409. stderr: false
  410. },
  411. displayStats: {
  412. command: "cat src/static/stats.txt"
  413. },
  414. cleanGit: {
  415. command: "git gc --prune=now --aggressive"
  416. },
  417. deployGhPages: {
  418. command: [
  419. "git add build/prod/index.html -v",
  420. "COMMIT_HASH=$(git rev-parse HEAD)",
  421. "git commit -m \"GitHub Pages release for ${COMMIT_HASH}\"",
  422. "git push origin `git subtree split --prefix build/prod master`:gh-pages --force",
  423. "git reset HEAD~",
  424. "git checkout build/prod/index.html"
  425. ].join(";")
  426. }
  427. },
  428. execute: {
  429. test: "build/test/index.js"
  430. },
  431. watch: {
  432. css: {
  433. files: ["src/css/**/*.css", "src/css/**/*.less"],
  434. tasks: ["webpack:web", "chmod:build"]
  435. },
  436. js: {
  437. files: "src/js/**/*.js",
  438. tasks: ["webpack:web", "chmod:build"]
  439. },
  440. html: {
  441. files: "src/html/**/*.html",
  442. tasks: ["copy:htmlDev", "chmod:build"]
  443. },
  444. static: {
  445. files: ["src/static/**/*", "src/static/**/.*"],
  446. tasks: ["copy:staticDev", "chmod:build"]
  447. },
  448. grunt: {
  449. files: "Gruntfile.js",
  450. tasks: ["clean:dev", "webpack:web", "copy:htmlDev", "copy:staticDev", "chmod:build"]
  451. }
  452. },
  453. });
  454. };