2022-09-09 00:43:28 +08:00
|
|
|
import { writeFileSync, copyFileSync, readFileSync, existsSync } from "fs";
|
|
|
|
|
import { readFile, writeFile } from "fs/promises";
|
2023-11-23 07:32:32 +08:00
|
|
|
import { join, basename, resolve, dirname } from "path";
|
2022-09-09 00:43:28 +08:00
|
|
|
import { exec, execSync } from "child_process";
|
|
|
|
|
import fetch from "node-fetch";
|
|
|
|
|
import { createRequire } from "module";
|
2024-05-04 01:08:26 +08:00
|
|
|
import { finished } from "stream/promises";
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
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";
|
2023-04-25 04:01:09 +08:00
|
|
|
import { rimraf } from "rimraf";
|
|
|
|
|
import { mkdirp } from "mkdirp";
|
2022-09-09 00:43:28 +08:00
|
|
|
import karma from "karma";
|
|
|
|
|
import yargs from "yargs";
|
|
|
|
|
import typeScript from "typescript";
|
|
|
|
|
import { build as esbuild } from "esbuild";
|
|
|
|
|
import { createInstrumenter } from "istanbul-lib-instrument";
|
|
|
|
|
|
|
|
|
|
import {
|
2022-11-02 03:39:57 +08:00
|
|
|
buildCesium,
|
|
|
|
|
buildEngine,
|
|
|
|
|
buildWidgets,
|
|
|
|
|
bundleWorkers,
|
2022-07-09 03:43:52 +08:00
|
|
|
glslToJavaScript,
|
2023-01-07 03:29:27 +08:00
|
|
|
createCombinedSpecList,
|
2022-07-09 03:43:52 +08:00
|
|
|
createJsHintOptions,
|
2022-11-02 03:39:57 +08:00
|
|
|
defaultESBuildOptions,
|
2023-09-15 03:56:25 +08:00
|
|
|
} from "./scripts/build.js";
|
2022-09-09 00:43:28 +08:00
|
|
|
|
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");
|
|
|
|
|
|
2023-10-28 01:58:36 +08:00
|
|
|
const devDeployUrl = "https://ci-builds.cesium.com/cesium/";
|
2023-09-20 04:04:38 +08:00
|
|
|
const isProduction = process.env.PROD;
|
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 = [
|
2022-11-15 03:50:20 +08:00
|
|
|
"packages/engine/Source/**/*.js",
|
|
|
|
|
"!packages/engine/Source/*.js",
|
|
|
|
|
"packages/widgets/Source/**/*.js",
|
|
|
|
|
"!packages/widgets/Source/*.js",
|
|
|
|
|
"!packages/engine/Source/Shaders/**",
|
|
|
|
|
"!packages/engine/Source/ThirdParty/Workers/**",
|
|
|
|
|
"!packages/engine/Source/ThirdParty/google-earth-dbroot-parser.js",
|
|
|
|
|
"!packages/engine/Source/ThirdParty/_*",
|
2020-05-16 07:17:07 +08:00
|
|
|
];
|
|
|
|
|
|
2022-06-07 00:35:09 +08:00
|
|
|
const watchedSpecFiles = [
|
2022-11-15 03:50:20 +08:00
|
|
|
"packages/engine/Specs/**/*Spec.js",
|
|
|
|
|
"!packages/engine/Specs/SpecList.js",
|
|
|
|
|
"packages/widgets/Specs/**/*Spec.js",
|
|
|
|
|
"!packages/widgets/Specs/SpecList.js",
|
2022-06-07 00:35:09 +08:00
|
|
|
"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 = {
|
2022-11-30 08:40:39 +08:00
|
|
|
development: !noDevelopmentGallery,
|
2022-11-02 03:39:57 +08:00
|
|
|
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
|
|
|
|
2024-05-21 02:21:41 +08:00
|
|
|
export const buildWatch = gulp.series(build, async function buildWatch() {
|
2022-09-09 00:43:28 +08:00
|
|
|
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-15 03:13:30 +08:00
|
|
|
const bundles = await buildCesium({
|
2022-09-09 00:43:28 +08:00
|
|
|
minify: minify,
|
|
|
|
|
path: outputDirectory,
|
|
|
|
|
removePragmas: removePragmas,
|
|
|
|
|
sourcemap: sourcemap,
|
|
|
|
|
incremental: true,
|
|
|
|
|
});
|
2023-03-14 17:31:24 +08:00
|
|
|
|
|
|
|
|
const esm = bundles.esm;
|
|
|
|
|
const cjs = bundles.node;
|
|
|
|
|
const iife = bundles.iife;
|
|
|
|
|
const specs = bundles.specs;
|
2022-09-09 00:43:28 +08:00
|
|
|
|
|
|
|
|
gulp.watch(shaderFiles, async () => {
|
2022-11-02 03:39:57 +08:00
|
|
|
glslToJavaScript(minify, "Build/minifyShaders.state", "engine");
|
2023-03-14 17:31:24 +08:00
|
|
|
await esm.rebuild();
|
2022-09-09 00:43:28 +08:00
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (iife) {
|
|
|
|
|
await iife.rebuild();
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (cjs) {
|
|
|
|
|
await cjs.rebuild();
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
gulp.watch(
|
|
|
|
|
[
|
|
|
|
|
...sourceFiles,
|
|
|
|
|
// Shader results are generated in the previous watch task; no need to rebuild twice
|
|
|
|
|
"!Source/Shaders/**",
|
|
|
|
|
],
|
|
|
|
|
async () => {
|
|
|
|
|
createJsHintOptions();
|
2023-03-14 17:31:24 +08:00
|
|
|
await esm.rebuild();
|
2022-05-28 03:27:47 +08:00
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (iife) {
|
|
|
|
|
await iife.rebuild();
|
2022-05-28 03:27:47 +08:00
|
|
|
}
|
|
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (cjs) {
|
|
|
|
|
await cjs.rebuild();
|
2022-05-28 03:27:47 +08:00
|
|
|
}
|
2023-07-06 23:44:40 +08:00
|
|
|
|
|
|
|
|
await bundleWorkers({
|
|
|
|
|
minify: minify,
|
|
|
|
|
path: outputDirectory,
|
|
|
|
|
removePragmas: removePragmas,
|
|
|
|
|
sourcemap: sourcemap,
|
|
|
|
|
});
|
2024-09-20 23:24:24 +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
|
|
|
gulp.watch(
|
|
|
|
|
watchedSpecFiles,
|
|
|
|
|
{
|
|
|
|
|
events: ["add", "unlink"],
|
|
|
|
|
},
|
|
|
|
|
async () => {
|
2023-01-07 03:29:27 +08:00
|
|
|
createCombinedSpecList();
|
2023-03-14 17:31:24 +08:00
|
|
|
await specs.rebuild();
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
2022-05-26 00:31:43 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
gulp.watch(
|
|
|
|
|
watchedSpecFiles,
|
|
|
|
|
{
|
|
|
|
|
events: ["change"],
|
|
|
|
|
},
|
|
|
|
|
async () => {
|
2023-03-14 17:31:24 +08:00
|
|
|
await specs.rebuild();
|
2024-09-20 23:24:24 +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
|
2023-03-14 17:31:24 +08:00
|
|
|
esm.dispose();
|
2022-05-28 03:27:47 +08:00
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (iife) {
|
|
|
|
|
iife.dispose();
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2022-05-28 03:27:47 +08:00
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
if (cjs) {
|
|
|
|
|
cjs.dispose();
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
2015-10-19 06:01:56 +08:00
|
|
|
|
2023-03-14 17:31:24 +08:00
|
|
|
specs.dispose();
|
2024-06-04 05:44:04 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
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 {
|
2022-12-07 02:27:32 +08:00
|
|
|
workspaces = packageJson.workspaces;
|
2022-11-02 03:39:57 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 23:42:35 +08:00
|
|
|
// TODO: we probably need to manage workspaces better now
|
|
|
|
|
workspaces = workspaces.filter(
|
|
|
|
|
(workspace) => !workspace.includes("sandcastle"),
|
|
|
|
|
);
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Generate types for passed packages in order.
|
|
|
|
|
const importModules = {};
|
|
|
|
|
for (const workspace of workspaces) {
|
2022-12-07 02:27:32 +08:00
|
|
|
const directory = workspace
|
|
|
|
|
.replace(`@${scope}/`, "")
|
|
|
|
|
.replace(`packages/`, "");
|
2022-11-02 03:39:57 +08:00
|
|
|
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,
|
2024-09-20 23:24:24 +08:00
|
|
|
importModules,
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
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/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",
|
2022-11-19 05:36:53 +08:00
|
|
|
"packages/**/*.tgz",
|
2022-09-09 00:43:28 +08:00
|
|
|
];
|
2016-03-04 07:00:26 +08:00
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
export async function clean() {
|
2023-02-01 23:53:16 +08:00
|
|
|
await rimraf("Build");
|
2022-09-09 00:43:28 +08:00
|
|
|
const files = await globby(filesToClean);
|
2023-02-01 23:53:16 +08:00
|
|
|
return Promise.all(files.map((file) => rimraf(file)));
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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" +
|
2022-11-21 22:59:09 +08:00
|
|
|
" packages/engine/Source/ packages/widgets/Source --exclude-dir=Assets,ThirdParty,Workers";
|
2020-05-16 07:17:07 +08:00
|
|
|
|
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 =
|
2022-11-21 22:59:09 +08:00
|
|
|
"npx cloc" +
|
|
|
|
|
" --quiet --progress-rate=0" +
|
2024-05-03 01:18:34 +08:00
|
|
|
" Specs/ packages/engine/Specs packages/widget/Specs --exclude-dir=Data --not-match-f=SpecList.js --not-match-f=eslint.config.js";
|
2022-09-09 00:43:28 +08:00
|
|
|
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.wasm",
|
2024-09-20 23:24:24 +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",
|
2024-09-20 23:24:24 +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",
|
2024-09-20 23:24:24 +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",
|
2024-09-20 23:24:24 +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",
|
2024-09-20 23:24:24 +08:00
|
|
|
"Tools/jsdoc/cesium_template/static/javascript/prism.js",
|
2022-06-17 02:13:05 +08:00
|
|
|
);
|
2022-09-09 00:43:28 +08:00
|
|
|
copyFileSync(
|
2022-06-17 02:13:05 +08:00
|
|
|
"node_modules/prismjs/themes/prism.min.css",
|
2024-09-20 23:24:24 +08:00
|
|
|
"Tools/jsdoc/cesium_template/static/styles/prism.css",
|
2022-06-17 02:13:05 +08:00
|
|
|
);
|
|
|
|
|
|
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"));
|
2024-05-04 01:08:26 +08:00
|
|
|
await finished(stream);
|
|
|
|
|
return stream;
|
2022-09-09 00:43:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2024-05-04 01:08:26 +08:00
|
|
|
export async 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",
|
2022-11-02 04:13:09 +08:00
|
|
|
env: Object.assign({}, process.env, {
|
|
|
|
|
CESIUM_VERSION: version,
|
|
|
|
|
CESIUM_PACKAGES: packageJson.workspaces,
|
|
|
|
|
}),
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2022-04-02 01:59:53 +08:00
|
|
|
);
|
2020-06-01 10:38:02 +08:00
|
|
|
|
2021-01-26 00:00:37 +08:00
|
|
|
const stream = gulp
|
2024-05-14 22:58:38 +08:00
|
|
|
.src(["Documentation/Images/**"], { encoding: false })
|
2020-06-01 10:38:02 +08:00
|
|
|
.pipe(gulp.dest("Build/Documentation/Images"));
|
|
|
|
|
|
2024-05-04 01:08:26 +08:00
|
|
|
await finished(stream);
|
|
|
|
|
return 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-11-30 23:06:08 +08:00
|
|
|
development: false,
|
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(
|
2023-11-02 01:49:14 +08:00
|
|
|
buildEngine,
|
|
|
|
|
buildWidgets,
|
2024-05-21 02:21:41 +08:00
|
|
|
function websiteReleaseBuild() {
|
2022-10-06 00:31:17 +08:00
|
|
|
return buildCesium({
|
2022-11-30 23:06:08 +08:00
|
|
|
development: false,
|
2022-10-06 00:31:17 +08:00
|
|
|
minify: false,
|
|
|
|
|
removePragmas: false,
|
|
|
|
|
node: false,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
combineForSandcastle,
|
2024-09-20 23:24:24 +08:00
|
|
|
buildDocs,
|
2022-10-06 00:31:17 +08:00
|
|
|
);
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
export const buildRelease = gulp.series(
|
2023-02-22 23:11:07 +08:00
|
|
|
buildEngine,
|
|
|
|
|
buildWidgets,
|
2022-11-30 23:06:08 +08:00
|
|
|
// Generate Build/CesiumUnminified
|
2024-05-21 02:21:41 +08:00
|
|
|
function buildCesiumForNode() {
|
2022-11-30 23:06:08 +08:00
|
|
|
return buildCesium({
|
|
|
|
|
minify: false,
|
|
|
|
|
removePragmas: false,
|
|
|
|
|
node: true,
|
|
|
|
|
sourcemap: false,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// Generate Build/Cesium
|
2024-05-21 02:21:41 +08:00
|
|
|
function buildMinifiedCesiumForNode() {
|
2022-11-30 23:06:08 +08:00
|
|
|
return buildCesium({
|
|
|
|
|
development: false,
|
|
|
|
|
minify: true,
|
|
|
|
|
removePragmas: true,
|
|
|
|
|
node: true,
|
|
|
|
|
sourcemap: false,
|
|
|
|
|
});
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
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,
|
2024-09-20 23:24:24 +08:00
|
|
|
gulp.parallel(buildTs, buildDocs),
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
|
2024-05-21 02:21:41 +08:00
|
|
|
export const postversion = async function () {
|
|
|
|
|
const workspace = argv.workspace;
|
|
|
|
|
if (!workspace) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const directory = workspace.replaceAll(`@${scope}/`, ``);
|
|
|
|
|
const workspacePackageJson = require(`./packages/${directory}/package.json`);
|
|
|
|
|
const version = workspacePackageJson.version;
|
|
|
|
|
|
|
|
|
|
// Iterate through all package JSONs that may depend on the updated package and
|
|
|
|
|
// update the version of the updated workspace.
|
|
|
|
|
const packageJsons = await globby([
|
|
|
|
|
"./package.json",
|
|
|
|
|
"./packages/*/package.json",
|
|
|
|
|
]);
|
|
|
|
|
const promises = packageJsons.map(async (packageJsonPath) => {
|
|
|
|
|
// Ensure that we don't check the updated workspace itself.
|
|
|
|
|
if (basename(dirname(packageJsonPath)) === directory) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Ensure that we only update workspaces where the dependency to the updated workspace already exists.
|
|
|
|
|
const packageJson = require(packageJsonPath);
|
|
|
|
|
if (!Object.hasOwn(packageJson.dependencies, workspace)) {
|
|
|
|
|
console.log(
|
2024-09-20 23:24:24 +08:00
|
|
|
`Skipping update for ${workspace} as it is not a dependency.`,
|
2024-05-21 02:21:41 +08:00
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Update the version for the updated workspace.
|
|
|
|
|
packageJson.dependencies[workspace] = `^${version}`;
|
|
|
|
|
await writeFile(packageJsonPath, JSON.stringify(packageJson, undefined, 2));
|
|
|
|
|
});
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
/**
|
|
|
|
|
* Removes scripts from package.json files to ensure that
|
|
|
|
|
* they still work when run from within the ZIP file.
|
|
|
|
|
*
|
2023-02-10 16:35:41 +08:00
|
|
|
* @param {string} packageJsonPath The path to the package.json.
|
2022-11-02 03:39:57 +08:00
|
|
|
* @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-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-15 22:57:16 +08:00
|
|
|
// Set server tasks to use production flag
|
|
|
|
|
scripts["start"] = "node server.js --production";
|
|
|
|
|
scripts["start-public"] = "node server.js --public --production";
|
2023-02-22 23:11:07 +08:00
|
|
|
scripts["start-public"] = "node server.js --public --production";
|
|
|
|
|
scripts["test"] = "gulp test --production";
|
|
|
|
|
scripts["test-all"] = "gulp test --all --production";
|
|
|
|
|
scripts["test-webgl"] = "gulp test --include WebGL --production";
|
|
|
|
|
scripts["test-non-webgl"] = "gulp test --exclude WebGL --production";
|
|
|
|
|
scripts["test-webgl-validation"] = "gulp test --webglValidation --production";
|
|
|
|
|
scripts["test-webgl-stub"] = "gulp test --webglStub --production";
|
|
|
|
|
scripts["test-release"] = "gulp test --release --production";
|
2022-11-15 22:57:16 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
// Write to a temporary package.json file.
|
|
|
|
|
const noPreparePackageJson = join(
|
|
|
|
|
dirname(packageJsonPath),
|
2024-09-20 23:24:24 +08:00
|
|
|
"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
|
|
|
|
2024-05-21 02:21:41 +08:00
|
|
|
return gulp.src(noPreparePackageJson, {
|
|
|
|
|
base: ".",
|
2022-12-02 10:48:08 +08:00
|
|
|
});
|
2024-05-21 02:21:41 +08:00
|
|
|
}
|
2022-11-02 03:39:57 +08:00
|
|
|
|
2024-05-04 01:08:26 +08:00
|
|
|
export const makeZip = gulp.series(release, async function createZipFile() {
|
2022-11-02 03:39:57 +08:00
|
|
|
//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(
|
2024-09-20 23:24:24 +08:00
|
|
|
"packages/engine/package.json",
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
const widgetsPackageJsonSrc = await pruneScriptsForZip(
|
2024-09-20 23:24:24 +08:00
|
|
|
"packages/widgets/package.json",
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
2021-09-02 03:01:13 +08:00
|
|
|
|
2024-05-04 01:08:26 +08:00
|
|
|
const src = gulp
|
2022-09-09 00:43:28 +08:00
|
|
|
.src("index.release.html")
|
2024-05-21 02:21:41 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulpRename((file) => {
|
|
|
|
|
if (file.basename === "index.release") {
|
|
|
|
|
file.basename = "index";
|
|
|
|
|
}
|
2024-09-20 23:24:24 +08:00
|
|
|
}),
|
2022-11-02 03:39:57 +08:00
|
|
|
)
|
2024-05-04 01:08:26 +08:00
|
|
|
.pipe(enginePackageJsonSrc)
|
|
|
|
|
.pipe(widgetsPackageJsonSrc)
|
2024-05-21 02:21:41 +08:00
|
|
|
.pipe(packageJsonSrc)
|
|
|
|
|
.pipe(
|
|
|
|
|
gulpRename((file) => {
|
|
|
|
|
if (file.basename === "package.noprepare") {
|
|
|
|
|
file.basename = "package";
|
|
|
|
|
}
|
2024-09-20 23:24:24 +08:00
|
|
|
}),
|
2024-05-21 02:21:41 +08:00
|
|
|
)
|
2024-05-04 01:08:26 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Build/Cesium/**",
|
|
|
|
|
"Build/CesiumUnminified/**",
|
|
|
|
|
"Build/Documentation/**",
|
|
|
|
|
"Build/Specs/**",
|
|
|
|
|
"Build/package.json",
|
|
|
|
|
"packages/engine/Build/**",
|
|
|
|
|
"packages/widgets/Build/**",
|
|
|
|
|
"!Build/Specs/e2e/**",
|
|
|
|
|
"!Build/InlineWorkers.js",
|
|
|
|
|
"!packages/engine/Build/Specs/**",
|
|
|
|
|
"!packages/widgets/Build/Specs/**",
|
|
|
|
|
"!packages/engine/Build/minifyShaders.state",
|
|
|
|
|
],
|
|
|
|
|
{
|
2024-05-14 22:58:38 +08:00
|
|
|
encoding: false,
|
2024-05-04 01:08:26 +08:00
|
|
|
base: ".",
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
|
|
|
|
),
|
2022-11-02 03:39:57 +08:00
|
|
|
)
|
2024-05-04 01:08:26 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Apps/**",
|
|
|
|
|
"Apps/Sandcastle/.jshintrc",
|
|
|
|
|
"packages/engine/index.js",
|
|
|
|
|
"packages/engine/index.d.ts",
|
|
|
|
|
"packages/engine/LICENSE.md",
|
|
|
|
|
"packages/engine/README.md",
|
|
|
|
|
"packages/engine/Source/**",
|
|
|
|
|
"packages/widgets/index.js",
|
|
|
|
|
"packages/widgets/index.d.ts",
|
|
|
|
|
"packages/widgets/LICENSE.md",
|
|
|
|
|
"packages/widgets/README.md",
|
|
|
|
|
"packages/widgets/Source/**",
|
|
|
|
|
"Source/**",
|
|
|
|
|
"Specs/**",
|
|
|
|
|
"ThirdParty/**",
|
2024-05-29 00:51:36 +08:00
|
|
|
"scripts/**",
|
2024-05-04 01:08:26 +08:00
|
|
|
"favicon.ico",
|
|
|
|
|
".prettierignore",
|
2024-05-29 00:51:36 +08:00
|
|
|
"eslint.config.js",
|
2024-05-04 01:08:26 +08:00
|
|
|
"gulpfile.js",
|
|
|
|
|
"server.js",
|
|
|
|
|
"index.cjs",
|
|
|
|
|
"LICENSE.md",
|
|
|
|
|
"CHANGES.md",
|
|
|
|
|
"README.md",
|
|
|
|
|
"web.config",
|
|
|
|
|
"!**/*.gitignore",
|
|
|
|
|
"!Specs/e2e/*-snapshots/**",
|
|
|
|
|
"!Apps/Sandcastle/gallery/development/**",
|
|
|
|
|
],
|
|
|
|
|
{
|
2024-05-14 22:58:38 +08:00
|
|
|
encoding: false,
|
2024-05-04 01:08:26 +08:00
|
|
|
base: ".",
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
|
|
|
|
),
|
2024-05-04 01:08:26 +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);
|
|
|
|
|
}
|
2024-09-20 23:24:24 +08:00
|
|
|
}),
|
2024-05-04 01:08:26 +08:00
|
|
|
)
|
|
|
|
|
.pipe(gulpZip(`Cesium-${version}.zip`))
|
|
|
|
|
.pipe(gulp.dest("."));
|
|
|
|
|
|
|
|
|
|
await finished(src);
|
|
|
|
|
|
2024-05-21 02:21:41 +08:00
|
|
|
rimraf.sync("./package.noprepare.json");
|
|
|
|
|
rimraf.sync("./packages/engine/package.noprepare.json");
|
|
|
|
|
rimraf.sync("./packages/widgets/package.noprepare.json");
|
2024-05-04 01:08:26 +08:00
|
|
|
|
|
|
|
|
return src;
|
2022-09-09 00:43:28 +08:00
|
|
|
});
|
2020-05-16 07:17:07 +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() {
|
|
|
|
|
const status = argv.status;
|
|
|
|
|
const message = argv.message;
|
2020-05-16 07:17:07 +08:00
|
|
|
|
2023-11-23 08:59:23 +08:00
|
|
|
const deployUrl = `${devDeployUrl + process.env.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 = `${
|
2023-09-20 04:04:38 +08:00
|
|
|
devDeployUrl + process.env.BRANCH
|
2022-02-03 04:40:21 +08:00
|
|
|
}/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
|
2023-09-20 04:04:38 +08:00
|
|
|
if (!process.env.GITHUB_TOKEN) {
|
2020-05-16 07:17:07 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 00:43:28 +08:00
|
|
|
const body = {
|
|
|
|
|
state: state,
|
|
|
|
|
target_url: targetUrl,
|
|
|
|
|
description: description,
|
|
|
|
|
context: context,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await fetch(
|
2023-09-20 04:04:38 +08:00
|
|
|
`https://api.github.com/repos/${process.env.GITHUB_REPO}/statuses/${process.env.GITHUB_SHA}`,
|
2022-09-09 00:43:28 +08:00
|
|
|
{
|
|
|
|
|
method: "post",
|
|
|
|
|
body: JSON.stringify(body),
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
2023-09-20 04:04:38 +08:00
|
|
|
Authorization: `token ${process.env.GITHUB_TOKEN}`,
|
2022-09-09 00:43:28 +08:00
|
|
|
"User-Agent": "Cesium",
|
|
|
|
|
},
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
|
|
|
|
|
2023-09-20 04:04:38 +08:00
|
|
|
const result = await response.json();
|
|
|
|
|
return result;
|
2016-03-26 01:58:37 +08:00
|
|
|
}
|
|
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
/**
|
|
|
|
|
* Generates coverage report.
|
|
|
|
|
*
|
2023-02-10 16:35:41 +08:00
|
|
|
* @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.
|
2022-11-02 03:39:57 +08:00
|
|
|
* @param {RegExp} options.filter The filter for finding which files should be instrumented.
|
2023-02-10 16:35:41 +08:00
|
|
|
* @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.
|
2022-11-02 03:39:57 +08:00
|
|
|
*/
|
|
|
|
|
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,
|
2024-09-20 23:24:24 +08:00
|
|
|
args.path,
|
2022-06-02 23:24:07 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return { contents: generatedCode };
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return {
|
|
|
|
|
errors: {
|
|
|
|
|
text: e.message,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2022-06-02 23:24:07 +08:00
|
|
|
);
|
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",
|
|
|
|
|
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-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: "Specs/Data/**", included: false },
|
|
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Build/CesiumUnminified/**", included: false },
|
|
|
|
|
{ pattern: "Build/Specs/TestWorkers/**.js", included: false },
|
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: karmaBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
pattern: specListBundle,
|
|
|
|
|
included: true,
|
|
|
|
|
type: "module",
|
|
|
|
|
},
|
|
|
|
|
{ pattern: "Specs/Data/**", included: false },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
2022-11-02 03:39:57 +08:00
|
|
|
{ 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 },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Build/Specs/TestWorkers/**.js", included: false },
|
2022-11-02 03:39:57 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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
|
|
|
},
|
|
|
|
|
},
|
2024-09-20 23:24:24 +08:00
|
|
|
{ promiseConfig: true, throwErrors: true },
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
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(
|
2024-09-20 23:24:24 +08:00
|
|
|
folder,
|
2022-08-06 00:57:00 +08:00
|
|
|
)}/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
|
|
|
|
2023-09-20 04:04:38 +08:00
|
|
|
if (!process.env.CI) {
|
2022-08-06 00:57:00 +08:00
|
|
|
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,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-22 23:11:07 +08:00
|
|
|
// Cache contexts for successive calls to test
|
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 : "";
|
2023-02-22 23:11:07 +08:00
|
|
|
const isProduction = argv.production;
|
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}/`, ``);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 02:59:01 +08:00
|
|
|
if (!isProduction && !release) {
|
2023-02-22 23:11:07 +08:00
|
|
|
console.log("Building specs...");
|
|
|
|
|
await buildCesium({
|
|
|
|
|
iife: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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" },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Build/Specs/TestWorkers/**.js", 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 },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
|
2022-11-02 03:39:57 +08:00
|
|
|
{ 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 },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Build/Specs/TestWorkers/**.js", included: false },
|
2022-11-02 03:39:57 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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" },
|
2023-07-24 22:18:08 +08:00
|
|
|
{ pattern: "Build/Specs/TestWorkers/**.js", 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
|
|
|
},
|
2024-09-20 23:24:24 +08:00
|
|
|
{ promiseConfig: true, throwErrors: true },
|
2022-09-09 00:43:28 +08:00
|
|
|
);
|
|
|
|
|
|
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
|
2023-02-10 16:35:41 +08:00
|
|
|
* @param {string} definitionsPath The path of the .d.ts file to generate.
|
2022-11-02 03:39:57 +08:00
|
|
|
* @param {*} configurationPath
|
|
|
|
|
* @param {*} processSourceFunc
|
|
|
|
|
* @param {*} processModulesFunc
|
|
|
|
|
* @param {*} importModules
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
function generateTypeScriptDefinitions(
|
|
|
|
|
workspaceName,
|
|
|
|
|
definitionsPath,
|
|
|
|
|
configurationPath,
|
|
|
|
|
processSourceFunc,
|
|
|
|
|
processModulesFunc,
|
2024-09-20 23:24:24 +08:00
|
|
|
importModules,
|
2022-11-02 03:39:57 +08:00
|
|
|
) {
|
|
|
|
|
// 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();
|
2024-06-04 05:44:04 +08:00
|
|
|
|
2022-11-02 03:39:57 +08:00
|
|
|
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
|
2024-09-20 23:24:24 +08:00
|
|
|
(match, p1) => `= WebGLConstants.${p1}`,
|
2022-11-02 03:39:57 +08:00
|
|
|
)
|
|
|
|
|
// Strip const enums which can cause errors - https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls
|
2024-03-27 01:05:13 +08:00
|
|
|
.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")
|
|
|
|
|
// Replace JSDoc generation version of defined with an improved version using TS type predicates
|
|
|
|
|
.replace(
|
2024-11-16 05:08:03 +08:00
|
|
|
/\n?export function defined\(value: any\): boolean;/gm,
|
|
|
|
|
`\n${readFileSync("./packages/engine/Source/Core/defined.d.ts")
|
|
|
|
|
.toString()
|
|
|
|
|
.replace(/\n*\/\*.*?\*\/\n*/gms, "")
|
|
|
|
|
.replace("export default", "export")}`,
|
2024-03-27 01:05:13 +08:00
|
|
|
)
|
2024-11-16 05:08:03 +08:00
|
|
|
// Replace JSDoc generation version of Check with one that asserts the type of variables after called
|
2024-03-27 01:05:13 +08:00
|
|
|
.replace(
|
|
|
|
|
/\/\*\*[\*\s\w]*?\*\/\nexport const Check: any;/m,
|
2024-11-16 05:08:03 +08:00
|
|
|
`\n${readFileSync("./packages/engine/Source/Core/Check.d.ts")
|
|
|
|
|
.toString()
|
2024-11-23 01:08:26 +08:00
|
|
|
.replace(/export default.*\n?/, "")
|
2024-11-16 05:08:03 +08:00
|
|
|
.replace("const Check", "export const Check")}`,
|
2024-05-01 02:50:35 +08:00
|
|
|
)
|
|
|
|
|
// Fix https://github.com/CesiumGS/cesium/issues/10498 so we can use the rest parameter expand tuple
|
|
|
|
|
.replace(
|
|
|
|
|
"raiseEvent(...arguments: Parameters<Listener>[]): void;",
|
2024-09-20 23:24:24 +08:00
|
|
|
"raiseEvent(...arguments: Parameters<Listener>): void;",
|
2024-03-27 01:05:13 +08:00
|
|
|
);
|
2022-11-02 03:39:57 +08:00
|
|
|
|
|
|
|
|
// 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(
|
2024-09-20 23:24:24 +08:00
|
|
|
(importModule) => source.indexOf(importModule) !== -1,
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
imports += `import { ${workspaceModules.join(
|
2024-09-20 23:24:24 +08:00
|
|
|
",\n",
|
2022-11-02 03:39:57 +08:00
|
|
|
)} } 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,
|
2024-09-20 23:24:24 +08:00
|
|
|
typeScript.ScriptTarget.Latest,
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
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,
|
2024-09-20 23:24:24 +08:00
|
|
|
node,
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
newSource += "\n\n";
|
|
|
|
|
node.forEachChild((child) => {
|
|
|
|
|
if (
|
|
|
|
|
typeScript.SyntaxKind[child.kind] !== "EnumDeclaration" ||
|
|
|
|
|
child.name.escapedText !== "WebGLConstants"
|
|
|
|
|
) {
|
|
|
|
|
newSource += printer.printNode(
|
|
|
|
|
typeScript.EmitHint.Unspecified,
|
|
|
|
|
child,
|
2024-09-20 23:24:24 +08:00
|
|
|
node,
|
2022-11-02 03:39:57 +08:00
|
|
|
);
|
|
|
|
|
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,
|
2024-09-20 23:24:24 +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,
|
2024-09-20 23:24:24 +08:00
|
|
|
node,
|
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
|
|
|
);
|
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,
|
2024-09-20 23:24:24 +08:00
|
|
|
node,
|
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
|
|
|
);
|
|
|
|
|
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();
|
2024-06-04 05:44:04 +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
|
|
|
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
|
2024-09-20 23:24:24 +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
|
2023-08-06 06:46:53 +08:00
|
|
|
.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")
|
|
|
|
|
// Replace JSDoc generation version of defined with an improved version using TS type predicates
|
|
|
|
|
.replace(
|
2024-11-16 05:08:03 +08:00
|
|
|
/\n?export function defined\(value: any\): boolean;/gm,
|
|
|
|
|
`\n${readFileSync("./packages/engine/Source/Core/defined.d.ts")
|
|
|
|
|
.toString()
|
|
|
|
|
.replace(/\n*\/\*.*?\*\/\n*/gms, "")
|
|
|
|
|
.replace("export default", "export")}`,
|
2024-03-27 01:05:13 +08:00
|
|
|
)
|
2024-11-16 05:08:03 +08:00
|
|
|
// Replace JSDoc generation version of Check with one that asserts the type of variables after called
|
2024-03-27 01:05:13 +08:00
|
|
|
.replace(
|
|
|
|
|
/\/\*\*[\*\s\w]*?\*\/\nexport const Check: any;/m,
|
2024-11-16 05:08:03 +08:00
|
|
|
`\n${readFileSync("./packages/engine/Source/Core/Check.d.ts")
|
|
|
|
|
.toString()
|
2024-11-23 01:08:26 +08:00
|
|
|
.replace(/export default.*\n?/, "")
|
2024-11-16 05:08:03 +08:00
|
|
|
.replace("const Check", "export const Check")}`,
|
2024-05-01 02:50:35 +08:00
|
|
|
)
|
|
|
|
|
// Fix https://github.com/CesiumGS/cesium/issues/10498 to have rest parameter expand tuple
|
|
|
|
|
.replace(
|
|
|
|
|
"raiseEvent(...arguments: Parameters<Listener>[]): void;",
|
2024-09-20 23:24:24 +08:00
|
|
|
"raiseEvent(...arguments: Parameters<Listener>): void;",
|
2023-08-06 06:46:53 +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
|
|
|
|
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
|
2023-02-10 16:35:41 +08:00
|
|
|
* @param {string} path Path to `ThirdParty.extra.json`
|
|
|
|
|
* @param {string[]} discoveredDependencies List of previously discovered modules
|
|
|
|
|
* @returns {Promise<object[]>} A promise to an array of objects with 'name`, `license`, and `url` strings
|
2022-04-14 23:43:12 +08:00
|
|
|
*/
|
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(
|
2022-11-19 04:43:01 +08:00
|
|
|
thirdPartyExtra.map(async function (module) {
|
2022-04-14 23:43:12 +08:00
|
|
|
if (!discoveredDependencies.includes(module.name)) {
|
2022-11-19 04:43:01 +08:00
|
|
|
let result = await getLicenseDataFromPackage(
|
|
|
|
|
packageJson,
|
2022-04-22 23:06:25 +08:00
|
|
|
module.name,
|
|
|
|
|
discoveredDependencies,
|
2022-04-28 23:09:25 +08:00
|
|
|
module.license,
|
2024-09-20 23:24:24 +08:00
|
|
|
module.notes,
|
2022-04-22 23:06:25 +08:00
|
|
|
);
|
2022-11-19 04:43:01 +08:00
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resursively check the workspaces
|
|
|
|
|
for (const workspace of packageJson.workspaces) {
|
|
|
|
|
const workspacePackageJson = require(`./${workspace}/package.json`);
|
|
|
|
|
result = await getLicenseDataFromPackage(
|
|
|
|
|
workspacePackageJson,
|
|
|
|
|
module.name,
|
|
|
|
|
discoveredDependencies,
|
|
|
|
|
module.license,
|
2024-09-20 23:24:24 +08:00
|
|
|
module.notes,
|
2022-11-19 04:43:01 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this is not a npm module, return existing info
|
|
|
|
|
discoveredDependencies.push(module.name);
|
|
|
|
|
return module;
|
2022-04-14 23:43:12 +08:00
|
|
|
}
|
2024-09-20 23:24:24 +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
|
|
|
*
|
2023-02-10 16:35:41 +08:00
|
|
|
* @param {string} packageName Name of package
|
|
|
|
|
* @param {string[]} discoveredDependencies List of previously discovered modules
|
|
|
|
|
* @param {string[]} licenseOverride If specified, override info fetched from package.json. Useful in the case where there are multiple licenses and we might chose a single one.
|
|
|
|
|
* @returns {Promise<object>} A promise to an object with 'name`, `license`, and `url` strings
|
2022-04-14 23:43:12 +08:00
|
|
|
*/
|
2022-09-09 00:43:28 +08:00
|
|
|
async function getLicenseDataFromPackage(
|
2022-11-19 04:43:01 +08:00
|
|
|
packageJson,
|
2022-04-28 23:09:25 +08:00
|
|
|
packageName,
|
|
|
|
|
discoveredDependencies,
|
|
|
|
|
licenseOverride,
|
2024-09-20 23:24:24 +08:00
|
|
|
notes,
|
2022-04-28 23:09:25 +08:00
|
|
|
) {
|
2022-11-19 04:43:01 +08:00
|
|
|
if (
|
|
|
|
|
!packageJson.dependencies[packageName] &&
|
|
|
|
|
(!packageJson.devDependencies || !packageJson.devDependencies[packageName])
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
2022-11-19 04:43:01 +08:00
|
|
|
|
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(
|
2024-09-20 23:24:24 +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",
|
2024-09-20 23:24:24 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2024-05-04 01:08:26 +08:00
|
|
|
async function buildSandcastle() {
|
2022-10-06 00:31:17 +08:00
|
|
|
const streams = [];
|
2024-05-14 22:58:38 +08:00
|
|
|
let appStream = gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Apps/Sandcastle/**",
|
|
|
|
|
"!Apps/Sandcastle/load-cesium-es6.js",
|
|
|
|
|
"!Apps/Sandcastle/images/**",
|
|
|
|
|
"!Apps/Sandcastle/gallery/**.jpg",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
encoding: false,
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2024-05-14 22:58:38 +08:00
|
|
|
);
|
2022-10-06 00:31:17 +08:00
|
|
|
|
|
|
|
|
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' +
|
2024-09-20 23:24:24 +08:00
|
|
|
' <script>window.CESIUM_BASE_URL = "../CesiumUnminified/";</script>',
|
|
|
|
|
),
|
2022-11-30 08:43:18 +08:00
|
|
|
)
|
|
|
|
|
.pipe(
|
|
|
|
|
gulpReplace(
|
|
|
|
|
' <script type="module" src="load-cesium-es6.js"></script>',
|
|
|
|
|
' <script src="CesiumUnminified/Cesium.js"></script>\n' +
|
2024-09-20 23:24:24 +08:00
|
|
|
' <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(
|
2024-09-20 23:24:24 +08:00
|
|
|
gulpReplace("../../Build/Documentation", "/learn/cesiumjs/ref-doc/"),
|
2022-10-06 00:31:17 +08:00
|
|
|
)
|
|
|
|
|
.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' +
|
2024-09-20 23:24:24 +08:00
|
|
|
' <script>window.CESIUM_BASE_URL = "../../../Build/CesiumUnminified/";</script>',
|
|
|
|
|
),
|
2022-10-06 00:31:17 +08:00
|
|
|
)
|
2022-11-30 08:40:39 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulpReplace(
|
|
|
|
|
' <script type="module" src="load-cesium-es6.js"></script>',
|
|
|
|
|
' <script src="../../CesiumUnminified/Cesium.js"></script>\n' +
|
2024-09-20 23:24:24 +08:00
|
|
|
' <script>window.CESIUM_BASE_URL = "../../CesiumUnminified/";</script>',
|
|
|
|
|
),
|
2022-11-30 08:40:39 +08:00
|
|
|
)
|
2022-10-06 00:31:17 +08:00
|
|
|
// 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",
|
2024-05-14 22:58:38 +08:00
|
|
|
encoding: false,
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
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
|
2024-06-07 22:43:55 +08:00
|
|
|
.src(["ThirdParty/**"], { encoding: false })
|
2022-10-06 00:31:17 +08:00
|
|
|
.pipe(gulp.dest("Build/Sandcastle/ThirdParty"));
|
|
|
|
|
streams.push(fileStream);
|
|
|
|
|
|
|
|
|
|
const dataStream = gulp
|
2024-06-05 21:18:37 +08:00
|
|
|
.src(["Apps/SampleData/**"], { encoding: false })
|
2022-10-06 00:31:17 +08:00
|
|
|
.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' +
|
2024-09-20 23:24:24 +08:00
|
|
|
' <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
|
|
|
|
2024-05-14 22:58:38 +08:00
|
|
|
return Promise.all(streams.map((s) => finished(s)));
|
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";
|
2023-03-29 22:21:19 +08:00
|
|
|
// Configure Cesium base path to use built
|
|
|
|
|
config.define = { CESIUM_BASE_URL: `"."` };
|
2022-07-13 22:26:10 +08:00
|
|
|
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",
|
|
|
|
|
},
|
2023-03-29 22:21:19 +08:00
|
|
|
outdir: join(cesiumViewerOutputDirectory, "Widgets"),
|
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({
|
2023-07-06 23:44:40 +08:00
|
|
|
minify: true,
|
|
|
|
|
removePragmas: true,
|
2022-05-24 20:54:14 +08:00
|
|
|
path: cesiumViewerOutputDirectory,
|
|
|
|
|
});
|
2015-10-19 01:03:20 +08:00
|
|
|
|
2024-05-04 01:08:26 +08:00
|
|
|
const stream = gulp
|
2024-05-14 22:58:38 +08:00
|
|
|
.src(
|
2022-05-24 20:54:14 +08:00
|
|
|
[
|
2024-05-14 22:58:38 +08:00
|
|
|
"Apps/CesiumViewer/**",
|
|
|
|
|
"!Apps/CesiumViewer/Images",
|
|
|
|
|
"!Apps/CesiumViewer/**/*.js",
|
|
|
|
|
"!Apps/CesiumViewer/**/*.css",
|
2022-05-24 20:54:14 +08:00
|
|
|
],
|
|
|
|
|
{
|
2024-05-14 22:58:38 +08:00
|
|
|
encoding: false,
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
2024-05-14 22:58:38 +08:00
|
|
|
)
|
2024-05-04 01:08:26 +08:00
|
|
|
.pipe(
|
|
|
|
|
gulp.src(
|
|
|
|
|
[
|
|
|
|
|
"Build/Cesium/Assets/**",
|
|
|
|
|
"Build/Cesium/Workers/**",
|
|
|
|
|
"Build/Cesium/ThirdParty/**",
|
|
|
|
|
"Build/Cesium/Widgets/**",
|
|
|
|
|
"!Build/Cesium/Widgets/**/*.css",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
base: "Build/Cesium",
|
|
|
|
|
nodir: true,
|
2024-05-14 22:58:38 +08:00
|
|
|
encoding: false,
|
2024-09-20 23:24:24 +08:00
|
|
|
},
|
|
|
|
|
),
|
2024-05-04 01:08:26 +08:00
|
|
|
)
|
|
|
|
|
.pipe(gulp.src(["web.config"]))
|
|
|
|
|
.pipe(gulp.dest(cesiumViewerOutputDirectory));
|
2015-10-19 01:03:20 +08:00
|
|
|
|
2024-05-21 02:21:41 +08:00
|
|
|
await finished(stream);
|
2024-05-04 01:08:26 +08:00
|
|
|
return stream;
|
2015-10-19 01:03:20 +08:00
|
|
|
}
|