Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-typescript): check workspace tsconfig.json #6175

Merged
merged 9 commits into from Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions .yarn/versions/dca3f277.yml
@@ -0,0 +1,23 @@
releases:
"@yarnpkg/cli": minor
"@yarnpkg/plugin-typescript": minor

declined:
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/core"
- "@yarnpkg/doctor"
Expand Up @@ -27,7 +27,7 @@ describe(`Plugins`, () => {
);

test(
`it should automatically enable automatic @types insertion when a tsconfig is detected`,
`it should automatically enable automatic @types insertion when a tsconfig is detected at the root of the project`,
makeTemporaryEnv({}, async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `tsconfig.json`), ``);

Expand All @@ -44,6 +44,91 @@ describe(`Plugins`, () => {
}),
);

test(
`it should automatically enable automatic @types insertion when a tsconfig is detected in the current workspace`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
}, {[`packages/foo`]: {}}, async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `packages/foo/tsconfig.json`), ``);

await run(`add`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.toMatchObject({
dependencies: {
[`is-number`]: `^2.0.0`,
},
devDependencies: {
[`@types/is-number`]: `^2`,
},
});
}),
);

test(
`it should automatically enable automatic @types insertion in the current workspace when tsEnableAutoTypes is set to true`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
}, {[`packages/foo`]: {}}, async ({path, run, source}) => {
await run(`config`, `set`, `tsEnableAutoTypes`, `true`);

await run(`add`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.toMatchObject({
dependencies: {
[`is-number`]: `^2.0.0`,
},
devDependencies: {
[`@types/is-number`]: `^2`,
},
});
}),
);

test(
`it should not automatically enable automatic @types insertion when a tsconfig is present in a sibling workspace`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
}, {[`packages/foo`]: {}, [`packages/bar`]: {}}, async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `packages/foo/tsconfig.json`), ``);

await run(`add`, `is-number`, {
cwd: `${path}/packages/bar` as PortablePath,
});

await expect(readManifest(`${path}/packages/bar` as PortablePath)).resolves.toMatchObject({
dependencies: {
[`is-number`]: `^2.0.0`,
},
});
}),
);

test(
`it should automatically enable automatic @types insertion when a tsconfig is detected in the root project of the current workspace`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
}, {[`packages/foo`]: {}}, async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `tsconfig.json`), ``);

await run(`add`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.toMatchObject({
dependencies: {
[`is-number`]: `^2.0.0`,
},
devDependencies: {
[`@types/is-number`]: `^2`,
},
});
}),
);

test(
`it should automatically add @types to devDependencies when package doesn't provide types`,
makeTemporaryEnv({}, {
Expand Down Expand Up @@ -297,7 +382,24 @@ describe(`Plugins`, () => {
describe(`Removing types`, () => {
for (const type of Manifest.allDependencies) {
test(
`it should automatically remove @types from ${type}`,
`it should not automatically remove @types from ${type} without tsconfig or tsEnableAutoTypes`,
makeTemporaryEnv(merge({
dependencies: {
[`is-number`]: `^1.0.0`,
},
}, {
[type]: {
[`@types/is-number`]: `1.0.0`,
},
}), {}, async ({path, run, source}) => {
await run(`remove`, `is-number`);

await expect(readManifest(path)).resolves.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should automatically remove @types from ${type} with tsEnableAutoTypes set to true`,
makeTemporaryEnv(merge({
dependencies: {
[`is-number`]: `^1.0.0`,
Expand All @@ -314,6 +416,102 @@ describe(`Plugins`, () => {
await expect(readManifest(path)).resolves.not.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should automatically remove @types from ${type} with tsconfig.json present`,
makeTemporaryEnv(merge({
dependencies: {
[`is-number`]: `^1.0.0`,
},
}, {
[type]: {
[`@types/is-number`]: `1.0.0`,
},
}), {}, async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `tsconfig.json`), ``);

await run(`remove`, `is-number`);

await expect(readManifest(path)).resolves.not.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should not automatically remove @types ${type} from the current workspace without tsconfig.json present or tsEnableAutoTypes`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
},
merge(
{[`packages/foo`]: {dependencies: {[`is-number`]: `^1.0.0`}}},
{[`packages/foo`]: {[type]: {[`@types/is-number`]: `1.0.0`}}},
),
async ({path, run, source}) => {
await run(`remove`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should automatically remove @types ${type} from the current workspace when a tsconfig is detected in the root project of the current workspace`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
},
merge(
{[`packages/foo`]: {dependencies: {[`is-number`]: `^1.0.0`}}},
{[`packages/foo`]: {[type]: {[`@types/is-number`]: `1.0.0`}}},
),
async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `tsconfig.json`), ``);

await run(`remove`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.not.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should automatically remove @types ${type} from the current workspace with tsEnableAutoTypes set to true`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
},
merge(
{[`packages/foo`]: {dependencies: {[`is-number`]: `^1.0.0`}}},
{[`packages/foo`]: {[type]: {[`@types/is-number`]: `1.0.0`}}},
),
async ({path, run, source}) => {
await run(`config`, `set`, `tsEnableAutoTypes`, `true`);
await run(`remove`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.not.toHaveProperty(`${type}.@types/is-number`);
}),
);

test(
`it should automatically remove @types ${type} from the current workspace with tsconfig.json present`,
makeTemporaryMonorepoEnv({
workspaces: [`packages/*`],
},
merge(
{[`packages/foo`]: {dependencies: {[`is-number`]: `^1.0.0`}}},
{[`packages/foo`]: {[type]: {[`@types/is-number`]: `1.0.0`}}},
),
async ({path, run, source}) => {
await xfs.writeFilePromise(ppath.join(path, `packages/foo/tsconfig.json`), ``);

await run(`remove`, `is-number`, {
cwd: `${path}/packages/foo` as PortablePath,
});

await expect(readManifest(`${path}/packages/foo` as PortablePath)).resolves.not.toHaveProperty(`${type}.@types/is-number`);
}),
);
}

test(
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus/static/configuration/yarnrc.json
Expand Up @@ -835,7 +835,7 @@
"tsEnableAutoTypes": {
"_package": "@yarnpkg/plugin-typescript",
"title": "Define whether to automatically install @types dependencies.",
"description": "If true, Yarn will automatically add `@types` dependencies when running `yarn add` with packages that don't provide their own typings (as reported by the Algolia npm database). This behavior is enabled by default if you have a tsconfig file at the root of your project.",
"description": "If true, Yarn will automatically add `@types` dependencies when running `yarn add` with packages that don't provide their own typings (as reported by the Algolia npm database). This behavior is enabled by default if you have a tsconfig.json file at the root of your project, or in your current workspace.",
"type": "boolean",
"examples": [true]
},
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-typescript/README.md
Expand Up @@ -9,6 +9,10 @@

This plugin is included by default starting from Yarn 4.

## Configuration

This plugin is enabled by default if you have a `tsconfig.json` file at the root of your project, or in your current workspace. See [`tsEnableAutoTypes`](https://yarnpkg.com/configuration/yarnrc#tsEnableAutoTypes) for more information.

## Example

```
Expand Down
12 changes: 8 additions & 4 deletions packages/plugin-typescript/sources/index.ts
Expand Up @@ -26,8 +26,10 @@ const afterWorkspaceDependencyAddition = async (
const {project} = workspace;
const {configuration} = project;

const tsEnableAutoTypes = configuration.get(`tsEnableAutoTypes`)
?? xfs.existsSync(ppath.join(project.cwd, `tsconfig.json`));
const tsEnableAutoTypes = configuration.get(`tsEnableAutoTypes`) ?? (
xfs.existsSync(ppath.join(workspace.cwd, `tsconfig.json`))
|| xfs.existsSync(ppath.join(project.cwd, `tsconfig.json`))
);

if (!tsEnableAutoTypes)
return;
Expand Down Expand Up @@ -118,8 +120,10 @@ const afterWorkspaceDependencyRemoval = async (
const {project} = workspace;
const {configuration} = project;

const tsEnableAutoTypes = configuration.get(`tsEnableAutoTypes`)
?? xfs.existsSync(ppath.join(project.cwd, `tsconfig.json`));
const tsEnableAutoTypes = configuration.get(`tsEnableAutoTypes`) ?? (
xfs.existsSync(ppath.join(workspace.cwd, `tsconfig.json`))
|| xfs.existsSync(ppath.join(project.cwd, `tsconfig.json`))
);

if (!tsEnableAutoTypes)
return;
Expand Down