Gruntfile.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*eslint-env node*/
  2. 'use strict';
  3. const fs = require('fs');
  4. const path = require('path');
  5. const semver = require('semver');
  6. const liveEditor = require('@gfxfundamentals/live-editor');
  7. const liveEditorPath = path.dirname(require.resolve('@gfxfundamentals/live-editor'));
  8. module.exports = function(grunt) {
  9. require('load-grunt-tasks')(grunt);
  10. const s_ignoreRE = /\.(md|py|sh|enc)$/i;
  11. function noMds(filename) {
  12. return !s_ignoreRE.test(filename);
  13. }
  14. const s_isMdRE = /\.md$/i;
  15. function mdsOnly(filename) {
  16. return s_isMdRE.test(filename);
  17. }
  18. function notFolder(filename) {
  19. return !fs.statSync(filename).isDirectory();
  20. }
  21. function noMdsNoFolders(filename) {
  22. return noMds(filename) && notFolder(filename);
  23. }
  24. grunt.initConfig({
  25. eslint: {
  26. lib: {
  27. src: [
  28. 'threejs/resources/*.js',
  29. ],
  30. },
  31. support: {
  32. src: [
  33. 'Gruntfile.js',
  34. 'build/js/build.js',
  35. ],
  36. },
  37. examples: {
  38. src: [
  39. 'threejs/*.html',
  40. 'threejs/lessons/resources/*.js',
  41. '!threejs/lessons/resources/prettify.js',
  42. 'threejs/lessons/resources/*.html',
  43. ],
  44. },
  45. },
  46. copy: {
  47. main: {
  48. files: [
  49. { expand: false, src: '*', dest: 'out/', filter: noMdsNoFolders, },
  50. { expand: true, cwd: `${liveEditor.monacoEditor}/`, src: 'min/**', dest: 'out/monaco-editor/', nonull: true, },
  51. { expand: true, cwd: `${liveEditorPath}/src/`, src: '**', dest: 'out/threejs/resources/', nonull: true, },
  52. { expand: true, src: 'threejs/**', dest: 'out/', filter: noMds, },
  53. { expand: true, src: '3rdparty/**', dest: 'out/', },
  54. ],
  55. },
  56. },
  57. clean: [
  58. 'out/**/*',
  59. ],
  60. buildlesson: {
  61. main: {
  62. files: [],
  63. },
  64. },
  65. watch: {
  66. main: {
  67. files: [
  68. 'threejs/**',
  69. '3rdparty/**',
  70. 'node_modules/@gfxfundamentals/live-editor/src/**',
  71. ],
  72. tasks: ['copy'],
  73. options: {
  74. spawn: false,
  75. },
  76. },
  77. lessons: {
  78. files: [
  79. 'threejs/lessons/**/threejs*.md',
  80. ],
  81. tasks: ['buildlesson'],
  82. options: {
  83. spawn: false,
  84. },
  85. },
  86. },
  87. });
  88. let changedFiles = {};
  89. const onChange = grunt.util._.debounce(function() {
  90. grunt.config('copy.main.files', Object.keys(changedFiles).filter(noMds).map((file) => {
  91. const copy = {
  92. src: file,
  93. dest: 'out/',
  94. };
  95. if (file.indexOf('live-editor') >= 0) {
  96. copy.cwd = `${path.dirname(file)}/`;
  97. copy.src = path.basename(file);
  98. copy.expand = true;
  99. copy.dest = 'out/threejs/resources/';
  100. }
  101. return copy;
  102. }));
  103. grunt.config('buildlesson.main.files', Object.keys(changedFiles).filter(mdsOnly).map((file) => {
  104. return {
  105. src: file,
  106. };
  107. }));
  108. changedFiles = {};
  109. }, 200);
  110. grunt.event.on('watch', function(action, filepath) {
  111. changedFiles[filepath] = action;
  112. onChange();
  113. });
  114. const buildSettings = {
  115. outDir: 'out',
  116. baseUrl: 'https://threejsfundamentals.org',
  117. rootFolder: 'threejs',
  118. lessonGrep: 'threejs*.md',
  119. siteName: 'ThreeJSFundamentals',
  120. siteThumbnail: 'threejsfundamentals.jpg', // in rootFolder/lessons/resources
  121. templatePath: 'build/templates',
  122. thumbnailOptions: {
  123. thumbnailBackground: 'threejsfundamentals-background.jpg',
  124. text: [
  125. {
  126. font: 'bold 100px lesson-font',
  127. verticalSpacing: 100,
  128. offset: [100, 120],
  129. textAlign: 'left',
  130. shadowOffset: [15, 15],
  131. strokeWidth: 15,
  132. textWrapWidth: 1000,
  133. },
  134. {
  135. font: 'bold 60px lesson-font',
  136. text: 'threejsfundamentals.org',
  137. verticalSpacing: 100,
  138. offset: [-100, -90],
  139. textAlign: 'right',
  140. shadowOffset: [8, 8],
  141. strokeWidth: 15,
  142. textWrapWidth: 1000,
  143. color: 'hsl(340, 100%, 70%)',
  144. },
  145. ],
  146. },
  147. };
  148. // just the hackiest way to get this working.
  149. grunt.registerMultiTask('buildlesson', 'build a lesson', function() {
  150. const filenames = new Set();
  151. this.files.forEach((files) => {
  152. files.src.forEach((filename) => {
  153. filenames.add(filename);
  154. });
  155. });
  156. const buildStuff = require('@gfxfundamentals/lesson-builder');
  157. const settings = Object.assign({}, buildSettings, {
  158. filenames,
  159. });
  160. const finish = this.async();
  161. buildStuff(settings).finally(finish);
  162. });
  163. grunt.registerTask('buildlessons', function() {
  164. const buildStuff = require('@gfxfundamentals/lesson-builder');
  165. const finish = this.async();
  166. buildStuff(buildSettings).finally(finish);
  167. });
  168. grunt.task.registerMultiTask('fixthreepaths', 'fix three paths', function() {
  169. const options = this.options({});
  170. const oldVersionRE = new RegExp(`/${options.oldVersionStr}/`, 'g');
  171. const newVersionReplacement = `/${options.newVersionStr}/`;
  172. this.files.forEach((files) => {
  173. files.src.forEach((filename) => {
  174. const oldContent = fs.readFileSync(filename, {encoding: 'utf8'});
  175. const newContent = oldContent.replace(oldVersionRE, newVersionReplacement);
  176. if (oldContent !== newContent) {
  177. grunt.log.writeln(`updating ${filename} to ${options.newVersionStr}`);
  178. fs.writeFileSync(filename, newContent);
  179. }
  180. });
  181. });
  182. });
  183. grunt.registerTask('bumpthree', function() {
  184. const lessonInfo = JSON.parse(fs.readFileSync('package.json', {encoding: 'utf8'}));
  185. const oldVersion = lessonInfo.threejsfundamentals.threeVersion;
  186. const oldVersionStr = `r${oldVersion}`;
  187. const threePath = path.dirname(path.dirname(require.resolve('three')));
  188. const threeInfo = JSON.parse(fs.readFileSync(path.join(threePath, 'package.json'), {encoding: 'utf8'}));
  189. const newVersion = semver.minor(threeInfo.version);
  190. const newVersionStr = `r${newVersion}`;
  191. const basePath = path.join('threejs', 'resources', 'threejs', newVersionStr);
  192. grunt.config.merge({
  193. copy: {
  194. threejs: {
  195. files: [
  196. { expand: true, cwd: `${threePath}/build/`, src: 'three.js', dest: `${basePath}/build/`, },
  197. { expand: true, cwd: `${threePath}/build/`, src: 'three.min.js', dest: `${basePath}/build/`, },
  198. { expand: true, cwd: `${threePath}/build/`, src: 'three.module.js', dest: `${basePath}/build/`, },
  199. { expand: true, cwd: `${threePath}/examples/js/`, src: '**', dest: `${basePath}/examples/js/`, },
  200. { expand: true, cwd: `${threePath}/examples/jsm/`, src: '**', dest: `${basePath}/examples/jsm/`, },
  201. ],
  202. },
  203. },
  204. fixthreepaths: {
  205. options: {
  206. oldVersionStr,
  207. newVersionStr,
  208. },
  209. src: [
  210. 'threejs/**/*.html',
  211. 'threejs/**/*.md',
  212. 'threejs/**/*.js',
  213. '!threejs/resources/threejs/**',
  214. ],
  215. },
  216. });
  217. lessonInfo.threejsfundamentals.threeVersion = newVersion;
  218. fs.writeFileSync('package.json', JSON.stringify(lessonInfo, null, 2));
  219. grunt.task.run(['copy:threejs', 'fixthreepaths']);
  220. });
  221. grunt.registerTask('build', ['clean', 'copy:main', 'buildlessons']);
  222. grunt.registerTask('buildwatch', ['build', 'watch']);
  223. grunt.registerTask('default', ['eslint', 'build']);
  224. };