diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 918b9122cc5..f36627ac9dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -596,6 +596,56 @@ jobs: - name: Verify generated types are in sync with schema run: npm run schema-typegen-diff-check + package-resolution: + needs: install-and-cibuild + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: ./.github/actions/setup-workspace + + - name: Pack the package + run: npm pack --pack-destination "$RUNNER_TEMP" + + - name: Install the tarball into a scratch project + working-directory: ${{ runner.temp }} + run: | + mkdir consumer && cd consumer + npm init -y + npm install "$RUNNER_TEMP"/plotly.js-*.tgz + + - name: Load every compiled module under Node + shell: node {0} + working-directory: ${{ runner.temp }}/consumer + run: | + const assert = require('node:assert'); + const fs = require('node:fs'); + + // src/lib/index.js reaches for these before it touches the DOM. + globalThis.self = globalThis; + globalThis.window = globalThis; + + const paths = [process.cwd()]; + const load = (name) => require(require.resolve(name, { paths })); + + const modules = fs + .globSync('src/**/*.ts', { cwd: process.env.GITHUB_WORKSPACE }) + .filter((file) => !file.endsWith('.d.ts')) + .map((file) => 'plotly.js/' + file.replace(/[.]ts$/, '')); + + if (modules.length === 0) throw new Error('Found no TypeScript sources to check'); + + for (const name of modules) load(name); + + const lib = load('plotly.js/src/lib/index'); + + assert.strictEqual(lib.mod(-1, 4), 3); + assert.strictEqual(lib.modHalf(3, 4), -1); + assert.deepStrictEqual(lib.sortObjectKeys({ b: 1, a: 2 }), ['a', 'b']); + assert.strictEqual(lib.cleanNumber(' 12 '), 12); + assert.strictEqual(typeof lib.counterRegex, 'function'); + + console.log('Loaded ' + modules.length + ' compiled modules and src/lib/index.js'); + # ============================================================ # Standalone jobs (no dependencies on install-and-cibuild) # ============================================================ diff --git a/.npmignore b/.npmignore index b340f1e4467..9b2732ef05d 100644 --- a/.npmignore +++ b/.npmignore @@ -14,3 +14,8 @@ stackgl_modules/node_modules tasks test topojson + +# Exclude the TypeScript files (but not declarations) because Node doesn't +# parse TS when installed in node_modules. +src/**/*.ts +!src/**/*.d.ts diff --git a/draftlogs/8000_fix.md b/draftlogs/8000_fix.md new file mode 100644 index 00000000000..2e3df165607 --- /dev/null +++ b/draftlogs/8000_fix.md @@ -0,0 +1 @@ +- Compile TypeScript files under `src/` to JavaScript during packaging to fix Node resolution [[#8000](https://github.com/plotly/plotly.js/pull/8000)] diff --git a/lib/index.d.ts b/lib/index.d.ts index ceaae5d7930..e17f8128ec8 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -61,12 +61,6 @@ export type { YAxisName } from '../src/types/core/layout'; -// --------------------------------------------------------------------------- -// Trace data -// --------------------------------------------------------------------------- - -export type { Data } from '../src/types/core/data'; - // --------------------------------------------------------------------------- // Configuration // --------------------------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index d54fc2b80a3..6ccab7df294 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@turf/area": "^7.3.5", "@turf/centroid": "^7.3.5", "@turf/meta": "^7.3.5", + "@types/d3": "^3.5.53", "base64-arraybuffer": "^1.0.2", "country-iso-search": "^0.1.2", "culori": "^4.0.2", @@ -54,7 +55,6 @@ "@biomejs/biome": "^2.5.5", "@plotly/mathjax-v3": "npm:mathjax@^3.2.2", "@plotly/mathjax-v4": "npm:mathjax@^4.1.3", - "@types/d3": "3.5.34", "@types/node": "^26.1.1", "assert": "^2.1.0", "buffer": "^6.0.3", @@ -1540,10 +1540,9 @@ } }, "node_modules/@types/d3": { - "version": "3.5.34", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.34.tgz", - "integrity": "sha512-2Ub7NdmaSLviC8lwRGt/7use4LBdLQi7iPEkM97yGKrbmCUqepOgOrGJLi1jPdR0/IIwBDpIbtOgdAOJWWXC+Q==", - "dev": true, + "version": "3.5.53", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.53.tgz", + "integrity": "sha512-8yKQA9cAS6+wGsJpBysmnhlaaxlN42Qizqkw+h2nILSlS+MAG2z4JdO6p+PJrJ+ACvimkmLJL281h157e52psQ==", "license": "MIT" }, "node_modules/@types/geojson": { diff --git a/package.json b/package.json index b2895d91a04..40ffca7239a 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,9 @@ "preversion": "check-node-version --node 22 --npm 10 && npm-link-check && npm ls --prod --all", "version": "npm run build && git add -A lib dist build src/version.js", "postversion": "node -e \"console.log('Version bumped and committed. If ok, run: git push && git push --tags')\"", - "postpublish": "node tasks/sync_packages.js" + "postpublish": "node tasks/sync_packages.js", + "prepack": "tsc -b tsconfig.build.json --force", + "postpack": "tsc -b tsconfig.build.json --clean" }, "dependencies": { "@plotly/d3": "3.8.2", @@ -73,6 +75,7 @@ "@turf/area": "^7.3.5", "@turf/centroid": "^7.3.5", "@turf/meta": "^7.3.5", + "@types/d3": "^3.5.53", "base64-arraybuffer": "^1.0.2", "country-iso-search": "^0.1.2", "culori": "^4.0.2", @@ -111,7 +114,6 @@ "@biomejs/biome": "^2.5.5", "@plotly/mathjax-v3": "npm:mathjax@^3.2.2", "@plotly/mathjax-v4": "npm:mathjax@^4.1.3", - "@types/d3": "3.5.34", "@types/node": "^26.1.1", "assert": "^2.1.0", "buffer": "^6.0.3", diff --git a/src/types/core/data.internal.d.ts b/src/types/core/data.internal.d.ts index 10e9036c72f..e1483f60fcd 100644 --- a/src/types/core/data.internal.d.ts +++ b/src/types/core/data.internal.d.ts @@ -2,11 +2,11 @@ * Internal data/trace types (not in public API) * * These are runtime-resolved versions of trace data with internal state - * properties. For public trace types, see data.d.ts. + * properties. For public trace types, see generated/schema.d.ts. */ +import type { Data } from '../generated/schema'; import type { Datum } from '../lib/common'; -import type { Data } from './data'; /** * Calculated trace data (internal). diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000000..ee48c7dcc71 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,30 @@ +{ + // Emit configuration for the published package. + // + // The repository authors a growing share of `src/` in TypeScript, but the + // published package must contain only JavaScript. Node's CommonJS resolver + // never tries a `.ts` extension, and Node refuses to strip types from any + // file below `node_modules`. So the `prepack` script writes a `.js` sibling + // for each `.ts` source, and `postpack` deletes it again. + // + // No `outDir` is set, so each `.js` lands next to its `.ts`. That is what + // makes `require('./mod')` resolve in the tarball. + // + // Build mode drives both scripts. `tsc -b` emits, and `tsc -b --clean` + // removes every generated file. Build mode also writes a state file, which + // `tsBuildInfoFile` parks below `build/`, because `build/` is already + // ignored by both git and npm. + // + // Type errors are not reported here. `npm run typecheck` owns that job and + // reads the whole program, including the JavaScript files. + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "noCheck": true, + "allowJs": false, + "module": "commonjs", + "tsBuildInfoFile": "build/tsconfig.build.tsbuildinfo" + }, + "include": ["src/**/*.ts"], + "exclude": ["src/types/**"] +}