gulpfile.cjs 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889
  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 typescript = require("typescript");
  36. var packageJson = require("./package.json");
  37. var version = packageJson.version;
  38. if (/\.0$/.test(version)) {
  39. version = version.substring(0, version.length - 2);
  40. }
  41. var karmaConfigFile = path.join(__dirname, "Specs/karma.conf.cjs");
  42. var travisDeployUrl =
  43. "http://cesium-dev.s3-website-us-east-1.amazonaws.com/cesium/";
  44. //Gulp doesn't seem to have a way to get the currently running tasks for setting
  45. //per-task variables. We use the command line argument here to detect which task is being run.
  46. var taskName = process.argv[2];
  47. var noDevelopmentGallery = taskName === "release" || taskName === "makeZipFile";
  48. var minifyShaders =
  49. taskName === "minify" ||
  50. taskName === "minifyRelease" ||
  51. taskName === "release" ||
  52. taskName === "makeZipFile" ||
  53. taskName === "buildApps";
  54. var verbose = yargs.argv.verbose;
  55. var concurrency = yargs.argv.concurrency;
  56. if (!concurrency) {
  57. concurrency = os.cpus().length;
  58. }
  59. var sourceFiles = [
  60. "Source/**/*.js",
  61. "!Source/*.js",
  62. "!Source/Workers/**",
  63. "!Source/WorkersES6/**",
  64. "Source/WorkersES6/createTaskProcessorWorker.js",
  65. "!Source/ThirdParty/Workers/**",
  66. "!Source/ThirdParty/google-earth-dbroot-parser.js",
  67. "!Source/ThirdParty/pako_inflate.js",
  68. "!Source/ThirdParty/crunch.js",
  69. ];
  70. var watchedFiles = [
  71. "Source/**/*.js",
  72. "!Source/Cesium.js",
  73. "!Source/Build/**",
  74. "!Source/Shaders/**/*.js",
  75. "Source/Shaders/**/*.glsl",
  76. "!Source/ThirdParty/Shaders/*.js",
  77. "Source/ThirdParty/Shaders/*.glsl",
  78. "!Source/Workers/**",
  79. "Source/Workers/cesiumWorkerBootstrapper.js",
  80. "Source/Workers/transferTypedArrayTest.js",
  81. "!Specs/SpecList.js",
  82. ];
  83. var filesToClean = [
  84. "Source/Cesium.js",
  85. "Source/Shaders/**/*.js",
  86. "Source/Workers/**",
  87. "!Source/Workers/cesiumWorkerBootstrapper.js",
  88. "!Source/Workers/transferTypedArrayTest.js",
  89. "Source/ThirdParty/Shaders/*.js",
  90. "Specs/SpecList.js",
  91. "Apps/Sandcastle/jsHintOptions.js",
  92. "Apps/Sandcastle/gallery/gallery-index.js",
  93. "Apps/Sandcastle/templates/bucket.css",
  94. "Cesium-*.zip",
  95. "cesium-*.tgz",
  96. ];
  97. var filesToConvertES6 = [
  98. "Source/**/*.js",
  99. "Specs/**/*.js",
  100. "!Source/ThirdParty/**",
  101. "!Source/Cesium.js",
  102. "!Source/copyrightHeader.js",
  103. "!Source/Shaders/**",
  104. "!Source/Workers/cesiumWorkerBootstrapper.js",
  105. "!Source/Workers/transferTypedArrayTest.js",
  106. "!Specs/karma-main.js",
  107. "!Specs/karma.conf.cjs",
  108. "!Specs/spec-main.js",
  109. "!Specs/SpecList.js",
  110. "!Specs/TestWorkers/**",
  111. ];
  112. function rollupWarning(message) {
  113. // Ignore eval warnings in third-party code we don't have control over
  114. if (
  115. message.code === "EVAL" &&
  116. /(protobuf-minimal|crunch)\.js$/.test(message.loc.file)
  117. ) {
  118. return;
  119. }
  120. console.log(message);
  121. }
  122. var copyrightHeader = fs.readFileSync(
  123. path.join("Source", "copyrightHeader.js"),
  124. "utf8"
  125. );
  126. function createWorkers() {
  127. rimraf.sync("Build/createWorkers");
  128. globby
  129. .sync([
  130. "Source/Workers/**",
  131. "!Source/Workers/cesiumWorkerBootstrapper.js",
  132. "!Source/Workers/transferTypedArrayTest.js",
  133. ])
  134. .forEach(function (file) {
  135. rimraf.sync(file);
  136. });
  137. var workers = globby.sync(["Source/WorkersES6/**"]);
  138. return rollup
  139. .rollup({
  140. input: workers,
  141. onwarn: rollupWarning,
  142. })
  143. .then(function (bundle) {
  144. return bundle.write({
  145. dir: "Build/createWorkers",
  146. banner:
  147. "/* This file is automatically rebuilt by the Cesium build process. */",
  148. format: "amd",
  149. });
  150. })
  151. .then(function () {
  152. return streamToPromise(
  153. gulp.src("Build/createWorkers/**").pipe(gulp.dest("Source/Workers"))
  154. );
  155. })
  156. .then(function () {
  157. rimraf.sync("Build/createWorkers");
  158. });
  159. }
  160. gulp.task("build", function () {
  161. mkdirp.sync("Build");
  162. fs.writeFileSync(
  163. "Build/package.json",
  164. JSON.stringify({
  165. type: "commonjs",
  166. }),
  167. "utf8"
  168. );
  169. glslToJavaScript(minifyShaders, "Build/minifyShaders.state");
  170. createCesiumJs();
  171. createSpecList();
  172. createJsHintOptions();
  173. return Promise.join(createWorkers(), createGalleryList());
  174. });
  175. gulp.task("build-watch", function () {
  176. return gulp.watch(watchedFiles, gulp.series("build"));
  177. });
  178. gulp.task("build-ts", function () {
  179. createTypeScriptDefinitions();
  180. return Promise.resolve();
  181. });
  182. gulp.task("buildApps", function () {
  183. return Promise.join(buildCesiumViewer(), buildSandcastle());
  184. });
  185. gulp.task("build-specs", function buildSpecs() {
  186. var externalCesium = rollupPluginExternalGlobals({
  187. "../Source/Cesium.js": "Cesium",
  188. "../../Source/Cesium.js": "Cesium",
  189. "../../../Source/Cesium.js": "Cesium",
  190. "../../../../Source/Cesium.js": "Cesium",
  191. });
  192. var removePragmas = rollupPluginStripPragma({
  193. pragmas: ["debug"],
  194. });
  195. var promise = Promise.join(
  196. rollup
  197. .rollup({
  198. input: "Specs/SpecList.js",
  199. plugins: [externalCesium],
  200. onwarn: rollupWarning,
  201. })
  202. .then(function (bundle) {
  203. return bundle.write({
  204. file: "Build/Specs/Specs.js",
  205. format: "iife",
  206. });
  207. })
  208. .then(function () {
  209. return rollup
  210. .rollup({
  211. input: "Specs/spec-main.js",
  212. plugins: [removePragmas, externalCesium],
  213. })
  214. .then(function (bundle) {
  215. return bundle.write({
  216. file: "Build/Specs/spec-main.js",
  217. format: "iife",
  218. });
  219. });
  220. })
  221. .then(function () {
  222. return rollup
  223. .rollup({
  224. input: "Specs/karma-main.js",
  225. plugins: [removePragmas, externalCesium],
  226. onwarn: rollupWarning,
  227. })
  228. .then(function (bundle) {
  229. return bundle.write({
  230. file: "Build/Specs/karma-main.js",
  231. name: "karmaMain",
  232. format: "iife",
  233. });
  234. });
  235. })
  236. );
  237. return promise;
  238. });
  239. gulp.task("clean", function (done) {
  240. rimraf.sync("Build");
  241. globby.sync(filesToClean).forEach(function (file) {
  242. rimraf.sync(file);
  243. });
  244. done();
  245. });
  246. function cloc() {
  247. var cmdLine;
  248. //Run cloc on primary Source files only
  249. var source = new Promise(function (resolve, reject) {
  250. cmdLine =
  251. "npx cloc" +
  252. " --quiet --progress-rate=0" +
  253. " Source/ --exclude-dir=Assets,ThirdParty,Workers --not-match-f=copyrightHeader.js";
  254. child_process.exec(cmdLine, function (error, stdout, stderr) {
  255. if (error) {
  256. console.log(stderr);
  257. return reject(error);
  258. }
  259. console.log("Source:");
  260. console.log(stdout);
  261. resolve();
  262. });
  263. });
  264. //If running cloc on source succeeded, also run it on the tests.
  265. return source.then(function () {
  266. return new Promise(function (resolve, reject) {
  267. cmdLine =
  268. "npx cloc" +
  269. " --quiet --progress-rate=0" +
  270. " Specs/ --exclude-dir=Data";
  271. child_process.exec(cmdLine, function (error, stdout, stderr) {
  272. if (error) {
  273. console.log(stderr);
  274. return reject(error);
  275. }
  276. console.log("Specs:");
  277. console.log(stdout);
  278. resolve();
  279. });
  280. });
  281. });
  282. }
  283. gulp.task("cloc", gulp.series("clean", cloc));
  284. function combine() {
  285. var outputDirectory = path.join("Build", "CesiumUnminified");
  286. return combineJavaScript({
  287. removePragmas: false,
  288. optimizer: "none",
  289. outputDirectory: outputDirectory,
  290. });
  291. }
  292. gulp.task("combine", gulp.series("build", combine));
  293. gulp.task("default", gulp.series("combine"));
  294. function combineRelease() {
  295. var outputDirectory = path.join("Build", "CesiumUnminified");
  296. return combineJavaScript({
  297. removePragmas: true,
  298. optimizer: "none",
  299. outputDirectory: outputDirectory,
  300. });
  301. }
  302. gulp.task("combineRelease", gulp.series("build", combineRelease));
  303. //Builds the documentation
  304. function generateDocumentation() {
  305. child_process.execSync("npx jsdoc --configure Tools/jsdoc/conf.json", {
  306. stdio: "inherit",
  307. env: Object.assign({}, process.env, { CESIUM_VERSION: version }),
  308. });
  309. var stream = gulp
  310. .src("Documentation/Images/**")
  311. .pipe(gulp.dest("Build/Documentation/Images"));
  312. return streamToPromise(stream);
  313. }
  314. gulp.task("generateDocumentation", generateDocumentation);
  315. gulp.task("generateDocumentation-watch", function () {
  316. return generateDocumentation().done(function () {
  317. console.log("Listening for changes in documentation...");
  318. return gulp.watch(sourceFiles, gulp.series("generateDocumentation"));
  319. });
  320. });
  321. gulp.task(
  322. "release",
  323. gulp.series(
  324. "build",
  325. "build-ts",
  326. combine,
  327. minifyRelease,
  328. generateDocumentation
  329. )
  330. );
  331. gulp.task(
  332. "makeZipFile",
  333. gulp.series("release", function () {
  334. //For now we regenerate the JS glsl to force it to be unminified in the release zip
  335. //See https://github.com/CesiumGS/cesium/pull/3106#discussion_r42793558 for discussion.
  336. glslToJavaScript(false, "Build/minifyShaders.state");
  337. var builtSrc = gulp.src(
  338. [
  339. "Build/Cesium/**",
  340. "Build/CesiumUnminified/**",
  341. "Build/Documentation/**",
  342. ],
  343. {
  344. base: ".",
  345. }
  346. );
  347. var staticSrc = gulp.src(
  348. [
  349. "Apps/**",
  350. "!Apps/Sandcastle/gallery/development/**",
  351. "Source/**",
  352. "Specs/**",
  353. "ThirdParty/**",
  354. "favicon.ico",
  355. "gulpfile.cjs",
  356. "server.cjs",
  357. "package.json",
  358. "LICENSE.md",
  359. "CHANGES.md",
  360. "README.md",
  361. "web.config",
  362. ],
  363. {
  364. base: ".",
  365. }
  366. );
  367. var indexSrc = gulp
  368. .src("index.release.html")
  369. .pipe(gulpRename("index.html"));
  370. return mergeStream(builtSrc, staticSrc, indexSrc)
  371. .pipe(
  372. gulpTap(function (file) {
  373. // Work around an issue with gulp-zip where archives generated on Windows do
  374. // not properly have their directory executable mode set.
  375. // see https://github.com/sindresorhus/gulp-zip/issues/64#issuecomment-205324031
  376. if (file.isDirectory()) {
  377. file.stat.mode = parseInt("40777", 8);
  378. }
  379. })
  380. )
  381. .pipe(gulpZip("Cesium-" + version + ".zip"))
  382. .pipe(gulp.dest("."));
  383. })
  384. );
  385. gulp.task(
  386. "minify",
  387. gulp.series("build", function () {
  388. return combineJavaScript({
  389. removePragmas: false,
  390. optimizer: "uglify2",
  391. outputDirectory: path.join("Build", "Cesium"),
  392. });
  393. })
  394. );
  395. function minifyRelease() {
  396. return combineJavaScript({
  397. removePragmas: true,
  398. optimizer: "uglify2",
  399. outputDirectory: path.join("Build", "Cesium"),
  400. });
  401. }
  402. gulp.task("minifyRelease", gulp.series("build", minifyRelease));
  403. function isTravisPullRequest() {
  404. return (
  405. process.env.TRAVIS_PULL_REQUEST !== undefined &&
  406. process.env.TRAVIS_PULL_REQUEST !== "false"
  407. );
  408. }
  409. gulp.task("deploy-s3", function (done) {
  410. if (isTravisPullRequest()) {
  411. console.log("Skipping deployment for non-pull request.");
  412. done();
  413. return;
  414. }
  415. var argv = yargs
  416. .usage("Usage: deploy-s3 -b [Bucket Name] -d [Upload Directory]")
  417. .demand(["b", "d"]).argv;
  418. var uploadDirectory = argv.d;
  419. var bucketName = argv.b;
  420. var cacheControl = argv.c ? argv.c : "max-age=3600";
  421. if (argv.confirm) {
  422. // skip prompt for travis
  423. deployCesium(bucketName, uploadDirectory, cacheControl, done);
  424. return;
  425. }
  426. var iface = readline.createInterface({
  427. input: process.stdin,
  428. output: process.stdout,
  429. });
  430. // prompt for confirmation
  431. iface.question(
  432. "Files from your computer will be published to the " +
  433. bucketName +
  434. " bucket. Continue? [y/n] ",
  435. function (answer) {
  436. iface.close();
  437. if (answer === "y") {
  438. deployCesium(bucketName, uploadDirectory, cacheControl, done);
  439. } else {
  440. console.log("Deploy aborted by user.");
  441. done();
  442. }
  443. }
  444. );
  445. });
  446. // Deploy cesium to s3
  447. function deployCesium(bucketName, uploadDirectory, cacheControl, done) {
  448. var readFile = Promise.promisify(fs.readFile);
  449. var gzip = Promise.promisify(zlib.gzip);
  450. var concurrencyLimit = 2000;
  451. var s3 = new AWS.S3({
  452. maxRetries: 10,
  453. retryDelayOptions: {
  454. base: 500,
  455. },
  456. });
  457. var existingBlobs = [];
  458. var totalFiles = 0;
  459. var uploaded = 0;
  460. var skipped = 0;
  461. var errors = [];
  462. var prefix = uploadDirectory + "/";
  463. return listAll(s3, bucketName, prefix, existingBlobs)
  464. .then(function () {
  465. return globby(
  466. [
  467. "Apps/**",
  468. "Build/**",
  469. "Source/**",
  470. "Specs/**",
  471. "ThirdParty/**",
  472. "*.md",
  473. "favicon.ico",
  474. "gulpfile.cjs",
  475. "index.html",
  476. "package.json",
  477. "server.cjs",
  478. "web.config",
  479. "*.zip",
  480. "*.tgz",
  481. ],
  482. {
  483. dot: true, // include hidden files
  484. }
  485. );
  486. })
  487. .then(function (files) {
  488. return Promise.map(
  489. files,
  490. function (file) {
  491. var blobName = uploadDirectory + "/" + file;
  492. var mimeLookup = getMimeType(blobName);
  493. var contentType = mimeLookup.type;
  494. var compress = mimeLookup.compress;
  495. var contentEncoding = compress ? "gzip" : undefined;
  496. var etag;
  497. totalFiles++;
  498. return readFile(file)
  499. .then(function (content) {
  500. if (!compress) {
  501. return content;
  502. }
  503. var alreadyCompressed =
  504. content[0] === 0x1f && content[1] === 0x8b;
  505. if (alreadyCompressed) {
  506. console.log(
  507. "Skipping compressing already compressed file: " + file
  508. );
  509. return content;
  510. }
  511. return gzip(content);
  512. })
  513. .then(function (content) {
  514. // compute hash and etag
  515. var hash = crypto.createHash("md5").update(content).digest("hex");
  516. etag = crypto.createHash("md5").update(content).digest("base64");
  517. var index = existingBlobs.indexOf(blobName);
  518. if (index <= -1) {
  519. return content;
  520. }
  521. // remove files as we find them on disk
  522. existingBlobs.splice(index, 1);
  523. // get file info
  524. return s3
  525. .headObject({
  526. Bucket: bucketName,
  527. Key: blobName,
  528. })
  529. .promise()
  530. .then(function (data) {
  531. if (
  532. data.ETag !== '"' + hash + '"' ||
  533. data.CacheControl !== cacheControl ||
  534. data.ContentType !== contentType ||
  535. data.ContentEncoding !== contentEncoding
  536. ) {
  537. return content;
  538. }
  539. // We don't need to upload this file again
  540. skipped++;
  541. return undefined;
  542. })
  543. .catch(function (error) {
  544. errors.push(error);
  545. });
  546. })
  547. .then(function (content) {
  548. if (!content) {
  549. return;
  550. }
  551. if (verbose) {
  552. console.log("Uploading " + blobName + "...");
  553. }
  554. var params = {
  555. Bucket: bucketName,
  556. Key: blobName,
  557. Body: content,
  558. ContentMD5: etag,
  559. ContentType: contentType,
  560. ContentEncoding: contentEncoding,
  561. CacheControl: cacheControl,
  562. };
  563. return s3
  564. .putObject(params)
  565. .promise()
  566. .then(function () {
  567. uploaded++;
  568. })
  569. .catch(function (error) {
  570. errors.push(error);
  571. });
  572. });
  573. },
  574. { concurrency: concurrencyLimit }
  575. );
  576. })
  577. .then(function () {
  578. console.log(
  579. "Skipped " +
  580. skipped +
  581. " files and successfully uploaded " +
  582. uploaded +
  583. " files of " +
  584. (totalFiles - skipped) +
  585. " files."
  586. );
  587. if (existingBlobs.length === 0) {
  588. return;
  589. }
  590. var objectsToDelete = [];
  591. existingBlobs.forEach(function (file) {
  592. //Don't delete generate zip files.
  593. if (!/\.(zip|tgz)$/.test(file)) {
  594. objectsToDelete.push({ Key: file });
  595. }
  596. });
  597. if (objectsToDelete.length > 0) {
  598. console.log("Cleaning " + objectsToDelete.length + " files...");
  599. // If more than 1000 files, we must issue multiple requests
  600. var batches = [];
  601. while (objectsToDelete.length > 1000) {
  602. batches.push(objectsToDelete.splice(0, 1000));
  603. }
  604. batches.push(objectsToDelete);
  605. return Promise.map(
  606. batches,
  607. function (objects) {
  608. return s3
  609. .deleteObjects({
  610. Bucket: bucketName,
  611. Delete: {
  612. Objects: objects,
  613. },
  614. })
  615. .promise()
  616. .then(function () {
  617. if (verbose) {
  618. console.log("Cleaned " + objects.length + " files.");
  619. }
  620. });
  621. },
  622. { concurrency: concurrency }
  623. );
  624. }
  625. })
  626. .catch(function (error) {
  627. errors.push(error);
  628. })
  629. .then(function () {
  630. if (errors.length === 0) {
  631. done();
  632. return;
  633. }
  634. console.log("Errors: ");
  635. errors.map(function (e) {
  636. console.log(e);
  637. });
  638. done(1);
  639. });
  640. }
  641. function getMimeType(filename) {
  642. var mimeType = mime.getType(filename);
  643. if (mimeType) {
  644. //Compress everything except zipfiles, binary images, and video
  645. var compress = !/^(image\/|video\/|application\/zip|application\/gzip)/i.test(
  646. mimeType
  647. );
  648. if (mimeType === "image/svg+xml") {
  649. compress = true;
  650. }
  651. return { type: mimeType, compress: compress };
  652. }
  653. //Non-standard mime types not handled by mime
  654. if (/\.(glsl|LICENSE|config|state)$/i.test(filename)) {
  655. return { type: "text/plain", compress: true };
  656. } else if (/\.(czml|topojson)$/i.test(filename)) {
  657. return { type: "application/json", compress: true };
  658. } else if (/\.(crn|tgz)$/i.test(filename)) {
  659. return { type: "application/octet-stream", compress: false };
  660. }
  661. // Handle dotfiles, such as .jshintrc
  662. var baseName = path.basename(filename);
  663. if (baseName[0] === "." || baseName.indexOf(".") === -1) {
  664. return { type: "text/plain", compress: true };
  665. }
  666. // Everything else can be octet-stream compressed but print a warning
  667. // if we introduce a type we aren't specifically handling.
  668. if (!/\.(terrain|b3dm|geom|pnts|vctr|cmpt|i3dm|metadata)$/i.test(filename)) {
  669. console.log("Unknown mime type for " + filename);
  670. }
  671. return { type: "application/octet-stream", compress: true };
  672. }
  673. // get all files currently in bucket asynchronously
  674. function listAll(s3, bucketName, prefix, files, marker) {
  675. return s3
  676. .listObjects({
  677. Bucket: bucketName,
  678. MaxKeys: 1000,
  679. Prefix: prefix,
  680. Marker: marker,
  681. })
  682. .promise()
  683. .then(function (data) {
  684. var items = data.Contents;
  685. for (var i = 0; i < items.length; i++) {
  686. files.push(items[i].Key);
  687. }
  688. if (data.IsTruncated) {
  689. // get next page of results
  690. return listAll(s3, bucketName, prefix, files, files[files.length - 1]);
  691. }
  692. });
  693. }
  694. gulp.task("deploy-set-version", function (done) {
  695. var buildVersion = yargs.argv.buildVersion;
  696. if (buildVersion) {
  697. // NPM versions can only contain alphanumeric and hyphen characters
  698. packageJson.version += "-" + buildVersion.replace(/[^[0-9A-Za-z-]/g, "");
  699. fs.writeFileSync("package.json", JSON.stringify(packageJson, undefined, 2));
  700. }
  701. done();
  702. });
  703. gulp.task("deploy-status", function () {
  704. if (isTravisPullRequest()) {
  705. console.log("Skipping deployment status for non-pull request.");
  706. return Promise.resolve();
  707. }
  708. var status = yargs.argv.status;
  709. var message = yargs.argv.message;
  710. var deployUrl = travisDeployUrl + process.env.TRAVIS_BRANCH + "/";
  711. var zipUrl = deployUrl + "Cesium-" + packageJson.version + ".zip";
  712. var npmUrl = deployUrl + "cesium-" + packageJson.version + ".tgz";
  713. var coverageUrl =
  714. travisDeployUrl + process.env.TRAVIS_BRANCH + "/Build/Coverage/index.html";
  715. return Promise.join(
  716. setStatus(status, deployUrl, message, "deployment"),
  717. setStatus(status, zipUrl, message, "zip file"),
  718. setStatus(status, npmUrl, message, "npm package"),
  719. setStatus(status, coverageUrl, message, "coverage results")
  720. );
  721. });
  722. function setStatus(state, targetUrl, description, context) {
  723. // skip if the environment does not have the token
  724. if (!process.env.TOKEN) {
  725. return;
  726. }
  727. var requestPost = Promise.promisify(request.post);
  728. return requestPost({
  729. url:
  730. "https://api.github.com/repos/" +
  731. process.env.TRAVIS_REPO_SLUG +
  732. "/statuses/" +
  733. process.env.TRAVIS_COMMIT,
  734. json: true,
  735. headers: {
  736. Authorization: "token " + process.env.TOKEN,
  737. "User-Agent": "Cesium",
  738. },
  739. body: {
  740. state: state,
  741. target_url: targetUrl,
  742. description: description,
  743. context: context,
  744. },
  745. });
  746. }
  747. gulp.task("coverage", function (done) {
  748. var argv = yargs.argv;
  749. var webglStub = argv.webglStub ? argv.webglStub : false;
  750. var suppressPassed = argv.suppressPassed ? argv.suppressPassed : false;
  751. var failTaskOnError = argv.failTaskOnError ? argv.failTaskOnError : false;
  752. var folders = [];
  753. var browsers = ["Chrome"];
  754. if (argv.browsers) {
  755. browsers = argv.browsers.split(",");
  756. }
  757. var karma = new Karma.Server(
  758. {
  759. configFile: karmaConfigFile,
  760. browsers: browsers,
  761. specReporter: {
  762. suppressErrorSummary: false,
  763. suppressFailed: false,
  764. suppressPassed: suppressPassed,
  765. suppressSkipped: true,
  766. },
  767. preprocessors: {
  768. "Source/Core/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  769. "Source/DataSources/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  770. "Source/Renderer/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  771. "Source/Scene/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  772. "Source/Shaders/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  773. "Source/Widgets/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  774. "Source/Workers/**/*.js": ["karma-coverage-istanbul-instrumenter"],
  775. },
  776. coverageIstanbulInstrumenter: {
  777. esModules: true,
  778. },
  779. reporters: ["spec", "coverage"],
  780. coverageReporter: {
  781. dir: "Build/Coverage",
  782. subdir: function (browserName) {
  783. folders.push(browserName);
  784. return browserName;
  785. },
  786. includeAllSources: true,
  787. },
  788. client: {
  789. captureConsole: verbose,
  790. args: [undefined, undefined, undefined, webglStub, undefined],
  791. },
  792. },
  793. function (e) {
  794. var html = "<!doctype html><html><body><ul>";
  795. folders.forEach(function (folder) {
  796. html +=
  797. '<li><a href="' +
  798. encodeURIComponent(folder) +
  799. '/index.html">' +
  800. folder +
  801. "</a></li>";
  802. });
  803. html += "</ul></body></html>";
  804. fs.writeFileSync("Build/Coverage/index.html", html);
  805. if (!process.env.TRAVIS) {
  806. folders.forEach(function (dir) {
  807. open("Build/Coverage/" + dir + "/index.html");
  808. });
  809. }
  810. return done(failTaskOnError ? e : undefined);
  811. }
  812. );
  813. karma.start();
  814. });
  815. gulp.task("test", function (done) {
  816. var argv = yargs.argv;
  817. var enableAllBrowsers = argv.all ? true : false;
  818. var includeCategory = argv.include ? argv.include : "";
  819. var excludeCategory = argv.exclude ? argv.exclude : "";
  820. var webglValidation = argv.webglValidation ? argv.webglValidation : false;
  821. var webglStub = argv.webglStub ? argv.webglStub : false;
  822. var release = argv.release ? argv.release : false;
  823. var failTaskOnError = argv.failTaskOnError ? argv.failTaskOnError : false;
  824. var suppressPassed = argv.suppressPassed ? argv.suppressPassed : false;
  825. var browsers = ["Chrome"];
  826. if (argv.browsers) {
  827. browsers = argv.browsers.split(",");
  828. }
  829. var files = [
  830. { pattern: "Specs/karma-main.js", included: true, type: "module" },
  831. { pattern: "Source/**", included: false, type: "module" },
  832. { pattern: "Specs/*.js", included: true, type: "module" },
  833. { pattern: "Specs/Core/**", included: true, type: "module" },
  834. { pattern: "Specs/Data/**", included: false },
  835. { pattern: "Specs/DataSources/**", included: true, type: "module" },
  836. { pattern: "Specs/Renderer/**", included: true, type: "module" },
  837. { pattern: "Specs/Scene/**", included: true, type: "module" },
  838. { pattern: "Specs/ThirdParty/**", included: true, type: "module" },
  839. { pattern: "Specs/Widgets/**", included: true, type: "module" },
  840. { pattern: "Specs/TestWorkers/**", included: false },
  841. ];
  842. if (release) {
  843. files = [
  844. { pattern: "Specs/Data/**", included: false },
  845. { pattern: "Specs/ThirdParty/**", included: true, type: "module" },
  846. { pattern: "Specs/TestWorkers/**", included: false },
  847. { pattern: "Build/Cesium/Cesium.js", included: true },
  848. { pattern: "Build/Cesium/**", included: false },
  849. { pattern: "Build/Specs/karma-main.js", included: true },
  850. { pattern: "Build/Specs/Specs.js", included: true },
  851. ];
  852. }
  853. var karma = new Karma.Server(
  854. {
  855. configFile: karmaConfigFile,
  856. browsers: browsers,
  857. specReporter: {
  858. suppressErrorSummary: false,
  859. suppressFailed: false,
  860. suppressPassed: suppressPassed,
  861. suppressSkipped: true,
  862. },
  863. detectBrowsers: {
  864. enabled: enableAllBrowsers,
  865. },
  866. logLevel: verbose ? Karma.constants.LOG_INFO : Karma.constants.LOG_ERROR,
  867. files: files,
  868. client: {
  869. captureConsole: verbose,
  870. args: [
  871. includeCategory,
  872. excludeCategory,
  873. webglValidation,
  874. webglStub,
  875. release,
  876. ],
  877. },
  878. },
  879. function (e) {
  880. return done(failTaskOnError ? e : undefined);
  881. }
  882. );
  883. karma.start();
  884. });
  885. gulp.task("convertToModules", function () {
  886. var requiresRegex = /([\s\S]*?(define|defineSuite|require)\((?:{[\s\S]*}, )?\[)([\S\s]*?)]([\s\S]*?function\s*)\(([\S\s]*?)\) {([\s\S]*)/;
  887. var noModulesRegex = /([\s\S]*?(define|defineSuite|require)\((?:{[\s\S]*}, )?\[?)([\S\s]*?)]?([\s\S]*?function\s*)\(([\S\s]*?)\) {([\s\S]*)/;
  888. var splitRegex = /,\s*/;
  889. var fsReadFile = Promise.promisify(fs.readFile);
  890. var fsWriteFile = Promise.promisify(fs.writeFile);
  891. var files = globby.sync(filesToConvertES6);
  892. return Promise.map(files, function (file) {
  893. return fsReadFile(file).then(function (contents) {
  894. contents = contents.toString();
  895. if (contents.startsWith("import")) {
  896. return;
  897. }
  898. var result = requiresRegex.exec(contents);
  899. if (result === null) {
  900. result = noModulesRegex.exec(contents);
  901. if (result === null) {
  902. return;
  903. }
  904. }
  905. var names = result[3].split(splitRegex);
  906. if (names.length === 1 && names[0].trim() === "") {
  907. names.length = 0;
  908. }
  909. var i;
  910. for (i = 0; i < names.length; ++i) {
  911. if (names[i].indexOf("//") >= 0 || names[i].indexOf("/*") >= 0) {
  912. console.log(
  913. file +
  914. " contains comments in the require list. Skipping so nothing gets broken."
  915. );
  916. return;
  917. }
  918. }
  919. var identifiers = result[5].split(splitRegex);
  920. if (identifiers.length === 1 && identifiers[0].trim() === "") {
  921. identifiers.length = 0;
  922. }
  923. for (i = 0; i < identifiers.length; ++i) {
  924. if (
  925. identifiers[i].indexOf("//") >= 0 ||
  926. identifiers[i].indexOf("/*") >= 0
  927. ) {
  928. console.log(
  929. file +
  930. " contains comments in the require list. Skipping so nothing gets broken."
  931. );
  932. return;
  933. }
  934. }
  935. var requires = [];
  936. for (i = 0; i < names.length && i < identifiers.length; ++i) {
  937. requires.push({
  938. name: names[i].trim(),
  939. identifier: identifiers[i].trim(),
  940. });
  941. }
  942. // Convert back to separate lists for the names and identifiers, and add
  943. // any additional names or identifiers that don't have a corresponding pair.
  944. var sortedNames = requires.map(function (item) {
  945. return item.name.slice(0, -1) + ".js'";
  946. });
  947. for (i = sortedNames.length; i < names.length; ++i) {
  948. sortedNames.push(names[i].trim());
  949. }
  950. var sortedIdentifiers = requires.map(function (item) {
  951. return item.identifier;
  952. });
  953. for (i = sortedIdentifiers.length; i < identifiers.length; ++i) {
  954. sortedIdentifiers.push(identifiers[i].trim());
  955. }
  956. contents = "";
  957. if (sortedNames.length > 0) {
  958. for (var q = 0; q < sortedNames.length; q++) {
  959. var modulePath = sortedNames[q];
  960. if (file.startsWith("Specs")) {
  961. modulePath = modulePath.substring(1, modulePath.length - 1);
  962. var sourceDir = path.dirname(file);
  963. if (modulePath.startsWith("Specs") || modulePath.startsWith(".")) {
  964. var importPath = modulePath;
  965. if (modulePath.startsWith("Specs")) {
  966. importPath = path.relative(sourceDir, modulePath);
  967. if (importPath[0] !== ".") {
  968. importPath = "./" + importPath;
  969. }
  970. }
  971. modulePath = "'" + importPath + "'";
  972. contents +=
  973. "import " +
  974. sortedIdentifiers[q] +
  975. " from " +
  976. modulePath +
  977. ";" +
  978. os.EOL;
  979. } else {
  980. modulePath =
  981. "'" + path.relative(sourceDir, "Source") + "/Cesium.js" + "'";
  982. if (sortedIdentifiers[q] === "CesiumMath") {
  983. contents +=
  984. "import { Math as CesiumMath } from " +
  985. modulePath +
  986. ";" +
  987. os.EOL;
  988. } else {
  989. contents +=
  990. "import { " +
  991. sortedIdentifiers[q] +
  992. " } from " +
  993. modulePath +
  994. ";" +
  995. os.EOL;
  996. }
  997. }
  998. } else {
  999. contents +=
  1000. "import " +
  1001. sortedIdentifiers[q] +
  1002. " from " +
  1003. modulePath +
  1004. ";" +
  1005. os.EOL;
  1006. }
  1007. }
  1008. }
  1009. var code;
  1010. var codeAndReturn = result[6];
  1011. if (file.endsWith("Spec.js")) {
  1012. var indi = codeAndReturn.lastIndexOf("});");
  1013. code = codeAndReturn.slice(0, indi);
  1014. code = code.trim().replace("'use strict';" + os.EOL, "");
  1015. contents += code + os.EOL;
  1016. } else {
  1017. var returnIndex = codeAndReturn.lastIndexOf("return");
  1018. code = codeAndReturn.slice(0, returnIndex);
  1019. code = code.trim().replace("'use strict';" + os.EOL, "");
  1020. contents += code + os.EOL;
  1021. var returnStatement = codeAndReturn.slice(returnIndex);
  1022. contents +=
  1023. returnStatement.split(";")[0].replace("return ", "export default ") +
  1024. ";" +
  1025. os.EOL;
  1026. }
  1027. return fsWriteFile(file, contents);
  1028. });
  1029. });
  1030. });
  1031. function combineCesium(debug, optimizer, combineOutput) {
  1032. var plugins = [];
  1033. if (!debug) {
  1034. plugins.push(
  1035. rollupPluginStripPragma({
  1036. pragmas: ["debug"],
  1037. })
  1038. );
  1039. }
  1040. if (optimizer === "uglify2") {
  1041. plugins.push(rollupPluginUglify.uglify());
  1042. }
  1043. return rollup
  1044. .rollup({
  1045. input: "Source/Cesium.js",
  1046. plugins: plugins,
  1047. onwarn: rollupWarning,
  1048. })
  1049. .then(function (bundle) {
  1050. return bundle.write({
  1051. format: "umd",
  1052. name: "Cesium",
  1053. file: path.join(combineOutput, "Cesium.js"),
  1054. sourcemap: debug,
  1055. banner: copyrightHeader,
  1056. });
  1057. });
  1058. }
  1059. function combineWorkers(debug, optimizer, combineOutput) {
  1060. //This is done waterfall style for concurrency reasons.
  1061. // Copy files that are already minified
  1062. return globby(["Source/ThirdParty/Workers/draco*.js"])
  1063. .then(function (files) {
  1064. var stream = gulp
  1065. .src(files, { base: "Source" })
  1066. .pipe(gulp.dest(combineOutput));
  1067. return streamToPromise(stream);
  1068. })
  1069. .then(function () {
  1070. return globby([
  1071. "Source/Workers/cesiumWorkerBootstrapper.js",
  1072. "Source/Workers/transferTypedArrayTest.js",
  1073. "Source/ThirdParty/Workers/*.js",
  1074. // Files are already minified, don't optimize
  1075. "!Source/ThirdParty/Workers/draco*.js",
  1076. ]);
  1077. })
  1078. .then(function (files) {
  1079. return Promise.map(
  1080. files,
  1081. function (file) {
  1082. return streamToPromise(
  1083. gulp
  1084. .src(file)
  1085. .pipe(gulpUglify())
  1086. .pipe(
  1087. gulp.dest(
  1088. path.dirname(
  1089. path.join(combineOutput, path.relative("Source", file))
  1090. )
  1091. )
  1092. )
  1093. );
  1094. },
  1095. { concurrency: concurrency }
  1096. );
  1097. })
  1098. .then(function () {
  1099. return globby(["Source/WorkersES6/*.js"]);
  1100. })
  1101. .then(function (files) {
  1102. var plugins = [];
  1103. if (!debug) {
  1104. plugins.push(
  1105. rollupPluginStripPragma({
  1106. pragmas: ["debug"],
  1107. })
  1108. );
  1109. }
  1110. if (optimizer === "uglify2") {
  1111. plugins.push(rollupPluginUglify.uglify());
  1112. }
  1113. return rollup
  1114. .rollup({
  1115. input: files,
  1116. plugins: plugins,
  1117. onwarn: rollupWarning,
  1118. })
  1119. .then(function (bundle) {
  1120. return bundle.write({
  1121. dir: path.join(combineOutput, "Workers"),
  1122. format: "amd",
  1123. sourcemap: debug,
  1124. banner: copyrightHeader,
  1125. });
  1126. });
  1127. });
  1128. }
  1129. function minifyCSS(outputDirectory) {
  1130. streamToPromise(
  1131. gulp
  1132. .src("Source/**/*.css")
  1133. .pipe(cleanCSS())
  1134. .pipe(gulp.dest(outputDirectory))
  1135. );
  1136. }
  1137. function minifyModules(outputDirectory) {
  1138. return streamToPromise(
  1139. gulp
  1140. .src("Source/ThirdParty/google-earth-dbroot-parser.js")
  1141. .pipe(gulpUglify())
  1142. .pipe(gulp.dest(outputDirectory + "/ThirdParty/"))
  1143. );
  1144. }
  1145. function combineJavaScript(options) {
  1146. var optimizer = options.optimizer;
  1147. var outputDirectory = options.outputDirectory;
  1148. var removePragmas = options.removePragmas;
  1149. var combineOutput = path.join("Build", "combineOutput", optimizer);
  1150. var promise = Promise.join(
  1151. combineCesium(!removePragmas, optimizer, combineOutput),
  1152. combineWorkers(!removePragmas, optimizer, combineOutput),
  1153. minifyModules(outputDirectory)
  1154. );
  1155. return promise.then(function () {
  1156. var promises = [];
  1157. //copy to build folder with copyright header added at the top
  1158. var stream = gulp
  1159. .src([combineOutput + "/**"])
  1160. .pipe(gulp.dest(outputDirectory));
  1161. promises.push(streamToPromise(stream));
  1162. var everythingElse = ["Source/**", "!**/*.js", "!**/*.glsl"];
  1163. if (optimizer === "uglify2") {
  1164. promises.push(minifyCSS(outputDirectory));
  1165. everythingElse.push("!**/*.css");
  1166. }
  1167. stream = gulp
  1168. .src(everythingElse, { nodir: true })
  1169. .pipe(gulp.dest(outputDirectory));
  1170. promises.push(streamToPromise(stream));
  1171. return Promise.all(promises).then(function () {
  1172. rimraf.sync(combineOutput);
  1173. });
  1174. });
  1175. }
  1176. function glslToJavaScript(minify, minifyStateFilePath) {
  1177. fs.writeFileSync(minifyStateFilePath, minify.toString());
  1178. var minifyStateFileLastModified = fs.existsSync(minifyStateFilePath)
  1179. ? fs.statSync(minifyStateFilePath).mtime.getTime()
  1180. : 0;
  1181. // collect all currently existing JS files into a set, later we will remove the ones
  1182. // we still are using from the set, then delete any files remaining in the set.
  1183. var leftOverJsFiles = {};
  1184. globby
  1185. .sync(["Source/Shaders/**/*.js", "Source/ThirdParty/Shaders/*.js"])
  1186. .forEach(function (file) {
  1187. leftOverJsFiles[path.normalize(file)] = true;
  1188. });
  1189. var builtinFunctions = [];
  1190. var builtinConstants = [];
  1191. var builtinStructs = [];
  1192. var glslFiles = globby.sync([
  1193. "Source/Shaders/**/*.glsl",
  1194. "Source/ThirdParty/Shaders/*.glsl",
  1195. ]);
  1196. glslFiles.forEach(function (glslFile) {
  1197. glslFile = path.normalize(glslFile);
  1198. var baseName = path.basename(glslFile, ".glsl");
  1199. var jsFile = path.join(path.dirname(glslFile), baseName) + ".js";
  1200. // identify built in functions, structs, and constants
  1201. var baseDir = path.join("Source", "Shaders", "Builtin");
  1202. if (
  1203. glslFile.indexOf(path.normalize(path.join(baseDir, "Functions"))) === 0
  1204. ) {
  1205. builtinFunctions.push(baseName);
  1206. } else if (
  1207. glslFile.indexOf(path.normalize(path.join(baseDir, "Constants"))) === 0
  1208. ) {
  1209. builtinConstants.push(baseName);
  1210. } else if (
  1211. glslFile.indexOf(path.normalize(path.join(baseDir, "Structs"))) === 0
  1212. ) {
  1213. builtinStructs.push(baseName);
  1214. }
  1215. delete leftOverJsFiles[jsFile];
  1216. var jsFileExists = fs.existsSync(jsFile);
  1217. var jsFileModified = jsFileExists ? fs.statSync(jsFile).mtime.getTime() : 0;
  1218. var glslFileModified = fs.statSync(glslFile).mtime.getTime();
  1219. if (
  1220. jsFileExists &&
  1221. jsFileModified > glslFileModified &&
  1222. jsFileModified > minifyStateFileLastModified
  1223. ) {
  1224. return;
  1225. }
  1226. var contents = fs.readFileSync(glslFile, "utf8");
  1227. contents = contents.replace(/\r\n/gm, "\n");
  1228. var copyrightComments = "";
  1229. var extractedCopyrightComments = contents.match(
  1230. /\/\*\*(?:[^*\/]|\*(?!\/)|\n)*?@license(?:.|\n)*?\*\//gm
  1231. );
  1232. if (extractedCopyrightComments) {
  1233. copyrightComments = extractedCopyrightComments.join("\n") + "\n";
  1234. }
  1235. if (minify) {
  1236. contents = glslStripComments(contents);
  1237. contents = contents
  1238. .replace(/\s+$/gm, "")
  1239. .replace(/^\s+/gm, "")
  1240. .replace(/\n+/gm, "\n");
  1241. contents += "\n";
  1242. }
  1243. contents = contents.split('"').join('\\"').replace(/\n/gm, "\\n\\\n");
  1244. contents =
  1245. copyrightComments +
  1246. '\
  1247. //This file is automatically rebuilt by the Cesium build process.\n\
  1248. export default "' +
  1249. contents +
  1250. '";\n';
  1251. fs.writeFileSync(jsFile, contents);
  1252. });
  1253. // delete any left over JS files from old shaders
  1254. Object.keys(leftOverJsFiles).forEach(function (filepath) {
  1255. rimraf.sync(filepath);
  1256. });
  1257. var generateBuiltinContents = function (contents, builtins, path) {
  1258. for (var i = 0; i < builtins.length; i++) {
  1259. var builtin = builtins[i];
  1260. contents.imports.push(
  1261. "import czm_" + builtin + " from './" + path + "/" + builtin + ".js'"
  1262. );
  1263. contents.builtinLookup.push("czm_" + builtin + " : " + "czm_" + builtin);
  1264. }
  1265. };
  1266. //generate the JS file for Built-in GLSL Functions, Structs, and Constants
  1267. var contents = {
  1268. imports: [],
  1269. builtinLookup: [],
  1270. };
  1271. generateBuiltinContents(contents, builtinConstants, "Constants");
  1272. generateBuiltinContents(contents, builtinStructs, "Structs");
  1273. generateBuiltinContents(contents, builtinFunctions, "Functions");
  1274. var fileContents =
  1275. "//This file is automatically rebuilt by the Cesium build process.\n" +
  1276. contents.imports.join("\n") +
  1277. "\n\nexport default {\n " +
  1278. contents.builtinLookup.join(",\n ") +
  1279. "\n};\n";
  1280. fs.writeFileSync(
  1281. path.join("Source", "Shaders", "Builtin", "CzmBuiltins.js"),
  1282. fileContents
  1283. );
  1284. }
  1285. function createCesiumJs() {
  1286. var contents = `export var VERSION = '${version}';\n`;
  1287. globby.sync(sourceFiles).forEach(function (file) {
  1288. file = path.relative("Source", file);
  1289. var moduleId = file;
  1290. moduleId = filePathToModuleId(moduleId);
  1291. var assignmentName = path.basename(file, path.extname(file));
  1292. if (moduleId.indexOf("Shaders/") === 0) {
  1293. assignmentName = "_shaders" + assignmentName;
  1294. }
  1295. assignmentName = assignmentName.replace(/(\.|-)/g, "_");
  1296. contents +=
  1297. "export { default as " +
  1298. assignmentName +
  1299. " } from './" +
  1300. moduleId +
  1301. ".js';" +
  1302. os.EOL;
  1303. });
  1304. fs.writeFileSync("Source/Cesium.js", contents);
  1305. }
  1306. function createTypeScriptDefinitions() {
  1307. // Run jsdoc with tsd-jsdoc to generate an initial Cesium.d.ts file.
  1308. child_process.execSync("npx jsdoc --configure Tools/jsdoc/ts-conf.json", {
  1309. stdio: "inherit",
  1310. });
  1311. var source = fs.readFileSync("Source/Cesium.d.ts").toString();
  1312. // All of our enum assignments that alias to WebGLConstants, such as PixelDatatype.js
  1313. // end up as enum strings instead of actually mapping values to WebGLConstants.
  1314. // We fix this with a simple regex replace later on, but it means the
  1315. // WebGLConstants constants enum needs to be defined in the file before it can
  1316. // be used. This block of code reads in the TS file, finds the WebGLConstants
  1317. // declaration, and then writes the file back out (in memory to source) with
  1318. // WebGLConstants being the first module.
  1319. const node = typescript.createSourceFile(
  1320. "Source/Cesium.d.ts",
  1321. source,
  1322. typescript.ScriptTarget.Latest
  1323. );
  1324. let firstNode;
  1325. node.forEachChild((child) => {
  1326. if (
  1327. typescript.SyntaxKind[child.kind] === "EnumDeclaration" &&
  1328. child.name.escapedText === "WebGLConstants"
  1329. ) {
  1330. firstNode = child;
  1331. }
  1332. });
  1333. const printer = typescript.createPrinter({
  1334. removeComments: false,
  1335. newLine: typescript.NewLineKind.LineFeed,
  1336. });
  1337. let newSource = "";
  1338. newSource += printer.printNode(
  1339. typescript.EmitHint.Unspecified,
  1340. firstNode,
  1341. node
  1342. );
  1343. node.forEachChild((child) => {
  1344. if (
  1345. typescript.SyntaxKind[child.kind] !== "EnumDeclaration" ||
  1346. child.name.escapedText !== "WebGLConstants"
  1347. ) {
  1348. newSource += printer.printNode(
  1349. typescript.EmitHint.Unspecified,
  1350. child,
  1351. node
  1352. );
  1353. newSource += "\n\n";
  1354. }
  1355. });
  1356. source = newSource;
  1357. // The next step is to find the list of Cesium modules exported by the Cesium API
  1358. // So that we can map these modules with a link back to their original source file.
  1359. var regex = /^declare (function|class|namespace|enum) (.+)/gm;
  1360. var matches;
  1361. var publicModules = new Set();
  1362. //eslint-disable-next-line no-cond-assign
  1363. while ((matches = regex.exec(source))) {
  1364. const moduleName = matches[2].match(/([^\s|\(]+)/);
  1365. publicModules.add(moduleName[1]);
  1366. }
  1367. // Math shows up as "Math" because of it's aliasing from CesiumMath and namespace collision with actual Math
  1368. // It fails the above regex so just add it directly here.
  1369. publicModules.add("Math");
  1370. // Fix up the output to match what we need
  1371. // declare => export since we are wrapping everything in a namespace
  1372. // CesiumMath => Math (because no CesiumJS build step would be complete without special logic for the Math class)
  1373. // Fix up the WebGLConstants aliasing we mentioned above by simply unquoting the strings.
  1374. source = source
  1375. .replace(/^declare /gm, "export ")
  1376. .replace(/module "Math"/gm, "namespace Math")
  1377. .replace(/CesiumMath/gm, "Math")
  1378. .replace(/Number\[]/gm, "number[]") // Workaround https://github.com/englercj/tsd-jsdoc/issues/117
  1379. .replace(/String\[]/gm, "string[]")
  1380. .replace(/Boolean\[]/gm, "boolean[]")
  1381. .replace(/Object\[]/gm, "object[]")
  1382. .replace(/<Number>/gm, "<number>")
  1383. .replace(/<String>/gm, "<string>")
  1384. .replace(/<Boolean>/gm, "<boolean>")
  1385. .replace(/<Object>/gm, "<object>")
  1386. .replace(
  1387. /= "WebGLConstants\.(.+)"/gm,
  1388. (match, p1) => `= WebGLConstants.${p1}`
  1389. );
  1390. // Wrap the source to actually be inside of a declared cesium module
  1391. // and add any workaround and private utility types.
  1392. source = `declare module "cesium" {
  1393. /**
  1394. * Private interfaces to support PropertyBag being a dictionary-like object.
  1395. */
  1396. interface DictionaryLike {
  1397. [index: string]: any;
  1398. }
  1399. ${source}
  1400. }
  1401. `;
  1402. // Map individual modules back to their source file so that TS still works
  1403. // when importing individual files instead of the entire cesium module.
  1404. globby.sync(sourceFiles).forEach(function (file) {
  1405. file = path.relative("Source", file);
  1406. var moduleId = file;
  1407. moduleId = filePathToModuleId(moduleId);
  1408. var assignmentName = path.basename(file, path.extname(file));
  1409. if (publicModules.has(assignmentName)) {
  1410. publicModules.delete(assignmentName);
  1411. source += `declare module "cesium/Source/${moduleId}" { import { ${assignmentName} } from 'cesium'; export default ${assignmentName}; }\n`;
  1412. }
  1413. });
  1414. // Write the final source file back out
  1415. fs.writeFileSync("Source/Cesium.d.ts", source);
  1416. // Use tsc to compile it and make sure it is valid
  1417. child_process.execSync("npx tsc -p Tools/jsdoc/tsconfig.json", {
  1418. stdio: "inherit",
  1419. });
  1420. // Also compile our smokescreen to make sure interfaces work as expected.
  1421. child_process.execSync("npx tsc -p Specs/TypeScript/tsconfig.json", {
  1422. stdio: "inherit",
  1423. });
  1424. // Below is a sanity check to make sure we didn't leave anything out that
  1425. // we don't already know about
  1426. // Intentionally ignored nested items
  1427. publicModules.delete("KmlFeatureData");
  1428. publicModules.delete("MaterialAppearance");
  1429. if (publicModules.size !== 0) {
  1430. throw new Error(
  1431. "Unexpected unexposed modules: " +
  1432. Array.from(publicModules.values()).join(", ")
  1433. );
  1434. }
  1435. }
  1436. function createSpecList() {
  1437. var specFiles = globby.sync(["Specs/**/*Spec.js"]);
  1438. var contents = "";
  1439. specFiles.forEach(function (file) {
  1440. contents +=
  1441. "import './" + filePathToModuleId(file).replace("Specs/", "") + ".js';\n";
  1442. });
  1443. fs.writeFileSync(path.join("Specs", "SpecList.js"), contents);
  1444. }
  1445. function createGalleryList() {
  1446. var demoObjects = [];
  1447. var demoJSONs = [];
  1448. var output = path.join("Apps", "Sandcastle", "gallery", "gallery-index.js");
  1449. var fileList = ["Apps/Sandcastle/gallery/**/*.html"];
  1450. if (noDevelopmentGallery) {
  1451. fileList.push("!Apps/Sandcastle/gallery/development/**/*.html");
  1452. }
  1453. // On travis, the version is set to something like '1.43.0-branch-name-travisBuildNumber'
  1454. // We need to extract just the Major.Minor version
  1455. var majorMinor = packageJson.version.match(/^(.*)\.(.*)\./);
  1456. var major = majorMinor[1];
  1457. var minor = Number(majorMinor[2]) - 1; // We want the last release, not current release
  1458. var tagVersion = major + "." + minor;
  1459. // Get an array of demos that were added since the last release.
  1460. // This includes newly staged local demos as well.
  1461. var newDemos = [];
  1462. try {
  1463. newDemos = child_process
  1464. .execSync(
  1465. "git diff --name-only --diff-filter=A " +
  1466. tagVersion +
  1467. " Apps/Sandcastle/gallery/*.html",
  1468. { stdio: ["pipe", "pipe", "ignore"] }
  1469. )
  1470. .toString()
  1471. .trim()
  1472. .split("\n");
  1473. } catch (e) {
  1474. // On a Cesium fork, tags don't exist so we can't generate the list.
  1475. }
  1476. var helloWorld;
  1477. globby.sync(fileList).forEach(function (file) {
  1478. var demo = filePathToModuleId(
  1479. path.relative("Apps/Sandcastle/gallery", file)
  1480. );
  1481. var demoObject = {
  1482. name: demo,
  1483. isNew: newDemos.includes(file),
  1484. };
  1485. if (fs.existsSync(file.replace(".html", "") + ".jpg")) {
  1486. demoObject.img = demo + ".jpg";
  1487. }
  1488. demoObjects.push(demoObject);
  1489. if (demo === "Hello World") {
  1490. helloWorld = demoObject;
  1491. }
  1492. });
  1493. demoObjects.sort(function (a, b) {
  1494. if (a.name < b.name) {
  1495. return -1;
  1496. } else if (a.name > b.name) {
  1497. return 1;
  1498. }
  1499. return 0;
  1500. });
  1501. var helloWorldIndex = Math.max(demoObjects.indexOf(helloWorld), 0);
  1502. var i;
  1503. for (i = 0; i < demoObjects.length; ++i) {
  1504. demoJSONs[i] = JSON.stringify(demoObjects[i], null, 2);
  1505. }
  1506. var contents =
  1507. "\
  1508. // This file is automatically rebuilt by the Cesium build process.\n\
  1509. var hello_world_index = " +
  1510. helloWorldIndex +
  1511. ";\n\
  1512. var VERSION = '" +
  1513. version +
  1514. "';\n\
  1515. var gallery_demos = [" +
  1516. demoJSONs.join(", ") +
  1517. "];\n\
  1518. var has_new_gallery_demos = " +
  1519. (newDemos.length > 0 ? "true;" : "false;") +
  1520. "\n";
  1521. fs.writeFileSync(output, contents);
  1522. // Compile CSS for Sandcastle
  1523. return streamToPromise(
  1524. gulp
  1525. .src(path.join("Apps", "Sandcastle", "templates", "bucketRaw.css"))
  1526. .pipe(cleanCSS())
  1527. .pipe(gulpRename("bucket.css"))
  1528. .pipe(
  1529. gulpInsert.prepend(
  1530. "/* This file is automatically rebuilt by the Cesium build process. */\n"
  1531. )
  1532. )
  1533. .pipe(gulp.dest(path.join("Apps", "Sandcastle", "templates")))
  1534. );
  1535. }
  1536. function createJsHintOptions() {
  1537. var primary = JSON.parse(
  1538. fs.readFileSync(path.join("Apps", ".jshintrc"), "utf8")
  1539. );
  1540. var gallery = JSON.parse(
  1541. fs.readFileSync(path.join("Apps", "Sandcastle", ".jshintrc"), "utf8")
  1542. );
  1543. primary.jasmine = false;
  1544. primary.predef = gallery.predef;
  1545. primary.unused = gallery.unused;
  1546. primary.esversion = gallery.esversion;
  1547. var contents =
  1548. "\
  1549. // This file is automatically rebuilt by the Cesium build process.\n\
  1550. var sandcastleJsHintOptions = " +
  1551. JSON.stringify(primary, null, 4) +
  1552. ";\n";
  1553. fs.writeFileSync(
  1554. path.join("Apps", "Sandcastle", "jsHintOptions.js"),
  1555. contents
  1556. );
  1557. }
  1558. function buildSandcastle() {
  1559. var appStream = gulp
  1560. .src([
  1561. "Apps/Sandcastle/**",
  1562. "!Apps/Sandcastle/load-cesium-es6.js",
  1563. "!Apps/Sandcastle/standalone.html",
  1564. "!Apps/Sandcastle/images/**",
  1565. "!Apps/Sandcastle/gallery/**.jpg",
  1566. ])
  1567. // Remove dev-only ES6 module loading for unbuilt Cesium
  1568. .pipe(
  1569. gulpReplace(
  1570. ' <script type="module" src="../load-cesium-es6.js"></script>',
  1571. ""
  1572. )
  1573. )
  1574. .pipe(gulpReplace("nomodule", ""))
  1575. // Fix relative paths for new location
  1576. .pipe(gulpReplace("../../../Build", "../../.."))
  1577. .pipe(gulpReplace("../../Source", "../../../Source"))
  1578. .pipe(gulpReplace("../../ThirdParty", "../../../ThirdParty"))
  1579. .pipe(gulpReplace("../../SampleData", "../../../../Apps/SampleData"))
  1580. .pipe(gulpReplace("Build/Documentation", "Documentation"))
  1581. .pipe(gulp.dest("Build/Apps/Sandcastle"));
  1582. var imageStream = gulp
  1583. .src(["Apps/Sandcastle/gallery/**.jpg", "Apps/Sandcastle/images/**"], {
  1584. base: "Apps/Sandcastle",
  1585. buffer: false,
  1586. })
  1587. .pipe(gulp.dest("Build/Apps/Sandcastle"));
  1588. var standaloneStream = gulp
  1589. .src(["Apps/Sandcastle/standalone.html"])
  1590. .pipe(
  1591. gulpReplace(
  1592. ' <script type="module" src="load-cesium-es6.js"></script>',
  1593. ""
  1594. )
  1595. )
  1596. .pipe(gulpReplace("nomodule", ""))
  1597. .pipe(gulpReplace("../../Build", "../.."))
  1598. .pipe(gulp.dest("Build/Apps/Sandcastle"));
  1599. return streamToPromise(mergeStream(appStream, imageStream, standaloneStream));
  1600. }
  1601. function buildCesiumViewer() {
  1602. var cesiumViewerOutputDirectory = "Build/Apps/CesiumViewer";
  1603. mkdirp.sync(cesiumViewerOutputDirectory);
  1604. var promise = Promise.join(
  1605. rollup
  1606. .rollup({
  1607. input: "Apps/CesiumViewer/CesiumViewer.js",
  1608. treeshake: {
  1609. moduleSideEffects: false,
  1610. },
  1611. plugins: [
  1612. rollupPluginStripPragma({
  1613. pragmas: ["debug"],
  1614. }),
  1615. rollupPluginUglify.uglify(),
  1616. ],
  1617. onwarn: rollupWarning,
  1618. })
  1619. .then(function (bundle) {
  1620. return bundle.write({
  1621. file: "Build/Apps/CesiumViewer/CesiumViewer.js",
  1622. format: "iife",
  1623. });
  1624. })
  1625. );
  1626. promise = promise.then(function () {
  1627. var stream = mergeStream(
  1628. gulp
  1629. .src("Build/Apps/CesiumViewer/CesiumViewer.js")
  1630. .pipe(gulpInsert.prepend(copyrightHeader))
  1631. .pipe(gulpReplace("../../Source", "."))
  1632. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1633. gulp
  1634. .src("Apps/CesiumViewer/CesiumViewer.css")
  1635. .pipe(cleanCSS())
  1636. .pipe(gulpReplace("../../Source", "."))
  1637. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1638. gulp
  1639. .src("Apps/CesiumViewer/index.html")
  1640. .pipe(gulpReplace('type="module"', ""))
  1641. .pipe(gulp.dest(cesiumViewerOutputDirectory)),
  1642. gulp.src([
  1643. "Apps/CesiumViewer/**",
  1644. "!Apps/CesiumViewer/index.html",
  1645. "!Apps/CesiumViewer/**/*.js",
  1646. "!Apps/CesiumViewer/**/*.css",
  1647. ]),
  1648. gulp.src(
  1649. [
  1650. "Build/Cesium/Assets/**",
  1651. "Build/Cesium/Workers/**",
  1652. "Build/Cesium/ThirdParty/**",
  1653. "Build/Cesium/Widgets/**",
  1654. "!Build/Cesium/Widgets/**/*.css",
  1655. ],
  1656. {
  1657. base: "Build/Cesium",
  1658. nodir: true,
  1659. }
  1660. ),
  1661. gulp.src(["Build/Cesium/Widgets/InfoBox/InfoBoxDescription.css"], {
  1662. base: "Build/Cesium",
  1663. }),
  1664. gulp.src(["web.config"])
  1665. );
  1666. return streamToPromise(stream.pipe(gulp.dest(cesiumViewerOutputDirectory)));
  1667. });
  1668. return promise;
  1669. }
  1670. function filePathToModuleId(moduleId) {
  1671. return moduleId.substring(0, moduleId.lastIndexOf(".")).replace(/\\/g, "/");
  1672. }