Skip to content

Commit

Permalink
feat(plugin-typescript): check workspace tsconfig.json (#6175)
Browse files Browse the repository at this point in the history
**What's the problem this PR addresses?**
<!-- Describe the rationale of your PR. -->
<!-- Link all issues that it closes. (Closes/Resolves #xxxx.) -->

Given the following monorepo setup, `plugin-typescript` is currently not
enabled by default in the `foo` workspace, as it only checks that a
`tsconfig.json` exists at the root of the project:

```
package.json
workspace/foo/package.json
workspace/foo/tsconfig.json
```

I also found the [current `README` of
`plugin-typescript`](https://github.com/yarnpkg/berry/blob/daa574791b3b2df01e76c1fdfd9c975050a0fb9d/packages/plugin-typescript/README.md)
not to be helpful in diagnosing my issue

**How did you fix it?**
<!-- A detailed description of your implementation. -->

I've added a check to see if the current workspace had a `tsconfig.json`

I've also added a Configuration section to
`plugin-typescript/README.md`, to help users find the
`tsEnableAutoTypes` option.

**Checklist**
<!--- Don't worry if you miss something, chores are automatically
tested. -->
<!--- This checklist exists to help you remember doing the chores when
you submit a PR. -->
<!--- Put an `x` in all the boxes that apply. -->
- [x] I have read the [Contributing
Guide](https://yarnpkg.com/advanced/contributing).

<!-- See
https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released
for more details. -->
<!-- Check with `yarn version check` and fix with `yarn version check
-i` -->
- [x] I have set the packages that need to be released for my changes to
be effective.

- I have only bumped `@yarnpkg/plugin-typescript` and `@yarnpkg/cli`,
let me know if I should also select the other plugins/core etc.

<!-- The "Testing chores" workflow validates that your PR follows our
guidelines. -->
<!-- If it doesn't pass, click on it to see details as to what your PR
might be missing. -->
- [x] I will check that all automated PR checks pass before the PR gets
reviewed.
  • Loading branch information
nmussy committed Apr 10, 2024
1 parent 4a889dc commit aa4029d
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 7 deletions.
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

0 comments on commit aa4029d

Please sign in to comment.