|
| 1 | +# `src`: the TypeScript transcription of MySQL2 |
| 2 | + |
| 3 | +`src` is the TypeScript transcription of the shipped JavaScript (the two entry points and `lib`), developed in parallel while that JavaScript remains what users install. Nothing in `src` is published, covered by the regular CI, or loaded at runtime until the community declares the transcription complete. |
| 4 | + |
| 5 | +Context: [#2803](https://github.com/sidorares/node-mysql2/issues/2803) and [#3695](https://github.com/sidorares/node-mysql2/issues/3695). |
| 6 | + |
| 7 | +## Rules |
| 8 | + |
| 9 | +1. **Runtime behavior never changes.** The built output must do exactly what the JavaScript does today: same exports, same lazy loading, same errors, same event order, same performance. Types describe the code, they do not redesign it. |
| 10 | +2. **Mirror the layout by default.** Each shipped file gets a TypeScript twin at the same path under `src`. Deviate only when TypeScript or the build requires it, and say why in the pull request. |
| 11 | +3. **Transcribe, do not refactor.** Keep names, structure and statement order. A bug found while transcribing is fixed in the shipped JavaScript first, through a regular pull request to `master`, then ported. |
| 12 | +4. **No `any`, no double casts.** Use `unknown` and narrow it. Prefer `type` over `interface`, and named exports over default exports, so the emitted CommonJS keeps the flat `exports` shape users rely on. |
| 13 | +5. **Imports carry the `.js` extension**, the way the built output resolves them. Biome enforces it. |
| 14 | +6. **Ambient declarations for dependencies without typings live in `src/types`.** Keep them faithful to how the shipped code uses each package, nothing more. |
| 15 | +7. **`typings` remains the public type contract until the switch.** When it disagrees with `src`, the mismatch is a finding to discuss in the pull request, not something to paper over. |
| 16 | + |
| 17 | +## Build |
| 18 | + |
| 19 | +The build emits to `dist`, git-ignored, mirroring the repository root: the two entry points, `lib`, and a declaration file next to each module. Class fields compile to constructor assignments, matching the JavaScript. |
| 20 | + |
| 21 | +| Setting | Value | Why | |
| 22 | +| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | |
| 23 | +| `target` | `es2020` | Node.js 14 is the real runtime floor and ES2020 is the newest syntax it runs natively, so nothing the JavaScript already uses gets down-leveled. | |
| 24 | +| `module` | `node16` | With the package declared as CommonJS, every file compiles to CommonJS, and Node.js, Bun and Deno keep loading the same module format. | |
| 25 | +| `declaration` | `true` | The emitted declarations become the type contract at the switch. | |
| 26 | +| `sourceMap` | `false` | Open decision: maps help debugging but add package size. Revisit before the switch. | |
| 27 | +| `skipLibCheck` | `true` | `aws-ssl-profiles` ships a declaration file the compiler rejects. Fixing it upstream lets this go back to `false`. | |
| 28 | + |
| 29 | +## Commands |
| 30 | + |
| 31 | +```sh |
| 32 | +npm run src:typecheck |
| 33 | +npm run src:build |
| 34 | +npm run src:test # Node.js |
| 35 | +npm run src:test:bun |
| 36 | +npm run src:test:deno |
| 37 | +``` |
| 38 | + |
| 39 | +`npm run lint` already covers `src`. |
| 40 | + |
| 41 | +## Transcribing a file |
| 42 | + |
| 43 | +1. Pick a file, starting from the leaves (constants, packets, parsers) and moving up to the connection, the promise wrapper and the entry points. |
| 44 | +2. Create its twin in `src` and transcribe it statement by statement. |
| 45 | +3. Run the typecheck and the linter. |
| 46 | +4. Open a pull request against `js-to-ts`, one file or one small directory each. |
| 47 | + |
| 48 | +## Keeping up with `master` |
| 49 | + |
| 50 | +`master` keeps changing the shipped JavaScript while the transcription happens here. Merge it into `js-to-ts` regularly. Since `src` and the shipped files never overlap, the merge never conflicts, it only brings changes the transcribed files may not reflect yet. Before merging, list what changed: |
| 51 | + |
| 52 | +```sh |
| 53 | +git fetch origin |
| 54 | +git diff --stat HEAD...origin/master -- index.js promise.js lib |
| 55 | +``` |
| 56 | + |
| 57 | +Merge, then port each change to its twin in `src`. Files not transcribed yet need nothing, since they will be transcribed from the current JavaScript later. |
| 58 | + |
| 59 | +## Testing |
| 60 | + |
| 61 | +The existing test suite, unchanged, is the acceptance test. The `src:test` scripts build `dist`, copy the tests next to it and run them there, so every relative import in a test resolves to the built output instead of the shipped JavaScript. `FILTER` works as usual, a MySQL server is needed as for `npm test`, and the run only makes sense once the entry points and everything they import exist in `src`. |
| 62 | + |
| 63 | +The dedicated workflow typechecks and builds `src` on every pull request that touches it, then loads the built entry points on the Node.js versions the regular CI already tests, once they exist. The regular workflows never look at `src`. |
| 64 | + |
| 65 | +## Known questions |
| 66 | + |
| 67 | +- The callback entry point exposes part of its API through lazy getters, so the promise API is not loaded eagerly and the module graph stays free of cycles, which the circular-import check enforces. ES module syntax has no lazy export, so the transcribed entry points need an explicit CommonJS getter and a lint exception for it. |
| 68 | +- Named placeholders are loaded lazily on first use. The transcription must keep that. |
| 69 | +- The row parsers generate code from strings. The generated source stays untyped by nature: type the generator inputs and the returned function, nothing inside the string. |
| 70 | + |
| 71 | +## The switch |
| 72 | + |
| 73 | +When `src` is complete, one pull request makes the built output the shipped code: |
| 74 | + |
| 75 | +1. Point the build output at the repository root. It then writes the entry points and `lib` exactly where they are today, so the package manifest, coverage, CodeQL and every test import keep working unchanged. |
| 76 | +2. Delete the JavaScript sources from git, ignore the generated paths, and build before packing. |
| 77 | +3. The emitted declarations become the type contract. Remove the hand-written typings and the manual compile checks: compiling `src` in its approved state is the type test. |
| 78 | +4. Fold the `src` scripts and workflow into the regular ones and delete the helper tools. |
| 79 | +5. Release as a major version, with a changelog entry stating that runtime behavior is unchanged. |
0 commit comments