@nx/js - Migrations
For an overview of the plugin and setup instructions, see the @nx/js introduction.
The @nx/js plugin provides various migrations to help you migrate to newer versions of js projects within your Nx workspace. Below is a complete reference for all available migrations.
23.1.x
Section titled “23.1.x”23-1-0-add-ignore-deprecations-for-ts6
Section titled “23-1-0-add-ignore-deprecations-for-ts6”Version: 23.1.0-beta.8
Adds "ignoreDeprecations": "6.0" to tsconfig files whose compilerOptions (or ts-node.compilerOptions) carry a TypeScript 6 deprecated option value, set directly or inherited through extends including from a base this migration does not edit (e.g. moduleResolution node/node10/classic, baseUrl, target es5, esModuleInterop false, outFile, module amd/umd/system/none, alwaysStrict false, allowSyntheticDefaultImports false, downlevelIteration set). Also pins "strict": false, "noUncheckedSideEffectImports": false, "types": ["*"], and "esModuleInterop": false in chain-root tsconfigs (no “extends”) that lack each key, preserving pre-TS6 behavior where these defaults changed; esModuleInterop false is itself deprecated (removed in TS7) so it also receives ignoreDeprecations, deferring the interop change to the TS7 migration. Also adds ignoreDeprecations to every tsconfig.json (the exact name jest/ts-node auto-resolve when compiling a config file; ts-node injects a deprecated default target: es5 there, so the load hits a TS6 error even on a clean config), even one with no deprecated value of its own; a stale local ignoreDeprecations (e.g. 5.0) that would otherwise override the inherited flag is upgraded.
Requires
Section titled “Requires”| Name | Version |
|---|---|
typescript | >=6.0.0 |
Keep Existing Workspaces Compiling on TypeScript 6
Section titled “Keep Existing Workspaces Compiling on TypeScript 6”TypeScript 6 turns several long-deprecated compiler options into hard errors and flips a few option defaults to stricter values. So that an existing workspace keeps compiling on TypeScript 6 without being migrated to a full TypeScript 6 setup, this migration edits the tsconfig*.json files in the workspace:
- Adds
"ignoreDeprecations": "6.0"to anycompilerOptions(orts-node.compilerOptions) block whose effective options carry a TypeScript 6 deprecated option, set directly or inherited throughextends(including from a base this migration does not edit, such as a package-provided config). Examples:moduleResolutionset tonode/node10/classic,baseUrl,targetset toes5,esModuleInterop: false,outFile,moduleset toamd/umd/system/none,alwaysStrict: false,allowSyntheticDefaultImports: false, ordownlevelIteration. A block that already inherits an effective"6.0"and sets no local value is left alone; a stale local value such as"5.0"is upgraded to"6.0", since it would otherwise override the inherited flag and still error. - Pins
"strict": false,"noUncheckedSideEffectImports": false,"types": ["*"], and"esModuleInterop": falseon every chain-root tsconfig (one without anextends) that does not already set them. TypeScript 6 treats an absentstrictastrue(it wasfalsewhen unset before), defaultsnoUncheckedSideEffectImportstotrue(which turns a bare side-effect import such asimport './styles.css'without an ambient module declaration into an error), no longer auto-loads every@typespackage whentypesis unset the way TypeScript 5 did (the"*"wildcard restores that last default), and flipsesModuleInteropfromfalsetotrue(so animport * as x from '<cjs>'binds a non-callable namespace object and a call ornewon that import fails at runtime). Pinning all four preserves the pre-TypeScript 6 behavior. BecauseesModuleInterop: falseis itself a TypeScript 6 deprecated value, this pin runs before theignoreDeprecationsedit above, so the addedfalseis silenced in the same run. - Adds
"ignoreDeprecations": "6.0"to everytsconfig.json(the exact file name jest and ts-node auto-resolve), even one that carries no deprecated option of its own. Those loaders compile config files such asjest.config.ts, and ts-node injects a defaulttarget: es5when the config leaves it unset. TypeScript 6 rejectses5as a deprecated value, so the flag keeps that load working. It is inert wherever nothing is actually deprecated.
Files that use extends inherit the pinned settings from their chain root, so the pins are not repeated on them, and pure solution-style tsconfigs ("files": [] with no include) receive no pins either, though a solution-style tsconfig.json still gets the config-load flag. The migration only runs when the workspace is on TypeScript 6.
Sample Code Changes
Section titled “Sample Code Changes”Before
Section titled “Before”{ "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "bundler" }}{ "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "bundler", "strict": false, "noUncheckedSideEffectImports": false, "types": ["*"], "esModuleInterop": false, "ignoreDeprecations": "6.0" }}23-1-0-set-tsconfig-root-dir-for-ts6
Section titled “23-1-0-set-tsconfig-root-dir-for-ts6”Version: 23.1.0-rc.2
Sets an explicit rootDir on project tsconfig*.json files that lack one, pinned to the source directory TypeScript 5 inferred implicitly, so programs keep compiling and emitting the same layout under TypeScript 6, which otherwise hard-fails with TS5011 or TS6059 (for example a spec tsconfig importing another project’s source through a paths alias). The pin is written even when the inferred directory already equals the tsconfig directory, because tools like ts-jest with isolatedModules compile a program per file and re-infer a deeper directory from that subset. The value is computed by the compiler, so emit layout is unchanged. Each config is written on its own; the migration never writes to a shared extends base, so a config never inherits a value computed for a sibling. Composite projects are pinned to their own directory: rootDir already defaults there under tsc so it is a no-op for a real composite build, but ts-jest strips composite for its per-file transpile and the explicit value is needed to avoid TS5011 there.
Requires
Section titled “Requires”| Name | Version |
|---|---|
typescript | >=6.0.0 |
Pin the Inferred rootDir for TypeScript 6
Section titled “Pin the Inferred rootDir for TypeScript 6”Before TypeScript 6, a tsconfig that did not set rootDir had it inferred as the common directory of the program’s non-declaration input files. TypeScript 6 changed that default to the tsconfig’s own directory. A program whose files resolve outside that directory (most commonly a spec or e2e tsconfig that imports another project’s source through a paths alias) now hard-fails with TS5011 or TS6059 because a file falls outside the assumed root.
For every project tsconfig*.json that does not already set rootDir (directly or through extends), this migration pins rootDir to exactly the directory TypeScript 5 would have inferred, so both compilation and emit layout stay the same under TypeScript 6. The value is computed by the TypeScript compiler itself, so it matches tsc exactly, including project-reference redirects. The pin is written even when the inferred directory already equals the tsconfig directory: inference is per-program, and a tool that compiles a subset of the config’s files — ts-jest with isolatedModules builds a program per test file — re-infers a deeper common directory from that subset and fails with TS5011. Configs that cannot hit the error are skipped: ones without an output option (outDir, outFile, sourceRoot, mapRoot, or declaration + declarationDir), and ones with no input files. The migration only runs when the workspace is on TypeScript 6.
Composite projects are pinned to their own directory ("."). Under tsc a composite rootDir already defaults there, so the pin is a no-op for a real composite build — but ts-jest strips composite for its per-file transpile, and TypeScript 6 only exempts genuinely-composite programs from the containment check, so a composite spec config compiled by ts-jest still fails with TS5011 without an explicit rootDir. Pinning the own directory (rather than a deeper file-derived value) keeps the composite build’s emit layout unchanged while fixing the ts-jest case.
Each config is updated on its own; the migration never writes rootDir to a shared extends base, so a config never inherits a value computed for a sibling. Because every config that emits is given its own explicit rootDir, none is left to inherit a value pinned on a base.
Sample Code Changes
Section titled “Sample Code Changes”Before
Section titled “Before”A libs/products/e2e/tsconfig.json whose specs import a shared library’s source:
{ "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "../../../dist/out-tsc" }, "include": ["src/**/*.ts"]}{ "extends": "../../../tsconfig.base.json", "compilerOptions": { "rootDir": "../../..", "outDir": "../../../dist/out-tsc" }, "include": ["src/**/*.ts"]}23.1.0-typescript-package-updates
Section titled “23.1.0-typescript-package-updates”Version: 23.1.0-beta.0
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
typescript | ~6.0.3 | Updated only |
23.1.0-swc-cli-package-updates
Section titled “23.1.0-swc-cli-package-updates”Version: 23.1.0-rc.3
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
@swc/cli | ~0.8.1 | Updated only |
23.0.x
Section titled “23.0.x”23-0-0-rewrite-internal-subpath-imports
Section titled “23-0-0-rewrite-internal-subpath-imports”Version: 23.0.0-beta.14
Rewrites @nx/js/src/* subpath imports to the new @nx/js/internal entry. The ./src/* wildcard has been removed from @nx/js’s exports map; @nx/js/src/release/version-actions is preserved as a non-wildcard entry for back-compat with existing nx.json release configs. If a rewritten import resolves to a symbol that lives on the public @nx/js entry (e.g. libraryGenerator, extractTsConfigBase, resolvePathsBaseUrl), change the specifier to @nx/js.
update-23-0-0-migrate-create-nodes-v2-import
Section titled “update-23-0-0-migrate-create-nodes-v2-import”Version: 23.0.0-beta.24
Rename imports of createNodesV2 from @nx/js/typescript to the canonical createNodes export.
Rename createNodesV2 imports to createNodes
Section titled “Rename createNodesV2 imports to createNodes”@nx/js renamed its primary inferred-plugin export from createNodesV2 to createNodes. The createNodesV2 name is preserved as a deprecated alias for now, but new code should use createNodes.
This migration scans every .ts, .tsx, .cts, and .mts file in your workspace and rewrites named imports and re-exports of createNodesV2 from @nx/js/typescript to createNodes.
Sample Code Changes
Section titled “Sample Code Changes”Before
Section titled “Before”import { createNodesV2 } from '@nx/js/typescript';import { createNodes } from '@nx/js/typescript';Aliases are preserved (createNodesV2 as cn becomes createNodes as cn), and if a file already imports both names ({ createNodes, createNodesV2 }) the redundant binding is dropped.
What is not rewritten
Section titled “What is not rewritten”Only static import/export named bindings from @nx/js/typescript are rewritten. Namespace imports, dynamic import(...), require(...) destructuring, and property access such as plugin.createNodesV2 are left untouched — they keep working through the createNodesV2 runtime alias. Update those by hand if you want to drop the deprecated name everywhere.
23.0.0-swc-cli-package-updates
Section titled “23.0.0-swc-cli-package-updates”Version: 23.0.0-beta.17
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
@swc/cli | ~0.8.0 | Updated only |
22.6.x
Section titled “22.6.x”22.6.4-package-updates
Section titled “22.6.4-package-updates”Version: 22.6.4
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
verdaccio | ^6.3.2 | Updated only |
22.5.x
Section titled “22.5.x”22.5.0-package-updates
Section titled “22.5.0-package-updates”Version: 22.5.0-beta.1
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
@swc/core | ^1.15.5 | Updated only |
@swc/helpers | ^0.5.18 | Updated only |
@swc-node/register | ^1.11.1 | Updated only |
22.5.0-swc-cli-package-updates
Section titled “22.5.0-swc-cli-package-updates”Version: 22.5.0-beta.1
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
@swc/cli | ^0.7.10 | Updated only |
22.1.x
Section titled “22.1.x”remove-redundant-ts-project-references
Section titled “remove-redundant-ts-project-references”Version: 22.1.0-rc.1
Removes redundant TypeScript project references from project’s tsconfig.json files when runtime tsconfig files (e.g., tsconfig.lib.json, tsconfig.app.json) exist.
Removes Redundant TypeScript Project References from tsconfig.json Files
Section titled “Removes Redundant TypeScript Project References from tsconfig.json Files”Removes redundant TypeScript project references from tsconfig.json files when runtime tsconfig files (e.g., tsconfig.lib.json, tsconfig.app.json) exist. Previously, external project references were duplicated in both the project’s tsconfig.json and runtime tsconfig files. This migration syncs the TypeScript project references to match the project graph, ensuring that external references only appear in runtime tsconfig files when they exist.
Examples
Section titled “Examples”When a project has runtime tsconfig files like tsconfig.lib.json, the migration will remove external project references from the project’s tsconfig.json file:
Before
Section titled “Before”{ "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ],}{ "compilerOptions": { "composite": true, }, "references": [],}The external references remain in the runtime tsconfig file where they belong:
Before
Section titled “Before”{ "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib/tsconfig.lib.json", }, ],}{ "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib/tsconfig.lib.json", }, ],}For projects without runtime tsconfig files, the project’s tsconfig.json file will continue to contain external project references:
Before
Section titled “Before”{ "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ],}{ "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ],}Internal project references (references within the same project directory) are preserved in the project’s tsconfig.json:
Before
Section titled “Before”{ "compilerOptions": { "composite": true, }, "references": [ { "path": "./tsconfig.lib.json", }, { "path": "./tsconfig.spec.json", }, ],}{ "compilerOptions": { "composite": true, }, "references": [ { "path": "./tsconfig.lib.json", }, { "path": "./tsconfig.spec.json", }, ],}22.0.x
Section titled “22.0.x”remove-external-options-from-js-executors
Section titled “remove-external-options-from-js-executors”Version: 22.0.0-beta.0
Remove the deprecated external and externalBuildTargets options from the @nx/js:swc and @nx/js:tsc executors.
Remove the external and externalBuildTargets Options from the @nx/js:swc and @nx/js:tsc Executors
Section titled “Remove the external and externalBuildTargets Options from the @nx/js:swc and @nx/js:tsc Executors”Remove the deprecated external and externalBuildTargets options from the @nx/js:swc and @nx/js:tsc executors. These options were used for inlining dependencies, which was an experimental feature and has been deprecated for a long time. The migration only removes the options from the project configuration and target defaults. If you rely on inlining dependencies, you need to make sure they are all buildable or use a different build tool that supports bundling.
Sample Code Changes
Section titled “Sample Code Changes”Remove external and externalBuildTargets from the @nx/js:swc or @nx/js:tsc executor options in project configuration.
Before
Section titled “Before”{ "targets": { "build": { "executor": "@nx/js:swc", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib", "tsConfig": "libs/my-lib/tsconfig.lib.json", "external": ["react", "react-dom"], "externalBuildTargets": ["build"] } } }}{ "targets": { "build": { "executor": "@nx/js:swc", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib", "tsConfig": "libs/my-lib/tsconfig.lib.json" } } }}Remove external and externalBuildTargets from the @nx/js:swc or @nx/js:tsc executor target defaults in nx.json.
Before
Section titled “Before”{ "targetDefaults": { "@nx/js:swc": { "options": { "main": "{projectRoot}/src/index.ts", "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json", "external": "all", "externalBuildTargets": ["build"] } } }}{ "targetDefaults": { "@nx/js:swc": { "options": { "main": "{projectRoot}/src/index.ts", "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json" } } }}21.5.x
Section titled “21.5.x”migrate-development-custom-condition
Section titled “migrate-development-custom-condition”Version: 21.5.0-beta.2
Migrate the legacy ‘development’ custom condition to a workspace-unique custom condition name.
Migrate development custom condition to unique workspace-specific name
Section titled “Migrate development custom condition to unique workspace-specific name”Replace the TypeScript development custom condition with a unique workspace-specific name to avoid conflicts when consuming packages in other workspaces.
Examples
Section titled “Examples”The migration will update the custom condition name in both tsconfig.base.json and all workspace package.json files that use the development custom condition:
Before
Section titled “Before”{ "compilerOptions": { "customConditions": ["development"] }}{ "compilerOptions": { "customConditions": ["@my-org/source"] // assuming the root package.json name is `@my-org/source` }}The migration also updates package.json files that use the development condition in their exports field and point to TypeScript files:
Before
Section titled “Before”{ "name": "@myorg/my-lib", "exports": { ".": { "development": "./src/index.ts", "default": "./dist/index.js" } }}{ "name": "@myorg/my-lib", "exports": { ".": { "@my-org/source": "./src/index.ts", "default": "./dist/index.js" } }}If the custom condition is not set to ["development"] or the package.json’s exports field doesn’t point to TypeScript files, the migration will not modify the configuration:
Before
Section titled “Before”{ "name": "@myorg/my-lib", "exports": { ".": { "development": "./dist/index.js", "default": "./dist/index.js" } }}{ "name": "@myorg/my-lib", "exports": { ".": { "development": "./dist/index.js", "default": "./dist/index.js" } }}21.5.0-package-updates
Section titled “21.5.0-package-updates”Version: 21.5.0-beta.2
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
typescript | ~5.9.2 | Updated only |
21.2.x
Section titled “21.2.x”21.2.0-package-updates
Section titled “21.2.0-package-updates”Version: 21.2.0-beta.0
Packages
Section titled “Packages”The following packages will be updated:
| Name | Version | Always add to package.json |
|---|---|---|
typescript | ~5.8.2 | Updated only |