This guide highlights behavior changes introduced in v4 and how to adapt existing workflows.
- Specifier rewrites now default to safer behavior.
--rewrite-policynow defaults tosafe; missing targets skip rewrites and emit warnings instead of silently rewriting. - Dual-package hazard detection enabled by default.
--detect-dual-package-hazardnow defaults towarn, and--dual-package-hazard-scopedefaults tofile. You may see new warnings (or errors if configured). - Build pipeline runs in a temp workspace copy. Dual builds no longer mutate the root
package.json; a temp copy is created with an adjustedtype. External tools that watched in-placepackage.jsonedits will see different behavior.- IMPORTANT: The temp-copy flow adds some I/O for large repos (copying sources/reference packages and running transforms there).
node_modulesis skipped; when references exist, existingdistmay be reused. Very large projects may see modestly slower runs compared to the old in-place mutation.
- IMPORTANT: The temp-copy flow adds some I/O for large repos (copying sources/reference packages and running transforms there).
- Cache/shadow location is project-local.
.duel-cachenow lives under the project root (e.g.,<project>/.duel-cache) instead of the parent directory to avoid “filesystem invasion.” Temp shadow workspaces and tsbuildinfo cache files stay inside that folder. Add.duel-cache/to your.gitignore. - Project references run with
tsc -b. Whentsconfig.jsoncontains references, builds switch to TypeScript build mode. Output shape can differ fromtsc -pfor some setups. - Referenced configs must be patchable. Duel now fails fast if a referenced
tsconfiglives outside the allowed workspace boundary (package root, packages root, or repo root, excludingnode_modules) or cannot be parsed in the temp workspace. Move references inside the repo and fix invalid configs so both primary and dual builds stay isolated. - Dual CJS builds enforce CJS semantics. The shadow workspace now uses
type: "commonjs"plusmodule: "NodeNext"for the dual build, so TypeScript will error on CJS-incompatible syntax likeimport.metaunless you adjust code or opt into--mode globals/--mode full(v3 previously allowed this to slip through). - Exports tooling additions. New flags (
--exports-config,--exports-validate) are available; when used, they can emit warnings or fail on invalid configs. - Deprecated flags removed.
--modules,--transform-syntax, and--target-extensionare gone; use--mode globalsor--mode fullinstead. - Copy strategy defaults to sources.
--copy-mode sourcesis the default (minimal temp copy of inputs/configs). Use--copy-mode fullto mirror the entire project like v3.
- Specifier rewrites: use
--rewrite-policy warnto continue rewriting even when targets are missing (previous behavior). To fully bypass rewrites, set--rewrite-policy skip. - Hazard detection: disable by passing
--detect-dual-package-hazard off(or set scope toprojectonly if you want aggregated warnings). - Build/package.json side effects: if tooling depended on in-place
package.jsonmutation, update it to read outputs from the temp dual build outputs (dist/esm/dist/cjsoroutDirvariants). No flag restores the old mutation pattern. - TypeScript references: if build mode changes output undesirably, remove
referencesor run your owntsc -pbefore callingduel.
- Pick a rewrite policy:
- Safety-first (default): keep
--rewrite-policy safe(default) and address any missing-target warnings by fixing paths or adding files. - Legacy: set
--rewrite-policy warnto mimic v3 rewrites.
- Safety-first (default): keep
- Decide on hazard handling:
- Keep defaults to surface hazards.
- Silence:
--detect-dual-package-hazard off.
- Check CI/build scripts: remove assumptions about
package.jsonbeing mutated; consume artifacts from the generated outDir(s). - Projects with TS references: expect
tsc -b; validate output paths and adjust if needed. - If using exports helpers: verify
--exports-configfiles and watch for new validation warnings/errors.
--rewrite-policy [safe|warn|skip](default:safe)--detect-dual-package-hazard [off|warn|error](default:warn)--dual-package-hazard-scope [file|project](default:file)--dual-package-hazard-allowlist <pkg1,pkg2>--exports-config <path>--exports-validate--verbose
- Why are some rewrites skipped now? Missing targets +
rewrite-policy safecauses skips with warnings. Usewarnto force rewrites or fix the missing files. - Can I suppress hazard warnings? Yes,
--detect-dual-package-hazard off. - Why isn’t package.json changed anymore? v4 writes to a temp copy to avoid mutating your root; watch the emitted outDir instead.
- Why do I see
import.metaerrors in CJS builds? v4 compiles the dual target in a CommonJS context (shadowpackage.jsonistype: "commonjs"), so TypeScript rejects CJS-incompatible syntax. Fix the source for CJS or run with--mode globals/--mode fullto inject compatibility transforms. - Where is
.duel-cachenow? Under your project root (e.g.,<project>/.duel-cache); the temp shadow workspace and tsbuildinfo cache files stay there to avoid writing to the parent directory.