gulpfile.js 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439
  1. /*eslint-env node*/
  2. 'use strict';
  3. var fs = require('fs');
  4. var path = require('path');
  5. var os = require('os');
  6. var child_process = require('child_process');
  7. var crypto = require('crypto');
  8. var zlib = require('zlib');
  9. var readline = require('readline');
  10. var request = require('request');
  11. var globby = require('globby');
  12. var gulpTap = require('gulp-tap');
  13. var gulpUglify = require('gulp-uglify');
  14. var open = require('open');
  15. var rimraf = require('rimraf');
  16. var glslStripComments = require('glsl-strip-comments');
  17. var mkdirp = require('mkdirp');
  18. var mergeStream = require('merge-stream');
  19. var streamToPromise = require('stream-to-promise');
  20. var gulp = require('gulp');
  21. var gulpInsert = require('gulp-insert');
  22. var gulpZip = require('gulp-zip');
  23. var gulpRename = require('gulp-rename');
  24. var gulpReplace = require('gulp-replace');
  25. var Promise = require('bluebird');
  26. var Karma = require('karma');
  27. var yargs = require('yargs');
  28. var AWS = require('aws-sdk');
  29. var mime = require('mime');
  30. var rollup = require('rollup');
  31. var rollupPluginStripPragma = require('rollup-plugin-strip-pragma');
  32. var rollupPluginExternalGlobals = require('rollup-plugin-external-globals');
  33. var rollupPluginUglify = require('rollup-plugin-uglify');
  34. var cleanCSS = require('gulp-clean-css');
  35. var packageJson = require('./package.json');
  36. var version = packageJson.version;
  37. if (/\.0$/.test(version)) {
  38. version = version.substring(0, version.length - 2);
  39. }
  40. var karmaConfigFile = path.join(__dirname, 'Specs/karma.conf.js');
  41. var travisDeployUrl = 'http://cesium-dev.s3-website-us-east-1.amazonaws.com/cesium/';
  42. //Gulp doesn't seem to have a way to get the currently running tasks for setting
  43. //per-task variables. We use the command line argument here to detect which task is being run.
  44. var taskName = process.argv[2];
  45. var noDevelopmentGallery = taskName === 'release' || taskName === 'makeZipFile';
  46. var minifyShaders = taskName === 'minify' || taskName === 'minifyRelease' || taskName === 'release' || taskName === 'makeZipFile' || taskName === 'buildApps';
  47. var verbose = yargs.argv.verbose;
  48. var concurrency = yargs.argv.concurrency;
  49. if (!concurrency) {
  50. concurrency = os.cpus().length;
  51. }
  52. var sourceFiles = ['Source/**/*.js',
  53. '!Source/*.js',
  54. '!Source/Workers/**',
  55. '!Source/WorkersES6/**',
  56. 'Source/WorkersES6/createTaskProcessorWorker.js',
  57. '!Source/ThirdParty/Workers/**',
  58. '!Source/ThirdParty/google-earth-dbroot-parser.js',
  59. '!Source/ThirdParty/pako_inflate.js',
  60. '!Source/ThirdParty/crunch.js'];
  61. var watchedFiles = ['Source/**/*.js',
  62. '!Source/Cesium.js',
  63. '!Source/Build/**',
  64. '!Source/Shaders/**/*.js',
  65. 'Source/Shaders/**/*.glsl',
  66. '!Source/ThirdParty/Shaders/*.js',
  67. 'Source/ThirdParty/Shaders/*.glsl',
  68. '!Source/Workers/**',
  69. 'Source/Workers/cesiumWorkerBootstrapper.js',
  70. 'Source/Workers/transferTypedArrayTest.js',
  71. '!Specs/SpecList.js'];
  72. var filesToClean = ['Source/Cesium.js',
  73. 'Source/Shaders/**/*.js',
  74. 'Source/Workers/**',
  75. '!Source/Workers/cesiumWorkerBootstrapper.js',
  76. '!Source/Workers/transferTypedArrayTest.js',
  77. 'Source/ThirdParty/Shaders/*.js',
  78. 'Specs/SpecList.js',
  79. 'Apps/Sandcastle/jsHintOptions.js',
  80. 'Apps/Sandcastle/gallery/gallery-index.js',
  81. 'Apps/Sandcastle/templates/bucket.css',
  82. 'Cesium-*.zip',
  83. 'cesium-*.tgz'];
  84. var filesToConvertES6 = ['Source/**/*.js',
  85. 'Specs/**/*.js',
  86. '!Source/ThirdParty/**',
  87. '!Source/Cesium.js',
  88. '!Source/copyrightHeader.js',
  89. '!Source/Shaders/**',
  90. '!Source/Workers/cesiumWorkerBootstrapper.js',
  91. '!Source/Workers/transferTypedArrayTest.js',
  92. '!Specs/karma-main.js',
  93. '!Specs/karma.conf.js',
  94. '!Specs/spec-main.js',
  95. '!Specs/SpecList.js',
  96. '!Specs/TestWorkers/**'
  97. ];
  98. function rollupWarning(message) {
  99. // Ignore eval warnings in third-party code we don't have control over
  100. if (message.code === 'EVAL' && /(protobuf-minimal|crunch)\.js$/.test(message.loc.file)) {
  101. return;
  102. }
  103. console.log(message);
  104. }
  105. function createWorkers() {
  106. rimraf.sync('Build/createWorkers');
  107. globby.sync([
  108. 'Source/Workers/**',
  109. '!Source/Workers/cesiumWorkerBootstrapper.js',
  110. '!Source/Workers/transferTypedArrayTest.js'
  111. ]).forEach(function(file) {
  112. rimraf.sync(file);
  113. });
  114. var workers = globby.sync([
  115. 'Source/WorkersES6/**'
  116. ]);
  117. return rollup.rollup({
  118. input: workers,
  119. onwarn: rollupWarning
  120. }).then(function(bundle) {
  121. return bundle.write({
  122. dir: 'Build/createWorkers',
  123. banner: '/* This file is automatically rebuilt by the Cesium build process. */',
  124. format: 'amd'
  125. });
  126. }).then(function(){
  127. return streamToPromise(
  128. gulp.src('Build/createWorkers/**').pipe(gulp.dest('Source/Workers'))
  129. );
  130. }).then(function() {
  131. rimraf.sync('Build/createWorkers');
  132. });
  133. }
  134. gulp.task('build', function() {
  135. mkdirp.sync('Build');
  136. glslToJavaScript(minifyShaders, 'Build/minifyShaders.state');
  137. createCesiumJs();
  138. createSpecList();
  139. // createJsHintOptions();
  140. return Promise.join(createWorkers(), createGalleryList());
  141. });
  142. gulp.task('build-watch', function() {
  143. return gulp.watch(watchedFiles, gulp.series('build'));
  144. });
  145. gulp.task('buildApps', function() {
  146. return Promise.join(
  147. buildCesiumViewer(),
  148. buildSandcastle()
  149. );
  150. });
  151. gulp.task('build-specs', function buildSpecs() {
  152. var externalCesium = rollupPluginExternalGlobals({
  153. '../Source/Cesium.js': 'Cesium',
  154. '../../Source/Cesium.js': 'Cesium',
  155. '../../../Source/Cesium.js': 'Cesium',
  156. '../../../../Source/Cesium.js': 'Cesium'
  157. });
  158. var removePragmas = rollupPluginStripPragma({
  159. pragmas: ['debug']
  160. });
  161. var promise = Promise.join(
  162. rollup.rollup({
  163. input: 'Specs/SpecList.js',
  164. plugins: [externalCesium],
  165. onwarn: rollupWarning
  166. }).then(function(bundle) {
  167. return bundle.write({
  168. file: 'Build/Specs/Specs.js',
  169. format: 'iife'
  170. });
  171. }).then(function(){
  172. return rollup.rollup({
  173. input: 'Specs/spec-main.js',
  174. plugins: [removePragmas, externalCesium]
  175. }).then(function(bundle) {
  176. return bundle.write({
  177. file: 'Build/Specs/spec-main.js',
  178. format: 'iife'
  179. });
  180. });
  181. }).then(function(){
  182. return rollup.rollup({
  183. input: 'Specs/karma-main.js',
  184. plugins: [removePragmas, externalCesium],
  185. onwarn: rollupWarning
  186. }).then(function(bundle) {
  187. return bundle.write({
  188. file: 'Build/Specs/karma-main.js',
  189. name: 'karmaMain',
  190. format: 'iife'
  191. });
  192. });
  193. })
  194. );
  195. return promise;
  196. });
  197. gulp.task('clean', function(done) {
  198. rimraf.sync('Build');
  199. globby.sync(filesToClean).forEach(function(file) {
  200. rimraf.sync(file);
  201. });
  202. done();
  203. });
  204. function cloc() {
  205. var cmdLine;
  206. var clocPath = path.join('node_modules', 'cloc', 'lib', 'cloc');
  207. //Run cloc on primary Source files only
  208. var source = new Promise(function(resolve, reject) {
  209. cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' +
  210. ' Source/ --exclude-dir=Assets,ThirdParty,Workers --not-match-f=copyrightHeader.js';
  211. child_process.exec(cmdLine, function(error, stdout, stderr) {
  212. if (error) {
  213. console.log(stderr);
  214. return reject(error);
  215. }
  216. console.log('Source:');
  217. console.log(stdout);
  218. resolve();
  219. });
  220. });
  221. //If running cloc on source succeeded, also run it on the tests.
  222. return source.then(function() {
  223. return new Promise(function(resolve, reject) {
  224. cmdLine = 'perl ' + clocPath + ' --quiet --progress-rate=0' +
  225. ' Specs/ --exclude-dir=Data';
  226. child_process.exec(cmdLine, function(error, stdout, stderr) {
  227. if (error) {
  228. console.log(stderr);
  229. return reject(error);
  230. }
  231. console.log('Specs:');
  232. console.log(stdout);
  233. resolve();
  234. });
  235. });
  236. });
  237. }
  238. gulp.task('cloc', gulp.series('clean', cloc));
  239. function combine() {
  240. var outputDirectory = path.join('Build', 'CesiumUnminified');
  241. return combineJavaScript({
  242. removePragmas: false,
  243. optimizer: 'none',
  244. outputDirectory: outputDirectory
  245. });
  246. }
  247. gulp.task('combine', gulp.series('build', combine));
  248. gulp.task('default', gulp.series('combine'));
  249. function combineRelease() {
  250. var outputDirectory = path.join('Build', 'CesiumUnminified');
  251. return combineJavaScript({
  252. removePragmas: true,
  253. optimizer: 'none',
  254. outputDirectory: outputDirectory
  255. });
  256. }
  257. gulp.task('combineRelease', gulp.series('build', combineRelease));
  258. //Builds the documentation
  259. function generateDocumentation() {
  260. var envPathSeperator = os.platform() === 'win32' ? ';' : ':';
  261. return new Promise(function(resolve, reject) {
  262. child_process.exec('jsdoc --configure Tools/jsdoc/conf.json', {
  263. env : {
  264. PATH : process.env.PATH + envPathSeperator + 'node_modules/.bin',
  265. CESIUM_VERSION : version
  266. }
  267. }, function(error, stdout, stderr) {
  268. if (error) {
  269. console.log(stderr);
  270. return reject(error);
  271. }
  272. console.log(stdout);
  273. var stream = gulp.src('Documentation/Images/**').pipe(gulp.dest('Build/Documentation/Images'));
  274. return streamToPromise(stream).then(resolve);
  275. });
  276. });
  277. }
  278. gulp.task('generateDocumentation', generateDocumentation);
  279. gulp.task('generateDocumentation-watch', function() {
  280. return generateDocumentation().done(function() {
  281. console.log('Listening for changes in documentation...');
  282. return gulp.watch(sourceFiles, gulp.series('generateDocumentation'));
  283. });
  284. });
  285. gulp.task('release', gulp.series('build', combine, minifyRelease, generateDocumentation));
  286. gulp.task('makeZipFile', gulp.series('release', function() {
  287. //For now we regenerate the JS glsl to force it to be unminified in the release zip
  288. //See https://github.com/AnalyticalGraphicsInc/cesium/pull/3106#discussion_r42793558 for discussion.
  289. glslToJavaScript(false, 'Build/minifyShaders.state');
  290. var builtSrc = gulp.src([
  291. 'Build/Cesium/**',
  292. 'Build/CesiumUnminified/**',
  293. 'Build/Documentation/**'
  294. ], {
  295. base : '.'
  296. });
  297. var staticSrc = gulp.src([
  298. 'Apps/**',
  299. '!Apps/Sandcastle/gallery/development/**',
  300. 'Source/**',
  301. 'Specs/**',
  302. 'ThirdParty/**',
  303. 'favicon.ico',
  304. 'gulpfile.js',
  305. 'server.js',
  306. 'package.json',
  307. 'LICENSE.md',
  308. 'CHANGES.md',
  309. 'README.md',
  310. 'web.config'
  311. ], {
  312. base : '.'
  313. });
  314. var indexSrc = gulp.src('index.release.html').pipe(gulpRename('index.html'));
  315. return mergeStream(builtSrc, staticSrc, indexSrc)
  316. .pipe(gulpTap(function(file) {
  317. // Work around an issue with gulp-zip where archives generated on Windows do
  318. // not properly have their directory executable mode set.
  319. // see https://github.com/sindresorhus/gulp-zip/issues/64#issuecomment-205324031
  320. if (file.isDirectory()) {
  321. file.stat.mode = parseInt('40777', 8);
  322. }
  323. }))
  324. .pipe(gulpZip('Cesium-' + version + '.zip'))
  325. .pipe(gulp.dest('.'));
  326. }));
  327. gulp.task('minify', gulp.series('build', function() {
  328. return combineJavaScript({
  329. removePragmas : false,
  330. optimizer : 'uglify2',
  331. outputDirectory : path.join('Build', 'Cesium')
  332. });
  333. }));
  334. function minifyRelease() {
  335. return combineJavaScript({
  336. removePragmas: true,
  337. optimizer: 'uglify2',
  338. outputDirectory: path.join('Build', 'Cesium')
  339. });
  340. }
  341. gulp.task('minifyRelease', gulp.series('build', minifyRelease));
  342. function isTravisPullRequest() {
  343. return process.env.TRAVIS_PULL_REQUEST !== undefined && process.env.TRAVIS_PULL_REQUEST !== 'false';
  344. }
  345. gulp.task('deploy-s3', function(done) {
  346. if (isTravisPullRequest()) {
  347. console.log('Skipping deployment for non-pull request.');
  348. done();
  349. return;
  350. }
  351. var argv = yargs.usage('Usage: deploy-s3 -b [Bucket Name] -d [Upload Directory]')
  352. .demand(['b', 'd']).argv;
  353. var uploadDirectory = argv.d;
  354. var bucketName = argv.b;
  355. var cacheControl = argv.c ? argv.c : 'max-age=3600';
  356. if (argv.confirm) {
  357. // skip prompt for travis
  358. deployCesium(bucketName, uploadDirectory, cacheControl, done);
  359. return;
  360. }
  361. var iface = readline.createInterface({
  362. input: process.stdin,
  363. output: process.stdout
  364. });
  365. // prompt for confirmation
  366. iface.question('Files from your computer will be published to the ' + bucketName + ' bucket. Continue? [y/n] ', function(answer) {
  367. iface.close();
  368. if (answer === 'y') {
  369. deployCesium(bucketName, uploadDirectory, cacheControl, done);
  370. } else {
  371. console.log('Deploy aborted by user.');
  372. done();
  373. }
  374. });
  375. });
  376. // Deploy cesium to s3
  377. function deployCesium(bucketName, uploadDirectory, cacheControl, done) {
  378. var readFile = Promise.promisify(fs.readFile);
  379. var gzip = Promise.promisify(zlib.gzip);
  380. var concurrencyLimit = 2000;
  381. var s3 = new AWS.S3({
  382. maxRetries : 10,
  383. retryDelayOptions : {
  384. base : 500
  385. }
  386. });
  387. var existingBlobs = [];
  388. var totalFiles = 0;
  389. var uploaded = 0;
  390. var skipped = 0;
  391. var errors = [];
  392. var prefix = uploadDirectory + '/';
  393. return listAll(s3, bucketName, prefix, existingBlobs)
  394. .then(function() {
  395. return globby([
  396. 'Apps/**',
  397. 'Build/**',
  398. 'Source/**',
  399. 'Specs/**',
  400. 'ThirdParty/**',
  401. '*.md',
  402. 'favicon.ico',
  403. 'gulpfile.js',
  404. 'index.html',
  405. 'package.json',
  406. 'server.js',
  407. 'web.config',
  408. '*.zip',
  409. '*.tgz'
  410. ], {
  411. dot : true // include hidden files
  412. });
  413. }).then(function(files) {
  414. return Promise.map(files, function(file) {
  415. var blobName = uploadDirectory + '/' + file;
  416. var mimeLookup = getMimeType(blobName);
  417. var contentType = mimeLookup.type;
  418. var compress = mimeLookup.compress;
  419. var contentEncoding = compress ? 'gzip' : undefined;
  420. var etag;
  421. totalFiles++;
  422. return readFile(file)
  423. .then(function(content) {
  424. if (!compress) {
  425. return content;
  426. }
  427. var alreadyCompressed = (content[0] === 0x1f) && (content[1] === 0x8b);
  428. if (alreadyCompressed) {
  429. console.log('Skipping compressing already compressed file: ' + file);
  430. return content;
  431. }
  432. return gzip(content);
  433. })
  434. .then(function(content) {
  435. // compute hash and etag
  436. var hash = crypto.createHash('md5').update(content).digest('hex');
  437. etag = crypto.createHash('md5').update(content).digest('base64');
  438. var index = existingBlobs.indexOf(blobName);
  439. if (index <= -1) {
  440. return content;
  441. }
  442. // remove files as we find them on disk
  443. existingBlobs.splice(index, 1);
  444. // get file info
  445. return s3.headObject({
  446. Bucket: bucketName,
  447. Key: blobName
  448. }).promise().then(function(data) {
  449. if (data.ETag !== ('"' + hash + '"') ||
  450. data.CacheControl !== cacheControl ||
  451. data.ContentType !== contentType ||
  452. data.ContentEncoding !== contentEncoding) {
  453. return content;
  454. }
  455. // We don't need to upload this file again
  456. skipped++;
  457. return undefined;
  458. })
  459. .catch(function(error) {
  460. errors.push(error);
  461. });
  462. })
  463. .then(function(content) {
  464. if (!content) {
  465. return;
  466. }
  467. if (verbose) {
  468. console.log('Uploading ' + blobName + '...');
  469. }
  470. var params = {
  471. Bucket : bucketName,
  472. Key : blobName,
  473. Body : content,
  474. ContentMD5 : etag,
  475. ContentType : contentType,
  476. ContentEncoding : contentEncoding,
  477. CacheControl : cacheControl
  478. };
  479. return s3.putObject(params).promise()
  480. .then(function() {
  481. uploaded++;
  482. })
  483. .catch(function(error) {
  484. errors.push(error);
  485. });
  486. });
  487. }, {concurrency : concurrencyLimit});
  488. }).then(function() {
  489. console.log('Skipped ' + skipped + ' files and successfully uploaded ' + uploaded + ' files of ' + (totalFiles - skipped) + ' files.');
  490. if (existingBlobs.length === 0) {
  491. return;
  492. }
  493. var objectsToDelete = [];
  494. existingBlobs.forEach(function(file) {
  495. //Don't delete generate zip files.
  496. if (!/\.(zip|tgz)$/.test(file)) {
  497. objectsToDelete.push({Key : file});
  498. }
  499. });
  500. if (objectsToDelete.length > 0) {
  501. console.log('Cleaning ' + objectsToDelete.length + ' files...');
  502. // If more than 1000 files, we must issue multiple requests
  503. var batches = [];
  504. while (objectsToDelete.length > 1000) {
  505. batches.push(objectsToDelete.splice(0, 1000));
  506. }
  507. batches.push(objectsToDelete);
  508. return Promise.map(batches, function(objects) {
  509. return s3.deleteObjects({
  510. Bucket: bucketName,
  511. Delete: {
  512. Objects: objects
  513. }
  514. }).promise().then(function() {
  515. if (verbose) {
  516. console.log('Cleaned ' + objects.length + ' files.');
  517. }
  518. });
  519. }, {concurrency : concurrency});
  520. }
  521. }).catch(function(error) {
  522. errors.push(error);
  523. }).then(function() {
  524. if (errors.length === 0) {
  525. done();
  526. return;
  527. }
  528. console.log('Errors: ');
  529. errors.map(function(e) {
  530. console.log(e);
  531. });
  532. done(1);
  533. });
  534. }
  535. function getMimeType(filename) {
  536. var mimeType = mime.getType(filename);
  537. if (mimeType) {
  538. //Compress everything except zipfiles, binary images, and video
  539. var compress = !/^(image\/|video\/|application\/zip|application\/gzip)/i.test(mimeType);
  540. if (mimeType === 'image/svg+xml') {
  541. compress = true;
  542. }
  543. return { type: mimeType, compress: compress };
  544. }
  545. //Non-standard mime types not handled by mime
  546. if (/\.(glsl|LICENSE|config|state)$/i.test(filename)) {
  547. return { type: 'text/plain', compress: true };
  548. } else if (/\.(czml|topojson)$/i.test(filename)) {
  549. return { type: 'application/json', compress: true };
  550. } else if (/\.(crn|tgz)$/i.test(filename)) {
  551. return { type: 'application/octet-stream', compress: false };
  552. }
  553. // Handle dotfiles, such as .jshintrc
  554. var baseName = path.basename(filename);
  555. if (baseName[0] === '.' || baseName.indexOf('.') === -1) {
  556. return { type: 'text/plain', compress: true };
  557. }
  558. // Everything else can be octet-stream compressed but print a warning
  559. // if we introduce a type we aren't specifically handling.
  560. if (!/\.(terrain|b3dm|geom|pnts|vctr|cmpt|i3dm|metadata)$/i.test(filename)) {
  561. console.log('Unknown mime type for ' + filename);
  562. }
  563. return { type: 'application/octet-stream', compress: true };
  564. }
  565. // get all files currently in bucket asynchronously
  566. function listAll(s3, bucketName, prefix, files, marker) {
  567. return s3.listObjects({
  568. Bucket: bucketName,
  569. MaxKeys: 1000,
  570. Prefix: prefix,
  571. Marker: marker
  572. }).promise().then(function(data) {
  573. var items = data.Contents;
  574. for (var i = 0; i < items.length; i++) {
  575. files.push(items[i].Key);
  576. }
  577. if (data.IsTruncated) {
  578. // get next page of results
  579. return listAll(s3, bucketName, prefix, files, files[files.length - 1]);
  580. }
  581. });
  582. }
  583. gulp.task('deploy-set-version', function(done) {
  584. var buildVersion = yargs.argv.buildVersion;
  585. if (buildVersion) {
  586. // NPM versions can only contain alphanumeric and hyphen characters
  587. packageJson.version += '-' + buildVersion.replace(/[^[0-9A-Za-z-]/g, '');
  588. fs.writeFileSync('package.json', JSON.stringify(packageJson, undefined, 2));
  589. }
  590. done();
  591. });
  592. gulp.task('deploy-status', function() {
  593. if (isTravisPullRequest()) {
  594. console.log('Skipping deployment status for non-pull request.');
  595. return Promise.resolve();
  596. }
  597. var status = yargs.argv.status;
  598. var message = yargs.argv.message;
  599. var deployUrl = travisDeployUrl + process.env.TRAVIS_BRANCH + '/';
  600. var zipUrl = deployUrl + 'Cesium-' + packageJson.version + '.zip';
  601. var npmUrl = deployUrl + 'cesium-' + packageJson.version + '.tgz';
  602. var coverageUrl = travisDeployUrl + process.env.TRAVIS_BRANCH + '/Build/Coverage/index.html';
  603. return Promise.join(
  604. setStatus(status, deployUrl, message, 'deployment'),
  605. setStatus(status, zipUrl, message, 'zip file'),
  606. setStatus(status, npmUrl, message, 'npm package'),
  607. setStatus(status, coverageUrl, message, 'coverage results')
  608. );
  609. });
  610. function setStatus(state, targetUrl, description, context) {
  611. // skip if the environment does not have the token
  612. if (!process.env.TOKEN) {
  613. return;
  614. }
  615. var requestPost = Promise.promisify(request.post);
  616. return requestPost({
  617. url: 'https://api.github.com/repos/' + process.env.TRAVIS_REPO_SLUG + '/statuses/' + process.env.TRAVIS_COMMIT,
  618. json: true,
  619. headers: {
  620. 'Authorization': 'token ' + process.env.TOKEN,
  621. 'User-Agent': 'Cesium'
  622. },
  623. body: {
  624. state: state,
  625. target_url: targetUrl,
  626. description: description,
  627. context: context
  628. }
  629. });
  630. }
  631. gulp.task('coverage', function(done) {
  632. var argv = yargs.argv;
  633. var webglStub = argv.webglStub ? argv.webglStub : false;
  634. var suppressPassed = argv.suppressPassed ? argv.suppressPassed : false;
  635. var failTaskOnError = argv.failTaskOnError ? argv.failTaskOnError : false;
  636. var folders = [];
  637. var browsers = ['Chrome'];
  638. if (argv.browsers) {
  639. browsers = argv.browsers.split(',');
  640. }
  641. var karma = new Karma.Server({
  642. configFile: karmaConfigFile,
  643. browsers: browsers,
  644. specReporter: {
  645. suppressErrorSummary: false,
  646. suppressFailed: false,
  647. suppressPassed: suppressPassed,
  648. suppressSkipped: true
  649. },
  650. preprocessors: {
  651. 'Source/Core/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  652. 'Source/DataSources/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  653. 'Source/Renderer/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  654. 'Source/Scene/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  655. 'Source/Shaders/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  656. 'Source/Widgets/**/*.js': ['karma-coverage-istanbul-instrumenter'],
  657. 'Source/Workers/**/*.js': ['karma-coverage-istanbul-instrumenter']
  658. },
  659. coverageIstanbulInstrumenter: {
  660. esModules: true
  661. },
  662. reporters: ['spec', 'coverage'],
  663. coverageReporter: {
  664. dir: 'Build/Coverage',
  665. subdir: function(browserName) {
  666. folders.push(browserName);
  667. return browserName;
  668. },
  669. includeAllSources: true
  670. },
  671. client: {
  672. captureConsole: verbose,
  673. args: [undefined, undefined, undefined, webglStub, undefined]
  674. }
  675. }, function(e) {
  676. var html = '<!doctype html><html><body><ul>';
  677. folders.forEach(function(folder) {
  678. html += '<li><a href="' + encodeURIComponent(folder) + '/index.html">' + folder + '</a></li>';
  679. });
  680. html += '</ul></body></html>';
  681. fs.writeFileSync('Build/Coverage/index.html', html);
  682. if (!process.env.TRAVIS) {
  683. folders.forEach(function(dir) {
  684. open('Build/Coverage/' + dir + '/index.html');
  685. });
  686. }
  687. return done(failTaskOnError ? e : undefined);
  688. });
  689. karma.start();
  690. });
  691. gulp.task('test', function(done) {
  692. var argv = yargs.argv;
  693. var enableAllBrowsers = argv.all ? true : false;
  694. var includeCategory = argv.include ? argv.include : '';
  695. var excludeCategory = argv.exclude ? argv.exclude : '';
  696. var webglValidation = argv.webglValidation ? argv.webglValidation : false;
  697. var webglStub = argv.webglStub ? argv.webglStub : false;
  698. var release = argv.release ? argv.release : false;
  699. var failTaskOnError = argv.failTaskOnError ? argv.failTaskOnError : false;
  700. var suppressPassed = argv.suppressPassed ? argv.suppressPassed : false;
  701. var browsers = ['Chrome'];
  702. if (argv.browsers) {
  703. browsers = argv.browsers.split(',');
  704. }
  705. var files = [
  706. { pattern: 'Specs/karma-main.js', included: true, type: 'module' },
  707. { pattern: 'Source/**', included: false, type: 'module' },
  708. { pattern: 'Specs/*.js', included: true, type: 'module' },
  709. { pattern: 'Specs/Core/**', included: true, type: 'module' },
  710. { pattern: 'Specs/Data/**', included: false },
  711. { pattern: 'Specs/DataSources/**', included: true, type: 'module' },
  712. { pattern: 'Specs/Renderer/**', included: true, type: 'module' },
  713. { pattern: 'Specs/Scene/**', included: true, type: 'module' },
  714. { pattern: 'Specs/ThirdParty/**', included: true, type: 'module' },
  715. { pattern: 'Specs/Widgets/**', included: true, type: 'module' },
  716. { pattern: 'Specs/TestWorkers/**', included: false }
  717. ];
  718. if (release) {
  719. files = [
  720. { pattern: 'Specs/Data/**', included: false },
  721. { pattern: 'Specs/ThirdParty/**', included: true, type: 'module' },
  722. { pattern: 'Specs/TestWorkers/**', included: false },
  723. { pattern: 'Build/Cesium/Cesium.js', included: true },
  724. { pattern: 'Build/Cesium/**', included: false },
  725. { pattern: 'Build/Specs/karma-main.js', included: true },
  726. { pattern: 'Build/Specs/Specs.js', included: true }
  727. ];
  728. }
  729. var karma = new Karma.Server({
  730. configFile: karmaConfigFile,
  731. browsers: browsers,
  732. specReporter: {
  733. suppressErrorSummary: false,
  734. suppressFailed: false,
  735. suppressPassed: suppressPassed,
  736. suppressSkipped: true
  737. },
  738. detectBrowsers: {
  739. enabled: enableAllBrowsers
  740. },
  741. logLevel: verbose ? Karma.constants.LOG_INFO : Karma.constants.LOG_ERROR,
  742. files: files,
  743. client: {
  744. captureConsole: verbose,
  745. args: [includeCategory, excludeCategory, webglValidation, webglStub, release]
  746. }
  747. }, function(e) {
  748. return done(failTaskOnError ? e : undefined);
  749. });
  750. karma.start();
  751. });
  752. gulp.task('convertToModules', function() {
  753. var requiresRegex = /([\s\S]*?(define|defineSuite|require)\((?:{[\s\S]*}, )?\[)([\S\s]*?)]([\s\S]*?function\s*)\(([\S\s]*?)\) {([\s\S]*)/;
  754. var noModulesRegex = /([\s\S]*?(define|defineSuite|require)\((?:{[\s\S]*}, )?\[?)([\S\s]*?)]?([\s\S]*?function\s*)\(([\S\s]*?)\) {([\s\S]*)/;
  755. var splitRegex = /,\s*/;
  756. var fsReadFile = Promise.promisify(fs.readFile);
  757. var fsWriteFile = Promise.promisify(fs.writeFile);
  758. var files = globby.sync(filesToConvertES6);
  759. return Promise.map(files, function(file) {
  760. return fsReadFile(file).then(function(contents) {
  761. contents = contents.toString();
  762. if (contents.startsWith('import')) {
  763. return;
  764. }
  765. var result = requiresRegex.exec(contents);
  766. if (result === null) {
  767. result = noModulesRegex.exec(contents);
  768. if (result === null) {
  769. return;
  770. }
  771. }
  772. var names = result[3].split(splitRegex);
  773. if (names.length === 1 && names[0].trim() === '') {
  774. names.length = 0;
  775. }
  776. var i;
  777. for (i = 0; i < names.length; ++i) {
  778. if (names[i].indexOf('//') >= 0 || names[i].indexOf('/*') >= 0) {
  779. console.log(file + ' contains comments in the require list. Skipping so nothing gets broken.');
  780. return;
  781. }
  782. }
  783. var identifiers = result[5].split(splitRegex);
  784. if (identifiers.length === 1 && identifiers[0].trim() === '') {
  785. identifiers.length = 0;
  786. }
  787. for (i = 0; i < identifiers.length; ++i) {
  788. if (identifiers[i].indexOf('//') >= 0 || identifiers[i].indexOf('/*') >= 0) {
  789. console.log(file + ' contains comments in the require list. Skipping so nothing gets broken.');
  790. return;
  791. }
  792. }
  793. var requires = [];
  794. for (i = 0; i < names.length && i < identifiers.length; ++i) {
  795. requires.push({
  796. name : names[i].trim(),
  797. identifier : identifiers[i].trim()
  798. });
  799. }
  800. // Convert back to separate lists for the names and identifiers, and add
  801. // any additional names or identifiers that don't have a corresponding pair.
  802. var sortedNames = requires.map(function(item) {
  803. return item.name.slice(0, -1) + '.js\'';
  804. });
  805. for (i = sortedNames.length; i < names.length; ++i) {
  806. sortedNames.push(names[i].trim());
  807. }
  808. var sortedIdentifiers = requires.map(function(item) {
  809. return item.identifier;
  810. });
  811. for (i = sortedIdentifiers.length; i < identifiers.length; ++i) {
  812. sortedIdentifiers.push(identifiers[i].trim());
  813. }
  814. contents = '';
  815. if (sortedNames.length > 0) {
  816. for (var q = 0; q < sortedNames.length; q++) {
  817. var modulePath = sortedNames[q];
  818. if (file.startsWith('Specs')) {
  819. modulePath = modulePath.substring(1, modulePath.length - 1);
  820. var sourceDir = path.dirname(file);
  821. if (modulePath.startsWith('Specs') || modulePath.startsWith('.')) {
  822. var importPath = modulePath;
  823. if (modulePath.startsWith('Specs')) {
  824. importPath = path.relative(sourceDir, modulePath);
  825. if (importPath[0] !== '.') {
  826. importPath = './' + importPath;
  827. }
  828. }
  829. modulePath = '\'' + importPath + '\'';
  830. contents += 'import ' + sortedIdentifiers[q] + ' from ' + modulePath + ';' + os.EOL;
  831. } else {
  832. modulePath = '\'' + path.relative(sourceDir, 'Source') + '/Cesium.js' + '\'';
  833. if (sortedIdentifiers[q] === 'CesiumMath') {
  834. contents += 'import { Math as CesiumMath } from ' + modulePath + ';' + os.EOL;
  835. } else {
  836. contents += 'import { ' + sortedIdentifiers[q] + ' } from ' + modulePath + ';' + os.EOL;
  837. }
  838. }
  839. } else {
  840. contents += 'import ' + sortedIdentifiers[q] + ' from ' + modulePath + ';' + os.EOL;
  841. }
  842. }
  843. }
  844. var code;
  845. var codeAndReturn = result[6];
  846. if (file.endsWith('Spec.js')) {
  847. var indi = codeAndReturn.lastIndexOf('});');
  848. code = codeAndReturn.slice(0, indi);
  849. code = code.trim().replace("'use strict';" + os.EOL, '');
  850. contents += code + os.EOL;
  851. } else {
  852. var returnIndex = codeAndReturn.lastIndexOf('return');
  853. code = codeAndReturn.slice(0, returnIndex);
  854. code = code.trim().replace("'use strict';" + os.EOL, '');
  855. contents += code + os.EOL;
  856. var returnStatement = codeAndReturn.slice(returnIndex);
  857. contents += returnStatement.split(';')[0].replace('return ', 'export default ') + ';' + os.EOL;
  858. }
  859. return fsWriteFile(file, contents);
  860. });
  861. });
  862. });
  863. function combineCesium(debug, optimizer, combineOutput) {
  864. var plugins = [];
  865. if (!debug) {
  866. plugins.push(rollupPluginStripPragma({
  867. pragmas: ['debug']
  868. }));
  869. }
  870. if (optimizer === 'uglify2') {
  871. plugins.push(rollupPluginUglify.uglify());
  872. }
  873. return rollup.rollup({
  874. input: 'Source/Cesium.js',
  875. plugins: plugins,
  876. onwarn: rollupWarning
  877. }).then(function(bundle) {
  878. return bundle.write({
  879. format: 'umd',
  880. name: 'Cesium',
  881. file: path.join(combineOutput, 'Cesium.js')
  882. });
  883. });
  884. }
  885. function combineWorkers(debug, optimizer, combineOutput) {
  886. //This is done waterfall style for concurrency reasons.
  887. // Copy files that are already minified
  888. return globby(['Source/ThirdParty/Workers/draco*.js'])
  889. .then(function(files) {
  890. var stream = gulp.src(files, { base: 'Source' })
  891. .pipe(gulp.dest(combineOutput));
  892. return streamToPromise(stream);
  893. })
  894. .then(function () {
  895. return globby(['Source/Workers/cesiumWorkerBootstrapper.js',
  896. 'Source/Workers/transferTypedArrayTest.js',
  897. 'Source/ThirdParty/Workers/*.js',
  898. // Files are already minified, don't optimize
  899. '!Source/ThirdParty/Workers/draco*.js']);
  900. })
  901. .then(function(files) {
  902. return Promise.map(files, function(file) {
  903. return streamToPromise(gulp.src(file)
  904. .pipe(gulpUglify())
  905. .pipe(gulp.dest(path.dirname(path.join(combineOutput, path.relative('Source', file))))));
  906. }, {concurrency : concurrency});
  907. })
  908. .then(function() {
  909. return globby(['Source/WorkersES6/*.js']);
  910. })
  911. .then(function(files) {
  912. var plugins = [];
  913. if (!debug) {
  914. plugins.push(rollupPluginStripPragma({
  915. pragmas: ['debug']
  916. }));
  917. }
  918. if (optimizer === 'uglify2') {
  919. plugins.push(rollupPluginUglify.uglify());
  920. }
  921. return rollup.rollup({
  922. input: files,
  923. plugins: plugins,
  924. onwarn: rollupWarning
  925. }).then(function(bundle) {
  926. return bundle.write({
  927. dir: path.join(combineOutput, 'Workers'),
  928. format: 'amd'
  929. });
  930. });
  931. });
  932. }
  933. function minifyCSS(outputDirectory) {
  934. streamToPromise(
  935. gulp.src('Source/**/*.css')
  936. .pipe(cleanCSS())
  937. .pipe(gulp.dest(outputDirectory))
  938. );
  939. }
  940. function minifyModules(outputDirectory) {
  941. return streamToPromise(gulp.src('Source/ThirdParty/google-earth-dbroot-parser.js')
  942. .pipe(gulpUglify())
  943. .pipe(gulp.dest(outputDirectory + '/ThirdParty/')));
  944. }
  945. function combineJavaScript(options) {
  946. var optimizer = options.optimizer;
  947. var outputDirectory = options.outputDirectory;
  948. var removePragmas = options.removePragmas;
  949. var combineOutput = path.join('Build', 'combineOutput', optimizer);
  950. var copyrightHeader = fs.readFileSync(path.join('Source', 'copyrightHeader.js'));
  951. var promise = Promise.join(
  952. combineCesium(!removePragmas, optimizer, combineOutput),
  953. combineWorkers(!removePragmas, optimizer, combineOutput),
  954. minifyModules(outputDirectory)
  955. );
  956. return promise.then(function() {
  957. var promises = [];
  958. //copy to build folder with copyright header added at the top
  959. var stream = gulp.src([combineOutput + '/**'])
  960. .pipe(gulpInsert.prepend(copyrightHeader))
  961. .pipe(gulp.dest(outputDirectory));
  962. promises.push(streamToPromise(stream));
  963. var everythingElse = ['Source/**', '!**/*.js', '!**/*.glsl'];
  964. if (optimizer === 'uglify2') {
  965. promises.push(minifyCSS(outputDirectory));
  966. everythingElse.push('!**/*.css');
  967. }
  968. stream = gulp.src(everythingElse, { nodir: true }).pipe(gulp.dest(outputDirectory));
  969. promises.push(streamToPromise(stream));
  970. return Promise.all(promises).then(function() {
  971. rimraf.sync(combineOutput);
  972. });
  973. });
  974. }
  975. function glslToJavaScript(minify, minifyStateFilePath) {
  976. fs.writeFileSync(minifyStateFilePath, minify);
  977. var minifyStateFileLastModified = fs.existsSync(minifyStateFilePath) ? fs.statSync(minifyStateFilePath).mtime.getTime() : 0;
  978. // collect all currently existing JS files into a set, later we will remove the ones
  979. // we still are using from the set, then delete any files remaining in the set.
  980. var leftOverJsFiles = {};
  981. globby.sync(['Source/Shaders/**/*.js', 'Source/ThirdParty/Shaders/*.js']).forEach(function(file) {
  982. leftOverJsFiles[path.normalize(file)] = true;
  983. });
  984. var builtinFunctions = [];
  985. var builtinConstants = [];
  986. var builtinStructs = [];
  987. var glslFiles = globby.sync(['Source/Shaders/**/*.glsl', 'Source/ThirdParty/Shaders/*.glsl']);
  988. glslFiles.forEach(function(glslFile) {
  989. glslFile = path.normalize(glslFile);
  990. var baseName = path.basename(glslFile, '.glsl');
  991. var jsFile = path.join(path.dirname(glslFile), baseName) + '.js';
  992. // identify built in functions, structs, and constants
  993. var baseDir = path.join('Source', 'Shaders', 'Builtin');
  994. if (glslFile.indexOf(path.normalize(path.join(baseDir, 'Functions'))) === 0) {
  995. builtinFunctions.push(baseName);
  996. }
  997. else if (glslFile.indexOf(path.normalize(path.join(baseDir, 'Constants'))) === 0) {
  998. builtinConstants.push(baseName);
  999. }
  1000. else if (glslFile.indexOf(path.normalize(path.join(baseDir, 'Structs'))) === 0) {
  1001. builtinStructs.push(baseName);
  1002. }
  1003. delete leftOverJsFiles[jsFile];
  1004. var jsFileExists = fs.existsSync(jsFile);
  1005. var jsFileModified = jsFileExists ? fs.statSync(jsFile).mtime.getTime() : 0;
  1006. var glslFileModified = fs.statSync(glslFile).mtime.getTime();
  1007. if (jsFileExists && jsFileModified > glslFileModified && jsFileModified > minifyStateFileLastModified) {
  1008. return;
  1009. }
  1010. var contents = fs.readFileSync(glslFile, 'utf8');
  1011. contents = contents.replace(/\r\n/gm, '\n');
  1012. var copyrightComments = '';
  1013. var extractedCopyrightComments = contents.match(/\/\*\*(?:[^*\/]|\*(?!\/)|\n)*?@license(?:.|\n)*?\*\//gm);
  1014. if (extractedCopyrightComments) {
  1015. copyrightComments = extractedCopyrightComments.join('\n') + '\n';
  1016. }
  1017. if (minify) {
  1018. contents = glslStripComments(contents);
  1019. contents = contents.replace(/\s+$/gm, '').replace(/^\s+/gm, '').replace(/\n+/gm, '\n');
  1020. contents += '\n';
  1021. }
  1022. contents = contents.split('"').join('\\"').replace(/\n/gm, '\\n\\\n');
  1023. contents = copyrightComments + '\
  1024. //This file is automatically rebuilt by the Cesium build process.\n\
  1025. export default "' + contents + '";\n';
  1026. fs.writeFileSync(jsFile, contents);
  1027. });
  1028. // delete any left over JS files from old shaders
  1029. Object.keys(leftOverJsFiles).forEach(function(filepath) {
  1030. rimraf.sync(filepath);
  1031. });
  1032. var generateBuiltinContents = function(contents, builtins, path) {
  1033. for (var i = 0; i < builtins.length; i++) {
  1034. var builtin = builtins[i];
  1035. contents.imports.push('import czm_' + builtin + ' from \'./' + path + '/' + builtin + '.js\'');
  1036. contents.builtinLookup.push('czm_' + builtin + ' : ' + 'czm_' + builtin);
  1037. }
  1038. };
  1039. //generate the JS file for Built-in GLSL Functions, Structs, and Constants
  1040. var contents = {
  1041. imports : [],
  1042. builtinLookup: []
  1043. };
  1044. generateBuiltinContents(contents, builtinConstants, 'Constants');
  1045. generateBuiltinContents(contents, builtinStructs, 'Structs');
  1046. generateBuiltinContents(contents, builtinFunctions, 'Functions');
  1047. var fileContents = '//This file is automatically rebuilt by the Cesium build process.\n' +
  1048. contents.imports.join('\n') +
  1049. '\n\nexport default {\n ' + contents.builtinLookup.join(',\n ') + '\n};\n';
  1050. fs.writeFileSync(path.join('Source', 'Shaders', 'Builtin', 'CzmBuiltins.js'), fileContents);
  1051. }
  1052. function createCesiumJs() {
  1053. var contents = `export var VERSION = '${version}';\n`;
  1054. globby.sync(sourceFiles).forEach(function(file) {
  1055. file = path.relative('Source', file);
  1056. var moduleId = file;
  1057. moduleId = filePathToModuleId(moduleId);
  1058. var assignmentName = path.basename(file, path.extname(file));
  1059. if (moduleId.indexOf('Shaders/') === 0) {
  1060. assignmentName = '_shaders' + assignmentName;
  1061. }
  1062. assignmentName = assignmentName.replace(/(\.|-)/g, '_');
  1063. contents += 'export { default as ' + assignmentName + " } from './" + moduleId + ".js';" + os.EOL;
  1064. });
  1065. fs.writeFileSync('Source/Cesium.js', contents);
  1066. }
  1067. function createSpecList() {
  1068. var specFiles = globby.sync(['Specs/**/*Spec.js']);
  1069. var contents = '';
  1070. specFiles.forEach(function(file) {
  1071. contents += "import './" + filePathToModuleId(file).replace('Specs/', '') + ".js';\n";
  1072. });
  1073. fs.writeFileSync(path.join('Specs', 'SpecList.js'), contents);
  1074. }
  1075. function createGalleryList() {
  1076. var demoObjects = [];
  1077. var demoJSONs = [];
  1078. var output = path.join('Apps', 'Sandcastle', 'gallery', 'gallery-index.js');
  1079. var fileList = ['Apps/Sandcastle/gallery/**/*.html'];
  1080. if (noDevelopmentGallery) {
  1081. fileList.push('!Apps/Sandcastle/gallery/development/**/*.html');
  1082. }
  1083. // On travis, the version is set to something like '1.43.0-branch-name-travisBuildNumber'
  1084. // We need to extract just the Major.Minor version
  1085. var majorMinor = packageJson.version.match(/^(.*)\.(.*)\./);
  1086. var major = majorMinor[1];
  1087. var minor = Number(majorMinor[2]) - 1; // We want the last release, not current release
  1088. var tagVersion = major + '.' + minor;
  1089. // Get an array of demos that were added since the last release.
  1090. // This includes newly staged local demos as well.
  1091. var newDemos = [];
  1092. try {
  1093. newDemos = child_process.execSync('git diff --name-only --diff-filter=A ' + tagVersion + ' Apps/Sandcastle/gallery/*.html', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim().split('\n');
  1094. } catch (e) {
  1095. // On a Cesium fork, tags don't exist so we can't generate the list.
  1096. }
  1097. var helloWorld;
  1098. globby.sync(fileList).forEach(function(file) {
  1099. var demo = filePathToModuleId(path.relative('Apps/Sandcastle/gallery', file));
  1100. var demoObject = {
  1101. name : demo,
  1102. isNew: newDemos.includes(file)
  1103. };
  1104. if (fs.existsSync(file.replace('.html', '') + '.jpg')) {
  1105. demoObject.img = demo + '.jpg';
  1106. }
  1107. demoObjects.push(demoObject);
  1108. if (demo === 'Hello World') {
  1109. helloWorld = demoObject;
  1110. }
  1111. });
  1112. demoObjects.sort(function(a, b) {
  1113. if (a.name < b.name) {
  1114. return -1;
  1115. } else if (a.name > b.name) {
  1116. return 1;
  1117. }
  1118. return 0;
  1119. });
  1120. var helloWorldIndex = Math.max(demoObjects.indexOf(helloWorld), 0);
  1121. var i;
  1122. for (i = 0; i < demoObjects.length; ++i) {
  1123. demoJSONs[i] = JSON.stringify(demoObjects[i], null, 2);
  1124. }
  1125. var contents = '\
  1126. // This file is automatically rebuilt by the Cesium build process.\n\
  1127. var hello_world_index = ' + helloWorldIndex + ';\n\
  1128. var VERSION = \'' + version + '\';\n\
  1129. var gallery_demos = [' + demoJSONs.join(', ') + '];\n\
  1130. var has_new_gallery_demos = ' + (newDemos.length > 0 ? 'true;' : 'false;') + '\n';
  1131. fs.writeFileSync(output, contents);
  1132. // Compile CSS for Sandcastle
  1133. return streamToPromise(gulp.src(path.join('Apps', 'Sandcastle', 'templates', 'bucketRaw.css'))
  1134. .pipe(cleanCSS())
  1135. .pipe(gulpRename('bucket.css'))
  1136. .pipe(gulpInsert.prepend('/* This file is automatically rebuilt by the Cesium build process. */\n'))
  1137. .pipe(gulp.dest(path.join('Apps', 'Sandcastle', 'templates'))));
  1138. }
  1139. function createJsHintOptions() {
  1140. var primary = JSON.parse(fs.readFileSync(path.join('Apps', '.jshintrc'), 'utf8'));
  1141. var gallery = JSON.parse(fs.readFileSync(path.join('Apps', 'Sandcastle', '.jshintrc'), 'utf8'));
  1142. primary.jasmine = false;
  1143. primary.predef = gallery.predef;
  1144. primary.unused = gallery.unused;
  1145. var contents = '\
  1146. // This file is automatically rebuilt by the Cesium build process.\n\
  1147. var sandcastleJsHintOptions = ' + JSON.stringify(primary, null, 4) + ';\n';
  1148. fs.writeFileSync(path.join('Apps', 'Sandcastle', 'jsHintOptions.js'), contents);
  1149. }
  1150. function buildSandcastle() {
  1151. var appStream = gulp.src([
  1152. 'Apps/Sandcastle/**',
  1153. '!Apps/Sandcastle/load-cesium-es6.js',
  1154. '!Apps/Sandcastle/standalone.html',
  1155. '!Apps/Sandcastle/images/**',
  1156. '!Apps/Sandcastle/gallery/**.jpg'
  1157. ])
  1158. // Remove dev-only ES6 module loading for unbuilt Cesium
  1159. .pipe(gulpReplace(' <script type="module" src="../load-cesium-es6.js"></script>', ''))
  1160. .pipe(gulpReplace('nomodule', ''))
  1161. // Fix relative paths for new location
  1162. .pipe(gulpReplace('../../../Build', '../../..'))
  1163. .pipe(gulpReplace('../../Source', '../../../Source'))
  1164. .pipe(gulpReplace('../../ThirdParty', '../../../ThirdParty'))
  1165. .pipe(gulpReplace('../../SampleData', '../../../../Apps/SampleData'))
  1166. .pipe(gulpReplace('Build/Documentation', 'Documentation'))
  1167. .pipe(gulp.dest('Build/Apps/Sandcastle'));
  1168. var imageStream = gulp.src([
  1169. 'Apps/Sandcastle/gallery/**.jpg',
  1170. 'Apps/Sandcastle/images/**'
  1171. ], {
  1172. base: 'Apps/Sandcastle',
  1173. buffer: false
  1174. })
  1175. .pipe(gulp.dest('Build/Apps/Sandcastle'));
  1176. var standaloneStream = gulp.src([
  1177. 'Apps/Sandcastle/standalone.html'
  1178. ])
  1179. .pipe(gulpReplace(' <script type="module" src="load-cesium-es6.js"></script>', ''))
  1180. .pipe(gulpReplace('nomodule', ''))
  1181. .pipe(gulpReplace('../../Build', '../..'))
  1182. .pipe(gulp.dest('Build/Apps/Sandcastle'));
  1183. return streamToPromise(mergeStream(appStream, imageStream, standaloneStream));
  1184. }
  1185. function buildCesiumViewer() {
  1186. var cesiumViewerOutputDirectory = 'Build/Apps/CesiumViewer';
  1187. mkdirp.sync(cesiumViewerOutputDirectory);
  1188. var promise = Promise.join(
  1189. rollup.rollup({
  1190. input: 'Apps/CesiumViewer/CesiumViewer.js',
  1191. treeshake: {
  1192. moduleSideEffects: false
  1193. },
  1194. plugins: [
  1195. rollupPluginStripPragma({
  1196. pragmas: ['debug']
  1197. }),
  1198. rollupPluginUglify.uglify()
  1199. ],
  1200. onwarn: rollupWarning
  1201. }).then(function(bundle) {
  1202. return bundle.write({
  1203. file: 'Build/Apps/CesiumViewer/CesiumViewer.js',
  1204. format: 'iife'
  1205. });
  1206. })
  1207. );
  1208. promise = promise.then(function() {
  1209. var copyrightHeader = fs.readFileSync(path.join('Source', 'copyrightHeader.js'));
  1210. var stream = mergeStream(
  1211. gulp.src('Build/Apps/CesiumViewer/CesiumViewer.js')
  1212. .pipe(gulpInsert.prepend(copyrightHeader))
  1213. .pipe(gulpReplace('../../Source', '.'))
  1214. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1215. gulp.src('Apps/CesiumViewer/CesiumViewer.css')
  1216. .pipe(cleanCSS())
  1217. .pipe(gulpReplace('../../Source', '.'))
  1218. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1219. gulp.src('Apps/CesiumViewer/index.html')
  1220. .pipe(gulpReplace('type="module"', ''))
  1221. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1222. gulp.src(['Apps/CesiumViewer/**',
  1223. '!Apps/CesiumViewer/index.html',
  1224. '!Apps/CesiumViewer/**/*.js',
  1225. '!Apps/CesiumViewer/**/*.css']),
  1226. gulp.src(['Build/Cesium/Assets/**',
  1227. 'Build/Cesium/Workers/**',
  1228. 'Build/Cesium/ThirdParty/**',
  1229. 'Build/Cesium/Widgets/**',
  1230. '!Build/Cesium/Widgets/**/*.css'],
  1231. {
  1232. base : 'Build/Cesium',
  1233. nodir : true
  1234. }),
  1235. gulp.src(['Build/Cesium/Widgets/InfoBox/InfoBoxDescription.css'], {
  1236. base : 'Build/Cesium'
  1237. }),
  1238. gulp.src(['web.config'])
  1239. );
  1240. return streamToPromise(stream.pipe(gulp.dest(cesiumViewerOutputDirectory)));
  1241. });
  1242. return promise;
  1243. }
  1244. function filePathToModuleId(moduleId) {
  1245. return moduleId.substring(0, moduleId.lastIndexOf('.')).replace(/\\/g, '/');
  1246. }