2021-01-20 02:30:31 +08:00
|
|
|
/*eslint-env node*/
|
2022-09-09 00:43:28 +08:00
|
|
|
import { writeFileSync, copyFileSync, readFileSync, existsSync } from "fs";
|
|
|
|
|
import { readFile, writeFile } from "fs/promises";
|
2022-11-02 03:39:57 +08:00
|
|
|
import { join, basename, resolve, posix, dirname } from "path";
|
2022-09-09 00:43:28 +08:00
|
|
|
import { exec, execSync } from "child_process";
|
|
|
|
|
import { createHash } from "crypto";
|
2022-09-09 20:58:56 +08:00
|
|
|
import { gzipSync } from "zlib";
|
2022-09-09 00:43:28 +08:00
|
|
|
import { createInterface } from "readline";
|
|
|
|
|
import fetch from "node-fetch";
|
|
|
|
|
import { createRequire } from "module";
|
|
|
|
|
|
|
|
|
|
import gulp from "gulp";
|
|
|
|
|
import gulpTap from "gulp-tap";
|
|
|
|
|
import gulpZip from "gulp-zip";
|
|
|
|
|
import gulpRename from "gulp-rename";
|
|
|
|
|
import gulpReplace from "gulp-replace";
|
2022-11-02 03:39:57 +08:00
|
|
|
import { globby } from "globby";
|
2022-09-09 00:43:28 +08:00
|
|
|
import open from "open";
|
|
|
|
|
import rimraf from "rimraf";
|
|
|
|
|
import mkdirp from "mkdirp";
|
|
|
|
|
import mergeStream from "merge-stream";
|
|
|
|
|
import streamToPromise from "stream-to-promise";
|
|
|
|
|
import karma from "karma";
|
|
|
|
|
import yargs from "yargs";
|
|
|
|
|
import aws from "aws-sdk";
|
|
|
|
|
import mime from "mime";
|
|
|
|
|
import typeScript from "typescript";
|
|
|
|
|
import { build as esbuild } from "esbuild";
|
|
|
|
|
import { createInstrumenter } from "istanbul-lib-instrument";
|
|
|
|
|
import pLimit from "p-limit";
|
2022-10-06 00:31:17 +08:00
|
|
|
import download from "download";
|
|
|
|
|
import decompress from "decompress";
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
import {
|
2022-11-02 03:39:57 +08:00
|
|
|
buildCesium,
|
|
|
|
|
buildEngine,
|
|
|
|
|
buildWidgets,
|
|
|
|
|
bundleCesiumJs,
|
|
|
|
|
bundleWorkers,
|
2022-07-09 03:43:52 +08:00
|
|
|
glslToJavaScript,
|
|
|
|
|
createSpecList,
|
2022-11-02 03:39:57 +08:00
|
|
|
bundleCombinedSpecs,
|
2022-07-09 03:43:52 +08:00
|
|
|
createJsHintOptions,
|
2022-11-02 03:39:57 +08:00
|
|
|
defaultESBuildOptions,
|
2022-09-09 00:43:28 +08:00
|
|
|
} from "./build.js";
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Determines the scope of the workspace packages. If the scope is set to cesium, the workspaces should be @cesium/engine.
|
|
|
|
|
// This should match the scope of the dependencies of the root level package.json.
|
|
|
|
|
const scope = "cesium";
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const require = createRequire(import.meta.url);
|
2021-01-26 00:00:37 +08:00
|
|
|
const packageJson = require("./package.json");
|
|
|
|
|
let version = packageJson.version;
|
2015-10-23 03:26:55 +08:00
|
|
|
if (/\.0$/.test(version)) {
|
2020-05-16 07:17:07 +08:00
|
|
|
version = version.substring(0, version.length - 2);
|
2015-10-23 03:26:55 +08:00
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
const karmaConfigFile = resolve("./Specs/karma.conf.cjs");
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const travisDeployUrl =
|
2020-05-16 07:17:07 +08:00
|
|
|
"http://cesium-dev.s3-website-us-east-1.amazonaws.com/cesium/";
|
2022-10-06 00:31:17 +08:00
|
|
|
const isProduction = process.env.TRAVIS_BRANCH === "cesium.com";
|
2015-12-24 04:17:49 +08:00
|
|
|
|
2015-10-17 05:30:14 +08:00
|
|
|
//Gulp doesn't seem to have a way to get the currently running tasks for setting
|
|
|
|
|
//per-task variables. We use the command line argument here to detect which task is being run.
|
2021-01-26 00:00:37 +08:00
|
|
|
const taskName = process.argv[2];
|
2022-10-06 00:31:17 +08:00
|
|
|
const noDevelopmentGallery =
|
|
|
|
|
taskName === "release" ||
|
|
|
|
|
taskName === "makeZip" ||
|
|
|
|
|
taskName === "websiteRelease";
|
2022-09-09 00:43:28 +08:00
|
|
|
const argv = yargs(process.argv).argv;
|
|
|
|
|
const verbose = argv.verbose;
|
2018-11-02 00:00:08 +08:00
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const sourceFiles = [
|
2020-05-16 07:17:07 +08:00
|
|
|
"Source/**/*.js",
|
|
|
|
|
"!Source/*.js",
|
|
|
|
|
"!Source/Workers/**",
|
|
|
|
|
"!Source/WorkersES6/**",
|
|
|
|
|
"Source/WorkersES6/createTaskProcessorWorker.js",
|
|
|
|
|
"!Source/ThirdParty/Workers/**",
|
|
|
|
|
"!Source/ThirdParty/google-earth-dbroot-parser.js",
|
2021-08-01 06:46:14 +08:00
|
|
|
"!Source/ThirdParty/_*",
|
2020-05-16 07:17:07 +08:00
|
|
|
];
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
const workerSourceFiles = ["packages/engine/Source/WorkersES6/**"];
|
2022-06-07 00:35:09 +08:00
|
|
|
const watchedSpecFiles = [
|
|
|
|
|
"Specs/**/*Spec.js",
|
|
|
|
|
"Specs/*.js",
|
2022-07-12 03:39:36 +08:00
|
|
|
"!Specs/SpecList.js",
|
2022-06-07 00:35:09 +08:00
|
|
|
"Specs/TestWorkers/*.js",
|
|
|
|
|
];
|
2022-05-28 02:45:53 +08:00
|
|
|
const shaderFiles = [
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/Shaders/**/*.glsl",
|
|
|
|
|
"packages/engine/Source/ThirdParty/Shaders/*.glsl",
|
2022-05-28 02:45:53 +08:00
|
|
|
];
|
2019-10-07 20:25:26 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
// Print an esbuild warning
|
|
|
|
|
function printBuildWarning({ location, text }) {
|
|
|
|
|
const { column, file, line, lineText, suggestion } = location;
|
2022-05-12 04:53:41 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
let message = `\n
|
|
|
|
|
> ${file}:${line}:${column}: warning: ${text}
|
|
|
|
|
${lineText}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
if (suggestion && suggestion !== "") {
|
|
|
|
|
message += `\n${suggestion}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ignore `eval` warnings in third-party code we don't have control over
|
|
|
|
|
function handleBuildWarnings(result) {
|
|
|
|
|
for (const warning of result.warnings) {
|
2022-05-28 02:45:53 +08:00
|
|
|
if (
|
|
|
|
|
!warning.location.file.includes("protobufjs.js") &&
|
2022-05-28 03:27:47 +08:00
|
|
|
!warning.location.file.includes("Build/Cesium")
|
2022-05-28 02:45:53 +08:00
|
|
|
) {
|
2022-05-24 20:54:14 +08:00
|
|
|
printBuildWarning(warning);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export async function build() {
|
|
|
|
|
// Configure build options from command line arguments.
|
|
|
|
|
const minify = argv.minify ?? false;
|
|
|
|
|
const removePragmas = argv.pragmas ?? false;
|
|
|
|
|
const sourcemap = argv.sourcemap ?? true;
|
|
|
|
|
const node = argv.node ?? true;
|
Start of replacing submitted third party libraries with npm modules
Rather than submit libraries to Source/ThirdParty, which always end up
being modified for our build system and quickly go out of date, this
change starts to use libraries via npm instead. Currently Autolinker,
earcut, when, tween.js, rbush, kdbush, quickselect, and topojson are
ported.
The main hurdle that prevented us from doing this sooner was the fact that
Cesium has a long history of not requiring a build step after every code
change and has a goal of writing valid JS code, unlike many libraries today
that mandate a bundler to turn invalid JS code into valid code.
Rather than mandate a bundler during development, this initial step adds a
"buildThirdyParty" function to the current "build" step. This function runs
third party libraries (defined in the ThirdParty/npm/ folder) through
RollUp and creates an equivalent file in `Source/ThirdPartyNpm`.
The change to end users will be non-existent, especially since the combined
Cesium.js will still re-export any third party modules as part of the
private API just like it used to.
This doesn't prevent code duplication for users using some of the same
third party libraries as Cesium. This is just an improvement as to how
depend on third party libraries internally.
I think Cesium's days of being "bundler free" are probably limited long
term, performance is the main hurdle and newer tools like esbuild may make
that no longer a problem. But that's outside the scope of this initial
goal.
2021-04-12 03:02:36 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
const buildOptions = {
|
|
|
|
|
development: noDevelopmentGallery,
|
|
|
|
|
iife: true,
|
2022-05-26 02:20:47 +08:00
|
|
|
minify: minify,
|
|
|
|
|
removePragmas: removePragmas,
|
2022-05-27 23:14:30 +08:00
|
|
|
sourcemap: sourcemap,
|
2022-05-26 02:20:47 +08:00
|
|
|
node: node,
|
2022-11-02 03:39:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Configure build target.
|
|
|
|
|
const workspace = argv.workspace ? argv.workspace : undefined;
|
|
|
|
|
|
|
|
|
|
if (workspace === `@${scope}/engine`) {
|
|
|
|
|
return buildEngine(buildOptions);
|
|
|
|
|
} else if (workspace === `@${scope}/widgets`) {
|
|
|
|
|
return buildWidgets(buildOptions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await buildEngine(buildOptions);
|
|
|
|
|
await buildWidgets(buildOptions);
|
|
|
|
|
await buildCesium(buildOptions);
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
export default build;
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export const buildWatch = gulp.series(build, async function () {
|
|
|
|
|
const minify = argv.minify ? argv.minify : false;
|
|
|
|
|
const removePragmas = argv.pragmas ? argv.pragmas : false;
|
|
|
|
|
const sourcemap = argv.sourcemap ? argv.sourcemap : true;
|
2022-05-12 04:53:41 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const outputDirectory = join("Build", `Cesium${!minify ? "Unminified" : ""}`);
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
let [esmResult, iifeResult, cjsResult] = await bundleCesiumJs({
|
2022-09-09 00:43:28 +08:00
|
|
|
minify: minify,
|
|
|
|
|
path: outputDirectory,
|
|
|
|
|
removePragmas: removePragmas,
|
|
|
|
|
sourcemap: sourcemap,
|
|
|
|
|
incremental: true,
|
|
|
|
|
});
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
let specResult = await bundleCombinedSpecs({
|
2022-09-09 00:43:28 +08:00
|
|
|
incremental: true,
|
|
|
|
|
});
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
await bundleWorkers({
|
2022-09-09 00:43:28 +08:00
|
|
|
minify: minify,
|
|
|
|
|
path: outputDirectory,
|
|
|
|
|
removePragmas: removePragmas,
|
|
|
|
|
sourcemap: sourcemap,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
gulp.watch(shaderFiles, async () => {
|
2022-11-02 03:39:57 +08:00
|
|
|
glslToJavaScript(minify, "Build/minifyShaders.state", "engine");
|
2022-09-09 00:43:28 +08:00
|
|
|
esmResult = await esmResult.rebuild();
|
|
|
|
|
|
|
|
|
|
if (iifeResult) {
|
|
|
|
|
iifeResult = await iifeResult.rebuild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cjsResult) {
|
|
|
|
|
cjsResult = await cjsResult.rebuild();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
gulp.watch(
|
|
|
|
|
[
|
|
|
|
|
...sourceFiles,
|
|
|
|
|
// Shader results are generated in the previous watch task; no need to rebuild twice
|
|
|
|
|
"!Source/Shaders/**",
|
|
|
|
|
],
|
|
|
|
|
async () => {
|
|
|
|
|
createJsHintOptions();
|
2022-05-28 03:27:47 +08:00
|
|
|
esmResult = await esmResult.rebuild();
|
|
|
|
|
|
|
|
|
|
if (iifeResult) {
|
|
|
|
|
iifeResult = await iifeResult.rebuild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cjsResult) {
|
|
|
|
|
cjsResult = await cjsResult.rebuild();
|
|
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
);
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
gulp.watch(
|
|
|
|
|
watchedSpecFiles,
|
|
|
|
|
{
|
|
|
|
|
events: ["add", "unlink"],
|
|
|
|
|
},
|
|
|
|
|
async () => {
|
|
|
|
|
createSpecList();
|
|
|
|
|
specResult = await specResult.rebuild();
|
|
|
|
|
}
|
|
|
|
|
);
|
2022-05-26 00:31:43 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
gulp.watch(
|
|
|
|
|
watchedSpecFiles,
|
|
|
|
|
{
|
|
|
|
|
events: ["change"],
|
|
|
|
|
},
|
|
|
|
|
async () => {
|
|
|
|
|
specResult = await specResult.rebuild();
|
|
|
|
|
}
|
|
|
|
|
);
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
gulp.watch(workerSourceFiles, () => {
|
2022-11-02 03:39:57 +08:00
|
|
|
return bundleWorkers({
|
2022-09-09 00:43:28 +08:00
|
|
|
minify: minify,
|
|
|
|
|
path: outputDirectory,
|
|
|
|
|
removePragmas: removePragmas,
|
|
|
|
|
sourcemap: sourcemap,
|
2022-05-24 20:54:14 +08:00
|
|
|
});
|
2022-09-09 00:43:28 +08:00
|
|
|
});
|
2022-05-24 20:54:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
process.on("SIGINT", () => {
|
|
|
|
|
// Free up resources
|
|
|
|
|
esmResult.rebuild.dispose();
|
2022-05-28 03:27:47 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (iifeResult) {
|
|
|
|
|
iifeResult.rebuild.dispose();
|
|
|
|
|
}
|
2022-05-28 03:27:47 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (cjsResult) {
|
|
|
|
|
cjsResult.rebuild.dispose();
|
|
|
|
|
}
|
2015-10-19 06:01:56 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
specResult.rebuild.dispose();
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
});
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export async function buildTs() {
|
|
|
|
|
let workspaces;
|
|
|
|
|
if (argv.workspace && !Array.isArray(argv.workspace)) {
|
|
|
|
|
workspaces = [argv.workspace];
|
|
|
|
|
} else if (argv.workspace) {
|
|
|
|
|
workspaces = argv.workspace;
|
|
|
|
|
} else {
|
|
|
|
|
workspaces = Object.keys(packageJson.dependencies);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate types for passed packages in order.
|
|
|
|
|
const importModules = {};
|
|
|
|
|
for (const workspace of workspaces) {
|
|
|
|
|
const directory = workspace.replace(`@${scope}/`, ``);
|
|
|
|
|
const workspaceModules = await generateTypeScriptDefinitions(
|
|
|
|
|
directory,
|
|
|
|
|
`packages/${directory}/index.d.ts`,
|
|
|
|
|
`packages/${directory}/tsd-conf.json`,
|
|
|
|
|
// The engine package needs additional processing for its enum strings
|
|
|
|
|
directory === "engine" ? processEngineSource : undefined,
|
|
|
|
|
// Handle engine's module naming exceptions
|
|
|
|
|
directory === "engine" ? processEngineModules : undefined,
|
|
|
|
|
importModules
|
|
|
|
|
);
|
|
|
|
|
importModules[directory] = workspaceModules;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (argv.workspace) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate types for CesiumJS.
|
|
|
|
|
await createTypeScriptDefinitions();
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export function buildApps() {
|
|
|
|
|
return Promise.all([buildCesiumViewer(), buildSandcastle()]);
|
|
|
|
|
}
|
2022-05-05 04:13:18 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const filesToClean = [
|
|
|
|
|
"Source/Cesium.js",
|
|
|
|
|
"Source/Shaders/**/*.js",
|
|
|
|
|
"Source/Workers/**",
|
|
|
|
|
"!Source/Workers/cesiumWorkerBootstrapper.js",
|
|
|
|
|
"!Source/Workers/transferTypedArrayTest.js",
|
|
|
|
|
"!Source/Workers/package.json",
|
|
|
|
|
"Source/ThirdParty/Shaders/*.js",
|
|
|
|
|
"Source/**/*.d.ts",
|
|
|
|
|
"Specs/SpecList.js",
|
|
|
|
|
"Specs/jasmine/**",
|
|
|
|
|
"Apps/Sandcastle/jsHintOptions.js",
|
|
|
|
|
"Apps/Sandcastle/gallery/gallery-index.js",
|
|
|
|
|
"Apps/Sandcastle/templates/bucket.css",
|
|
|
|
|
"Cesium-*.zip",
|
|
|
|
|
"cesium-*.tgz",
|
|
|
|
|
];
|
2016-03-04 07:00:26 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function clean() {
|
|
|
|
|
const rimrafAsync = (file) => new Promise((resolve) => rimraf(file, resolve));
|
|
|
|
|
await rimrafAsync("Build");
|
|
|
|
|
const files = await globby(filesToClean);
|
|
|
|
|
return Promise.all(files.map(rimrafAsync));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function clocSource() {
|
2021-01-26 00:00:37 +08:00
|
|
|
let cmdLine;
|
2020-05-16 07:17:07 +08:00
|
|
|
|
|
|
|
|
//Run cloc on primary Source files only
|
2021-01-26 00:00:37 +08:00
|
|
|
const source = new Promise(function (resolve, reject) {
|
2020-05-16 07:17:07 +08:00
|
|
|
cmdLine =
|
2020-06-01 10:38:02 +08:00
|
|
|
"npx cloc" +
|
2020-05-16 07:17:07 +08:00
|
|
|
" --quiet --progress-rate=0" +
|
|
|
|
|
" Source/ --exclude-dir=Assets,ThirdParty,Workers --not-match-f=copyrightHeader.js";
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
exec(cmdLine, function (error, stdout, stderr) {
|
2020-05-16 07:17:07 +08:00
|
|
|
if (error) {
|
|
|
|
|
console.log(stderr);
|
|
|
|
|
return reject(error);
|
|
|
|
|
}
|
|
|
|
|
console.log("Source:");
|
|
|
|
|
console.log(stdout);
|
|
|
|
|
resolve();
|
2015-10-22 05:54:08 +08:00
|
|
|
});
|
2020-05-16 07:17:07 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//If running cloc on source succeeded, also run it on the tests.
|
2022-09-09 00:43:28 +08:00
|
|
|
await source;
|
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
|
cmdLine =
|
|
|
|
|
"npx cloc" + " --quiet --progress-rate=0" + " Specs/ --exclude-dir=Data";
|
|
|
|
|
exec(cmdLine, function (error, stdout, stderr) {
|
|
|
|
|
if (error) {
|
|
|
|
|
console.log(stderr);
|
|
|
|
|
return reject(error);
|
|
|
|
|
}
|
|
|
|
|
console.log("Specs:");
|
|
|
|
|
console.log(stdout);
|
|
|
|
|
resolve();
|
2015-10-22 05:54:08 +08:00
|
|
|
});
|
2020-05-16 07:17:07 +08:00
|
|
|
});
|
2018-11-07 04:03:16 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function prepare() {
|
2021-10-07 13:50:17 +08:00
|
|
|
// Copy Draco3D files from node_modules into Source
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2021-10-07 13:50:17 +08:00
|
|
|
"node_modules/draco3d/draco_decoder_nodejs.js",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/ThirdParty/Workers/draco_decoder_nodejs.js"
|
2021-10-07 13:50:17 +08:00
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2021-10-07 13:50:17 +08:00
|
|
|
"node_modules/draco3d/draco_decoder.wasm",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/ThirdParty/draco_decoder.wasm"
|
2021-10-07 13:50:17 +08:00
|
|
|
);
|
2022-06-17 02:13:05 +08:00
|
|
|
|
2021-09-30 04:35:11 +08:00
|
|
|
// Copy pako and zip.js worker files to Source/ThirdParty
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2021-09-30 04:35:11 +08:00
|
|
|
"node_modules/pako/dist/pako_inflate.min.js",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/ThirdParty/Workers/pako_inflate.min.js"
|
2021-09-30 04:35:11 +08:00
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2021-09-30 04:35:11 +08:00
|
|
|
"node_modules/pako/dist/pako_deflate.min.js",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/ThirdParty/Workers/pako_deflate.min.js"
|
2021-09-30 04:35:11 +08:00
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2021-09-30 04:35:11 +08:00
|
|
|
"node_modules/@zip.js/zip.js/dist/z-worker-pako.js",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Source/ThirdParty/Workers/z-worker-pako.js"
|
2021-09-30 04:35:11 +08:00
|
|
|
);
|
2022-04-20 21:45:05 +08:00
|
|
|
|
2022-06-17 02:13:05 +08:00
|
|
|
// Copy prism.js and prism.css files into Tools
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2022-06-17 02:13:05 +08:00
|
|
|
"node_modules/prismjs/prism.js",
|
|
|
|
|
"Tools/jsdoc/cesium_template/static/javascript/prism.js"
|
|
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2022-06-17 02:13:05 +08:00
|
|
|
"node_modules/prismjs/themes/prism.min.css",
|
|
|
|
|
"Tools/jsdoc/cesium_template/static/styles/prism.css"
|
|
|
|
|
);
|
|
|
|
|
|
2022-04-20 21:45:05 +08:00
|
|
|
// Copy jasmine runner files into Specs
|
2022-09-09 00:43:28 +08:00
|
|
|
const files = await globby([
|
2022-04-20 21:45:05 +08:00
|
|
|
"node_modules/jasmine-core/lib/jasmine-core",
|
|
|
|
|
"!node_modules/jasmine-core/lib/jasmine-core/example",
|
2022-09-09 00:43:28 +08:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const stream = gulp.src(files).pipe(gulp.dest("Specs/jasmine"));
|
|
|
|
|
return streamToPromise(stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const cloc = gulp.series(clean, clocSource);
|
2021-09-01 21:04:06 +08:00
|
|
|
|
2015-10-17 05:30:14 +08:00
|
|
|
//Builds the documentation
|
2022-09-09 00:43:28 +08:00
|
|
|
export function buildDocs() {
|
2022-04-02 01:59:53 +08:00
|
|
|
const generatePrivateDocumentation = argv.private ? "--private" : "";
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
execSync(
|
2022-04-02 01:59:53 +08:00
|
|
|
`npx jsdoc --configure Tools/jsdoc/conf.json --pedantic ${generatePrivateDocumentation}`,
|
|
|
|
|
{
|
|
|
|
|
stdio: "inherit",
|
|
|
|
|
env: Object.assign({}, process.env, { CESIUM_VERSION: version }),
|
|
|
|
|
}
|
|
|
|
|
);
|
2020-06-01 10:38:02 +08:00
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const stream = gulp
|
2020-06-01 10:38:02 +08:00
|
|
|
.src("Documentation/Images/**")
|
|
|
|
|
.pipe(gulp.dest("Build/Documentation/Images"));
|
|
|
|
|
|
|
|
|
|
return streamToPromise(stream);
|
2018-10-31 02:53:19 +08:00
|
|
|
}
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function buildDocsWatch() {
|
|
|
|
|
await buildDocs();
|
|
|
|
|
console.log("Listening for changes in documentation...");
|
|
|
|
|
return gulp.watch(sourceFiles, buildDocs);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
function combineForSandcastle() {
|
|
|
|
|
const outputDirectory = join("Build", "Sandcastle", "CesiumUnminified");
|
2022-10-06 21:57:19 +08:00
|
|
|
return buildCesium({
|
2022-10-06 00:31:17 +08:00
|
|
|
minify: false,
|
|
|
|
|
removePragmas: false,
|
2022-10-06 21:57:19 +08:00
|
|
|
node: false,
|
2022-10-06 00:31:17 +08:00
|
|
|
outputDirectory: outputDirectory,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const websiteRelease = gulp.series(
|
|
|
|
|
function () {
|
|
|
|
|
return buildCesium({
|
|
|
|
|
minify: false,
|
|
|
|
|
removePragmas: false,
|
|
|
|
|
node: false,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
combineForSandcastle,
|
|
|
|
|
buildDocs
|
|
|
|
|
);
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export const buildRelease = gulp.series(
|
2022-09-09 00:43:28 +08:00
|
|
|
function () {
|
2022-11-02 03:39:57 +08:00
|
|
|
return buildEngine();
|
2022-09-09 00:43:28 +08:00
|
|
|
},
|
|
|
|
|
function () {
|
2022-11-02 03:39:57 +08:00
|
|
|
return buildWidgets();
|
2022-09-09 00:43:28 +08:00
|
|
|
},
|
2022-11-02 03:39:57 +08:00
|
|
|
gulp.parallel(
|
|
|
|
|
// Generate Build/CesiumUnminified
|
|
|
|
|
function () {
|
|
|
|
|
return buildCesium({
|
|
|
|
|
minify: false,
|
|
|
|
|
removePragmas: false,
|
|
|
|
|
node: true,
|
|
|
|
|
sourcemap: false,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// Generate Build/Cesium
|
|
|
|
|
function () {
|
|
|
|
|
return buildCesium({
|
|
|
|
|
minify: true,
|
|
|
|
|
removePragmas: true,
|
|
|
|
|
node: true,
|
|
|
|
|
sourcemap: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
)
|
2022-05-26 02:20:47 +08:00
|
|
|
);
|
2018-11-07 04:03:16 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export const release = gulp.series(
|
|
|
|
|
buildRelease,
|
|
|
|
|
gulp.parallel(buildTs, buildDocs)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes scripts from package.json files to ensure that
|
|
|
|
|
* they still work when run from within the ZIP file.
|
|
|
|
|
*
|
|
|
|
|
* @param {String} packageJsonPath The path to the package.json.
|
|
|
|
|
* @returns {WritableStream} A stream that writes to the updated package.json file.
|
|
|
|
|
*/
|
|
|
|
|
async function pruneScriptsForZip(packageJsonPath) {
|
|
|
|
|
// Read the contents of the file.
|
|
|
|
|
const contents = await readFile(packageJsonPath);
|
|
|
|
|
const contentsJson = JSON.parse(contents);
|
|
|
|
|
|
|
|
|
|
const scripts = contentsJson.scripts;
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
// Remove prepare step from package.json to avoid running "prepare" an extra time.
|
|
|
|
|
delete scripts.prepare;
|
|
|
|
|
|
|
|
|
|
// Remove build and transform tasks since they do not function as intended from within the release zip
|
|
|
|
|
delete scripts.build;
|
2022-11-02 03:39:57 +08:00
|
|
|
delete scripts["build-release"];
|
2022-09-09 00:43:28 +08:00
|
|
|
delete scripts["build-watch"];
|
|
|
|
|
delete scripts["build-ts"];
|
|
|
|
|
delete scripts["build-third-party"];
|
|
|
|
|
delete scripts["build-apps"];
|
|
|
|
|
delete scripts.clean;
|
|
|
|
|
delete scripts.cloc;
|
|
|
|
|
delete scripts["build-docs"];
|
|
|
|
|
delete scripts["build-docs-watch"];
|
|
|
|
|
delete scripts["make-zip"];
|
|
|
|
|
delete scripts.release;
|
|
|
|
|
delete scripts.prettier;
|
|
|
|
|
|
|
|
|
|
// Remove deploy tasks
|
|
|
|
|
delete scripts["deploy-s3"];
|
|
|
|
|
delete scripts["deploy-status"];
|
|
|
|
|
delete scripts["deploy-set-version"];
|
2022-10-06 00:31:17 +08:00
|
|
|
delete scripts["website-release"];
|
2022-09-09 00:43:28 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Write to a temporary package.json file.
|
|
|
|
|
const noPreparePackageJson = join(
|
|
|
|
|
dirname(packageJsonPath),
|
|
|
|
|
"Build/package.noprepare.json"
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
2022-11-02 03:39:57 +08:00
|
|
|
await writeFile(noPreparePackageJson, JSON.stringify(contentsJson, null, 2));
|
2021-09-01 23:19:16 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
return gulp.src(noPreparePackageJson).pipe(gulpRename(packageJsonPath));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const postversion = function () {
|
|
|
|
|
const workspace = argv.workspace;
|
|
|
|
|
if (!workspace) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// After the workspace version is bumped by `npm version`,
|
|
|
|
|
// update the root `package.json` file to depend on the new version
|
|
|
|
|
const directory = workspace.replaceAll(`@${scope}/`, ``);
|
|
|
|
|
const workspacePackageJson = require(`./packages/${directory}/package.json`);
|
|
|
|
|
const version = workspacePackageJson.version;
|
|
|
|
|
|
|
|
|
|
packageJson.dependencies[workspace] = version;
|
|
|
|
|
return writeFile("package.json", JSON.stringify(packageJson, undefined, 2));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const makeZip = gulp.series(release, async function () {
|
|
|
|
|
//For now we regenerate the JS glsl to force it to be unminified in the release zip
|
|
|
|
|
//See https://github.com/CesiumGS/cesium/pull/3106#discussion_r42793558 for discussion.
|
|
|
|
|
await glslToJavaScript(false, "Build/minifyShaders.state", "engine");
|
|
|
|
|
|
|
|
|
|
const packageJsonSrc = await pruneScriptsForZip("package.json");
|
|
|
|
|
const enginePackageJsonSrc = await pruneScriptsForZip(
|
|
|
|
|
"packages/engine/package.json"
|
|
|
|
|
);
|
|
|
|
|
const widgetsPackageJsonSrc = await pruneScriptsForZip(
|
|
|
|
|
"packages/widgets/package.json"
|
|
|
|
|
);
|
2021-09-02 03:01:13 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const builtSrc = gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Build/Cesium/**",
|
|
|
|
|
"Build/CesiumUnminified/**",
|
|
|
|
|
"Build/Documentation/**",
|
|
|
|
|
"Build/package.json",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/Build/**",
|
|
|
|
|
"packages/widgets/Build/**",
|
|
|
|
|
"!packages/engine/Build/Specs/**",
|
|
|
|
|
"!packages/widgets/Build/Specs/**",
|
|
|
|
|
"!packages/engine/Build/minifyShaders.state",
|
|
|
|
|
"!packages/engine/Build/package.noprepare.json",
|
|
|
|
|
"!packages/widgets/Build/package.noprepare.json",
|
2022-09-09 00:43:28 +08:00
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
base: ".",
|
|
|
|
|
}
|
|
|
|
|
);
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const staticSrc = gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Apps/**",
|
|
|
|
|
"Apps/**/.eslintrc.json",
|
|
|
|
|
"Apps/Sandcastle/.jshintrc",
|
|
|
|
|
"!Apps/Sandcastle/gallery/development/**",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/engine/index.js",
|
|
|
|
|
"packages/engine/index.d.ts",
|
|
|
|
|
"packages/engine/LICENSE.md",
|
|
|
|
|
"packages/engine/README.md",
|
|
|
|
|
"packages/engine/Source/**",
|
|
|
|
|
"!packages/engine/.gitignore",
|
|
|
|
|
"packages/widgets/index.js",
|
|
|
|
|
"packages/widgets/index.d.ts",
|
|
|
|
|
"packages/widgets/LICENSE.md",
|
|
|
|
|
"packages/widgets/README.md",
|
|
|
|
|
"packages/widgets/Source/**",
|
|
|
|
|
"!packages/widgets/.gitignore",
|
2022-09-09 00:43:28 +08:00
|
|
|
"Source/**",
|
|
|
|
|
"Source/**/.eslintrc.json",
|
|
|
|
|
"Specs/**",
|
|
|
|
|
"Specs/**/.eslintrc.json",
|
|
|
|
|
"ThirdParty/**",
|
|
|
|
|
"favicon.ico",
|
|
|
|
|
".eslintignore",
|
|
|
|
|
".eslintrc.json",
|
|
|
|
|
".prettierignore",
|
|
|
|
|
"build.js",
|
|
|
|
|
"gulpfile.js",
|
|
|
|
|
"server.js",
|
|
|
|
|
"index.cjs",
|
|
|
|
|
"LICENSE.md",
|
|
|
|
|
"CHANGES.md",
|
|
|
|
|
"README.md",
|
|
|
|
|
"web.config",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
base: ".",
|
|
|
|
|
}
|
|
|
|
|
);
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const indexSrc = gulp
|
|
|
|
|
.src("index.release.html")
|
|
|
|
|
.pipe(gulpRename("index.html"));
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
return streamToPromise(
|
2022-11-02 03:39:57 +08:00
|
|
|
mergeStream(
|
|
|
|
|
packageJsonSrc,
|
|
|
|
|
enginePackageJsonSrc,
|
|
|
|
|
widgetsPackageJsonSrc,
|
|
|
|
|
builtSrc,
|
|
|
|
|
staticSrc,
|
|
|
|
|
indexSrc
|
|
|
|
|
)
|
2020-05-16 07:17:07 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulpTap(function (file) {
|
|
|
|
|
// Work around an issue with gulp-zip where archives generated on Windows do
|
|
|
|
|
// not properly have their directory executable mode set.
|
|
|
|
|
// see https://github.com/sindresorhus/gulp-zip/issues/64#issuecomment-205324031
|
|
|
|
|
if (file.isDirectory()) {
|
|
|
|
|
file.stat.mode = parseInt("40777", 8);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
)
|
2022-02-03 04:40:21 +08:00
|
|
|
.pipe(gulpZip(`Cesium-${version}.zip`))
|
2021-09-02 03:01:13 +08:00
|
|
|
.pipe(gulp.dest("."))
|
|
|
|
|
.on("finish", function () {
|
2021-09-02 03:52:06 +08:00
|
|
|
rimraf.sync("./Build/package.noprepare.json");
|
2022-11-02 03:39:57 +08:00
|
|
|
rimraf.sync("./packages/engine/Build/package.noprepare.json");
|
|
|
|
|
rimraf.sync("./packages/widgets/Build/package.noprepare.json");
|
2022-09-09 00:43:28 +08:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2016-05-25 22:18:57 +08:00
|
|
|
function isTravisPullRequest() {
|
2020-05-16 07:17:07 +08:00
|
|
|
return (
|
|
|
|
|
process.env.TRAVIS_PULL_REQUEST !== undefined &&
|
|
|
|
|
process.env.TRAVIS_PULL_REQUEST !== "false"
|
|
|
|
|
);
|
2016-05-25 22:18:57 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function deployS3() {
|
2020-05-16 07:17:07 +08:00
|
|
|
if (isTravisPullRequest()) {
|
|
|
|
|
console.log("Skipping deployment for non-pull request.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 01:29:46 +08:00
|
|
|
const argv = yargs(process.argv)
|
2020-05-16 07:17:07 +08:00
|
|
|
.usage("Usage: deploy-s3 -b [Bucket Name] -d [Upload Directory]")
|
2022-09-09 01:29:46 +08:00
|
|
|
.options({
|
|
|
|
|
bucket: {
|
|
|
|
|
alias: "b",
|
|
|
|
|
description: "Bucket name.",
|
|
|
|
|
type: "string",
|
|
|
|
|
demandOption: true,
|
|
|
|
|
},
|
|
|
|
|
directory: {
|
|
|
|
|
alias: "d",
|
|
|
|
|
description: "Upload directory.",
|
|
|
|
|
type: "string",
|
|
|
|
|
},
|
|
|
|
|
"cache-control": {
|
|
|
|
|
alias: "c",
|
2022-09-10 03:28:32 +08:00
|
|
|
description:
|
|
|
|
|
"The cache control option set on the objects uploaded to S3.",
|
2022-09-09 01:29:46 +08:00
|
|
|
type: "string",
|
|
|
|
|
default: "max-age=3600",
|
|
|
|
|
},
|
|
|
|
|
"dry-run": {
|
|
|
|
|
description: "Only print file paths and S3 keys.",
|
|
|
|
|
type: "boolean",
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
confirm: {
|
2022-10-06 00:31:17 +08:00
|
|
|
description: "Skip confirmation step, useful for CI.",
|
2022-09-09 01:29:46 +08:00
|
|
|
type: "boolean",
|
|
|
|
|
default: false,
|
|
|
|
|
},
|
|
|
|
|
}).argv;
|
|
|
|
|
|
|
|
|
|
const uploadDirectory = argv.directory;
|
|
|
|
|
const bucketName = argv.bucket;
|
|
|
|
|
const dryRun = argv.dryRun;
|
|
|
|
|
const cacheControl = argv.cacheControl ? argv.cacheControl : "max-age=3600";
|
2020-05-16 07:17:07 +08:00
|
|
|
|
|
|
|
|
if (argv.confirm) {
|
2022-10-06 00:31:17 +08:00
|
|
|
return deployCesium(bucketName, uploadDirectory, cacheControl, dryRun);
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const iface = createInterface({
|
2020-05-16 07:17:07 +08:00
|
|
|
input: process.stdin,
|
|
|
|
|
output: process.stdout,
|
|
|
|
|
});
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
// prompt for confirmation
|
|
|
|
|
iface.question(
|
|
|
|
|
`Files from your computer will be published to the ${bucketName} bucket. Continue? [y/n] `,
|
|
|
|
|
function (answer) {
|
|
|
|
|
iface.close();
|
|
|
|
|
if (answer === "y") {
|
2022-09-09 01:29:46 +08:00
|
|
|
resolve(
|
|
|
|
|
deployCesium(bucketName, uploadDirectory, cacheControl, dryRun)
|
|
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
} else {
|
|
|
|
|
console.log("Deploy aborted by user.");
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-03-22 22:44:36 +08:00
|
|
|
|
2016-03-23 05:03:55 +08:00
|
|
|
// Deploy cesium to s3
|
2022-09-09 01:29:46 +08:00
|
|
|
async function deployCesium(bucketName, uploadDirectory, cacheControl, dryRun) {
|
2022-09-09 00:43:28 +08:00
|
|
|
// Limit promise concurrency since we are reading many
|
|
|
|
|
// files off disk in parallel
|
|
|
|
|
const limit = pLimit(2000);
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
const refDocPrefix = "cesiumjs/ref-doc/";
|
|
|
|
|
const sandcastlePrefix = "sandcastle/";
|
|
|
|
|
const cesiumViewerPrefix = "cesiumjs/cesium-viewer/";
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const s3 = new aws.S3({
|
2020-05-16 07:17:07 +08:00
|
|
|
maxRetries: 10,
|
|
|
|
|
retryDelayOptions: {
|
|
|
|
|
base: 500,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const existingBlobs = [];
|
|
|
|
|
let totalFiles = 0;
|
|
|
|
|
let uploaded = 0;
|
|
|
|
|
let skipped = 0;
|
|
|
|
|
const errors = [];
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
if (!isProduction) {
|
|
|
|
|
await listAll(s3, bucketName, `${uploadDirectory}/`, existingBlobs);
|
|
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
async function getContents(file, blobName) {
|
|
|
|
|
const mimeLookup = getMimeType(blobName);
|
|
|
|
|
const contentType = mimeLookup.type;
|
|
|
|
|
const compress = mimeLookup.compress;
|
|
|
|
|
const contentEncoding = compress ? "gzip" : undefined;
|
|
|
|
|
|
|
|
|
|
totalFiles++;
|
|
|
|
|
|
|
|
|
|
let content = await readFile(file);
|
|
|
|
|
|
2022-09-15 21:23:59 +08:00
|
|
|
if (compress) {
|
|
|
|
|
const alreadyCompressed = content[0] === 0x1f && content[1] === 0x8b;
|
|
|
|
|
if (alreadyCompressed) {
|
2022-10-06 00:31:17 +08:00
|
|
|
if (verbose) {
|
|
|
|
|
console.log(`Skipping compressing already compressed file: ${file}`);
|
|
|
|
|
}
|
2022-09-15 21:23:59 +08:00
|
|
|
} else {
|
|
|
|
|
content = gzipSync(content);
|
|
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-15 21:23:59 +08:00
|
|
|
const computeEtag = (content) => {
|
|
|
|
|
return createHash("md5").update(content).digest("base64");
|
|
|
|
|
};
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
const index = existingBlobs.indexOf(blobName);
|
|
|
|
|
if (index <= -1) {
|
|
|
|
|
return {
|
|
|
|
|
content,
|
|
|
|
|
etag: computeEtag(content),
|
|
|
|
|
contentType,
|
|
|
|
|
contentEncoding,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 21:23:59 +08:00
|
|
|
// remove files from the list to clean later
|
|
|
|
|
// as we find them on disk
|
2022-09-09 00:43:28 +08:00
|
|
|
existingBlobs.splice(index, 1);
|
|
|
|
|
|
|
|
|
|
// get file info
|
|
|
|
|
const data = await s3
|
|
|
|
|
.headObject({
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
Key: blobName,
|
|
|
|
|
})
|
|
|
|
|
.promise();
|
|
|
|
|
|
|
|
|
|
const hash = createHash("md5").update(content).digest("hex");
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
data.ETag !== `"${hash}"` ||
|
|
|
|
|
data.CacheControl !== cacheControl ||
|
|
|
|
|
data.ContentType !== contentType ||
|
|
|
|
|
data.ContentEncoding !== contentEncoding
|
|
|
|
|
) {
|
|
|
|
|
return {
|
|
|
|
|
content,
|
|
|
|
|
etag: computeEtag(content),
|
|
|
|
|
contentType,
|
|
|
|
|
contentEncoding,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't need to upload this file again
|
|
|
|
|
skipped++;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
async function readAndUpload(prefix, existingPrefix, file) {
|
|
|
|
|
const blobName = `${prefix}${file.replace(existingPrefix, "")}`;
|
2022-09-09 00:43:28 +08:00
|
|
|
|
2022-09-12 21:51:32 +08:00
|
|
|
let fileContents;
|
2022-09-09 00:43:28 +08:00
|
|
|
try {
|
2022-09-12 21:51:32 +08:00
|
|
|
fileContents = await getContents(file, blobName);
|
2022-09-09 00:43:28 +08:00
|
|
|
} catch (e) {
|
|
|
|
|
errors.push(e);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-12 21:51:32 +08:00
|
|
|
if (!fileContents) {
|
2022-09-09 00:43:28 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-12 21:51:32 +08:00
|
|
|
const content = fileContents.content;
|
|
|
|
|
const etag = fileContents.etag;
|
|
|
|
|
const contentType = fileContents.contentType;
|
2022-09-13 04:42:56 +08:00
|
|
|
const contentEncoding = fileContents.contentEncoding;
|
2022-09-12 21:51:32 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (verbose) {
|
|
|
|
|
console.log(`Uploading ${blobName}...`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const params = {
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
Key: blobName,
|
|
|
|
|
Body: content,
|
|
|
|
|
ContentMD5: etag,
|
|
|
|
|
ContentType: contentType,
|
|
|
|
|
ContentEncoding: contentEncoding,
|
|
|
|
|
CacheControl: cacheControl,
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-09 01:29:46 +08:00
|
|
|
if (dryRun) {
|
|
|
|
|
uploaded++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
try {
|
|
|
|
|
await s3.putObject(params).promise();
|
|
|
|
|
uploaded++;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
errors.push(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
let uploads;
|
|
|
|
|
if (isProduction) {
|
|
|
|
|
const uploadSandcastle = async () => {
|
|
|
|
|
const files = await globby(["Build/Sandcastle/**"]);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
files.map((file) => {
|
|
|
|
|
return limit(() =>
|
|
|
|
|
readAndUpload(sandcastlePrefix, "Build/Sandcastle/", file)
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uploadRefDoc = async () => {
|
|
|
|
|
const files = await globby(["Build/Documentation/**"]);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
files.map((file) => {
|
|
|
|
|
return limit(() =>
|
|
|
|
|
readAndUpload(refDocPrefix, "Build/Documentation/", file)
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uploadCesiumViewer = async () => {
|
|
|
|
|
const files = await globby(["Build/CesiumViewer/**"]);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
files.map((file) => {
|
|
|
|
|
return limit(() =>
|
|
|
|
|
readAndUpload(cesiumViewerPrefix, "Build/CesiumViewer/", file)
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uploads = [
|
|
|
|
|
uploadSandcastle(),
|
|
|
|
|
uploadRefDoc(),
|
|
|
|
|
uploadCesiumViewer(),
|
|
|
|
|
deployCesiumRelease(bucketName, s3, errors),
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
const files = await globby(
|
|
|
|
|
[
|
|
|
|
|
"Apps/**",
|
|
|
|
|
"Build/**",
|
2022-10-07 01:24:27 +08:00
|
|
|
"!Build/CesiumDev/**",
|
2022-11-02 03:39:57 +08:00
|
|
|
"packages/**",
|
2022-10-06 00:31:17 +08:00
|
|
|
"Source/**",
|
|
|
|
|
"Specs/**",
|
|
|
|
|
"ThirdParty/**",
|
|
|
|
|
"*.md",
|
|
|
|
|
"favicon.ico",
|
|
|
|
|
"gulpfile.js",
|
|
|
|
|
"index.html",
|
|
|
|
|
"package.json",
|
|
|
|
|
"server.js",
|
|
|
|
|
"web.config",
|
|
|
|
|
"*.zip",
|
|
|
|
|
"*.tgz",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
dot: true, // include hidden files
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
uploads = files.map((file) => {
|
|
|
|
|
return limit(() => readAndUpload(`${uploadDirectory}/`, "", file));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await Promise.all(uploads);
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
console.log(
|
|
|
|
|
`Skipped ${skipped} files and successfully uploaded ${uploaded} files of ${
|
|
|
|
|
totalFiles - skipped
|
|
|
|
|
} files.`
|
|
|
|
|
);
|
|
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
if (!isProduction && existingBlobs.length >= 0) {
|
|
|
|
|
const objectsToDelete = [];
|
|
|
|
|
existingBlobs.forEach(function (file) {
|
|
|
|
|
// Don't delete generated zip files
|
|
|
|
|
if (!/\.(zip|tgz)$/.test(file)) {
|
|
|
|
|
objectsToDelete.push({ Key: file });
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-09-09 00:43:28 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
if (objectsToDelete.length > 0) {
|
|
|
|
|
console.log(`Cleaning ${objectsToDelete.length} files...`);
|
2018-06-01 22:22:31 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
// If more than 1000 files, we must issue multiple requests
|
|
|
|
|
const batches = [];
|
|
|
|
|
while (objectsToDelete.length > 1000) {
|
|
|
|
|
batches.push(objectsToDelete.splice(0, 1000));
|
|
|
|
|
}
|
|
|
|
|
batches.push(objectsToDelete);
|
|
|
|
|
|
|
|
|
|
const deleteObjects = async (objects) => {
|
|
|
|
|
try {
|
|
|
|
|
if (!dryRun) {
|
|
|
|
|
await s3
|
|
|
|
|
.deleteObjects({
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
Delete: {
|
|
|
|
|
Objects: objects,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
.promise();
|
|
|
|
|
}
|
2022-09-09 01:29:46 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
if (verbose) {
|
|
|
|
|
console.log(`Cleaned ${objects.length} files.`);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
errors.push(e);
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
2022-10-06 00:31:17 +08:00
|
|
|
};
|
2016-03-25 23:38:38 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
await Promise.all(batches.map(deleteObjects));
|
|
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (errors.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log("Errors: ");
|
|
|
|
|
errors.map(console.log);
|
|
|
|
|
return Promise.reject("There was an error while deploying Cesium");
|
2016-03-22 22:44:36 +08:00
|
|
|
}
|
2016-03-17 05:44:54 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
async function deployCesiumRelease(bucketName, s3, errors) {
|
|
|
|
|
const releaseDir = "cesiumjs/releases";
|
|
|
|
|
const quiet = process.env.TRAVIS;
|
|
|
|
|
|
|
|
|
|
let release;
|
|
|
|
|
try {
|
|
|
|
|
// Deploy any new releases
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
"https://api.github.com/repos/CesiumGS/cesium/releases/latest",
|
|
|
|
|
{
|
|
|
|
|
method: "GET",
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: process.env.TOKEN
|
|
|
|
|
? `token ${process.env.TOKEN}`
|
|
|
|
|
: undefined,
|
|
|
|
|
"User-Agent": "cesium.com-build",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const body = await response.json();
|
|
|
|
|
|
|
|
|
|
release = {
|
|
|
|
|
tag: body.tag_name,
|
|
|
|
|
name: body.name,
|
|
|
|
|
url: body.assets[0].browser_download_url,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await s3
|
|
|
|
|
.headObject({
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
Key: posix.join(releaseDir, release.tag, "cesium.zip"),
|
|
|
|
|
})
|
|
|
|
|
.promise();
|
|
|
|
|
console.log(
|
|
|
|
|
`Cesium version ${release.tag} up to date. Skipping release deployment.`
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// The current version is not uploaded
|
|
|
|
|
if (error.code === "NotFound") {
|
|
|
|
|
console.log("Updating cesium version...");
|
|
|
|
|
const data = await download(release.url);
|
|
|
|
|
// upload and unzip contents
|
|
|
|
|
const key = posix.join(releaseDir, release.tag, "cesium.zip");
|
|
|
|
|
await uploadObject(bucketName, s3, key, data, quiet);
|
|
|
|
|
const files = await decompress(data);
|
|
|
|
|
const limit = pLimit(5);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
files.map((file) => {
|
|
|
|
|
return limit(() => {
|
|
|
|
|
if (file.path.startsWith("Apps")) {
|
|
|
|
|
// skip uploading apps and sandcastle
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Upload to release directory
|
|
|
|
|
const key = posix.join(releaseDir, release.tag, file.path);
|
|
|
|
|
return uploadObject(bucketName, s3, key, file.data, quiet);
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// else, unexpected error
|
|
|
|
|
errors.push(error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function uploadObject(bucketName, s3, key, contents, quiet) {
|
|
|
|
|
if (!quiet) {
|
|
|
|
|
console.log(`Uploading ${key}...`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s3
|
|
|
|
|
.upload({
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
Key: key,
|
|
|
|
|
Body: contents,
|
|
|
|
|
ContentType: mime.getType(key) || undefined,
|
|
|
|
|
CacheControl: "public, max-age=1800",
|
|
|
|
|
})
|
|
|
|
|
.promise();
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-22 23:09:33 +08:00
|
|
|
function getMimeType(filename) {
|
2022-10-06 22:27:07 +08:00
|
|
|
//Remove page anchors from documentation, as mime does not properly handle them
|
|
|
|
|
filename = filename.split("#")[0];
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const mimeType = mime.getType(filename);
|
2020-05-16 07:17:07 +08:00
|
|
|
if (mimeType) {
|
|
|
|
|
//Compress everything except zipfiles, binary images, and video
|
2021-01-26 00:00:37 +08:00
|
|
|
let compress = !/^(image\/|video\/|application\/zip|application\/gzip)/i.test(
|
2020-05-16 07:17:07 +08:00
|
|
|
mimeType
|
|
|
|
|
);
|
|
|
|
|
if (mimeType === "image/svg+xml") {
|
|
|
|
|
compress = true;
|
2019-04-30 03:27:26 +08:00
|
|
|
}
|
2020-05-16 07:17:07 +08:00
|
|
|
return { type: mimeType, compress: compress };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Non-standard mime types not handled by mime
|
|
|
|
|
if (/\.(glsl|LICENSE|config|state)$/i.test(filename)) {
|
|
|
|
|
return { type: "text/plain", compress: true };
|
|
|
|
|
} else if (/\.(czml|topojson)$/i.test(filename)) {
|
|
|
|
|
return { type: "application/json", compress: true };
|
2021-06-23 02:49:04 +08:00
|
|
|
} else if (/\.tgz$/i.test(filename)) {
|
2020-05-16 07:17:07 +08:00
|
|
|
return { type: "application/octet-stream", compress: false };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle dotfiles, such as .jshintrc
|
2022-09-09 00:43:28 +08:00
|
|
|
const baseName = basename(filename);
|
2020-05-16 07:17:07 +08:00
|
|
|
if (baseName[0] === "." || baseName.indexOf(".") === -1) {
|
|
|
|
|
return { type: "text/plain", compress: true };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Everything else can be octet-stream compressed but print a warning
|
|
|
|
|
// if we introduce a type we aren't specifically handling.
|
|
|
|
|
if (!/\.(terrain|b3dm|geom|pnts|vctr|cmpt|i3dm|metadata)$/i.test(filename)) {
|
2022-02-03 04:40:21 +08:00
|
|
|
console.log(`Unknown mime type for ${filename}`);
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { type: "application/octet-stream", compress: true };
|
2016-03-22 23:09:33 +08:00
|
|
|
}
|
|
|
|
|
|
2016-03-22 04:29:43 +08:00
|
|
|
// get all files currently in bucket asynchronously
|
2022-09-09 00:43:28 +08:00
|
|
|
async function listAll(s3, bucketName, prefix, files, marker) {
|
2022-09-09 01:29:46 +08:00
|
|
|
const data = await s3
|
2020-05-16 07:17:07 +08:00
|
|
|
.listObjects({
|
|
|
|
|
Bucket: bucketName,
|
|
|
|
|
MaxKeys: 1000,
|
|
|
|
|
Prefix: prefix,
|
|
|
|
|
Marker: marker,
|
|
|
|
|
})
|
2022-09-09 00:43:28 +08:00
|
|
|
.promise();
|
|
|
|
|
const items = data.Contents;
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
files.push(items[i].Key);
|
|
|
|
|
}
|
2016-03-22 22:44:36 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (data.IsTruncated) {
|
|
|
|
|
// get next page of results
|
|
|
|
|
return listAll(s3, bucketName, prefix, files, files[files.length - 1]);
|
|
|
|
|
}
|
2016-03-22 04:29:43 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function deploySetVersion() {
|
|
|
|
|
const buildVersion = argv.buildVersion;
|
2020-05-16 07:17:07 +08:00
|
|
|
if (buildVersion) {
|
|
|
|
|
// NPM versions can only contain alphanumeric and hyphen characters
|
2022-09-15 23:06:28 +08:00
|
|
|
packageJson.version += `-${buildVersion.replace(/[^[0-9A-Za-z-]/g, "")}`;
|
2022-09-09 00:43:28 +08:00
|
|
|
return writeFile("package.json", JSON.stringify(packageJson, undefined, 2));
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2016-03-26 01:58:37 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function deployStatus() {
|
2020-05-16 07:17:07 +08:00
|
|
|
if (isTravisPullRequest()) {
|
|
|
|
|
console.log("Skipping deployment status for non-pull request.");
|
2022-09-09 00:43:28 +08:00
|
|
|
return;
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const status = argv.status;
|
|
|
|
|
const message = argv.message;
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-02-03 04:40:21 +08:00
|
|
|
const deployUrl = `${travisDeployUrl + process.env.TRAVIS_BRANCH}/`;
|
2022-09-09 00:43:28 +08:00
|
|
|
const zipUrl = `${deployUrl}Cesium-${version}.zip`;
|
|
|
|
|
const npmUrl = `${deployUrl}cesium-${version}.tgz`;
|
2022-02-03 04:40:21 +08:00
|
|
|
const coverageUrl = `${
|
|
|
|
|
travisDeployUrl + process.env.TRAVIS_BRANCH
|
|
|
|
|
}/Build/Coverage/index.html`;
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
return Promise.all([
|
2020-05-16 07:17:07 +08:00
|
|
|
setStatus(status, deployUrl, message, "deployment"),
|
|
|
|
|
setStatus(status, zipUrl, message, "zip file"),
|
|
|
|
|
setStatus(status, npmUrl, message, "npm package"),
|
2022-09-09 00:43:28 +08:00
|
|
|
setStatus(status, coverageUrl, message, "coverage results"),
|
|
|
|
|
]);
|
|
|
|
|
}
|
2016-03-26 01:58:37 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
async function setStatus(state, targetUrl, description, context) {
|
2020-05-16 07:17:07 +08:00
|
|
|
// skip if the environment does not have the token
|
|
|
|
|
if (!process.env.TOKEN) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const body = {
|
|
|
|
|
state: state,
|
|
|
|
|
target_url: targetUrl,
|
|
|
|
|
description: description,
|
|
|
|
|
context: context,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
`https://api.github.com/repos/${process.env.TRAVIS_REPO_SLUG}/statuses/${process.env.TRAVIS_COMMIT}`,
|
|
|
|
|
{
|
|
|
|
|
method: "post",
|
|
|
|
|
body: JSON.stringify(body),
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
Authorization: `token ${process.env.TOKEN}`,
|
|
|
|
|
"User-Agent": "Cesium",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response.json();
|
2016-03-26 01:58:37 +08:00
|
|
|
}
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
/**
|
|
|
|
|
* Generates coverage report.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} options An object with the following properties:
|
|
|
|
|
* @param {String} options.outputDirectory The output directory for the generated build artifacts.
|
|
|
|
|
* @param {String} options.coverageDirectory The path where the coverage reports should be saved to.
|
|
|
|
|
* @param {String} options.specList The path to the spec list for the package.
|
|
|
|
|
* @param {RegExp} options.filter The filter for finding which files should be instrumented.
|
|
|
|
|
* @param {Boolean} [options.webglStub=false] True if WebGL stub should be used when running tests.
|
|
|
|
|
* @param {Boolean} [options.suppressPassed=false] True if output should be suppressed for tests that pass.
|
|
|
|
|
* @param {Boolean} [options.failTaskOnError=false] True if the gulp task should fail on errors in the tests.
|
|
|
|
|
* @param {String} options.workspace The name of the workspace, if any.
|
|
|
|
|
*/
|
|
|
|
|
export async function runCoverage(options) {
|
|
|
|
|
const webglStub = options.webglStub ?? false;
|
|
|
|
|
const suppressPassed = options.suppressPassed ?? false;
|
|
|
|
|
const failTaskOnError = options.failTaskOnError ?? false;
|
|
|
|
|
const workspace = options.workspace;
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const folders = [];
|
|
|
|
|
let browsers = ["Chrome"];
|
2020-05-16 07:17:07 +08:00
|
|
|
if (argv.browsers) {
|
|
|
|
|
browsers = argv.browsers.split(",");
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const instrumenter = createInstrumenter({
|
2022-06-02 23:24:07 +08:00
|
|
|
esModules: true,
|
|
|
|
|
});
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Setup plugin to use instrumenter on source files.
|
|
|
|
|
|
2022-06-02 23:24:07 +08:00
|
|
|
const instrumentPlugin = {
|
2022-06-09 04:05:17 +08:00
|
|
|
name: "instrument",
|
2022-06-02 23:24:07 +08:00
|
|
|
setup: (build) => {
|
|
|
|
|
build.onLoad(
|
|
|
|
|
{
|
2022-11-02 03:39:57 +08:00
|
|
|
filter: options.filter,
|
2019-08-28 09:10:14 +08:00
|
|
|
},
|
2022-06-02 23:24:07 +08:00
|
|
|
async (args) => {
|
2022-09-09 00:43:28 +08:00
|
|
|
const source = await readFile(args.path, { encoding: "utf8" });
|
2022-06-02 23:24:07 +08:00
|
|
|
try {
|
|
|
|
|
const generatedCode = instrumenter.instrumentSync(
|
|
|
|
|
source,
|
|
|
|
|
args.path
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return { contents: generatedCode };
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return {
|
|
|
|
|
errors: {
|
|
|
|
|
text: e.message,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
2020-05-16 07:17:07 +08:00
|
|
|
},
|
2022-06-02 23:24:07 +08:00
|
|
|
};
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
const karmaBundle = join(options.outputDirectory, "karma-main.js");
|
|
|
|
|
await esbuild({
|
|
|
|
|
entryPoints: ["Specs/karma-main.js"],
|
|
|
|
|
bundle: true,
|
|
|
|
|
sourcemap: true,
|
|
|
|
|
format: "esm",
|
|
|
|
|
target: "es2020",
|
|
|
|
|
external: ["https", "http", "url", "zlib"],
|
|
|
|
|
outfile: karmaBundle,
|
|
|
|
|
logLevel: "error", // print errors immediately, and collect warnings so we can filter out known ones
|
|
|
|
|
});
|
2022-06-02 23:24:07 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Generate instrumented bundle for Specs.
|
|
|
|
|
|
|
|
|
|
const specListBundle = join(options.outputDirectory, "SpecList.js");
|
|
|
|
|
await esbuild({
|
|
|
|
|
entryPoints: [options.specList],
|
2022-06-02 23:24:07 +08:00
|
|
|
bundle: true,
|
|
|
|
|
sourcemap: true,
|
2022-11-02 03:39:57 +08:00
|
|
|
format: "esm",
|
2022-06-23 02:28:19 +08:00
|
|
|
target: "es2020",
|
2022-06-02 23:24:07 +08:00
|
|
|
external: ["https", "http", "url", "zlib"],
|
2022-11-02 03:39:57 +08:00
|
|
|
outfile: specListBundle,
|
2022-06-02 23:24:07 +08:00
|
|
|
plugins: [instrumentPlugin],
|
|
|
|
|
logLevel: "error", // print errors immediately, and collect warnings so we can filter out known ones
|
|
|
|
|
});
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
let files = [
|
|
|
|
|
{
|
|
|
|
|
pattern: karmaBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
pattern: specListBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
// Static assets are always served from the shared/combined folders.
|
|
|
|
|
{ pattern: "Build/CesiumUnminified/**", included: false },
|
|
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let proxies;
|
|
|
|
|
if (workspace) {
|
|
|
|
|
// Setup files and proxies for the engine package first, since it is the lowest level dependency.
|
|
|
|
|
files = [
|
|
|
|
|
{
|
|
|
|
|
pattern: karmaBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
pattern: specListBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Build/Workers/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/Assets/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/ThirdParty/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/Widget/*.css", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**", included: false },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
proxies = {
|
|
|
|
|
"/base/Build/CesiumUnminified/Assets/":
|
|
|
|
|
"/base/packages/engine/Source/Assets/",
|
|
|
|
|
"/base/Build/CesiumUnminified/ThirdParty/":
|
|
|
|
|
"/base/packages/engine/Source/ThirdParty/",
|
|
|
|
|
"/base/Build/CesiumUnminified/Widgets/CesiumWidget/":
|
|
|
|
|
"/base/packages/engine/Source/Widget/",
|
|
|
|
|
"/base/Build/CesiumUnminified/Workers/":
|
|
|
|
|
"/base/packages/engine/Build/Workers/",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup Karma config.
|
2022-06-02 23:24:07 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const config = await karma.config.parseConfig(
|
|
|
|
|
karmaConfigFile,
|
|
|
|
|
{
|
|
|
|
|
configFile: karmaConfigFile,
|
|
|
|
|
browsers: browsers,
|
|
|
|
|
specReporter: {
|
|
|
|
|
suppressErrorSummary: false,
|
|
|
|
|
suppressFailed: false,
|
|
|
|
|
suppressPassed: suppressPassed,
|
|
|
|
|
suppressSkipped: true,
|
2022-06-02 23:24:07 +08:00
|
|
|
},
|
2022-11-02 03:39:57 +08:00
|
|
|
files: files,
|
|
|
|
|
proxies: proxies,
|
2022-09-09 00:43:28 +08:00
|
|
|
reporters: ["spec", "coverage"],
|
|
|
|
|
coverageReporter: {
|
2022-11-02 03:39:57 +08:00
|
|
|
dir: options.coverageDirectory,
|
2022-09-09 00:43:28 +08:00
|
|
|
subdir: function (browserName) {
|
|
|
|
|
folders.push(browserName);
|
|
|
|
|
return browserName;
|
|
|
|
|
},
|
|
|
|
|
includeAllSources: true,
|
2022-08-06 00:57:00 +08:00
|
|
|
},
|
2022-09-09 00:43:28 +08:00
|
|
|
client: {
|
|
|
|
|
captureConsole: false,
|
|
|
|
|
args: [
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
webglStub,
|
|
|
|
|
undefined,
|
|
|
|
|
],
|
2022-08-06 00:57:00 +08:00
|
|
|
},
|
|
|
|
|
},
|
2022-09-09 00:43:28 +08:00
|
|
|
{ promiseConfig: true, throwErrors: true }
|
|
|
|
|
);
|
2022-06-02 23:24:07 +08:00
|
|
|
|
2022-08-06 00:57:00 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
2022-09-09 00:43:28 +08:00
|
|
|
const server = new karma.Server(config, function doneCallback(e) {
|
2022-08-06 00:57:00 +08:00
|
|
|
let html = "<!doctype html><html><body><ul>";
|
|
|
|
|
folders.forEach(function (folder) {
|
|
|
|
|
html += `<li><a href="${encodeURIComponent(
|
|
|
|
|
folder
|
|
|
|
|
)}/index.html">${folder}</a></li>`;
|
|
|
|
|
});
|
|
|
|
|
html += "</ul></body></html>";
|
2022-11-02 03:39:57 +08:00
|
|
|
writeFileSync(join(options.coverageDirectory, "index.html"), html);
|
2022-06-02 23:24:07 +08:00
|
|
|
|
2022-08-06 00:57:00 +08:00
|
|
|
if (!process.env.TRAVIS) {
|
|
|
|
|
folders.forEach(function (dir) {
|
2022-11-02 03:39:57 +08:00
|
|
|
open(join(options.coverageDirectory, `${dir}/index.html`));
|
2022-08-06 00:57:00 +08:00
|
|
|
});
|
|
|
|
|
}
|
2022-06-02 23:24:07 +08:00
|
|
|
|
2022-08-06 00:57:00 +08:00
|
|
|
if (failTaskOnError && e) {
|
|
|
|
|
reject(e);
|
|
|
|
|
return;
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
2022-08-06 00:57:00 +08:00
|
|
|
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
2022-09-09 00:43:28 +08:00
|
|
|
server.start();
|
2022-06-02 23:24:07 +08:00
|
|
|
});
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2021-01-26 00:00:37 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export async function coverage() {
|
|
|
|
|
let workspace = argv.workspace;
|
|
|
|
|
if (workspace) {
|
|
|
|
|
workspace = workspace.replaceAll(`@${scope}/`, ``);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (workspace === "engine") {
|
|
|
|
|
return runCoverage({
|
|
|
|
|
outputDirectory: "packages/engine/Build/Instrumented",
|
|
|
|
|
coverageDirectory: "packages/engine/Build/Coverage",
|
|
|
|
|
specList: "packages/engine/Specs/SpecList.js",
|
|
|
|
|
filter: /packages(\\|\/)engine(\\|\/)Source((\\|\/)\w+)+\.js$/,
|
|
|
|
|
webglStub: argv.webglStub,
|
|
|
|
|
suppressPassed: argv.suppressPassed,
|
|
|
|
|
failTaskOnError: argv.failTaskOnError,
|
|
|
|
|
workspace: workspace,
|
|
|
|
|
});
|
|
|
|
|
} else if (workspace === "widgets") {
|
|
|
|
|
return runCoverage({
|
|
|
|
|
outputDirectory: "packages/widgets/Build/Instrumented",
|
|
|
|
|
coverageDirectory: "packages/widgets/Build/Coverage",
|
|
|
|
|
specList: "packages/widgets/Specs/SpecList.js",
|
|
|
|
|
filter: /packages(\\|\/)widgets(\\|\/)Source((\\|\/)\w+)+\.js$/,
|
|
|
|
|
webglStub: argv.webglStub,
|
|
|
|
|
suppressPassed: argv.suppressPassed,
|
|
|
|
|
failTaskOnError: argv.failTaskOnError,
|
|
|
|
|
workspace: workspace,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return runCoverage({
|
|
|
|
|
outputDirectory: "Build/Instrumented",
|
|
|
|
|
coverageDirectory: "Build/Coverage",
|
|
|
|
|
specList: "Specs/SpecList.js",
|
|
|
|
|
filter: /packages(\\|\/)(engine|widgets)(\\|\/)Source((\\|\/)\w+)+\.js$/,
|
|
|
|
|
webglStub: argv.webglStub,
|
|
|
|
|
suppressPassed: argv.suppressPassed,
|
|
|
|
|
failTaskOnError: argv.failTaskOnError,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function test() {
|
2021-01-26 00:00:37 +08:00
|
|
|
const enableAllBrowsers = argv.all ? true : false;
|
|
|
|
|
const includeCategory = argv.include ? argv.include : "";
|
|
|
|
|
const excludeCategory = argv.exclude ? argv.exclude : "";
|
|
|
|
|
const webglValidation = argv.webglValidation ? argv.webglValidation : false;
|
|
|
|
|
const webglStub = argv.webglStub ? argv.webglStub : false;
|
|
|
|
|
const release = argv.release ? argv.release : false;
|
|
|
|
|
const failTaskOnError = argv.failTaskOnError ? argv.failTaskOnError : false;
|
|
|
|
|
const suppressPassed = argv.suppressPassed ? argv.suppressPassed : false;
|
2022-03-08 02:28:24 +08:00
|
|
|
const debug = argv.debug ? false : true;
|
2022-08-17 04:43:37 +08:00
|
|
|
const debugCanvasWidth = argv.debugCanvasWidth;
|
|
|
|
|
const debugCanvasHeight = argv.debugCanvasHeight;
|
2022-03-08 02:28:24 +08:00
|
|
|
const includeName = argv.includeName ? argv.includeName : "";
|
2021-01-26 00:00:37 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
let workspace = argv.workspace;
|
|
|
|
|
if (workspace) {
|
|
|
|
|
workspace = workspace.replaceAll(`@${scope}/`, ``);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
let browsers = ["Chrome"];
|
2020-05-16 07:17:07 +08:00
|
|
|
if (argv.browsers) {
|
|
|
|
|
browsers = argv.browsers.split(",");
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
let files = [
|
2020-05-16 07:17:07 +08:00
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
2022-06-02 23:49:39 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
2022-05-28 03:27:47 +08:00
|
|
|
{ pattern: "Build/CesiumUnminified/Cesium.js", included: true },
|
|
|
|
|
{ pattern: "Build/CesiumUnminified/Cesium.js.map", included: false },
|
2022-05-27 22:42:45 +08:00
|
|
|
{ pattern: "Build/CesiumUnminified/**", included: false },
|
|
|
|
|
{ pattern: "Build/Specs/karma-main.js", included: true, type: "module" },
|
|
|
|
|
{ pattern: "Build/Specs/SpecList.js", included: true, type: "module" },
|
2022-07-06 22:21:22 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**", included: false },
|
2020-05-16 07:17:07 +08:00
|
|
|
];
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
let proxies;
|
|
|
|
|
if (workspace) {
|
|
|
|
|
// Setup files and proxies for the engine package first, since it is the lowest level dependency.
|
|
|
|
|
|
|
|
|
|
files = [
|
|
|
|
|
{
|
|
|
|
|
pattern: `packages/${workspace}/Build/Specs/karma-main.js`,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
pattern: `packages/${workspace}/Build/Specs/SpecList.js`,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Build/Workers/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/Assets/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/ThirdParty/**", included: false },
|
|
|
|
|
{ pattern: "packages/engine/Source/Widget/*.css", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**", included: false },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
proxies = {
|
|
|
|
|
"/base/Build/CesiumUnminified/Assets/":
|
|
|
|
|
"/base/packages/engine/Source/Assets/",
|
|
|
|
|
"/base/Build/CesiumUnminified/ThirdParty/":
|
|
|
|
|
"/base/packages/engine/Source/ThirdParty/",
|
|
|
|
|
"/base/Build/CesiumUnminified/Widgets/CesiumWidget/":
|
|
|
|
|
"/base/packages/engine/Source/Widget/",
|
|
|
|
|
"/base/Build/CesiumUnminified/Workers/":
|
|
|
|
|
"/base/packages/engine/Build/Workers/",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-16 07:17:07 +08:00
|
|
|
if (release) {
|
|
|
|
|
files = [
|
|
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
2022-06-02 23:49:39 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
2022-06-01 04:44:36 +08:00
|
|
|
{ pattern: "Specs/ThirdParty/**", included: false, type: "module" },
|
2022-05-28 03:27:47 +08:00
|
|
|
{ pattern: "Build/Cesium/Cesium.js", included: true },
|
|
|
|
|
{ pattern: "Build/Cesium/Cesium.js.map", included: false },
|
2020-05-16 07:17:07 +08:00
|
|
|
{ pattern: "Build/Cesium/**", included: false },
|
|
|
|
|
{ pattern: "Build/Specs/karma-main.js", included: true },
|
2022-05-27 22:42:45 +08:00
|
|
|
{ pattern: "Build/Specs/SpecList.js", included: true, type: "module" },
|
2022-07-06 22:21:22 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**", included: false },
|
2016-02-11 00:57:00 +08:00
|
|
|
];
|
2020-05-16 07:17:07 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const config = await karma.config.parseConfig(
|
|
|
|
|
karmaConfigFile,
|
|
|
|
|
{
|
|
|
|
|
port: 9876,
|
|
|
|
|
singleRun: debug,
|
|
|
|
|
browsers: browsers,
|
|
|
|
|
specReporter: {
|
|
|
|
|
suppressErrorSummary: false,
|
|
|
|
|
suppressFailed: false,
|
|
|
|
|
suppressPassed: suppressPassed,
|
|
|
|
|
suppressSkipped: true,
|
|
|
|
|
},
|
|
|
|
|
detectBrowsers: {
|
|
|
|
|
enabled: enableAllBrowsers,
|
|
|
|
|
},
|
|
|
|
|
logLevel: verbose ? karma.constants.LOG_INFO : karma.constants.LOG_ERROR,
|
|
|
|
|
files: files,
|
2022-11-02 03:39:57 +08:00
|
|
|
proxies: proxies,
|
2022-09-09 00:43:28 +08:00
|
|
|
client: {
|
|
|
|
|
captureConsole: verbose,
|
|
|
|
|
args: [
|
|
|
|
|
includeCategory,
|
|
|
|
|
excludeCategory,
|
|
|
|
|
"--grep",
|
|
|
|
|
includeName,
|
|
|
|
|
webglValidation,
|
|
|
|
|
webglStub,
|
|
|
|
|
release,
|
|
|
|
|
debugCanvasWidth,
|
|
|
|
|
debugCanvasHeight,
|
|
|
|
|
],
|
|
|
|
|
},
|
2022-06-01 00:53:20 +08:00
|
|
|
},
|
2022-09-09 00:43:28 +08:00
|
|
|
{ promiseConfig: true, throwErrors: true }
|
|
|
|
|
);
|
|
|
|
|
|
2022-10-11 01:55:56 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
2022-09-09 00:43:28 +08:00
|
|
|
const server = new karma.Server(config, function doneCallback(exitCode) {
|
2022-10-11 01:55:56 +08:00
|
|
|
if (failTaskOnError && exitCode) {
|
|
|
|
|
reject(exitCode);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resolve();
|
2022-09-09 00:43:28 +08:00
|
|
|
});
|
|
|
|
|
server.start();
|
2022-06-01 00:53:20 +08:00
|
|
|
});
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2022-11-02 03:39:57 +08:00
|
|
|
/**
|
|
|
|
|
* Generates TypeScript definition file (.d.ts) for a package.
|
|
|
|
|
*
|
|
|
|
|
* @param {*} workspaceName
|
|
|
|
|
* @param {String} definitionsPath The path of the .d.ts file to generate.
|
|
|
|
|
* @param {*} configurationPath
|
|
|
|
|
* @param {*} processSourceFunc
|
|
|
|
|
* @param {*} processModulesFunc
|
|
|
|
|
* @param {*} importModules
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
function generateTypeScriptDefinitions(
|
|
|
|
|
workspaceName,
|
|
|
|
|
definitionsPath,
|
|
|
|
|
configurationPath,
|
|
|
|
|
processSourceFunc,
|
|
|
|
|
processModulesFunc,
|
|
|
|
|
importModules
|
|
|
|
|
) {
|
|
|
|
|
// Run JSDoc with tsd-jsdoc to generate an initial definition file.
|
|
|
|
|
execSync(`npx jsdoc --configure ${configurationPath}`, {
|
|
|
|
|
stdio: `inherit`,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let source = readFileSync(definitionsPath).toString();
|
|
|
|
|
|
|
|
|
|
if (processSourceFunc) {
|
|
|
|
|
source = processSourceFunc(definitionsPath, source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The next step is to find the list of Cesium modules exported by the Cesium API
|
|
|
|
|
// So that we can map these modules with a link back to their original source file.
|
|
|
|
|
|
|
|
|
|
const regex = /^declare[ const ]*(function|class|namespace|enum) (.+)/gm;
|
|
|
|
|
let matches;
|
|
|
|
|
let publicModules = new Set();
|
|
|
|
|
//eslint-disable-next-line no-cond-assign
|
|
|
|
|
while ((matches = regex.exec(source))) {
|
|
|
|
|
const moduleName = matches[2].match(/([^<\s|\(]+)/);
|
|
|
|
|
publicModules.add(moduleName[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processModulesFunc) {
|
|
|
|
|
publicModules = processModulesFunc(publicModules);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fix up the output to match what we need
|
|
|
|
|
// declare => export since we are wrapping everything in a namespace
|
|
|
|
|
// CesiumMath => Math (because no CesiumJS build step would be complete without special logic for the Math class)
|
|
|
|
|
// Fix up the WebGLConstants aliasing we mentioned above by simply unquoting the strings.
|
|
|
|
|
source = source
|
|
|
|
|
.replace(/^declare /gm, "export ")
|
|
|
|
|
.replace(/module "Math"/gm, "namespace Math")
|
|
|
|
|
.replace(/CesiumMath/gm, "Math")
|
|
|
|
|
.replace(/Number\[]/gm, "number[]") // Workaround https://github.com/englercj/tsd-jsdoc/issues/117
|
|
|
|
|
.replace(/String\[]/gm, "string[]")
|
|
|
|
|
.replace(/Boolean\[]/gm, "boolean[]")
|
|
|
|
|
.replace(/Object\[]/gm, "object[]")
|
|
|
|
|
.replace(/<Number>/gm, "<number>")
|
|
|
|
|
.replace(/<String>/gm, "<string>")
|
|
|
|
|
.replace(/<Boolean>/gm, "<boolean>")
|
|
|
|
|
.replace(/<Object>/gm, "<object>")
|
|
|
|
|
.replace(
|
|
|
|
|
/= "WebGLConstants\.(.+)"/gm,
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
|
(match, p1) => `= WebGLConstants.${p1}`
|
|
|
|
|
)
|
|
|
|
|
// Strip const enums which can cause errors - https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls
|
|
|
|
|
.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4");
|
|
|
|
|
|
|
|
|
|
// Wrap the source to actually be inside of a declared cesium module
|
|
|
|
|
// and add any workaround and private utility types.
|
|
|
|
|
source = `declare module "@${scope}/${workspaceName}" {
|
|
|
|
|
${source}
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
if (importModules) {
|
|
|
|
|
let imports = "";
|
|
|
|
|
Object.keys(importModules).forEach((workspace) => {
|
|
|
|
|
const workspaceModules = Array.from(importModules[workspace]).filter(
|
|
|
|
|
(importModule) => source.indexOf(importModule) !== -1
|
|
|
|
|
);
|
|
|
|
|
imports += `import { ${workspaceModules.join(
|
|
|
|
|
",\n"
|
|
|
|
|
)} } from "@${scope}/${workspace}";\n`;
|
|
|
|
|
});
|
|
|
|
|
source = imports + source;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the final source file back out
|
|
|
|
|
writeFileSync(definitionsPath, source);
|
|
|
|
|
|
|
|
|
|
return Promise.resolve(publicModules);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function processEngineModules(modules) {
|
|
|
|
|
// Math shows up as "Math" because of it's aliasing from CesiumMath and namespace collision with actual Math
|
|
|
|
|
// It fails the above regex so just add it directly here.
|
|
|
|
|
modules.add("Math");
|
|
|
|
|
return modules;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function processEngineSource(definitionsPath, source) {
|
|
|
|
|
// All of our enum assignments that alias to WebGLConstants, such as PixelDatatype.js
|
|
|
|
|
// end up as enum strings instead of actually mapping values to WebGLConstants.
|
|
|
|
|
// We fix this with a simple regex replace later on, but it means the
|
|
|
|
|
// WebGLConstants constants enum needs to be defined in the file before it can
|
|
|
|
|
// be used. This block of code reads in the TS file, finds the WebGLConstants
|
|
|
|
|
// declaration, and then writes the file back out (in memory to source) with
|
|
|
|
|
// WebGLConstants being the first module.
|
|
|
|
|
const node = typeScript.createSourceFile(
|
|
|
|
|
definitionsPath,
|
|
|
|
|
source,
|
|
|
|
|
typeScript.ScriptTarget.Latest
|
|
|
|
|
);
|
|
|
|
|
let firstNode;
|
|
|
|
|
node.forEachChild((child) => {
|
|
|
|
|
if (
|
|
|
|
|
typeScript.SyntaxKind[child.kind] === "EnumDeclaration" &&
|
|
|
|
|
child.name.escapedText === "WebGLConstants"
|
|
|
|
|
) {
|
|
|
|
|
firstNode = child;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const printer = typeScript.createPrinter({
|
|
|
|
|
removeComments: false,
|
|
|
|
|
newLine: typeScript.NewLineKind.LineFeed,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let newSource = "";
|
|
|
|
|
newSource += printer.printNode(
|
|
|
|
|
typeScript.EmitHint.Unspecified,
|
|
|
|
|
firstNode,
|
|
|
|
|
node
|
|
|
|
|
);
|
|
|
|
|
newSource += "\n\n";
|
|
|
|
|
node.forEachChild((child) => {
|
|
|
|
|
if (
|
|
|
|
|
typeScript.SyntaxKind[child.kind] !== "EnumDeclaration" ||
|
|
|
|
|
child.name.escapedText !== "WebGLConstants"
|
|
|
|
|
) {
|
|
|
|
|
newSource += printer.printNode(
|
|
|
|
|
typeScript.EmitHint.Unspecified,
|
|
|
|
|
child,
|
|
|
|
|
node
|
|
|
|
|
);
|
|
|
|
|
newSource += "\n\n";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Manually add a type definition from Viewer to avoid circular dependency
|
|
|
|
|
// with the widgets package. This will no longer be needed past Cesium 1.100.
|
|
|
|
|
newSource += `
|
|
|
|
|
/**
|
|
|
|
|
* @property scene - The scene in the widget.
|
|
|
|
|
*/
|
|
|
|
|
export type Viewer = {
|
|
|
|
|
scene: Scene;
|
|
|
|
|
};
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
return newSource;
|
|
|
|
|
}
|
2016-02-06 03:11:30 +08:00
|
|
|
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
function createTypeScriptDefinitions() {
|
|
|
|
|
// Run jsdoc with tsd-jsdoc to generate an initial Cesium.d.ts file.
|
2022-09-09 00:43:28 +08:00
|
|
|
execSync("npx jsdoc --configure Tools/jsdoc/ts-conf.json", {
|
2020-06-01 10:38:02 +08:00
|
|
|
stdio: "inherit",
|
|
|
|
|
});
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
let source = readFileSync("Source/Cesium.d.ts").toString();
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
|
|
|
|
|
// All of our enum assignments that alias to WebGLConstants, such as PixelDatatype.js
|
|
|
|
|
// end up as enum strings instead of actually mapping values to WebGLConstants.
|
|
|
|
|
// We fix this with a simple regex replace later on, but it means the
|
|
|
|
|
// WebGLConstants constants enum needs to be defined in the file before it can
|
|
|
|
|
// be used. This block of code reads in the TS file, finds the WebGLConstants
|
|
|
|
|
// declaration, and then writes the file back out (in memory to source) with
|
|
|
|
|
// WebGLConstants being the first module.
|
2022-09-09 00:43:28 +08:00
|
|
|
const node = typeScript.createSourceFile(
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
"Source/Cesium.d.ts",
|
|
|
|
|
source,
|
2022-09-09 00:43:28 +08:00
|
|
|
typeScript.ScriptTarget.Latest
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
);
|
|
|
|
|
let firstNode;
|
|
|
|
|
node.forEachChild((child) => {
|
|
|
|
|
if (
|
2022-09-09 00:43:28 +08:00
|
|
|
typeScript.SyntaxKind[child.kind] === "EnumDeclaration" &&
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
child.name.escapedText === "WebGLConstants"
|
|
|
|
|
) {
|
|
|
|
|
firstNode = child;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const printer = typeScript.createPrinter({
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
removeComments: false,
|
2022-09-09 00:43:28 +08:00
|
|
|
newLine: typeScript.NewLineKind.LineFeed,
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let newSource = "";
|
|
|
|
|
newSource += printer.printNode(
|
2022-09-09 00:43:28 +08:00
|
|
|
typeScript.EmitHint.Unspecified,
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
firstNode,
|
|
|
|
|
node
|
|
|
|
|
);
|
2021-05-02 22:50:28 +08:00
|
|
|
newSource += "\n\n";
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
node.forEachChild((child) => {
|
|
|
|
|
if (
|
2022-09-09 00:43:28 +08:00
|
|
|
typeScript.SyntaxKind[child.kind] !== "EnumDeclaration" ||
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
child.name.escapedText !== "WebGLConstants"
|
|
|
|
|
) {
|
|
|
|
|
newSource += printer.printNode(
|
2022-09-09 00:43:28 +08:00
|
|
|
typeScript.EmitHint.Unspecified,
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
child,
|
|
|
|
|
node
|
|
|
|
|
);
|
|
|
|
|
newSource += "\n\n";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
source = newSource;
|
|
|
|
|
|
|
|
|
|
// The next step is to find the list of Cesium modules exported by the Cesium API
|
|
|
|
|
// So that we can map these modules with a link back to their original source file.
|
|
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const regex = /^declare (function|class|namespace|enum) (.+)/gm;
|
|
|
|
|
let matches;
|
|
|
|
|
const publicModules = new Set();
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
//eslint-disable-next-line no-cond-assign
|
|
|
|
|
while ((matches = regex.exec(source))) {
|
2022-04-13 17:50:14 +08:00
|
|
|
const moduleName = matches[2].match(/([^<\s|\(]+)/);
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
publicModules.add(moduleName[1]);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-01 08:41:44 +08:00
|
|
|
// Math shows up as "Math" because of it's aliasing from CesiumMath and namespace collision with actual Math
|
|
|
|
|
// It fails the above regex so just add it directly here.
|
|
|
|
|
publicModules.add("Math");
|
|
|
|
|
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
// Fix up the output to match what we need
|
|
|
|
|
// declare => export since we are wrapping everything in a namespace
|
|
|
|
|
// CesiumMath => Math (because no CesiumJS build step would be complete without special logic for the Math class)
|
|
|
|
|
// Fix up the WebGLConstants aliasing we mentioned above by simply unquoting the strings.
|
|
|
|
|
source = source
|
|
|
|
|
.replace(/^declare /gm, "export ")
|
2020-05-30 13:45:28 +08:00
|
|
|
.replace(/module "Math"/gm, "namespace Math")
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
.replace(/CesiumMath/gm, "Math")
|
2020-05-28 07:28:24 +08:00
|
|
|
.replace(/Number\[]/gm, "number[]") // Workaround https://github.com/englercj/tsd-jsdoc/issues/117
|
2020-05-28 19:43:54 +08:00
|
|
|
.replace(/String\[]/gm, "string[]")
|
|
|
|
|
.replace(/Boolean\[]/gm, "boolean[]")
|
|
|
|
|
.replace(/Object\[]/gm, "object[]")
|
2020-05-30 05:13:31 +08:00
|
|
|
.replace(/<Number>/gm, "<number>")
|
|
|
|
|
.replace(/<String>/gm, "<string>")
|
|
|
|
|
.replace(/<Boolean>/gm, "<boolean>")
|
|
|
|
|
.replace(/<Object>/gm, "<object>")
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
.replace(
|
|
|
|
|
/= "WebGLConstants\.(.+)"/gm,
|
2021-01-26 00:00:37 +08:00
|
|
|
// eslint-disable-next-line no-unused-vars
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
(match, p1) => `= WebGLConstants.${p1}`
|
2022-02-04 04:56:14 +08:00
|
|
|
)
|
|
|
|
|
// Strip const enums which can cause errors - https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls
|
|
|
|
|
.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4");
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
|
2020-05-28 07:04:55 +08:00
|
|
|
// Wrap the source to actually be inside of a declared cesium module
|
2020-05-28 20:19:38 +08:00
|
|
|
// and add any workaround and private utility types.
|
2020-05-28 07:04:55 +08:00
|
|
|
source = `declare module "cesium" {
|
|
|
|
|
${source}
|
2020-05-28 10:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
`;
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
|
|
|
|
|
// Write the final source file back out
|
2022-09-09 00:43:28 +08:00
|
|
|
writeFileSync("Source/Cesium.d.ts", source);
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
|
|
|
|
|
// Use tsc to compile it and make sure it is valid
|
2022-09-09 00:43:28 +08:00
|
|
|
execSync("npx tsc -p Tools/jsdoc/tsconfig.json", {
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
stdio: "inherit",
|
|
|
|
|
});
|
|
|
|
|
|
2020-06-04 01:08:24 +08:00
|
|
|
// Also compile our smokescreen to make sure interfaces work as expected.
|
2022-09-09 00:43:28 +08:00
|
|
|
execSync("npx tsc -p Specs/TypeScript/tsconfig.json", {
|
2020-06-04 01:08:24 +08:00
|
|
|
stdio: "inherit",
|
|
|
|
|
});
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
return Promise.resolve();
|
Generate official TypeScript type definitions
It's been a long requested feature for us to have official TypeScript type
definitions. While the community has done a yeoman's job of manually
supporting various efforts, the most recent incarnation of which is
`@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code
base makes manual maintenance a Sisyphean task.
Thankfully, our decision to maintain meticulous JSDoc API documentation
continues to pay dividends and is what makes automatically generating
TypeScript definitions possible. Using the excellent
https://github.com/englercj/tsd-jsdoc project we can now automatically
generate and even partially validate official definitions as part of the
build process. (Thanks to @bampakoa who contributed some early PRs to both
CesiumJS and tsd-jsdoc over a year ago and is how I learned about
tsd-jsdoc)
While tsd-jsdoc output is mostly where we need it to be, we do
post-processing on it as well. This lets us clean up the output and also
make sure these definitions work whether users include cesium via module,
i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e.
`'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also
some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR
into that project from us. The post-processing is part typescript compiler
API, part string manipulation. It works and is straightforward but we might
want to go full TS api in the future if we decide we need to do more
complicated tasks. The output of tsd-jsdoc is currently a little noisy
because of some incorrect error reporting, but I'm talking with the
maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them
fixed. No need to hold up this PR for it though.
The definition is generated as a single `Cesium.d.ts` file in Source, so it
lives alongside Cesium.js. It is ignored by git but generated by a separate
`build-ts` task as part of CI and makeZipFile. This task also validates the
file by compiling it with TypeScript, so if a developer does anything too
egregious, the build will fail. Definitions are automatically included in
our npm packages and release zips and will be automatically used by IDEs
thanks to the `types` setting in package.json. This means that IDEs such as
VS Code will prefer these types over the existing `@types/cesium` version
by default.
I didn't want to slow the `build` step down, so I made this a separate
step, but in the future we could consider running it by default and we
could also unignore this file in Git so that PR reviewers can see the
impact, if any, our code changes have on the generated definitions. This
might be a good idea as an additional sanity check and should only actually
change when the public API itself changes. But the issue would be
remembering to run it before submitting the code (or we could use git hooks
I suppose?) I just don't want to slow down devs so I'm hesitant to do
anything like this out of the gate. We can definitely revisit in the
future.
A particular exciting thing about this approach is that it exposed a ton of
badness in our current JSDoc markup, which is now fixed. Previously, our
only concern was "does the doc look right" and we didn't pay attention to
whether the meta information generated by JSDoc correctly captured type
information (because up until it didn't matter). We leaned particular hard
on `@exports` which acted as a catch-all but has now been completely
removed from the codebase. All this means is that our documentation as a
whole has now improved greatly and will continue to be maintained at this
new higher level thanks to incorporating TS definition creation into our
pipeline!
One minor caveat here is that obviously we changed our JSDoc usage to both
make it correct and also accommodate TypeScript. The main drawback to these
fixes is that enums are now generated as globals in the doc, rather than
modules. This means they no longer have their own dedicated page and are
instead on the globals page, but I changed the code to ensure they are
still in the table of contents that we generate. I think this trade-off is
perfectly fine, but I wanted to mention it since it does change the doc
some. We can certainly look into whether we can generate enums on their own
page if we think that makes sense. (I actually like this approach a little
better personally).
Last major piece, the actual code. 99% of the changes in this PR only
affect the JSDoc. There are two exceptions:
A few of our enums also have private functions tacked onto them. I had to
move these functions to be outside the initializer but otherwise they are
unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm?
We had a few places where we had method signatures with optional parameters
that came _before_ required parameters, which is silly. This is invalid
TypeScript (and not good API design no matter the language). In 99% of
cases this was `equalsEpsilon` style functions where the lhs/rhs were
optional but the epsilon was not. I remember the discussion around this
when we first did it because we were paranoid about defaulting to 0, but
it's an edge case and it's silly so I just changed the epsilon functions
to default to zero now, problem solved.
Here's a high level summary of the JS changes:
* Use proper `@enum` notation instead of `@exports` for enums.
* Use proper `@namespace` instead of `@exports` for static classes.
* Use proper `@function` instead of `@exports` for standalone functions.
* Fix `Promise` markup to actually include the type in all cases, i.e.
`Promise` => `Promise<void>` or `Promise<Cartesian3[]>`.
* Fix bad markup that referenced types that do not exist (or no longer
exist) at the spec level, `Image` => `HTMLImageElement`,
`Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not
exist and much be expressed as a lsit of all applicable types,
`Int8Array|Uint8Array|Int16Array|Uint16Array...`.
* Use dot notation instead of tilde in callbacks, to better support
TypeScript, i.e. `EasingFunction~Callback` becomes
`EasingFunction.Callback`. The only exception is when we had a callback
type that was i.e. `exportKml~ModelCallback` becomes
`exportKmlModelCallback` (a module global type rather than a type on
exportKml). This is because it's not possible to have exportKml be both a
function and a namespace in this current toolchain. Not a big deal either
way since these are meta-types used for defining callbacks but I wanted to
mention it.
* There were some edge cases where private types that were referenced in
the public API but don't exist in the JSDoc. These were trivial to fix by
either tweaking the JSDoc to avoid leaking the type or in some cases, just
as `PixelDataType`, simply exposing the private type as public. I also
found a few cases where things were accidentally public, I marked these as
private (these were extreme edge cases so I'm not concerned about breaking
changes). Appearances took an optional `RenderState` in their options, I
just changed the type to `Object` which we can clean up further later if
we need to.
* Lots of other little misc JSDoc issues that became obvious once we
started to generate definitions (duplicate parameters for example).
Thanks again to the community for helping generate ideas and discussions
around TS definitions over the last few years and a big thanks to @javagl
for helping behind the scenes on this specific effort by evaluating a few
different approaches and workaround before we settled on this one (I'm
working on a blog with all of the gory details for those interested).
Finally, while I'm thrilled with how this turned out (all ~41000 lines
and 1.9MB of it), I can guarantee we will uncover some issues with the
type definitions as more people use it. The good news is that improving it
is now just a matter of fixing the JSDoc, which will benefit the community
as a whole and not just TS users.
Fixes #2730
Fixes #5717
2020-05-27 10:40:05 +08:00
|
|
|
}
|
|
|
|
|
|
2022-04-14 23:43:12 +08:00
|
|
|
/**
|
|
|
|
|
* Reads `ThirdParty.extra.json` file
|
2022-04-22 23:06:25 +08:00
|
|
|
* @param path {string} Path to `ThirdParty.extra.json`
|
|
|
|
|
* @param discoveredDependencies {Array<string>} List of previously discovered modules
|
2022-04-14 23:43:12 +08:00
|
|
|
* @returns {Promise<Array<Object>>} A promise to an array of objects with 'name`, `license`, and `url` strings
|
|
|
|
|
*/
|
2022-09-09 00:43:28 +08:00
|
|
|
async function getLicenseDataFromThirdPartyExtra(path, discoveredDependencies) {
|
|
|
|
|
if (!existsSync(path)) {
|
2022-05-05 04:13:18 +08:00
|
|
|
return Promise.reject(`${path} does not exist`);
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const contents = await readFile(path);
|
|
|
|
|
const thirdPartyExtra = JSON.parse(contents);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
thirdPartyExtra.map(function (module) {
|
2022-04-14 23:43:12 +08:00
|
|
|
if (!discoveredDependencies.includes(module.name)) {
|
|
|
|
|
// If this is not a npm module, return existing info
|
2022-07-21 01:47:14 +08:00
|
|
|
if (
|
|
|
|
|
!packageJson.dependencies[module.name] &&
|
|
|
|
|
!packageJson.devDependencies[module.name]
|
|
|
|
|
) {
|
2022-04-14 23:43:12 +08:00
|
|
|
discoveredDependencies.push(module.name);
|
|
|
|
|
return Promise.resolve(module);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-22 23:06:25 +08:00
|
|
|
return getLicenseDataFromPackage(
|
|
|
|
|
module.name,
|
|
|
|
|
discoveredDependencies,
|
2022-04-28 23:09:25 +08:00
|
|
|
module.license,
|
2022-04-22 23:06:25 +08:00
|
|
|
module.notes
|
|
|
|
|
);
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
2022-09-09 00:43:28 +08:00
|
|
|
})
|
|
|
|
|
);
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-05-05 02:06:29 +08:00
|
|
|
* Extracts name, license, and url from `package.json` file.
|
2022-04-14 23:43:12 +08:00
|
|
|
*
|
|
|
|
|
* @param packageName {string} Name of package
|
2022-04-22 23:06:25 +08:00
|
|
|
* @param discoveredDependencies {Array<string>} List of previously discovered modules
|
2022-05-05 02:06:29 +08:00
|
|
|
* @param licenseOverride {Array<string>} If specified, override info fetched from package.json. Useful in the case where there are multiple licenses and we might chose a single one.
|
2022-04-14 23:43:12 +08:00
|
|
|
* @returns {Promise<Object>} A promise to an object with 'name`, `license`, and `url` strings
|
|
|
|
|
*/
|
2022-09-09 00:43:28 +08:00
|
|
|
async function getLicenseDataFromPackage(
|
2022-04-28 23:09:25 +08:00
|
|
|
packageName,
|
|
|
|
|
discoveredDependencies,
|
|
|
|
|
licenseOverride,
|
|
|
|
|
notes
|
|
|
|
|
) {
|
2022-04-14 23:43:12 +08:00
|
|
|
if (discoveredDependencies.includes(packageName)) {
|
2022-09-09 00:43:28 +08:00
|
|
|
return [];
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
discoveredDependencies.push(packageName);
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const packagePath = join("node_modules", packageName, "package.json");
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
let contents;
|
|
|
|
|
if (existsSync(packagePath)) {
|
2022-05-05 02:06:29 +08:00
|
|
|
// Package exists at top-level, so use it.
|
2022-09-09 00:43:28 +08:00
|
|
|
contents = await readFile(packagePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!contents) {
|
2022-04-14 23:43:12 +08:00
|
|
|
return Promise.reject(
|
2022-09-09 00:43:28 +08:00
|
|
|
new Error(`Unable to read ${packageName} license information`)
|
2022-04-14 23:43:12 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const packageData = JSON.parse(contents);
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
// Check for license
|
|
|
|
|
let licenseField = licenseOverride;
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (!licenseField) {
|
|
|
|
|
licenseField = [packageData.license];
|
|
|
|
|
}
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (!licenseField && packageData.licenses) {
|
|
|
|
|
licenseField = packageData.licenses;
|
|
|
|
|
}
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
if (!licenseField) {
|
|
|
|
|
console.log(`No license found for ${packageName}`);
|
|
|
|
|
licenseField = ["NONE"];
|
|
|
|
|
}
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
let packageVersion = packageData.version;
|
|
|
|
|
if (!packageData.version) {
|
|
|
|
|
console.log(`No version information found for ${packageName}`);
|
|
|
|
|
packageVersion = "NONE";
|
|
|
|
|
}
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
return {
|
|
|
|
|
name: packageName,
|
|
|
|
|
license: licenseField,
|
|
|
|
|
version: packageVersion,
|
|
|
|
|
url: `https://www.npmjs.com/package/${packageName}`,
|
|
|
|
|
notes: notes,
|
|
|
|
|
};
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function buildThirdParty() {
|
2022-04-14 23:43:12 +08:00
|
|
|
let licenseJson = [];
|
|
|
|
|
const discoveredDependencies = [];
|
|
|
|
|
|
2022-05-05 02:06:29 +08:00
|
|
|
// Generate ThirdParty.json from ThirdParty.extra.json and package.json
|
2022-09-09 00:43:28 +08:00
|
|
|
const licenseInfo = await getLicenseDataFromThirdPartyExtra(
|
2022-04-14 23:43:12 +08:00
|
|
|
"ThirdParty.extra.json",
|
|
|
|
|
discoveredDependencies
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
2022-04-14 23:43:12 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
licenseJson = licenseJson.concat(licenseInfo);
|
|
|
|
|
|
|
|
|
|
licenseJson.sort(function (a, b) {
|
|
|
|
|
const nameA = a.name.toLowerCase();
|
|
|
|
|
const nameB = b.name.toLowerCase();
|
|
|
|
|
if (nameA < nameB) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (nameA > nameB) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return writeFile("ThirdParty.json", JSON.stringify(licenseJson, null, 2));
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
|
|
|
|
|
2017-09-23 02:56:50 +08:00
|
|
|
function buildSandcastle() {
|
2022-10-06 00:31:17 +08:00
|
|
|
const streams = [];
|
|
|
|
|
let appStream = gulp.src([
|
|
|
|
|
"Apps/Sandcastle/**",
|
|
|
|
|
"!Apps/Sandcastle/load-cesium-es6.js",
|
|
|
|
|
"!Apps/Sandcastle/standalone.html",
|
|
|
|
|
"!Apps/Sandcastle/images/**",
|
|
|
|
|
"!Apps/Sandcastle/gallery/**.jpg",
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (isProduction) {
|
2022-05-28 02:45:53 +08:00
|
|
|
// Remove swap out ESM modules for the IIFE build
|
2022-10-06 00:31:17 +08:00
|
|
|
appStream = appStream
|
|
|
|
|
.pipe(
|
|
|
|
|
gulpReplace(
|
|
|
|
|
' <script type="module" src="../load-cesium-es6.js"></script>',
|
|
|
|
|
' <script src="../CesiumUnminified/Cesium.js"></script>\n' +
|
|
|
|
|
' <script>window.CESIUM_BASE_URL = "../CesiumUnminified/";</script>";'
|
|
|
|
|
)
|
2020-05-16 07:17:07 +08:00
|
|
|
)
|
2022-10-06 00:31:17 +08:00
|
|
|
// Fix relative paths for new location
|
|
|
|
|
.pipe(gulpReplace("../../../Build", ".."))
|
|
|
|
|
.pipe(gulpReplace("../../../Source", "../CesiumUnminified"))
|
|
|
|
|
.pipe(gulpReplace("../../Source", "."))
|
|
|
|
|
.pipe(gulpReplace("../../../ThirdParty", "./ThirdParty"))
|
|
|
|
|
.pipe(gulpReplace("../../ThirdParty", "./ThirdParty"))
|
|
|
|
|
.pipe(gulpReplace("../ThirdParty", "./ThirdParty"))
|
|
|
|
|
.pipe(gulpReplace("../Apps/Sandcastle", "."))
|
|
|
|
|
.pipe(gulpReplace("../../SampleData", "../SampleData"))
|
|
|
|
|
.pipe(
|
|
|
|
|
gulpReplace("../../Build/Documentation", "/learn/cesiumjs/ref-doc/")
|
|
|
|
|
)
|
|
|
|
|
.pipe(gulp.dest("Build/Sandcastle"));
|
|
|
|
|
} else {
|
|
|
|
|
// Remove swap out ESM modules for the IIFE build
|
|
|
|
|
appStream = appStream
|
|
|
|
|
.pipe(
|
|
|
|
|
gulpReplace(
|
|
|
|
|
' <script type="module" src="../load-cesium-es6.js"></script>',
|
|
|
|
|
' <script src="../../../Build/CesiumUnminified/Cesium.js"></script>\n' +
|
|
|
|
|
' <script>window.CESIUM_BASE_URL = "../../../Build/CesiumUnminified/";</script>";'
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
// Fix relative paths for new location
|
|
|
|
|
.pipe(gulpReplace("../../../Build", "../../.."))
|
|
|
|
|
.pipe(gulpReplace("../../Source", "../../../Source"))
|
|
|
|
|
.pipe(gulpReplace("../../ThirdParty", "../../../ThirdParty"))
|
|
|
|
|
.pipe(gulpReplace("../../SampleData", "../../../../Apps/SampleData"))
|
|
|
|
|
.pipe(gulpReplace("Build/Documentation", "Documentation"))
|
|
|
|
|
.pipe(gulp.dest("Build/Apps/Sandcastle"));
|
|
|
|
|
}
|
|
|
|
|
streams.push(appStream);
|
|
|
|
|
|
|
|
|
|
let imageStream = gulp.src(
|
|
|
|
|
["Apps/Sandcastle/gallery/**.jpg", "Apps/Sandcastle/images/**"],
|
|
|
|
|
{
|
2020-05-16 07:17:07 +08:00
|
|
|
base: "Apps/Sandcastle",
|
|
|
|
|
buffer: false,
|
2022-10-06 00:31:17 +08:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
if (isProduction) {
|
|
|
|
|
imageStream = imageStream.pipe(gulp.dest("Build/Sandcastle"));
|
|
|
|
|
} else {
|
|
|
|
|
imageStream = imageStream.pipe(gulp.dest("Build/Apps/Sandcastle"));
|
|
|
|
|
}
|
|
|
|
|
streams.push(imageStream);
|
|
|
|
|
|
|
|
|
|
if (isProduction) {
|
|
|
|
|
const fileStream = gulp
|
|
|
|
|
.src(["ThirdParty/**"])
|
|
|
|
|
.pipe(gulp.dest("Build/Sandcastle/ThirdParty"));
|
|
|
|
|
streams.push(fileStream);
|
|
|
|
|
|
|
|
|
|
const dataStream = gulp
|
|
|
|
|
.src(["Apps/SampleData/**"])
|
|
|
|
|
.pipe(gulp.dest("Build/Sandcastle/SampleData"));
|
|
|
|
|
streams.push(dataStream);
|
|
|
|
|
}
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const standaloneStream = gulp
|
2020-05-16 07:17:07 +08:00
|
|
|
.src(["Apps/Sandcastle/standalone.html"])
|
2022-10-06 00:31:17 +08:00
|
|
|
.pipe(gulpReplace("../../../", "."))
|
2020-05-16 07:17:07 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulpReplace(
|
|
|
|
|
' <script type="module" src="load-cesium-es6.js"></script>',
|
2022-10-06 00:31:17 +08:00
|
|
|
' <script src="../CesiumUnminified/Cesium.js"></script>\n' +
|
|
|
|
|
' <script>window.CESIUM_BASE_URL = "../CesiumUnminified/";</script>";'
|
2020-05-16 07:17:07 +08:00
|
|
|
)
|
|
|
|
|
)
|
2022-10-06 00:31:17 +08:00
|
|
|
.pipe(gulpReplace("../../Build", "."))
|
|
|
|
|
.pipe(gulp.dest("Build/Sandcastle"));
|
|
|
|
|
streams.push(standaloneStream);
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-10-06 00:31:17 +08:00
|
|
|
return streamToPromise(mergeStream(...streams));
|
2017-09-23 02:56:50 +08:00
|
|
|
}
|
|
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
async function buildCesiumViewer() {
|
2022-10-06 21:57:19 +08:00
|
|
|
const cesiumViewerOutputDirectory = isProduction
|
|
|
|
|
? "Build/CesiumViewer"
|
|
|
|
|
: "Build/Apps/CesiumViewer";
|
2020-05-16 07:17:07 +08:00
|
|
|
mkdirp.sync(cesiumViewerOutputDirectory);
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
const config = defaultESBuildOptions();
|
2022-07-13 22:26:10 +08:00
|
|
|
config.entryPoints = [
|
|
|
|
|
"Apps/CesiumViewer/CesiumViewer.js",
|
|
|
|
|
"Apps/CesiumViewer/CesiumViewer.css",
|
|
|
|
|
];
|
|
|
|
|
config.bundle = true; // Tree-shaking is enabled automatically
|
|
|
|
|
config.minify = true;
|
|
|
|
|
config.loader = {
|
|
|
|
|
".gif": "text",
|
|
|
|
|
".png": "text",
|
|
|
|
|
};
|
|
|
|
|
config.format = "iife";
|
|
|
|
|
config.inject = ["Apps/CesiumViewer/index.js"];
|
|
|
|
|
config.external = ["https", "http", "zlib"];
|
|
|
|
|
config.outdir = cesiumViewerOutputDirectory;
|
|
|
|
|
config.outbase = "Apps/CesiumViewer";
|
|
|
|
|
config.logLevel = "error"; // print errors immediately, and collect warnings so we can filter out known ones
|
2022-09-09 00:43:28 +08:00
|
|
|
const result = await esbuild(config);
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
handleBuildWarnings(result);
|
2015-10-17 05:30:14 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
await esbuild({
|
2022-11-02 03:39:57 +08:00
|
|
|
entryPoints: ["packages/widgets/Source/InfoBox/InfoBoxDescription.css"],
|
2022-05-28 03:27:47 +08:00
|
|
|
minify: true,
|
|
|
|
|
bundle: true,
|
|
|
|
|
loader: {
|
|
|
|
|
".gif": "text",
|
|
|
|
|
".png": "text",
|
|
|
|
|
},
|
|
|
|
|
outdir: cesiumViewerOutputDirectory,
|
2022-11-02 03:39:57 +08:00
|
|
|
outbase: "packages/widgets/Source/",
|
2022-05-28 03:27:47 +08:00
|
|
|
});
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
await bundleWorkers({
|
|
|
|
|
input: [
|
|
|
|
|
"packages/engine/Source/Workers/**",
|
|
|
|
|
"packages/engine/Source/ThirdParty/Workers/**",
|
|
|
|
|
],
|
|
|
|
|
inputES6: ["packages/engine/Source/WorkersES6/*.js"],
|
2022-05-24 20:54:14 +08:00
|
|
|
path: cesiumViewerOutputDirectory,
|
|
|
|
|
});
|
2015-10-19 01:03:20 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
const stream = mergeStream(
|
|
|
|
|
gulp.src([
|
|
|
|
|
"Apps/CesiumViewer/**",
|
|
|
|
|
"!Apps/CesiumViewer/Images",
|
|
|
|
|
"!Apps/CesiumViewer/**/*.js",
|
|
|
|
|
"!Apps/CesiumViewer/**/*.css",
|
|
|
|
|
]),
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Build/Cesium/Assets/**",
|
|
|
|
|
"Build/Cesium/Workers/**",
|
|
|
|
|
"Build/Cesium/ThirdParty/**",
|
|
|
|
|
"Build/Cesium/Widgets/**",
|
|
|
|
|
"!Build/Cesium/Widgets/**/*.css",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
base: "Build/Cesium",
|
|
|
|
|
nodir: true,
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
gulp.src(["web.config"])
|
|
|
|
|
);
|
2015-10-19 01:03:20 +08:00
|
|
|
|
2022-05-24 20:54:14 +08:00
|
|
|
return streamToPromise(stream.pipe(gulp.dest(cesiumViewerOutputDirectory)));
|
2015-10-19 01:03:20 +08:00
|
|
|
}
|