Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ jobs:
- name: Verify generated types are in sync with schema
run: npm run schema-typegen-diff-check

- name: Verify entry point declarations are in sync
run: npm run entry-point-types-check

package-resolution:
needs: install-and-cibuild
runs-on: ubuntu-latest
Expand Down Expand Up @@ -646,6 +649,21 @@ jobs:

console.log('Loaded ' + modules.length + ' compiled modules and src/lib/index.js');

- name: Type-check type imports from the entry points
working-directory: ${{ runner.temp }}/consumer
run: |
cat > entry-point-types.ts <<'EOF'
import type * as Root from 'plotly.js';
import type { Config, Data, Layout } from 'plotly.js/lib/core';
import type * as calendars from 'plotly.js/lib/calendars';
import type * as indexBasic from 'plotly.js/lib/index-basic';
import type * as scatter from 'plotly.js/lib/scatter';
import type * as de from 'plotly.js/lib/locales/de';
EOF
"$GITHUB_WORKSPACE"/node_modules/.bin/tsc entry-point-types.ts \
--noEmit --strict --skipLibCheck \
--target es2022 --module esnext --moduleResolution bundler

# ============================================================
# Standalone jobs (no dependencies on install-and-cibuild)
# ============================================================
Expand Down
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
"license": "MIT",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"typesVersions": {
"*": {
"lib/index.d.ts": [
"./lib/index.d.ts"
],
"lib/index": [
"./lib/index.d.ts"
],
"lib/*": [
"./src/types/generated/entry_points/*.d.ts"
]
}
},
"webpack": "./dist/plotly.js",
"repository": {
"type": "git",
Expand All @@ -30,14 +43,16 @@
"extra-bundles": "node tasks/extra_bundles.mjs",
"locales": "node tasks/locales.mjs",
"schema": "node tasks/schema.mjs",
"schema-typegen-diff-check": "npm run schema && git diff --exit-code src/types/generated/ test/plot-schema.json",
"schema-typegen-diff-check": "npm run schema && git diff --exit-code src/types/generated/schema.d.ts test/plot-schema.json",
"entry-point-types": "node tasks/entry_point_types.mjs",
"entry-point-types-check": "node tasks/entry_point_types.mjs --check",
"stats": "node tasks/stats.mjs",
"find-strings": "node tasks/find_locale_strings.js",
"preprocess": "node tasks/preprocess.js",
"use-draftlogs": "node tasks/use_draftlogs.js",
"empty-draftlogs": "node tasks/empty_draftlogs.js",
"empty-dist": "node tasks/empty_dist.js",
"build": "npm run empty-dist && npm run preprocess && npm run find-strings && npm run bundle && npm run extra-bundles && npm run locales && npm run schema dist && npm run stats",
"build": "npm run empty-dist && npm run preprocess && npm run entry-point-types && npm run find-strings && npm run bundle && npm run extra-bundles && npm run locales && npm run schema dist && npm run stats",
"regl-codegen": "node devtools/regl_codegen/server.mjs",
"cibuild": "npm run empty-dist && npm run preprocess && node tasks/cibundle.mjs",
"lint": "npx @biomejs/biome lint",
Expand Down
29 changes: 28 additions & 1 deletion src/types/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ How TypeScript types are organized in plotly.js.
│ │ │ schema.d.ts — common enums, │
│ │ │ traces, layout, animation, │
│ │ │ config, _internal namespace │
│ │ │ │
│ │ │ entry_points/ — one │
│ │ │ declaration per lib/ path │
└──────────────────────────┘ └────────────────────────────────┘
```

Expand Down Expand Up @@ -119,9 +122,33 @@ src/types/
│ └── attributes.d.ts # AttributeMap, AttrInfo (compile-time validation)
└── generated/ # machine-generated types
└── schema.d.ts # all traces + layout + shared types (from plot-schema.json)
├── schema.d.ts # all traces + layout + shared types (from plot-schema.json)
└── entry_points/ # one declaration per lib/ entry point
├── core.d.ts # plotly.js/lib/core
├── scatter.d.ts # plotly.js/lib/scatter, and one per trace
└── locales/ # plotly.js/lib/locales/<id>, one per locale
```

### The `generated/entry_points/` directory

`lib/` holds one entry point per trace, component, bundle, and locale, so a
consumer can import `plotly.js/lib/scatter` instead of the whole library. Each
entry point needs its own declaration. Those declarations live here rather than
beside the entry points so `lib/` stays readable.

`typesVersions` in package.json maps `plotly.js/lib/<entry>` onto this directory.
Two things follow from that:

- The `lib/index.d.ts` key in that table is necessary. TypeScript 5 runs the
resolved `types` field back through the table, and without the key the `lib/*`
pattern captures it and breaks the plain `plotly.js` import.
- WARNING: TypeScript ignores `typesVersions` once a package has an `exports`
field. Adding `exports` to package.json breaks every subpath declaration here.

Run `npm run entry-point-types` to regenerate. Run
`npm run entry-point-types-check` to fail when the committed output is stale. CI
runs the check, so a new entry point cannot ship without its declaration.

### The `.internal.d.ts` convention

Files with the `.internal.d.ts` suffix contain types that are **not** part of the
Expand Down
2 changes: 1 addition & 1 deletion src/types/CONVERTING_ATTRIBUTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ should be added to the corresponding `Full*` interface instead.
```bash
npm run typecheck # zero errors
npm run schema-typegen-diff-check # regen + check test/plot-schema.json
# and src/types/generated/ are unchanged
# and generated/schema.d.ts are unchanged
```

The `schema-typegen-diff-check` script regenerates both the runtime schema
Expand Down
8 changes: 6 additions & 2 deletions src/types/GENERATOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,14 @@ wildcard) but their bare names are not.
## CI integration

`npm run schema-typegen-diff-check` runs the generator and then verifies that
both `test/plot-schema.json` and `src/types/generated/` are unchanged via
`git diff --exit-code`. If either differs, the command fails with exit code 1
both `test/plot-schema.json` and `src/types/generated/schema.d.ts` are unchanged
via `git diff --exit-code`. If either differs, the command fails with exit code 1
and outputs the diff to the console.

The path names `schema.d.ts` rather than the whole `generated/` directory, so an
uncommitted change to the entry point declarations cannot fail this check.
`npm run entry-point-types-check` covers those.

This is what makes the JS-to-TS conversion workflow safe: a correct
conversion produces a byte-identical schema, so the check passes; an
incorrect conversion (typo in a `values` array, missed default, wrong
Expand Down
19 changes: 16 additions & 3 deletions src/types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This directory documents the TypeScript conversion in progress.
| [SETUP.md](SETUP.md) | First-time contributor — toolchain overview, npm scripts |
| [ARCHITECTURE.md](ARCHITECTURE.md) | Anyone working with types — directory layout, public/private split |
| [CONVERTING_ATTRIBUTES.md](CONVERTING_ATTRIBUTES.md) | **Contributor doing conversion work** — step-by-step recipe |
| [GENERATOR.md](GENERATOR.md) | Maintainer extending or debugging the type generator |
| [GENERATOR.md](GENERATOR.md) | Maintainer extending or debugging the schema type generator |

## Status

Expand All @@ -16,7 +16,8 @@ This directory documents the TypeScript conversion in progress.
- `AttributeMap` validation machinery: ✅ done
- **Schema-based type generator**: ✅ done — all trace types + layout + shared interfaces
- Consumer entry point (`lib/index.d.ts`, wired via `package.json#types`): ✅ done
- CI gates (`typecheck` + `schema-typegen-diff-check`): ✅ done
- Modular entry points (`plotly.js/lib/<entry>`, wired via `package.json#typesVersions`): ✅ done
- CI gates (`typecheck` + `schema-typegen-diff-check` + `entry-point-types-check`): ✅ done
- First attribute file converted (modebar): ✅ done
- Conversion of remaining files: 🚧 in progress

Expand All @@ -36,12 +37,24 @@ This directory documents the TypeScript conversion in progress.
`Datum[] | Datum[][] | TypedArray` for *every* `data_array` so 2D/3D usage
typechecks, but the trade-off is that 1D-only fields also accept 2D arrays.

The published consumer surface lives at [`lib/index.d.ts`](../../lib/index.d.ts).
The published consumer surface has two parts. [`lib/index.d.ts`](../../lib/index.d.ts)
declares the full library for `import ... from 'plotly.js'`. The generated
declarations in [`generated/entry_points/`](generated/entry_points/) cover the
modular entry points, one file each: `plotly.js/lib/core`, every trace and
component, the partial bundles, and every locale under
`plotly.js/lib/locales/`.

This `src/types/` directory is the authoring location — internal types live
here, public types are re-exported through `lib/index.d.ts` to consumers.

## Generated types

Two tasks generate types. This section covers the schema generator. For the
entry point declarations under
[`generated/entry_points/`](generated/entry_points/), see
[ARCHITECTURE.md](ARCHITECTURE.md#the-generatedentry_points-directory) and run
`npm run entry-point-types`.

The following are **auto-generated from `plot-schema.json`** by
`tasks/generate_schema_types.mjs`:

Expand Down
10 changes: 8 additions & 2 deletions src/types/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ npm run typecheck-watch # incremental rechecking on change

npm run schema # rebuild test/plot-schema.json + regenerate types under src/types/generated/
npm run schema-typegen-diff-check # regenerate + verify no changes to test/plot-schema.json or src/types/generated/schema.d.ts

npm run entry-point-types # regenerate the entry point declarations under src/types/generated/entry_points/
npm run entry-point-types-check # verify the committed entry point declarations are current

npm run build # full production build (regenerate all files under `dist/`)
```

Expand All @@ -48,14 +52,16 @@ npm start

```bash
npm run typecheck
npm run schema # if attribute files changed
npm run schema # if attribute files changed
npm run entry-point-types # if you added or removed a lib/ entry point
```

**CI** runs both checks as separate jobs (see `.github/workflows/ci.yml`):
**CI** runs these checks (see `.github/workflows/ci.yml`):

```bash
npm run typecheck # validates the type system is internally consistent
npm run schema-typegen-diff-check # verifies generated types match the schema
npm run entry-point-types-check # verifies every lib/ entry point has a current declaration
```

## How esbuild handles `.ts`
Expand Down
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/bar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/bar.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `bar` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as bar from 'plotly.js/lib/bar';
*
* Plotly.register([bar]);
*/
declare const bar: RegisterTraceModule & { name: 'bar' };

export = bar;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/barpolar.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/barpolar.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `barpolar` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as barpolar from 'plotly.js/lib/barpolar';
*
* Plotly.register([barpolar]);
*/
declare const barpolar: RegisterTraceModule & { name: 'barpolar' };

export = barpolar;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/box.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/box.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `box` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as box from 'plotly.js/lib/box';
*
* Plotly.register([box]);
*/
declare const box: RegisterTraceModule & { name: 'box' };

export = box;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/calendars.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/calendars.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterComponentModule } from '../../core/api';

/**
* The `calendars` component module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as calendars from 'plotly.js/lib/calendars';
*
* Plotly.register([calendars]);
*/
declare const calendars: RegisterComponentModule & { name: 'calendars' };

export = calendars;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/candlestick.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/candlestick.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `candlestick` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as candlestick from 'plotly.js/lib/candlestick';
*
* Plotly.register([candlestick]);
*/
declare const candlestick: RegisterTraceModule & { name: 'candlestick' };

export = candlestick;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/carpet.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/carpet.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `carpet` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as carpet from 'plotly.js/lib/carpet';
*
* Plotly.register([carpet]);
*/
declare const carpet: RegisterTraceModule & { name: 'carpet' };

export = carpet;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/choropleth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/choropleth.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `choropleth` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as choropleth from 'plotly.js/lib/choropleth';
*
* Plotly.register([choropleth]);
*/
declare const choropleth: RegisterTraceModule & { name: 'choropleth' };

export = choropleth;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/choroplethmap.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/choroplethmap.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `choroplethmap` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as choroplethmap from 'plotly.js/lib/choroplethmap';
*
* Plotly.register([choroplethmap]);
*/
declare const choroplethmap: RegisterTraceModule & { name: 'choroplethmap' };

export = choroplethmap;
19 changes: 19 additions & 0 deletions src/types/generated/entry_points/cone.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Generated from lib/cone.js by tasks/generate_entry_point_types.mjs.
* Do not edit by hand — run `npm run entry-point-types` to regenerate.
*/

import type { RegisterTraceModule } from '../../core/api';

/**
* The `cone` trace module, for `Plotly.register`.
*
* @example
* import * as Plotly from 'plotly.js/lib/core';
* import * as cone from 'plotly.js/lib/cone';
*
* Plotly.register([cone]);
*/
declare const cone: RegisterTraceModule & { name: 'cone' };

export = cone;
Loading
Loading