Prompt
When managing configuration via package.json/tsconfig.json, treat version and target specifiers as compatibility controls—not defaults. Choose constraints based on the dependency/toolchain’s real compatibility behavior, and wire compatibility between packages explicitly.
Apply these rules:
- Non-semver or signature-risk dependencies: avoid
^; use~x.y.zor a fixed version (and document the exception). - Compatibility-coupled packages: if you bump one package (e.g., a runtime), verify whether related packages (e.g.,
@types/*) are fixed/unchangeable and don’t assume compatible upgrades. - Version range choice:
- Use
^only when the dependency truly follows semver and you’re OK with patch/minor updates. - Use
~when you only want patch updates. - Use a fixed version when you need deterministic behavior.
- Use
- Package boundary & opt-in behavior: if functionality is meant to be added during initialization, don’t hard-bundle those packages as workspace deps; prefer documented opt-in wiring.
- Cross-package compatibility: add peerDependencies when the library/external package version pairing matters so mismatches produce clear warnings.
- Config file targets: ensure
tsconfigmodule/target settings match current TypeScript semantics (e.g., preferNode16/Node18settings over ambiguousNodeNextif TS changed behavior).
Example:
{
"dependencies": {
"langium": "1.2.0", // non-semver: pin
"chokidar": "^3.6.0", // semver: allow compatible updates
"@iconify/utils": "3.0.1" // pinned to known-good version
},
"peerDependencies": {
"mermaid": "~11.7.0" // compatible minor/patch range
}
}
And for tsconfig.json, keep module/moduleResolution consistent with the TS version you run (e.g., Node16/Node16 or Node18/Node16), rather than blindly using NodeNext.