fix(js): do not add typecheck target if tsc is used for build (#30211)
This PR adds support for skipping `typecheck` targets when using `@nx/js/typescript`. Inside `tsconfig.json` for each project, you can set `nx.addTypecheckTarget` to `false` to not infer `typecheck`. This allows use to skip `typecheck` for JS projects using `tsc` to build. Since `tsc` is already used during build, we don't need to run `typecheck` that is just duplicated work. <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior JS libs using `tsc` to build will do typechecking twice. Once during `build` and once during `typecheck`. ## Expected Behavior JS libs using `tsc` do not infer `typecheck` by default. And users can change this behavior by setting `nx.addTypecheckTarget` in `tsconfig.json`. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
This commit is contained in:
parent
bd78ac25b8
commit
b992e2586b
@ -63,6 +63,104 @@ Nx 20 updates the TS monorepo setup when using `--preset=ts`. The workspace is s
|
|||||||
To create with the older setup for TS monorepo with `compilerOptions.paths`, use `create-nx-workspace --preset=apps`.
|
To create with the older setup for TS monorepo with `compilerOptions.paths`, use `create-nx-workspace --preset=apps`.
|
||||||
{% /callout %}
|
{% /callout %}
|
||||||
|
|
||||||
|
### How @nx/js Infers Tasks
|
||||||
|
|
||||||
|
The `@nx/js/typescript` plugin will add a `typecheck` task to projects that have a `tsconfig.json`.
|
||||||
|
|
||||||
|
This plugin adds a `build` task for projects that:
|
||||||
|
|
||||||
|
1. Have a runtime tsconfig file (defaults to `tsconfig.lib.json`).
|
||||||
|
2. Have a `package.json` file containing entry points that are not source files.
|
||||||
|
|
||||||
|
For example, this project is buildable and will have a `build` task.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/package.json" %}
|
||||||
|
{
|
||||||
|
"name": "@acme/pkg1",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whereas this project points to source files and will not have a `build` task.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/package.json" %}
|
||||||
|
{
|
||||||
|
"name": "@acme/pkg1",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": "./src/index.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Inferred Tasks
|
||||||
|
|
||||||
|
To view inferred tasks for a project, open the [project details view](/concepts/inferred-tasks) in Nx Console or run `nx show project my-project` in the command line.
|
||||||
|
|
||||||
|
### @nx/js Configuration
|
||||||
|
|
||||||
|
The `@nx/js/typescript` plugin is configured in the `plugins` array in `nx.json`.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/js/typescript",
|
||||||
|
"options": {
|
||||||
|
"typecheck": {
|
||||||
|
"targetName": "typecheck"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"targetName": "build",
|
||||||
|
"configName": "tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set `typecheck` and `build` options to `false` to not infer the corresponding tasks.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/js/typescript",
|
||||||
|
"options": {
|
||||||
|
"build": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable Typechecking
|
||||||
|
|
||||||
|
To disable `typecheck` task for a specific project, set the `nx.addTypecheckTarget` property to `false` in `tsconfig.json`.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/tsconfig.json" highlightLines=["10-12"] %}
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nx": {
|
||||||
|
"addTypecheckTarget": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Create Libraries
|
## Create Libraries
|
||||||
|
|
||||||
You can add a new JS/TS library with the following command:
|
You can add a new JS/TS library with the following command:
|
||||||
|
|||||||
@ -63,6 +63,104 @@ Nx 20 updates the TS monorepo setup when using `--preset=ts`. The workspace is s
|
|||||||
To create with the older setup for TS monorepo with `compilerOptions.paths`, use `create-nx-workspace --preset=apps`.
|
To create with the older setup for TS monorepo with `compilerOptions.paths`, use `create-nx-workspace --preset=apps`.
|
||||||
{% /callout %}
|
{% /callout %}
|
||||||
|
|
||||||
|
### How @nx/js Infers Tasks
|
||||||
|
|
||||||
|
The `@nx/js/typescript` plugin will add a `typecheck` task to projects that have a `tsconfig.json`.
|
||||||
|
|
||||||
|
This plugin adds a `build` task for projects that:
|
||||||
|
|
||||||
|
1. Have a runtime tsconfig file (defaults to `tsconfig.lib.json`).
|
||||||
|
2. Have a `package.json` file containing entry points that are not source files.
|
||||||
|
|
||||||
|
For example, this project is buildable and will have a `build` task.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/package.json" %}
|
||||||
|
{
|
||||||
|
"name": "@acme/pkg1",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whereas this project points to source files and will not have a `build` task.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/package.json" %}
|
||||||
|
{
|
||||||
|
"name": "@acme/pkg1",
|
||||||
|
"exports": {
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": "./src/index.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Inferred Tasks
|
||||||
|
|
||||||
|
To view inferred tasks for a project, open the [project details view](/concepts/inferred-tasks) in Nx Console or run `nx show project my-project` in the command line.
|
||||||
|
|
||||||
|
### @nx/js Configuration
|
||||||
|
|
||||||
|
The `@nx/js/typescript` plugin is configured in the `plugins` array in `nx.json`.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/js/typescript",
|
||||||
|
"options": {
|
||||||
|
"typecheck": {
|
||||||
|
"targetName": "typecheck"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"targetName": "build",
|
||||||
|
"configName": "tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set `typecheck` and `build` options to `false` to not infer the corresponding tasks.
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"plugin": "@nx/js/typescript",
|
||||||
|
"options": {
|
||||||
|
"build": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable Typechecking
|
||||||
|
|
||||||
|
To disable `typecheck` task for a specific project, set the `nx.addTypecheckTarget` property to `false` in `tsconfig.json`.
|
||||||
|
|
||||||
|
```json {% fileName="packages/pkg1/tsconfig.json" highlightLines=["10-12"] %}
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nx": {
|
||||||
|
"addTypecheckTarget": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Create Libraries
|
## Create Libraries
|
||||||
|
|
||||||
You can add a new JS/TS library with the following command:
|
You can add a new JS/TS library with the following command:
|
||||||
|
|||||||
@ -131,19 +131,16 @@ ${content}`
|
|||||||
|
|
||||||
// check typecheck
|
// check typecheck
|
||||||
expect(runCLI(`typecheck ${esbuildParentLib}`)).toContain(
|
expect(runCLI(`typecheck ${esbuildParentLib}`)).toContain(
|
||||||
`Successfully ran target typecheck for project @proj/${esbuildParentLib} and 5 tasks it depends on`
|
`Successfully ran target typecheck for project @proj/${esbuildParentLib} and 4 tasks it depends on`
|
||||||
);
|
);
|
||||||
expect(runCLI(`typecheck ${rollupParentLib}`)).toContain(
|
expect(runCLI(`typecheck ${rollupParentLib}`)).toContain(
|
||||||
`Successfully ran target typecheck for project @proj/${rollupParentLib} and 5 tasks it depends on`
|
`Successfully ran target typecheck for project @proj/${rollupParentLib} and 4 tasks it depends on`
|
||||||
);
|
);
|
||||||
expect(runCLI(`typecheck ${swcParentLib}`)).toContain(
|
expect(runCLI(`typecheck ${swcParentLib}`)).toContain(
|
||||||
`Successfully ran target typecheck for project @proj/${swcParentLib} and 5 tasks it depends on`
|
`Successfully ran target typecheck for project @proj/${swcParentLib} and 4 tasks it depends on`
|
||||||
);
|
|
||||||
expect(runCLI(`typecheck ${tscParentLib}`)).toContain(
|
|
||||||
`Successfully ran target typecheck for project @proj/${tscParentLib} and 5 tasks it depends on`
|
|
||||||
);
|
);
|
||||||
expect(runCLI(`typecheck ${viteParentLib}`)).toContain(
|
expect(runCLI(`typecheck ${viteParentLib}`)).toContain(
|
||||||
`Successfully ran target typecheck for project @proj/${viteParentLib} and 5 tasks it depends on`
|
`Successfully ran target typecheck for project @proj/${viteParentLib} and 4 tasks it depends on`
|
||||||
);
|
);
|
||||||
|
|
||||||
// check lint
|
// check lint
|
||||||
|
|||||||
@ -105,7 +105,6 @@ describe('Node Applications', () => {
|
|||||||
expect(() => runCLI(`lint ${nodelib}`)).not.toThrow();
|
expect(() => runCLI(`lint ${nodelib}`)).not.toThrow();
|
||||||
expect(() => runCLI(`test ${nodelib}`)).not.toThrow();
|
expect(() => runCLI(`test ${nodelib}`)).not.toThrow();
|
||||||
expect(() => runCLI(`build ${nodelib}`)).not.toThrow();
|
expect(() => runCLI(`build ${nodelib}`)).not.toThrow();
|
||||||
expect(() => runCLI(`typecheck ${nodelib}`)).not.toThrow();
|
|
||||||
|
|
||||||
const p = await runCommandUntil(
|
const p = await runCommandUntil(
|
||||||
`serve ${nodeapp}`,
|
`serve ${nodeapp}`,
|
||||||
|
|||||||
@ -45,9 +45,6 @@ describe('Nx Plugin (TS solution)', () => {
|
|||||||
expect(runCLI(`lint @proj/${plugin}`)).toContain(
|
expect(runCLI(`lint @proj/${plugin}`)).toContain(
|
||||||
`Successfully ran target lint for project @proj/${plugin}`
|
`Successfully ran target lint for project @proj/${plugin}`
|
||||||
);
|
);
|
||||||
expect(runCLI(`typecheck @proj/${plugin}`)).toContain(
|
|
||||||
`Successfully ran target typecheck for project @proj/${plugin}`
|
|
||||||
);
|
|
||||||
expect(runCLI(`build @proj/${plugin}`)).toContain(
|
expect(runCLI(`build @proj/${plugin}`)).toContain(
|
||||||
`Successfully ran target build for project @proj/${plugin}`
|
`Successfully ran target build for project @proj/${plugin}`
|
||||||
);
|
);
|
||||||
|
|||||||
@ -106,7 +106,7 @@ ${content}`
|
|||||||
|
|
||||||
// check typecheck
|
// check typecheck
|
||||||
expect(runCLI(`typecheck ${reactApp}`)).toContain(
|
expect(runCLI(`typecheck ${reactApp}`)).toContain(
|
||||||
`Successfully ran target typecheck for project @proj/${reactApp} and 6 tasks it depends on`
|
`Successfully ran target typecheck for project @proj/${reactApp} and 5 tasks it depends on`
|
||||||
);
|
);
|
||||||
}, 300_000);
|
}, 300_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2320,5 +2320,23 @@ describe('lib', () => {
|
|||||||
'packages/**',
|
'packages/**',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add nx.addTypecheckTarget to tsconfig.json when using tsc to build to avoid duplicated typechecks', async () => {
|
||||||
|
await libraryGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
useProjectJson: false,
|
||||||
|
directory: 'my-ts-lib',
|
||||||
|
bundler: 'tsc',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
linter: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(readJson(tree, 'my-ts-lib/tsconfig.json').nx)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"addTypecheckTarget": false,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1049,6 +1049,12 @@ function createProjectTsConfigs(
|
|||||||
json.references.push({
|
json.references.push({
|
||||||
path: './tsconfig.lib.json',
|
path: './tsconfig.lib.json',
|
||||||
});
|
});
|
||||||
|
// If using `tsc` to build, then we do not want a typecheck target that duplicates the work, since both run `tsc`.
|
||||||
|
// This applies to `@nx/js/typescript` plugin only.
|
||||||
|
if (options.bundler === 'tsc') {
|
||||||
|
json['nx'] ??= {};
|
||||||
|
json['nx'].addTypecheckTarget = false;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -1059,6 +1065,12 @@ function createProjectTsConfigs(
|
|||||||
include: [],
|
include: [],
|
||||||
references: [{ path: './tsconfig.lib.json' }],
|
references: [{ path: './tsconfig.lib.json' }],
|
||||||
};
|
};
|
||||||
|
// If using `tsc` to build, then we do not want a typecheck target that duplicates the work, since both run `tsc`.
|
||||||
|
// This applies to `@nx/js/typescript` plugin only.
|
||||||
|
if (options.bundler === 'tsc') {
|
||||||
|
tsconfig['nx'] ??= {};
|
||||||
|
tsconfig['nx'].addTypecheckTarget = false;
|
||||||
|
}
|
||||||
writeJson(
|
writeJson(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(options.projectRoot, 'tsconfig.json'),
|
joinPathFragments(options.projectRoot, 'tsconfig.json'),
|
||||||
|
|||||||
@ -657,6 +657,77 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
|||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not infer typecheck target when nx.addTypecheckTarget is false in tsconfig.json', async () => {
|
||||||
|
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||||
|
'libs/my-lib/tsconfig.json': JSON.stringify({
|
||||||
|
nx: { addTypecheckTarget: false },
|
||||||
|
}),
|
||||||
|
'libs/my-lib/tsconfig.lib.json': JSON.stringify({
|
||||||
|
compilerOptions: { outDir: 'out-tsc/my-lib', rootDir: 'src' },
|
||||||
|
files: ['src/main.ts'],
|
||||||
|
}),
|
||||||
|
'libs/my-lib/package.json': `{}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
invokeCreateNodesOnMatchingFiles(context, {
|
||||||
|
build: {
|
||||||
|
configName: 'tsconfig.lib.json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).resolves.toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"libs/my-lib": {
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "tsc --build tsconfig.lib.json",
|
||||||
|
"dependsOn": [
|
||||||
|
"^build",
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"production",
|
||||||
|
"^production",
|
||||||
|
{
|
||||||
|
"externalDependencies": [
|
||||||
|
"typescript",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Builds the project with \`tsc\`.",
|
||||||
|
"help": {
|
||||||
|
"command": "npx tsc --build --help",
|
||||||
|
"example": {
|
||||||
|
"args": [
|
||||||
|
"--force",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"typescript",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"cwd": "libs/my-lib",
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
"{projectRoot}/out-tsc/my-lib",
|
||||||
|
"{projectRoot}/out-tsc/*.tsbuildinfo",
|
||||||
|
],
|
||||||
|
"syncGenerators": [
|
||||||
|
"@nx/js:typescript-sync",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
describe('inputs', () => {
|
describe('inputs', () => {
|
||||||
it('should add the config file and the `include` and `exclude` patterns', async () => {
|
it('should add the config file and the `include` and `exclude` patterns', async () => {
|
||||||
await applyFilesToTempFsAndContext(tempFs, context, {
|
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||||
|
|||||||
@ -380,7 +380,11 @@ function buildTscTargets(
|
|||||||
|
|
||||||
let internalProjectReferences: Record<string, ParsedTsconfigData>;
|
let internalProjectReferences: Record<string, ParsedTsconfigData>;
|
||||||
// Typecheck target
|
// Typecheck target
|
||||||
if (basename(configFilePath) === 'tsconfig.json' && options.typecheck) {
|
if (
|
||||||
|
basename(configFilePath) === 'tsconfig.json' &&
|
||||||
|
options.typecheck &&
|
||||||
|
tsConfig.raw?.['nx']?.addTypecheckTarget !== false
|
||||||
|
) {
|
||||||
internalProjectReferences = resolveInternalProjectReferences(
|
internalProjectReferences = resolveInternalProjectReferences(
|
||||||
tsConfig,
|
tsConfig,
|
||||||
context.workspaceRoot,
|
context.workspaceRoot,
|
||||||
@ -1291,6 +1295,9 @@ function toAbsolutePaths(
|
|||||||
updatedCache[key] = {
|
updatedCache[key] = {
|
||||||
data: {
|
data: {
|
||||||
options: { noEmit: data.options.noEmit },
|
options: { noEmit: data.options.noEmit },
|
||||||
|
raw: {
|
||||||
|
nx: { addTypecheckTarget: data.raw?.['nx']?.addTypecheckTarget },
|
||||||
|
},
|
||||||
extendedConfigFile: data.extendedConfigFile,
|
extendedConfigFile: data.extendedConfigFile,
|
||||||
},
|
},
|
||||||
extendedFilesHash,
|
extendedFilesHash,
|
||||||
@ -1347,6 +1354,9 @@ function toRelativePaths(
|
|||||||
updatedCache[key] = {
|
updatedCache[key] = {
|
||||||
data: {
|
data: {
|
||||||
options: { noEmit: data.options.noEmit },
|
options: { noEmit: data.options.noEmit },
|
||||||
|
raw: {
|
||||||
|
nx: { addTypecheckTarget: data.raw?.['nx']?.addTypecheckTarget },
|
||||||
|
},
|
||||||
extendedConfigFile: data.extendedConfigFile,
|
extendedConfigFile: data.extendedConfigFile,
|
||||||
},
|
},
|
||||||
extendedFilesHash,
|
extendedFilesHash,
|
||||||
|
|||||||
@ -463,6 +463,9 @@ describe('NxPlugin Plugin Generator', () => {
|
|||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
|
"nx": {
|
||||||
|
"addTypecheckTarget": false,
|
||||||
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.lib.json",
|
"path": "./tsconfig.lib.json",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user