Gruntfile.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. var webpack = require("webpack"),
  2. ExtractTextPlugin = require("extract-text-webpack-plugin"),
  3. HtmlWebpackPlugin = require("html-webpack-plugin"),
  4. Inliner = require("web-resource-inliner");
  5. module.exports = function (grunt) {
  6. grunt.file.defaultEncoding = "utf8";
  7. grunt.file.preserveBOM = false;
  8. // Tasks
  9. grunt.registerTask("dev",
  10. "A persistent task which creates a development build whenever source files are modified.",
  11. ["clean:dev", "webpack:webDev"]);
  12. grunt.registerTask("node",
  13. "Compiles CyberChef into a single NodeJS module.",
  14. ["clean:node", "webpack:node", "chmod:build"]);
  15. grunt.registerTask("test",
  16. "A task which runs all the tests in test/tests.",
  17. ["clean:test", "webpack:tests", "execute:test"]);
  18. grunt.registerTask("docs",
  19. "Compiles documentation in the /docs directory.",
  20. ["clean:docs", "jsdoc", "chmod:docs"]);
  21. grunt.registerTask("prod",
  22. "Creates a production-ready build. Use the --msg flag to add a compile message.",
  23. ["eslint", "clean:prod", "webpack:webProd", "inline", "chmod"]);
  24. grunt.registerTask("default",
  25. "Lints the code base",
  26. ["eslint", "exec:repoSize"]);
  27. grunt.registerTask("inline",
  28. "Compiles a production build of CyberChef into a single, portable web page.",
  29. runInliner);
  30. grunt.registerTask("doc", "docs");
  31. grunt.registerTask("tests", "test");
  32. grunt.registerTask("lint", "eslint");
  33. // Load tasks provided by each plugin
  34. grunt.loadNpmTasks("grunt-eslint");
  35. grunt.loadNpmTasks("grunt-webpack");
  36. grunt.loadNpmTasks("grunt-jsdoc");
  37. grunt.loadNpmTasks("grunt-contrib-clean");
  38. grunt.loadNpmTasks("grunt-contrib-copy");
  39. grunt.loadNpmTasks("grunt-chmod");
  40. grunt.loadNpmTasks("grunt-exec");
  41. grunt.loadNpmTasks("grunt-execute");
  42. grunt.loadNpmTasks("grunt-accessibility");
  43. // Project configuration
  44. var compileTime = grunt.template.today("dd/mm/yyyy HH:MM:ss") + " UTC",
  45. banner = "/**\n" +
  46. "* CyberChef - The Cyber Swiss Army Knife\n" +
  47. "*\n" +
  48. "* @copyright Crown Copyright 2016\n" +
  49. "* @license Apache-2.0\n" +
  50. "*\n" +
  51. "* Copyright 2016 Crown Copyright\n" +
  52. "*\n" +
  53. '* Licensed under the Apache License, Version 2.0 (the "License");\n' +
  54. "* you may not use this file except in compliance with the License.\n" +
  55. "* You may obtain a copy of the License at\n" +
  56. "*\n" +
  57. "* http://www.apache.org/licenses/LICENSE-2.0\n" +
  58. "*\n" +
  59. "* Unless required by applicable law or agreed to in writing, software\n" +
  60. '* distributed under the License is distributed on an "AS IS" BASIS,\n' +
  61. "* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
  62. "* See the License for the specific language governing permissions and\n" +
  63. "* limitations under the License.\n" +
  64. "*/\n";
  65. /**
  66. * Compiles a production build of CyberChef into a single, portable web page.
  67. */
  68. function runInliner() {
  69. var inlinerError = false;
  70. Inliner.html({
  71. relativeTo: "build/prod/",
  72. fileContent: grunt.file.read("build/prod/cyberchef.htm"),
  73. images: true,
  74. svgs: true,
  75. scripts: true,
  76. links: true,
  77. strict: true
  78. }, function(error, result) {
  79. if (error) {
  80. console.log(error);
  81. inlinerError = true;
  82. return false;
  83. }
  84. grunt.file.write("build/prod/cyberchef.htm", result);
  85. });
  86. return !inlinerError;
  87. }
  88. grunt.initConfig({
  89. clean: {
  90. dev: ["build/dev/*"],
  91. prod: ["build/prod/*"],
  92. test: ["build/test/*"],
  93. node: ["build/node/*"],
  94. docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"],
  95. },
  96. eslint: {
  97. options: {
  98. configFile: "src/.eslintrc.json"
  99. },
  100. configs: ["Gruntfile.js"],
  101. core: ["src/core/**/*.js", "!src/core/lib/**/*"],
  102. web: ["src/web/**/*.js"],
  103. node: ["src/node/**/*.js"],
  104. tests: ["test/**/*.js"],
  105. },
  106. jsdoc: {
  107. options: {
  108. destination: "docs",
  109. template: "node_modules/ink-docstrap/template",
  110. recurse: true,
  111. readme: "./README.md",
  112. configure: "docs/jsdoc.conf.json"
  113. },
  114. all: {
  115. src: [
  116. "src/**/*.js",
  117. "!src/core/lib/**/*",
  118. ],
  119. }
  120. },
  121. accessibility: {
  122. options: {
  123. accessibilityLevel: "WCAG2A"
  124. },
  125. test: {
  126. src: ["build/**/*.html"]
  127. }
  128. },
  129. webpack: {
  130. options: {
  131. plugins: [
  132. new webpack.ProvidePlugin({
  133. $: "jquery",
  134. jQuery: "jquery",
  135. moment: "moment-timezone"
  136. }),
  137. new webpack.BannerPlugin({
  138. banner: banner,
  139. raw: true,
  140. entryOnly: true
  141. }),
  142. new webpack.DefinePlugin({
  143. COMPILE_TIME: JSON.stringify(compileTime),
  144. COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || "")
  145. }),
  146. new ExtractTextPlugin("styles.css"),
  147. ],
  148. resolve: {
  149. alias: {
  150. jquery: "jquery/src/jquery"
  151. }
  152. },
  153. module: {
  154. rules: [
  155. {
  156. test: /\.js$/,
  157. exclude: /node_modules/,
  158. loader: "babel-loader?compact=false"
  159. },
  160. {
  161. test: /\.css$/,
  162. use: ExtractTextPlugin.extract({
  163. use: "css-loader?minimize"
  164. })
  165. },
  166. {
  167. test: /\.less$/,
  168. use: ExtractTextPlugin.extract({
  169. use: [
  170. { loader: "css-loader?minimize" },
  171. { loader: "less-loader" }
  172. ]
  173. })
  174. },
  175. {
  176. test: /\.(ico|eot|ttf|woff|woff2)$/,
  177. loader: "url-loader",
  178. options: {
  179. limit: 10000
  180. }
  181. },
  182. { // First party images are saved as files to be cached
  183. test: /\.(png|jpg|gif|svg)$/,
  184. exclude: /node_modules/,
  185. loader: "file-loader",
  186. options: {
  187. name: "images/[name].[ext]"
  188. }
  189. },
  190. { // Third party images are inlined
  191. test: /\.(png|jpg|gif|svg)$/,
  192. exclude: /web\/static/,
  193. loader: "url-loader",
  194. options: {
  195. limit: 10000
  196. }
  197. },
  198. ]
  199. },
  200. stats: {
  201. children: false
  202. }
  203. },
  204. webDev: {
  205. target: "web",
  206. entry: "./src/web/index.js",
  207. output: {
  208. filename: "scripts.js",
  209. path: __dirname + "/build/dev"
  210. },
  211. plugins: [
  212. new HtmlWebpackPlugin({
  213. filename: "index.html",
  214. template: "./src/web/html/index.html",
  215. compileTime: compileTime
  216. })
  217. ],
  218. watch: true
  219. },
  220. webProd: {
  221. target: "web",
  222. entry: "./src/web/index.js",
  223. output: {
  224. filename: "scripts.js",
  225. path: __dirname + "/build/prod"
  226. },
  227. plugins: [
  228. new webpack.optimize.UglifyJsPlugin({
  229. compress: {
  230. "screw_ie8": true,
  231. "dead_code": true,
  232. "unused": true,
  233. "warnings": false
  234. },
  235. comments: false,
  236. }),
  237. new HtmlWebpackPlugin({ // Main version
  238. filename: "index.html",
  239. template: "./src/web/html/index.html",
  240. compileTime: compileTime,
  241. minify: {
  242. removeComments: true,
  243. collapseWhitespace: true,
  244. minifyJS: true,
  245. minifyCSS: true
  246. }
  247. }),
  248. new HtmlWebpackPlugin({ // Inline version
  249. filename: "cyberchef.htm",
  250. template: "./src/web/html/index.html",
  251. compileTime: compileTime,
  252. inline: true,
  253. minify: {
  254. removeComments: true,
  255. collapseWhitespace: true,
  256. minifyJS: true,
  257. minifyCSS: true
  258. }
  259. }),
  260. ]
  261. },
  262. tests: {
  263. target: "node",
  264. entry: "./test/index.js",
  265. output: {
  266. filename: "index.js",
  267. path: __dirname + "/build/test"
  268. }
  269. },
  270. node: {
  271. target: "node",
  272. entry: "./src/node/index.js",
  273. output: {
  274. filename: "CyberChef.js",
  275. path: __dirname + "/build/node",
  276. library: "CyberChef",
  277. libraryTarget: "commonjs2"
  278. }
  279. }
  280. },
  281. copy: {
  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/web/static/ga.html") + "</body></html>");
  288. return grunt.template.process(content);
  289. }
  290. },
  291. src: "build/prod/index.html",
  292. dest: "build/prod/index.html"
  293. }
  294. },
  295. chmod: {
  296. build: {
  297. options: {
  298. mode: "755",
  299. },
  300. src: ["build/**/*", "build/"]
  301. },
  302. docs: {
  303. options: {
  304. mode: "755",
  305. },
  306. src: ["docs/**/*", "docs/"]
  307. }
  308. },
  309. exec: {
  310. repoSize: {
  311. command: [
  312. "git ls-files | wc -l | xargs printf '\n%b\ttracked files\n'",
  313. "du -hs | egrep -o '^[^\t]*' | xargs printf '%b\trepository size\n'"
  314. ].join(";"),
  315. stderr: false
  316. },
  317. cleanGit: {
  318. command: "git gc --prune=now --aggressive"
  319. },
  320. },
  321. execute: {
  322. test: "build/test/index.js"
  323. },
  324. });
  325. };