From 50326e41bdda86b5dbb5aa0d3e18390b4d041afa Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 30 Aug 2026 17:47:13 +0800 Subject: [PATCH 1/8] Agent skills, an llms.txt API index, and the defects verifying them uncovered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ship 23 task-oriented skills at `packages/melonjs/skills/`, versioned with the engine and installable in Claude Code as a plugin. Each covers one subsystem and ends with a symptom-to-cause table, because melonJS's characteristic failure is silent rather than fatal: the engine warns once and degrades, so wrong output — not a stack trace — is what the user sees. `AGENTS.md` ships alongside them for agents following that convention (Codex, Cursor, Gemini CLI), to copy into a game's project root. Add `scripts/generate-llms-txt.ts`, chained onto `pnpm doc`, publishing an index of every exported symbol at melonjs.github.io/melonJS/llms.txt — the escape hatch for anything the skills do not cover. It reads the same TSDoc the reference pages render, so it cannot drift; marks deprecated entries, which are exactly what a model trained on older melonJS reaches for; and skips the 50 `String.prototype` pages typedoc emits for the `loader.nocache` string, which would otherwise present `loader.nocache.trimEnd` as melonJS API. Verifying the skills against the source found six code defects. Each is fixed with a test written to fail first: - Tiled: an object with no explicit geometry got a *triangular* collision shape — `(0,0), (w,0), (w,h)` omits the fourth vertex, so the lower-left half of every plain rectangle in a map was not solid. - Timer: `setInterval`'s `pauseable` argument was discarded by `pauseable === true || true`, so no repeating timer could survive a pause. `setTimeout` was unaffected (#1619). - Loader: the error path read `this.onError` from a module function, which has no `this` — a detached `preload` reported "Cannot read properties of undefined" instead of the real load failure. - Loader: `onload` / `onProgress` / `onError` are removed. Deprecated since 18.2.0, they were `let` bindings on a module namespace, so assigning them always threw; the documented migration path never worked, and the branch guarding `onProgress` was dead code. - Input: `preventDefault` is documented as a global option but is read-only for the same reason. `setPreventDefault()` is the working form. - Canvas: `setBlendMode("none")` resolves to `"normal"` explicitly rather than by fall-through — there is no `globalCompositeOperation` that disables blending for the drawn area alone. The same pass corrected the API reference against the code it documents: `Renderable#draw` told you to draw at `(0, 0)` when `preDraw` never translates to `this.pos`; the `body` example called `viewport.follow(this.pos, …)`, which throws; `Camera3d` documented a `roll` axis that does not exist; `Mesh` omitted eight constructor settings including `lit`; the `Asset` typedef advertised `"tmj"` / `"tsj"` types that throw; `Body#addShape`'s documentation had drifted onto a private helper; and 18 effect classes carried an unresolvable `@param` import path plus examples teaching `renderable.shader =`, deprecated since 19.2.0. Also: bump melonjs to 20.3.0 so the built `version` matches the unreleased line, guard the plugin manifest against drifting from it in `release.ts`, and give the root tsconfig `types: ["node"]` so `scripts/` type-checks at all. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_012Aa37KGXZcnVrbn1yG4j1N --- .claude-plugin/marketplace.json | 12 + .claude-plugin/plugin.json | 38 +++ README.md | 40 +++ packages/melonjs/CHANGELOG.md | 9 + packages/melonjs/package.json | 9 +- packages/melonjs/scripts/generate-llms-txt.ts | 267 +++++++++++++++ packages/melonjs/skills/AGENTS.md | 67 ++++ .../skills/melonjs-20-migration/SKILL.md | 160 +++++++++ .../melonjs/skills/melonjs-3d-assets/SKILL.md | 239 +++++++++++++ packages/melonjs/skills/melonjs-3d/SKILL.md | 226 ++++++++++++ .../melonjs/skills/melonjs-audio/SKILL.md | 215 ++++++++++++ .../melonjs-camera-and-drawing/SKILL.md | 171 ++++++++++ .../skills/melonjs-deployment/SKILL.md | 113 ++++++ .../melonjs-effects-and-shaders/SKILL.md | 177 ++++++++++ .../melonjs/skills/melonjs-events/SKILL.md | 159 +++++++++ .../skills/melonjs-getting-started/SKILL.md | 183 ++++++++++ .../melonjs/skills/melonjs-input/SKILL.md | 166 +++++++++ .../melonjs/skills/melonjs-lighting/SKILL.md | 139 ++++++++ .../skills/melonjs-loading-assets/SKILL.md | 157 +++++++++ .../melonjs-particles-and-trails/SKILL.md | 122 +++++++ .../skills/melonjs-performance/SKILL.md | 178 ++++++++++ .../melonjs/skills/melonjs-physics/SKILL.md | 321 ++++++++++++++++++ .../melonjs/skills/melonjs-plugins/SKILL.md | 191 +++++++++++ .../skills/melonjs-renderables/SKILL.md | 214 ++++++++++++ .../skills/melonjs-renderer-backends/SKILL.md | 173 ++++++++++ .../skills/melonjs-scenes-and-state/SKILL.md | 180 ++++++++++ .../melonjs-sprites-and-animation/SKILL.md | 173 ++++++++++ .../melonjs/skills/melonjs-tilemaps/SKILL.md | 193 +++++++++++ .../skills/melonjs-ui-and-text/SKILL.md | 175 ++++++++++ packages/melonjs/skills/melonjs/SKILL.md | 155 +++++++++ packages/melonjs/src/application/settings.ts | 5 +- packages/melonjs/src/audio/audio.ts | 2 +- packages/melonjs/src/audio/playback.ts | 24 +- packages/melonjs/src/camera/camera2d.ts | 2 +- packages/melonjs/src/camera/camera3d.ts | 10 +- packages/melonjs/src/const.ts | 4 +- packages/melonjs/src/index.ts | 5 +- packages/melonjs/src/input/input.ts | 23 +- packages/melonjs/src/lang/deprecated.js | 9 +- packages/melonjs/src/level/gltf/GLTFScene.js | 10 + .../src/level/tiled/TMXObjectFactory.js | 4 + packages/melonjs/src/loader/loader.js | 65 +--- packages/melonjs/src/physics/adapter.ts | 6 +- packages/melonjs/src/physics/builtin/body.js | 105 +++--- packages/melonjs/src/plugin/plugin.ts | 10 +- packages/melonjs/src/renderable/mesh.js | 10 +- packages/melonjs/src/renderable/renderable.js | 26 +- packages/melonjs/src/renderable/sprite.js | 14 +- packages/melonjs/src/state/stage.ts | 6 +- packages/melonjs/src/system/event.ts | 2 +- packages/melonjs/src/system/timer.ts | 2 +- packages/melonjs/src/video/blendmodes.js | 4 +- .../src/video/canvas/canvas_renderer.js | 13 +- packages/melonjs/src/video/effects/blur.js | 6 +- .../src/video/effects/chromaticAberration.js | 8 +- .../melonjs/src/video/effects/colorMatrix.js | 8 +- .../melonjs/src/video/effects/desaturate.js | 8 +- .../melonjs/src/video/effects/dissolve.js | 6 +- .../melonjs/src/video/effects/dropShadow.js | 8 +- packages/melonjs/src/video/effects/flash.js | 6 +- packages/melonjs/src/video/effects/glow.js | 8 +- .../melonjs/src/video/effects/hologram.js | 6 +- packages/melonjs/src/video/effects/invert.js | 8 +- packages/melonjs/src/video/effects/outline.js | 10 +- .../melonjs/src/video/effects/pixelate.js | 6 +- .../src/video/effects/radialGradient.js | 4 +- .../melonjs/src/video/effects/scanline.js | 10 +- packages/melonjs/src/video/effects/sepia.js | 8 +- .../melonjs/src/video/effects/shadereffect.js | 22 +- packages/melonjs/src/video/effects/shine.js | 4 +- .../melonjs/src/video/effects/tintPulse.js | 6 +- .../melonjs/src/video/effects/vignette.js | 10 +- packages/melonjs/src/video/effects/wave.js | 6 +- packages/melonjs/src/video/renderer.js | 6 +- packages/melonjs/src/video/texture/atlas.js | 2 +- packages/melonjs/src/video/webgl/glshader.js | 4 +- .../melonjs/src/video/webgl/webgl_renderer.js | 5 +- .../src/video/webgpu/webgpu_renderer.js | 3 +- .../tests/blend-modes-conformance.spec.js | 55 +++ packages/melonjs/tests/input.spec.js | 36 ++ packages/melonjs/tests/lights.spec.js | 5 +- packages/melonjs/tests/loader.spec.js | 52 +++ packages/melonjs/tests/timer.spec.js | 72 +++- .../melonjs/tests/tmx-shape-factory.spec.js | 42 +++ scripts/release.ts | 42 ++- tsconfig.json | 3 +- 86 files changed, 5207 insertions(+), 261 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 packages/melonjs/scripts/generate-llms-txt.ts create mode 100644 packages/melonjs/skills/AGENTS.md create mode 100644 packages/melonjs/skills/melonjs-20-migration/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-3d-assets/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-3d/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-audio/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-camera-and-drawing/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-deployment/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-effects-and-shaders/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-events/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-getting-started/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-input/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-lighting/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-loading-assets/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-particles-and-trails/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-performance/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-physics/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-plugins/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-renderables/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-renderer-backends/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-scenes-and-state/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-sprites-and-animation/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-tilemaps/SKILL.md create mode 100644 packages/melonjs/skills/melonjs-ui-and-text/SKILL.md create mode 100644 packages/melonjs/skills/melonjs/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000000..941cabe28e --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "melonjs", + "description": "Skills for building games with melonJS, the open source 2.5D HTML5 game engine.", + "owner": { "name": "melonJS", "url": "https://melonjs.org" }, + "plugins": [ + { + "name": "melonjs", + "source": "./", + "description": "Build games with melonJS — engine conventions, API usage, and the pitfalls that make generated code fail silently." + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000000..03a2972318 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,38 @@ +{ + "name": "melonjs", + "version": "20.3.0", + "description": "Build games with melonJS \u2014 engine conventions, API usage, and the pitfalls that make generated code fail silently.", + "author": { + "name": "melonJS", + "url": "https://melonjs.org" + }, + "homepage": "https://melonjs.org", + "repository": "https://github.com/melonjs/melonJS", + "license": "MIT", + "keywords": ["melonjs", "game-engine", "html5", "webgpu", "webgl", "gamedev"], + "skills": [ + "./packages/melonjs/skills/melonjs", + "./packages/melonjs/skills/melonjs-getting-started", + "./packages/melonjs/skills/melonjs-20-migration", + "./packages/melonjs/skills/melonjs-renderables", + "./packages/melonjs/skills/melonjs-3d", + "./packages/melonjs/skills/melonjs-3d-assets", + "./packages/melonjs/skills/melonjs-physics", + "./packages/melonjs/skills/melonjs-sprites-and-animation", + "./packages/melonjs/skills/melonjs-input", + "./packages/melonjs/skills/melonjs-effects-and-shaders", + "./packages/melonjs/skills/melonjs-tilemaps", + "./packages/melonjs/skills/melonjs-audio", + "./packages/melonjs/skills/melonjs-scenes-and-state", + "./packages/melonjs/skills/melonjs-ui-and-text", + "./packages/melonjs/skills/melonjs-particles-and-trails", + "./packages/melonjs/skills/melonjs-camera-and-drawing", + "./packages/melonjs/skills/melonjs-lighting", + "./packages/melonjs/skills/melonjs-deployment", + "./packages/melonjs/skills/melonjs-loading-assets", + "./packages/melonjs/skills/melonjs-renderer-backends", + "./packages/melonjs/skills/melonjs-performance", + "./packages/melonjs/skills/melonjs-events", + "./packages/melonjs/skills/melonjs-plugins" + ] +} diff --git a/README.md b/README.md index 79f9d67c63..6f502464c4 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,46 @@ Transpiling costs some size and speed: private class members become `WeakMap` lookups, which are on hot paths in the renderer. Only reach for it if you have users on browsers that need it. +Building with AI agents +------------------------------------------------------------------------------- + +melonJS ships **skills** — guidance files that teach AI coding assistants the +engine's conventions and, more usefully, the mistakes that fail silently rather +than raising an error. + +They are installed with the package, at `node_modules/melonjs/skills/`, and are +versioned with the engine — so the guidance always matches the release you have. + +**Claude Code** — install as a plugin : + +``` +/plugin marketplace add melonjs/melonJS +``` + +or copy the skills into a project : + +```bash +mkdir -p .claude/skills && cp -r node_modules/melonjs/skills/melonjs* .claude/skills/ +``` + +**Other agents** (Codex, Cursor, Gemini CLI, …) read an `AGENTS.md` from your own +project root. One ships ready to use — copy it across : + +```bash +cp node_modules/melonjs/skills/AGENTS.md ./AGENTS.md +``` + +It points at the shipped skills, names the three rules that produce code which +runs and is wrong, and links the API index below. If you already have an +`AGENTS.md`, paste its sections into yours. + +The skills are plain markdown and can be read by any agent, or by a human. + +For anything the skills do not cover, the complete API is indexed for agents at +[llms.txt](https://melonjs.github.io/melonJS/llms.txt) — every exported class, +function and type with a one-line summary and a link to its reference page, +regenerated on every docs build. + Community ------------------------------------------------------------------------------- Join us and get help or share your projects : diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index 185ac160b5..615294a673 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -3,6 +3,8 @@ ## [20.3.0] (melonJS 2) - _unreleased_ ### Added +- Skills for AI coding assistants, shipped in the package at `skills/` and versioned with the engine. Twenty-three topic guides — bootstrap, scenes, renderables, sprites, input, physics, tilemaps, audio, UI and text, shaders, particles, lighting, the 3D tier and its asset formats, loading, backends, events, performance, plugins and the 20.x migration — each ending with a symptom-to-cause table for the failures that are silent rather than fatal: a custom `draw()` that ignores `this.pos`, `isKinematic` blocking pointer events, `.z` set after `addChild`, `removePostEffect()` destroying the effect. Installable in Claude Code via `/plugin marketplace add melonjs/melonJS`; for agents following the `AGENTS.md` convention (Codex, Cursor, Gemini CLI) a ready-made `skills/AGENTS.md` ships alongside them, to copy into a game's project root +- Documentation: an `llms.txt` index is now published with the API reference at , listing every exported class, function, interface, type and namespace with a one-line summary, a link to its page and a marker on the deprecated ones. Regenerated from the TSDoc comments on every docs build, so an agent that cannot find something in a skill has one URL to fetch rather than an API name to guess - Audio: sprite support — a clip can declare named regions of one file (`sprite: { jump: [0, 450] }`) and `audio.play("sfx", { sprite: "jump" })` plays one, so a single download can carry many effects. `play()`'s second argument now takes either the original `loop` boolean or an options object (`sprite`, `loop`, `onend`, `volume`); every existing call form is unchanged - Audio: `duration()`, `playing()` and `state()` report a clip's length, whether it is playing, and its load state - Audio: `pool`, `rate`, `mute`, `preload` and `format` can be set when loading a clip, along with an `on` map of lifecycle callbacks (`play`, `pause`, `stop`, `end`, `fade`, `seek`, `rate`, `volume`, `mute`, `unlock`). Each is forwarded only when set, so existing declarations are unaffected @@ -13,8 +15,15 @@ - Loader: the shared fetch helper moved from `loader/parsers/` to `utils/`. It is engine transport rather than loader policy, and every asset type — including audio, which needs it for the `file:` fallback — is a peer consumer of it. Internal module, no public API change - Loader: audio is registered like every other asset type, through a parser in `loader/parsers/`. The loader previously imported the whole audio module to register its load function directly, so it was the one type whose parser was not a parser. It now has no direct dependency on the audio module, which keeps a future backend change from rippling into the loader - Audio: the engine no longer depends on a third-party audio library. The backend is now maintained in-tree, which removes melonJS's last runtime dependency besides core-js and fixes a long-standing packaging wart: the emitted type declarations referenced the library, so consumers type-checking with `skipLibCheck: false` had to install it themselves +- Loader: the `onload`, `onProgress` and `onError` properties are removed. Deprecated since 18.2.0, they were `let` bindings on an ES module namespace — `loader.onProgress = fn` threw a `TypeError`, so the documented callback API could never be used by anyone. Use the `LOADER_COMPLETE` / `LOADER_PROGRESS` / `LOADER_ERROR` events, or `preload(assets, onloadcb)` +- Input: added `input.setPreventDefault(value)`. `input.preventDefault` is documented as a global option but is a read-only module binding, so assigning to it silently had no effect; the setter is the working form, and `bindKey`'s fourth argument still overrides it per binding ### Fixed +- Tiled: an object with no explicit geometry got a **triangular** collision shape. The default shape was built from three vertices — `(0,0)`, `(w,0)`, `(w,h)` — so the lower-left half of every plain rectangle in a map was not solid +- Timer: `setInterval`'s `pauseable` argument was ignored — the expression storing it was unconditionally true, so a repeating timer could never be made to keep running while the engine is paused. `setTimeout` was unaffected. A pause-menu animation or a countdown that should not freeze now works as documented ([#1619](https://github.com/melonjs/melonJS/issues/1619)) +- Loader: a failed asset reported `Cannot read properties of undefined (reading 'onError')` instead of `Failed loading resource ` when `preload` was called detached from the `loader` namespace, masking the real failure — the error path read `this.onError` from a module function, which has no `this` +- Lighting: the one-time console warning for normal-map lighting named the WebGL renderer and told you to switch to `video.WEBGL`, which sent WebGPU users chasing a fallback they were not on — WebGPU implements the lit pipeline too. It now names a GPU backend, and says that `Light2d` glow and stage lighting still draw on Canvas +- Docs: a sweep of the API reference against the source it documents. `Renderable#draw` told you to draw at `(0, 0)` when `preDraw` never translates to `this.pos`; the `body` example called `viewport.follow(this.pos, …)`, which throws; `Camera3d` documented a `roll` axis that does not exist; `Mesh` omitted eight constructor settings including `lit`, the switch for the whole lit-mesh path; the `Asset` typedef advertised `"tmj"` / `"tsj"` asset types that throw (those are file extensions under `"tmx"` / `"tsx"`); `Body#addShape`'s documentation had drifted onto a private helper, leaving the public method undocumented; and 18 effect classes carried an unresolvable `@param` import path plus examples teaching `renderable.shader =`, deprecated since 19.2.0. `setBlendMode` also claimed on all three backends that "every renderer supports the full set", when `"none"` needs a GPU backend — on Canvas it resolves to `"normal"`, which `setBlendMode` reports back - Docs: the README gained a short guide on transpiling for older browsers, including the trap that catches people — build setups skip `node_modules` by default, so melonJS is left untouched however low the target is set - Docs: the README described the engine as ES6 and gave no target for the published bundle, which reads as a compatibility claim it does not meet — the bundle uses private class members and ~700 ES2020 constructs, so an ES6-era browser cannot parse it at all. It now states the ES2022 target once, where someone deciding whether to adopt will look. The CDN example also still referenced v19 - Audio: sound files failed to load when the game is served from `file://` — a Cordova or Capacitor APK on Android, most commonly. `fetch()` cannot read that scheme in those WebViews, and audio was the only asset type still calling it directly instead of going through the engine's loader, which falls back to XHR diff --git a/packages/melonjs/package.json b/packages/melonjs/package.json index aade6dd5fe..6401415792 100644 --- a/packages/melonjs/package.json +++ b/packages/melonjs/package.json @@ -1,6 +1,6 @@ { "name": "melonjs", - "version": "20.2.0", + "version": "20.3.0", "description": "melonJS Game Engine", "homepage": "http://www.melonjs.org/", "type": "module", @@ -50,9 +50,10 @@ }, "files": [ "build", - "package.json", + "skills", "README.md", - "CHANGELOG" + "CHANGELOG", + "package.json" ], "devDependencies": { "@types/node": "^25.6.0", @@ -76,7 +77,7 @@ "build": "pnpm lint && tsx scripts/build.js && pnpm types", "dist": "pnpm clean && pnpm lint && pnpm vitest run && pnpm build && pnpm doc && cp ../../README.md .", "dist:publish": "pnpm clean && pnpm lint && pnpm build && pnpm doc && cp ../../README.md .", - "doc": "tsx scripts/check-doc-readme.ts && typedoc src/index.ts --tsconfig tsconfig.build.json --readme ../../DOC_README.md --hideGenerator --name 'melonJS' --navigation.includeCategories true --categorizeByGroup false", + "doc": "tsx scripts/check-doc-readme.ts && typedoc src/index.ts --tsconfig tsconfig.build.json --readme ../../DOC_README.md --hideGenerator --name 'melonJS' --navigation.includeCategories true --categorizeByGroup false && tsx scripts/generate-llms-txt.ts", "doc:watch": "typedoc src/index.ts --tsconfig tsconfig.build.json --readme ../../DOC_README.md --hideGenerator --name 'melonJS' --navigation.includeCategories true --categorizeByGroup false --watch --skipErrorChecking --preserveWatchOutput --logLevel Error", "serve": "serve docs", "prepublishOnly": "pnpm dist:publish", diff --git a/packages/melonjs/scripts/generate-llms-txt.ts b/packages/melonjs/scripts/generate-llms-txt.ts new file mode 100644 index 0000000000..09101836c9 --- /dev/null +++ b/packages/melonjs/scripts/generate-llms-txt.ts @@ -0,0 +1,267 @@ +/** + * Generate `docs/llms.txt` from the TypeDoc output. + * + * The agent skills under `skills/` cover the engine by topic, but they are + * hand-written and deliberately partial. `llms.txt` is the escape hatch: a + * machine-readable index of the *whole* generated API reference, regenerated on + * every docs build, so an agent that cannot find something in a skill has one + * URL to fetch instead of guessing at class names. + * + * Format follows llmstxt.org: an H1, a blockquote summary, then sections of + * `- [name](url): summary` links. Summaries come from the same TSDoc comments + * the HTML pages render, so this cannot drift from the docs it indexes. + * + * Runs after `typedoc` in the `doc` script; `docs/` is what the Pages workflow + * publishes, so the file lands at . + */ +import { readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const docsDir = resolve(here, "../docs"); +const baseUrl = "https://melonjs.github.io/melonJS/"; + +/** TypeDoc's output directories, in the order they are listed in the index */ +const sections = [ + { dir: "classes", title: "Classes" }, + { dir: "functions", title: "Functions" }, + { dir: "interfaces", title: "Interfaces" }, + { dir: "enums", title: "Enumerations" }, + { dir: "types", title: "Type aliases" }, + { dir: "variables", title: "Variables and namespaces" }, + { dir: "modules", title: "Modules" }, +]; + +const entities: Record = { + "&": "&", + "<": "<", + ">": ">", + """: '"', + "'": "'", + " ": " ", +}; + +const toText = (html: string): string => { + return html + .replace(/<[^>]+>/g, "") + .replace(/&[a-z#0-9]+;/g, (match) => { + return entities[match] ?? " "; + }) + .replace(/\s+/g, " ") + .trim(); +}; + +/** + * The substring of `html` covered by the div opening at `from`, closing tag + * included. Regex cannot match nested `
`s, and TypeDoc nests them. + * @param html - the page source + * @param from - index of the opening ` { + const tag = /<\/?div\b/g; + tag.lastIndex = from; + let depth = 0; + let match = tag.exec(html); + while (match !== null) { + depth += match[0] === "".length); + } + match = tag.exec(html); + } + return html.slice(from); +}; + +/** + * The first documented paragraph inside a block of TypeDoc HTML. + * + * `@deprecated` / `@see` render as `tsd-tag-*` blocks *inside* the comment, so + * they are lifted out first — otherwise a symbol whose only comment is + * `@deprecated since 17.1.0` gets indexed with "since 17.1.0" as its summary. + * Parameter and return blocks are cut away for the same reason: they carry + * comments of their own, and one of those is not a summary of the symbol. + * @param block - a fragment of TypeDoc HTML + * @returns the paragraph as plain text, or an empty string + */ +const firstParagraph = (block: string): string => { + const body = block + .split('
'); + while (at !== -1) { + let comment = balancedDiv(body, at); + for (;;) { + const tagAt = comment.indexOf('
([\s\S]*?)<\/p>/); + if (paragraph !== null) { + const text = toText(paragraph[1]); + if (text !== "") { + return text; + } + } + at = body.indexOf('
', at + 1); + } + return ""; +}; + +/** + * Whether the page documents something this repository actually defines. + * + * TypeDoc walks into the type of an exported value, so a `string` constant like + * `loader.nocache` contributes a page for every `String.prototype` method — + * 49 of them, all pointing at `lib.es5.d.ts`. Indexing those would tell an + * agent that `loader.nocache.trimEnd` is part of the melonJS API. + * @param html - the page source + * @returns true when at least one source link points into the repository + */ +const isOwnSymbol = (html: string): boolean => { + const sources = [ + ...html.matchAll(/