Repository navigation
chore: consolidate the repository into a pnpm workspace and move typeorm into packages - #12854
Conversation
commit: |
Code Review by Qodo
1. Legacy nightly releases stop publishing
|
Code Review by Qodo
1. Legacy package changes skip linting
|
| "editor.formatOnSave": true, | ||
| "git.branchProtection": ["master"], | ||
| "js/ts.tsdk.path": "node_modules/typescript/lib" | ||
| "js/ts.tsdk.path": "packages/typeorm/node_modules/typescript/lib" |
There was a problem hiding this comment.
shouldn't we have node_modules in the workspace root?
There was a problem hiding this comment.
The workspace root does have a node_modules, but it only holds what the root manifest itself declares: prettier and its plugins, husky, lint-staged, eslint and standard-changelog. TypeScript is a devDependency of packages/typeorm, so pnpm installs it at packages/typeorm/node_modules/typescript and there is no copy at the root for the old path to resolve against.
I pointed the setting at the package so the TypeScript version stays declared in one place. The alternative is adding typescript to the root devDependencies, which would let the setting go back to node_modules/typescript/lib at the cost of a second version spec to keep in sync with the package. Happy to switch if you prefer that.
Related, in case it looks odd: eslint is still a root devDependency because lint-staged runs eslint --fix from the repository root on pre-commit. eslint 10 resolves the config from the file being linted, so it picks up packages/typeorm/eslint.config.mjs. I checked that path works.
|
|
The pnpm-workspace migration (typeorm#12854) moved tests from the repo root to packages/typeorm/test. Rename detection carried the source change across, but these files are added by this branch so they kept the old root path.



What this does
Turns the repository into a real pnpm workspace and moves the
typeormpackage out of the repository root intopackages/typeorm, so that every published package lives in one place and publishing more than one of them stops being a manual, per-package affair.Before this, the repo already shipped three packages (
typeorm,@typeorm/codemod,@typeorm/legacy-naming-strategies) but was not a monorepo in any technical sense: nopnpm-workspace.yaml, a separatepnpm-lock.yamlper sub-package, a roottsconfig.jsonthat excludedpackages, and a root eslint config that ignoredpackages/**. Every workflow enumerated the packages by hand.@typeorm/legacy-naming-strategiesalso declared"typeorm": "1.1.1", so it tested against the published package rather than this repository's source. A breaking change to core could not be caught by it.Layout
packages/typeormtypeormpackages/codemod@typeorm/codemodpackages/legacy-naming-strategies@typeorm/legacy-naming-strategiesThe root keeps
docs/,docker/,docker-compose.yml,resources/, the markdown files and the shared tooling config. A new private rootpackage.jsonholds the workspace and delegatescompile,package,testandtypechecktopackages/typeorm, so the familiar root commands still work.For anyone with an open pull request
Everything moved as a pure rename, no file contents changed, so git records all 3292 renames at 100% similarity and can usually replay a branch across the move on its own.
There is one catch worth knowing: git's default rename limit is well below the number of files that moved, and without raising it git sees deletes plus adds and every touched file conflicts. So the plain instruction is:
scripts/migrate-pr-to-packages-layout.mddocuments this with a path mapping table, ascripts/migrate-pr-to-packages-layout.shhelper that backs your branch up and does the rebase for you (plus a--replayfallback that rewrites the paths of your net diff when the history is not worth saving), and a prompt you can hand to a coding agent. Both script modes were tested against a simulated pull request branch based on the currentmaster.Changes
pnpm-workspace.yamlcoveringpackages/*andplayground.docsis excluded; it is a Docusaurus site with its own dependency tree and deploy workflow.src,test,extra,package.json,gulpfile.ts, the tsconfigs,.mocharc.json,.c8rc.json,stryker.config.json,ormconfig.sample.json,sonar-project.propertiesandeslint.config.mjsintopackages/typeorm.publishConfig.directorypointing atbuild/package. The package manifest describes the built layout (mainis./index.js, which only exists after a build), so a plain workspace link does not resolve; this points pnpm at the build output instead.copyPackageFilestrips the entry so it never reaches the published package.@typeorm/legacy-naming-strategiesandplaygroundto"typeorm": "workspace:*".working-directory: packages/typeorm; the docker compose steps override back to the repository root.pnpm installsteps. A single install covers the workspace, and rootlintnow fans out withpnpm -r.tests-legacy-naming-strategies, and adds the core sources to that job's path filter so a change undersrc/actually runs it.pnpm-lock.yamlto the path filters. With the sub-package lockfiles gone, a dependency-only change would otherwise have matched no filter and run no tests.LICENSEwith the package (see below).playgroundprivate, addsdist/to.prettierignore.The published package
The built tree was compared file by file against a pre-move baseline built from
masterin a separate worktree:2949 files, 0 added, 0 removed, 0 with differing content. Byte-identical, by SHA-256 per file, sourcemaps included.
One deliberate change to the published artifact. The real
[email protected]tarball on npm contains no LICENSE file, despite the manifest declaring"license": "MIT". Addingpnpm-workspace.yamlmade pnpm start copying the rootLICENSEinto the tarball as a side effect (2948 files to 2949). Rather than leave that to a pnpm heuristic,gulpfile.tsnow copiesLICENSEexplicitly the same way it already copiesREADME.md. Verified deterministic: the tarball is 2949 files with the licence whether or not the workspace file is present, and the only difference against the current npm artifact is that added file.The resolved dependency graph for core is unchanged: the root importer has zero differences against
master, and no package was removed from the graph.Verification
Beyond the suites, the artifact was published to a local verdaccio registry with the same command CI runs, and installed into clean projects:
publishConfig, and nobuild/directory leaks into the tarball.require, ESM named and default imports, the deep subpathtypeorm/util/StringUtils, a sql.js DataSource round-trip and a QueryBuilder query.cli.js versionruns.strict: trueandskipLibCheck: falsetype-checks against decorators andRepository<T>generics.browser-map targets,browser/index.js,index.mjs, both shims and the react-native condition are present.pnpm versioninside the build directory, then publish) works inside a workspace.@typeorm/codemodpublishes, installs and runs, with no unresolvedworkspace:specifier.ormconfig.jsonstill resolves from its new location, and@typeorm/codemod(376) and@typeorm/legacy-naming-strategies(4) pass.The source linking was confirmed to work by renaming
hashinsrc/util/StringUtils.ts, rebuilding, and watching@typeorm/legacy-naming-strategiesfail withTS2305. Under the previous registry pin that rename passed CI.Known follow-ups, not in this PR
workspace:*resolves its published version from the builtpackage.json, which the nightly step rewrites in place. Packing@typeorm/legacy-naming-strategiesafter a nightly bump yieldsdevDependencies.typeorm: "1.1.1-nightly.<date>". Inert today, since that package is only a devDependency and nothing in CI publishes it. It stops being inert if that package is ever published from CI, or if any package usesworkspace:*independenciesorpeerDependencies.@typeorm/legacy-naming-strategiesis on npm but no workflow publishes it. 1.0.0 was pushed by hand.tsconfig.json,.mocharc.jsonand.c8rc.json. Hoisting those is the obvious next step.tests-legacy-naming-strategiesruns a full core build. Thebuildjob producesbuild/compiledrather thanbuild/package, so its artifact cannot be reused as-is without changing what the Linux and Windows jobs consume.--provenanceare untested. A local registry cannot exercise OIDC. The publish command is unchanged apart from its working directory, but a nightlydevrun would confirm it before a stable release.projectBaseDir: packages/typeorm. The paths inside the uploaded lcov reports should line up, but only a real run on this PR will confirm it.CI results on this branch
Every database suite passes on Node 20 and 24 plus Windows: Oracle, SAP HANA, Spanner, MSSQL, MongoDB, CockroachDB, Postgres 14 and 17, MySQL/MariaDB, sqljs and better-sqlite3.
build,format,preview,tests-codemodandtests-legacy-naming-strategiesall pass. Sonar reportsnew_coverageat 74.5% against a 70% threshold, which confirms coverage reporting still maps correctly underprojectBaseDir: packages/typeorm.Three checks failed. One was a real bug in this branch, now fixed; two are artifacts of the move that need a maintainer decision.
lint(fixed).@typeorm/legacy-naming-strategiesis type-checked againsttypeorm, which resolves tobuild/package, so eslint reported sixno-unsafe-*errors when the core package had not been built. Reproduced locally (exit 1 without the build, exit 0 with it) and fixed by building before linting, the same waytests-legacy-naming-strategiesalready does.Sonar quality gate (needs a decision). The gate fails on one condition only:
new_duplicated_lines_densityat 8.9% against a 3% threshold. Reliability, security, maintainability, coverage and security hotspots all pass. The cause is that Sonar seesnew_lines= 290101, because every file has a new path, so the whole codebase counts as new code and its pre-existing duplication (25931 lines, mostly the driverQueryRunnerclasses) is measured against the new-code gate. The same duplication exists onmastertoday. It will re-baseline once this lands. I have not touchedsonar.cpd.exclusionsor the gate, since loosening either to get one PR through seems like the wrong trade.CodeQL (needs a decision). One new high-severity alert,
js/weak-cryptographic-algorithmatpackages/typeorm/src/platform/PlatformTools.ts:102. It iscrypto.createHash("sha1")used to build internal index and alias names, documented in place as not for cryptographic security, and introduced in #12648. Alert #95 is the identical finding at the old pathsrc/platform/PlatformTools.tsand is already dismissed as "won't fix"; the dismissal does not follow a rename, so CodeQL raised it again as #97. It needs the same dismissal.This is deliberately scoped to layout and plumbing. It does not adopt Nx or Turborepo, and does not add a release tool. Splitting drivers into separate packages (#2479) and replacing gulp (#11691) remain separate conversations.