cesium/gulpfile.js

1811 lines
54 KiB
JavaScript
Raw Normal View History

2022-09-09 00:43:28 +08:00
import { writeFileSync, copyFileSync, readFileSync, existsSync } from "fs";
import { readFile, writeFile } from "fs/promises";
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,
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)) {
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/";
const isProduction = process.env.PROD;
//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];
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;
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/_*",
];
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
];
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-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;
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-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,
});
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");
await esm.rebuild();
2022-09-09 00:43:28 +08:00
if (iife) {
await iife.rebuild();
2022-09-09 00:43:28 +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();
await esm.rebuild();
2022-05-28 03:27:47 +08:00
if (iife) {
await iife.rebuild();
2022-05-28 03:27:47 +08:00
}
if (cjs) {
await cjs.rebuild();
2022-05-28 03:27:47 +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 () => {
createCombinedSpecList();
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 () => {
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
esm.dispose();
2022-05-28 03:27:47 +08:00
if (iife) {
iife.dispose();
2022-09-09 00:43:28 +08:00
}
2022-05-28 03:27:47 +08:00
if (cjs) {
cjs.dispose();
2022-09-09 00:43:28 +08:00
}
specs.dispose();
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 {
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) {
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
}
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
];
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;
//Run cloc on primary Source files only
2021-01-26 00:00:37 +08:00
const source = new Promise(function (resolve, reject) {
cmdLine =
"npx cloc" +
" --quiet --progress-rate=0" +
2022-11-21 22:59:09 +08:00
" packages/engine/Source/ packages/widgets/Source --exclude-dir=Assets,ThirdParty,Workers";
2022-09-09 00:43:28 +08:00
exec(cmdLine, function (error, stdout, stderr) {
if (error) {
console.log(stderr);
return reject(error);
}
console.log("Source:");
console.log(stdout);
resolve();
2015-10-22 05:54:08 +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" +
" 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
});
});
}
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
//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",
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
);
2021-01-26 00:00:37 +08:00
const stream = gulp
2024-05-14 22:58:38 +08:00
.src(["Documentation/Images/**"], { encoding: false })
.pipe(gulp.dest("Build/Documentation/Images"));
2024-05-04 01:08:26 +08:00
await finished(stream);
return stream;
}
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);
}
function combineForSandcastle() {
const outputDirectory = join("Build", "Sandcastle", "CesiumUnminified");
return buildCesium({
development: false,
minify: false,
removePragmas: false,
node: false,
outputDirectory: outputDirectory,
});
}
export const websiteRelease = gulp.series(
2023-11-02 01:49:14 +08:00
buildEngine,
buildWidgets,
function websiteReleaseBuild() {
return buildCesium({
development: false,
minify: false,
removePragmas: false,
node: false,
});
},
combineForSandcastle,
2024-09-20 23:24:24 +08:00
buildDocs,
);
2022-11-02 03:39:57 +08:00
export const buildRelease = gulp.series(
2023-02-22 23:11:07 +08:00
buildEngine,
buildWidgets,
// Generate Build/CesiumUnminified
function buildCesiumForNode() {
return buildCesium({
minify: false,
removePragmas: false,
node: true,
sourcemap: false,
});
},
// Generate Build/Cesium
function buildMinifiedCesiumForNode() {
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
);
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
);
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.`,
);
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.
*
* @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"];
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
return gulp.src(noPreparePackageJson, {
base: ".",
});
}
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")
.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)
.pipe(packageJsonSrc)
.pipe(
gulpRename((file) => {
if (file.basename === "package.noprepare") {
file.basename = "package";
}
2024-09-20 23:24:24 +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);
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
});
2022-09-09 00:43:28 +08:00
export async function deploySetVersion() {
const buildVersion = argv.buildVersion;
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));
}
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;
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`;
const coverageUrl = `${
devDeployUrl + process.env.BRANCH
}/Build/Coverage/index.html`;
2022-09-09 00:43:28 +08:00
return Promise.all([
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) {
// skip if the environment does not have the token
if (!process.env.GITHUB_TOKEN) {
return;
}
2022-09-09 00:43:28 +08:00
const body = {
state: state,
target_url: targetUrl,
description: description,
context: context,
};
const response = await fetch(
`https://api.github.com/repos/${process.env.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",
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
);
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.
*
* @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.
* @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;
2021-01-26 00:00:37 +08:00
const folders = [];
let browsers = ["Chrome"];
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,
},
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
);
},
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
});
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
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;
}
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;
const debug = argv.debug ? false : true;
const debugCanvasWidth = argv.debugCanvasWidth;
const debugCanvasHeight = argv.debugCanvasHeight;
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}/`, ``);
}
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"];
if (argv.browsers) {
browsers = argv.browsers.split(",");
}
2021-01-26 00:00:37 +08:00
let files = [
{ 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 },
];
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/",
};
}
if (release) {
files = [
{ pattern: "Specs/Data/**", included: false },
2022-06-02 23:49:39 +08:00
{ pattern: "Specs/TestWorkers/**/*.wasm", included: false },
{ 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 },
{ 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 },
];
}
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
* @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();
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
.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(
/\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")}`,
)
// Replace JSDoc generation version of Check with one that asserts the type of variables after called
.replace(
/\/\*\*[\*\s\w]*?\*\/\nexport const Check: any;/m,
`\n${readFileSync("./packages/engine/Source/Core/Check.d.ts")
.toString()
2024-11-23 01:08:26 +08:00
.replace(/export default.*\n?/, "")
.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;",
);
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", {
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
);
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();
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))) {
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 ")
.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")
.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[]")
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}`,
)
// Strip const enums which can cause errors - https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls
.replace(/^(\s*)(export )?const enum (\S+) {(\s*)$/gm, "$1$2enum $3 {$4")
// Replace JSDoc generation version of defined with an improved version using TS type predicates
.replace(
/\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")}`,
)
// Replace JSDoc generation version of Check with one that asserts the type of variables after called
.replace(
/\/\*\*[\*\s\w]*?\*\/\nexport const Check: any;/m,
`\n${readFileSync("./packages/engine/Source/Core/Check.d.ts")
.toString()
2024-11-23 01:08:26 +08:00
.replace(/export default.*\n?/, "")
.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;",
);
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
// Wrap the source to actually be inside of a declared cesium module
// and add any workaround and private utility types.
source = `declare module "cesium" {
${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
// 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",
});
// 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", {
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
}
/**
* Reads `ThirdParty.extra.json` file
* @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-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-09-09 00:43:28 +08:00
const contents = await readFile(path);
const thirdPartyExtra = JSON.parse(contents);
return Promise.all(
thirdPartyExtra.map(async function (module) {
if (!discoveredDependencies.includes(module.name)) {
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
);
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,
);
if (result) {
return result;
}
}
// If this is not a npm module, return existing info
discoveredDependencies.push(module.name);
return module;
}
2024-09-20 23:24:24 +08:00
}),
2022-09-09 00:43:28 +08:00
);
}
/**
2022-05-05 02:06:29 +08:00
* Extracts name, license, and url from `package.json` file.
*
* @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-09-09 00:43:28 +08:00
async function getLicenseDataFromPackage(
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
) {
if (
!packageJson.dependencies[packageName] &&
(!packageJson.devDependencies || !packageJson.devDependencies[packageName])
) {
return;
}
if (discoveredDependencies.includes(packageName)) {
2022-09-09 00:43:28 +08:00
return [];
}
discoveredDependencies.push(packageName);
2022-09-09 00:43:28 +08:00
const packagePath = join("node_modules", packageName, "package.json");
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) {
return Promise.reject(
2024-09-20 23:24:24 +08:00
new Error(`Unable to read ${packageName} license information`),
);
}
2022-09-09 00:43:28 +08:00
const packageData = JSON.parse(contents);
2022-09-09 00:43:28 +08:00
// Check for license
let licenseField = licenseOverride;
2022-09-09 00:43:28 +08:00
if (!licenseField) {
licenseField = [packageData.license];
}
2022-09-09 00:43:28 +08:00
if (!licenseField && packageData.licenses) {
licenseField = packageData.licenses;
}
2022-09-09 00:43:28 +08:00
if (!licenseField) {
console.log(`No license found for ${packageName}`);
licenseField = ["NONE"];
}
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-09-09 00:43:28 +08:00
return {
name: packageName,
license: licenseField,
version: packageVersion,
url: `https://www.npmjs.com/package/${packageName}`,
notes: notes,
};
}
2022-09-09 00:43:28 +08:00
export async function buildThirdParty() {
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(
"ThirdParty.extra.json",
2024-09-20 23:24:24 +08:00
discoveredDependencies,
2022-09-09 00:43:28 +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));
}
2024-05-04 01:08:26 +08:00
async function buildSandcastle() {
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
);
if (isProduction) {
2022-05-28 02:45:53 +08:00
// Remove swap out ESM modules for the IIFE build
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>',
),
)
// 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/"),
)
.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-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
)
// 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/**"],
{
base: "Apps/Sandcastle",
2024-05-14 22:58:38 +08:00
encoding: false,
2024-09-20 23:24:24 +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 })
.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 })
.pipe(gulp.dest("Build/Sandcastle/SampleData"));
streams.push(dataStream);
}
2021-01-26 00:00:37 +08:00
const standaloneStream = gulp
.src(["Apps/Sandcastle/standalone.html"])
.pipe(gulpReplace("../../../", "."))
.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>',
),
)
.pipe(gulpReplace("../../Build", "."))
.pipe(gulp.dest("Build/Sandcastle"));
streams.push(standaloneStream);
2024-05-14 22:58:38 +08:00
return Promise.all(streams.map((s) => finished(s)));
}
2022-05-24 20:54:14 +08:00
async function buildCesiumViewer() {
const cesiumViewerOutputDirectory = isProduction
? "Build/CesiumViewer"
: "Build/Apps/CesiumViewer";
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);
2022-05-24 20:54:14 +08:00
handleBuildWarnings(result);
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({
minify: true,
removePragmas: true,
2022-05-24 20:54:14 +08:00
path: cesiumViewerOutputDirectory,
});
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));
await finished(stream);
2024-05-04 01:08:26 +08:00
return stream;
}