Gruntfile.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. const webpack = require("webpack");
  2. const ExtractTextPlugin = require("extract-text-webpack-plugin");
  3. const HtmlWebpackPlugin = require("html-webpack-plugin");
  4. const 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. const compileTime = grunt.template.today("UTC: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. pkg = grunt.file.readJSON("package.json");
  66. /**
  67. * Compiles a production build of CyberChef into a single, portable web page.
  68. */
  69. function runInliner() {
  70. const done = this.async();
  71. Inliner.html({
  72. relativeTo: "build/prod/",
  73. fileContent: grunt.file.read("build/prod/cyberchef.htm"),
  74. images: true,
  75. svgs: true,
  76. scripts: true,
  77. links: true,
  78. strict: true
  79. }, function(error, result) {
  80. if (error) {
  81. if (error instanceof Error) {
  82. done(error);
  83. } else {
  84. done(new Error(error));
  85. }
  86. } else {
  87. grunt.file.write("build/prod/cyberchef.htm", result);
  88. done(true);
  89. }
  90. });
  91. }
  92. grunt.initConfig({
  93. clean: {
  94. dev: ["build/dev/*"],
  95. prod: ["build/prod/*"],
  96. test: ["build/test/*"],
  97. node: ["build/node/*"],
  98. docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"],
  99. },
  100. eslint: {
  101. options: {
  102. configFile: "./.eslintrc.json"
  103. },
  104. configs: ["Gruntfile.js"],
  105. core: ["src/core/**/*.js", "!src/core/lib/**/*"],
  106. web: ["src/web/**/*.js"],
  107. node: ["src/node/**/*.js"],
  108. tests: ["test/**/*.js"],
  109. },
  110. jsdoc: {
  111. options: {
  112. destination: "docs",
  113. template: "node_modules/ink-docstrap/template",
  114. recurse: true,
  115. readme: "./README.md",
  116. configure: "docs/jsdoc.conf.json"
  117. },
  118. all: {
  119. src: [
  120. "src/**/*.js",
  121. "!src/core/lib/**/*",
  122. ],
  123. }
  124. },
  125. accessibility: {
  126. options: {
  127. accessibilityLevel: "WCAG2A",
  128. verbose: false,
  129. ignore: [
  130. "WCAG2A.Principle1.Guideline1_3.1_3_1.H42.2"
  131. ]
  132. },
  133. test: {
  134. src: ["build/**/*.html"]
  135. }
  136. },
  137. webpack: {
  138. options: {
  139. plugins: [
  140. new webpack.ProvidePlugin({
  141. $: "jquery",
  142. jQuery: "jquery",
  143. moment: "moment-timezone"
  144. }),
  145. new webpack.BannerPlugin({
  146. banner: banner,
  147. raw: true,
  148. entryOnly: true
  149. }),
  150. new webpack.DefinePlugin({
  151. COMPILE_TIME: JSON.stringify(compileTime),
  152. COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || ""),
  153. PKG_VERSION: JSON.stringify(pkg.version)
  154. }),
  155. new ExtractTextPlugin("styles.css"),
  156. ],
  157. resolve: {
  158. alias: {
  159. jquery: "jquery/src/jquery"
  160. }
  161. },
  162. module: {
  163. rules: [
  164. {
  165. test: /\.js$/,
  166. exclude: /node_modules/,
  167. loader: "babel-loader?compact=false"
  168. },
  169. {
  170. test: /\.css$/,
  171. use: ExtractTextPlugin.extract({
  172. use: [
  173. { loader: "css-loader?minimize" },
  174. { loader: "postcss-loader" },
  175. ]
  176. })
  177. },
  178. {
  179. test: /\.less$/,
  180. use: ExtractTextPlugin.extract({
  181. use: [
  182. { loader: "css-loader?minimize" },
  183. { loader: "postcss-loader" },
  184. { loader: "less-loader" }
  185. ]
  186. })
  187. },
  188. {
  189. test: /\.(ico|eot|ttf|woff|woff2)$/,
  190. loader: "url-loader",
  191. options: {
  192. limit: 10000
  193. }
  194. },
  195. { // First party images are saved as files to be cached
  196. test: /\.(png|jpg|gif|svg)$/,
  197. exclude: /node_modules/,
  198. loader: "file-loader",
  199. options: {
  200. name: "images/[name].[ext]"
  201. }
  202. },
  203. { // Third party images are inlined
  204. test: /\.(png|jpg|gif|svg)$/,
  205. exclude: /web\/static/,
  206. loader: "url-loader",
  207. options: {
  208. limit: 10000
  209. }
  210. },
  211. ]
  212. },
  213. stats: {
  214. children: false,
  215. warningsFilter: /source-map/
  216. }
  217. },
  218. webDev: {
  219. target: "web",
  220. entry: "./src/web/index.js",
  221. output: {
  222. filename: "scripts.js",
  223. path: __dirname + "/build/dev"
  224. },
  225. plugins: [
  226. new HtmlWebpackPlugin({
  227. filename: "index.html",
  228. template: "./src/web/html/index.html",
  229. compileTime: compileTime,
  230. version: pkg.version,
  231. })
  232. ],
  233. watch: true
  234. },
  235. webProd: {
  236. target: "web",
  237. entry: "./src/web/index.js",
  238. output: {
  239. filename: "scripts.js",
  240. path: __dirname + "/build/prod"
  241. },
  242. plugins: [
  243. new webpack.optimize.UglifyJsPlugin({
  244. compress: {
  245. "screw_ie8": true,
  246. "dead_code": true,
  247. "unused": true,
  248. "warnings": false
  249. },
  250. comments: false,
  251. }),
  252. new HtmlWebpackPlugin({ // Main version
  253. filename: "index.html",
  254. template: "./src/web/html/index.html",
  255. compileTime: compileTime,
  256. version: pkg.version,
  257. minify: {
  258. removeComments: true,
  259. collapseWhitespace: true,
  260. minifyJS: true,
  261. minifyCSS: true
  262. }
  263. }),
  264. new HtmlWebpackPlugin({ // Inline version
  265. filename: "cyberchef.htm",
  266. template: "./src/web/html/index.html",
  267. compileTime: compileTime,
  268. version: pkg.version,
  269. inline: true,
  270. minify: {
  271. removeComments: true,
  272. collapseWhitespace: true,
  273. minifyJS: true,
  274. minifyCSS: true
  275. }
  276. }),
  277. ]
  278. },
  279. tests: {
  280. target: "node",
  281. entry: "./test/index.js",
  282. output: {
  283. filename: "index.js",
  284. path: __dirname + "/build/test"
  285. }
  286. },
  287. node: {
  288. target: "node",
  289. entry: "./src/node/index.js",
  290. output: {
  291. filename: "CyberChef.js",
  292. path: __dirname + "/build/node",
  293. library: "CyberChef",
  294. libraryTarget: "commonjs2"
  295. }
  296. }
  297. },
  298. copy: {
  299. ghPages: {
  300. options: {
  301. process: function (content) {
  302. // Add Google Analytics code to index.html
  303. content = content.replace("</body></html>",
  304. grunt.file.read("src/web/static/ga.html") + "</body></html>");
  305. return grunt.template.process(content);
  306. }
  307. },
  308. src: "build/prod/index.html",
  309. dest: "build/prod/index.html"
  310. }
  311. },
  312. chmod: {
  313. build: {
  314. options: {
  315. mode: "755",
  316. },
  317. src: ["build/**/*", "build/"]
  318. },
  319. docs: {
  320. options: {
  321. mode: "755",
  322. },
  323. src: ["docs/**/*", "docs/"]
  324. }
  325. },
  326. exec: {
  327. repoSize: {
  328. command: [
  329. "git ls-files | wc -l | xargs printf '\n%b\ttracked files\n'",
  330. "du -hs | egrep -o '^[^\t]*' | xargs printf '%b\trepository size\n'"
  331. ].join(";"),
  332. stderr: false
  333. },
  334. cleanGit: {
  335. command: "git gc --prune=now --aggressive"
  336. },
  337. },
  338. execute: {
  339. test: "build/test/index.js"
  340. },
  341. });
  342. };