Skip to content

Commit

Permalink
feat(linter): add dependency-checks to buildable lib generator (#18015)
Browse files Browse the repository at this point in the history
  • Loading branch information
meeroslav authored Jul 20, 2023
1 parent fef5b65 commit f4f4eb6
Show file tree
Hide file tree
Showing 20 changed files with 410 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ We use the version numbers of the installed packages when checking whether the v

## Usage

You can use the `dependency-checks` rule by adding it to your ESLint rules configuration:
Library generators from `@nx` packages will configure this rule automatically when you opt-in for bundler/build setup. This rule is intended for publishable/buildable libraries, so it will only run if a `build` target is detected in the configuration (this name can be modified - see [options](#options)).

```jsonc {% fileName=".eslintrc.json" %}
### Manual setup

To set it up manually for existing libraries, you need to add the `dependency-checks` rule to your project's ESLint configuration:

```jsonc {% fileName="<your-project-root>/.eslintrc.json" %}
{
// ... more ESLint config here
"overrides": [
Expand All @@ -26,9 +30,9 @@ You can use the `dependency-checks` rule by adding it to your ESLint rules confi
}
```

Linting `JSON` files is not enabled by default, so you will also need to add `package.json` to the `lintFilePatterns`:
Additionally, you need to adjust your `lintFilePatterns` to include the project's `package.json` file::

```jsonc {% fileName="project.json" %}
```jsonc {% fileName="<your-project-root>/project.json" %}
{
// ... project.json config
"targets": {
Expand All @@ -47,14 +51,20 @@ Linting `JSON` files is not enabled by default, so you will also need to add `pa
}
```

### Overriding defaults

Sometimes we intentionally want to add or remove a dependency from our `package.json` despite what the rule suggests. We can use the rule's options to override default behavior:

```jsonc {% fileName=".eslintrc.json" %}
{
"@nx/dependency-checks": [
"error",
{
// for available options check below
"buildTargets": ["build", "custom-build"], // add non standard build target names
"ignoredDependencies": ["lodash"], // these libs will be omitted from checks
"checkMissingDependencies": true, // toggle to disable
"checkObsoleteDependencies": true, // toggle to disable
"checkVersionMismatches": true // toggle to disable
}
]
}
Expand Down
20 changes: 15 additions & 5 deletions docs/shared/packages/linter/dependency-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ We use the version numbers of the installed packages when checking whether the v

## Usage

You can use the `dependency-checks` rule by adding it to your ESLint rules configuration:
Library generators from `@nx` packages will configure this rule automatically when you opt-in for bundler/build setup. This rule is intended for publishable/buildable libraries, so it will only run if a `build` target is detected in the configuration (this name can be modified - see [options](#options)).

```jsonc {% fileName=".eslintrc.json" %}
### Manual setup

To set it up manually for existing libraries, you need to add the `dependency-checks` rule to your project's ESLint configuration:

```jsonc {% fileName="<your-project-root>/.eslintrc.json" %}
{
// ... more ESLint config here
"overrides": [
Expand All @@ -26,9 +30,9 @@ You can use the `dependency-checks` rule by adding it to your ESLint rules confi
}
```

Linting `JSON` files is not enabled by default, so you will also need to add `package.json` to the `lintFilePatterns`:
Additionally, you need to adjust your `lintFilePatterns` to include the project's `package.json` file::

```jsonc {% fileName="project.json" %}
```jsonc {% fileName="<your-project-root>/project.json" %}
{
// ... project.json config
"targets": {
Expand All @@ -47,14 +51,20 @@ Linting `JSON` files is not enabled by default, so you will also need to add `pa
}
```

### Overriding defaults

Sometimes we intentionally want to add or remove a dependency from our `package.json` despite what the rule suggests. We can use the rule's options to override default behavior:

```jsonc {% fileName=".eslintrc.json" %}
{
"@nx/dependency-checks": [
"error",
{
// for available options check below
"buildTargets": ["build", "custom-build"], // add non standard build target names
"ignoredDependencies": ["lodash"], // these libs will be omitted from checks
"checkMissingDependencies": true, // toggle to disable
"checkObsoleteDependencies": true, // toggle to disable
"checkVersionMismatches": true // toggle to disable
}
]
}
Expand Down
11 changes: 11 additions & 0 deletions e2e/expo/src/expo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
runCommandUntil,
uniq,
updateFile,
updateJson,
} from '@nx/e2e/utils';
import { join } from 'path';

Expand All @@ -24,6 +25,16 @@ describe('expo', () => {

beforeAll(() => {
proj = newProject();
// we create empty preset above which skips creation of `production` named input
updateJson('nx.json', (nxJson) => {
nxJson.namedInputs = {
default: ['{projectRoot}/**/*', 'sharedGlobals'],
production: ['default'],
sharedGlobals: [],
};
nxJson.targetDefaults.build.inputs = ['production', '^production'];
return nxJson;
});
runCLI(`generate @nx/expo:application ${appName} --no-interactive`);
runCLI(
`generate @nx/expo:library ${libName} --buildable --publishable --importPath=${proj}/${libName}`
Expand Down
4 changes: 2 additions & 2 deletions e2e/linter/src/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,12 @@ describe('Linter', () => {
content.replace(/return .*;/, `return names(${mylib}).className;`)
);

// output should now report missing dependencies section
// output should now report missing dependency
out = runCLI(`lint ${mylib}`, { silenceError: true });
expect(out).toContain('they are missing');
expect(out).toContain('@nx/devkit');

// should fix the missing section issue
// should fix the missing dependency issue
out = runCLI(`lint ${mylib} --fix`, { silenceError: true });
expect(out).toContain(
`Successfully ran target lint for project ${mylib}`
Expand Down
6 changes: 5 additions & 1 deletion e2e/nx-misc/src/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
getPackageManagerCommand,
getSelectedPackageManager,
runCommand,
runCreateWorkspace,
} from '@nx/e2e/utils';

let proj: string;
Expand Down Expand Up @@ -176,6 +175,7 @@ describe('Workspace Tests', () => {
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`libs/shared/${lib1}/data-access/**/*.ts`,
`libs/shared/${lib1}/data-access/package.json`,
]);

/**
Expand Down Expand Up @@ -306,6 +306,7 @@ describe('Workspace Tests', () => {

expect(project.targets.lint.options.lintFilePatterns).toEqual([
`libs/shared/${lib1}/data-access/**/*.ts`,
`libs/shared/${lib1}/data-access/package.json`,
]);

/**
Expand Down Expand Up @@ -434,6 +435,7 @@ describe('Workspace Tests', () => {
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`packages/shared/${lib1}/data-access/**/*.ts`,
`packages/shared/${lib1}/data-access/package.json`,
]);
expect(project.tags).toEqual([]);

Expand Down Expand Up @@ -569,6 +571,7 @@ describe('Workspace Tests', () => {
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`libs/${lib1}/data-access/**/*.ts`,
`libs/${lib1}/data-access/package.json`,
]);

/**
Expand Down Expand Up @@ -682,6 +685,7 @@ describe('Workspace Tests', () => {
expect(project.sourceRoot).toBe(`${newPath}/src`);
expect(project.targets.lint.options.lintFilePatterns).toEqual([
`libs/shared/${lib1}/data-access/**/*.ts`,
`libs/shared/${lib1}/data-access/package.json`,
]);

/**
Expand Down
11 changes: 11 additions & 0 deletions e2e/react-native/src/react-native.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
runCommandUntil,
uniq,
updateFile,
updateJson,
} from '@nx/e2e/utils';
import { ChildProcess } from 'child_process';
import { join } from 'path';
Expand All @@ -25,6 +26,16 @@ describe('react native', () => {

beforeAll(() => {
proj = newProject();
// we create empty preset above which skips creation of `production` named input
updateJson('nx.json', (nxJson) => {
nxJson.namedInputs = {
default: ['{projectRoot}/**/*', 'sharedGlobals'],
production: ['default'],
sharedGlobals: [],
};
nxJson.targetDefaults.build.inputs = ['production', '^production'];
return nxJson;
});
runCLI(
`generate @nx/react-native:application ${appName} --install=false --no-interactive`
);
Expand Down
10 changes: 9 additions & 1 deletion packages/expo/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import { addTsConfigPath, getRelativePathToRootTsConfig } from '@nx/js';
import init from '../init/init';
import { addLinting } from '../../utils/add-linting';
import { addJest } from '../../utils/add-jest';
import { nxVersion } from '../../utils/versions';
import {
nxVersion,
reactNativeVersion,
reactVersion,
} from '../../utils/versions';
import { NormalizedSchema, normalizeOptions } from './lib/normalize-options';
import { Schema } from './schema';

Expand Down Expand Up @@ -193,6 +197,10 @@ function createFiles(host: Tree, options: NormalizedSchema) {
function updateLibPackageNpmScope(host: Tree, options: NormalizedSchema) {
return updateJson(host, `${options.projectRoot}/package.json`, (json) => {
json.name = options.importPath;
json.peerDependencies = {
react: reactVersion,
'react-native': reactNativeVersion,
};
return json;
});
}
Expand Down
4 changes: 3 additions & 1 deletion packages/jest/src/generators/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ function addTestInputs(tree: Tree) {
// Remove jest.config.js/ts
'!{projectRoot}/jest.config.[jt]s',
// Remove test-setup.js/ts
'!{projectRoot}/src/test-setup.[jt]s'
// TODO(meeroslav) this should be standardized
'!{projectRoot}/src/test-setup.[jt]s',
'!{projectRoot}/test-setup.[jt]s'
);
// Dedupe and set
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
Expand Down
42 changes: 39 additions & 3 deletions packages/js/src/generators/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,10 @@ describe('lib', () => {
executor: '@nx/linter:eslint',
outputs: ['{options.outputFile}'],
options: {
lintFilePatterns: ['libs/my-lib/**/*.ts'],
lintFilePatterns: [
'libs/my-lib/**/*.ts',
'libs/my-lib/package.json',
],
},
});
});
Expand Down Expand Up @@ -480,6 +483,15 @@ describe('lib', () => {
],
"rules": {},
},
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error",
},
},
],
}
`);
Expand All @@ -500,7 +512,10 @@ describe('lib', () => {
executor: '@nx/linter:eslint',
outputs: ['{options.outputFile}'],
options: {
lintFilePatterns: ['libs/my-dir/my-lib/**/*.ts'],
lintFilePatterns: [
'libs/my-dir/my-lib/**/*.ts',
'libs/my-dir/my-lib/package.json',
],
},
});
});
Expand Down Expand Up @@ -545,6 +560,15 @@ describe('lib', () => {
],
"rules": {},
},
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error",
},
},
],
}
`);
Expand Down Expand Up @@ -626,7 +650,10 @@ describe('lib', () => {
expect(
readProjectConfiguration(tree, 'my-dir-my-lib').targets.lint.options
.lintFilePatterns
).toEqual(['libs/my-dir/my-lib/**/*.js']);
).toEqual([
'libs/my-dir/my-lib/**/*.js',
'libs/my-dir/my-lib/package.json',
]);
expect(readJson(tree, 'libs/my-dir/my-lib/.eslintrc.json'))
.toMatchInlineSnapshot(`
{
Expand Down Expand Up @@ -660,6 +687,15 @@ describe('lib', () => {
],
"rules": {},
},
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error",
},
},
],
}
`);
Expand Down
25 changes: 24 additions & 1 deletion packages/js/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ import {
swcHelpersVersion,
tsLibVersion,
typesNodeVersion,
swcNodeVersion,
swcCoreVersion,
} from '../../utils/versions';
import jsInitGenerator from '../init/init';
import { PackageJson } from 'nx/src/utils/package-json';
import { type PackageJson } from 'nx/src/utils/package-json';
import setupVerdaccio from '../setup-verdaccio/generator';
import { tsConfigBaseOptions } from '../../utils/typescript/create-ts-config';

Expand Down Expand Up @@ -130,6 +132,10 @@ export async function projectGenerator(
]);
}

if (options.bundler !== 'none') {
addBundlerDependencies(tree, options);
}

if (!options.skipFormat) {
await formatFiles(tree);
}
Expand Down Expand Up @@ -249,6 +255,23 @@ export async function addLint(
});
}

function addBundlerDependencies(tree: Tree, options: NormalizedSchema) {
updateJson(tree, `${options.projectRoot}/package.json`, (json) => {
if (options.bundler === 'tsc') {
json.dependencies = {
...json.dependencies,
tslib: tsLibVersion,
};
} else if (options.bundler === 'swc') {
json.dependencies = {
...json.dependencies,
'@swc/helpers': swcHelpersVersion,
};
}
return json;
});
}

function updateTsConfig(tree: Tree, options: NormalizedSchema) {
updateJson(tree, join(options.projectRoot, 'tsconfig.json'), (json) => {
if (options.strict) {
Expand Down
3 changes: 3 additions & 0 deletions packages/linter/src/generators/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ function addTargetDefaults(tree: Tree) {
updateNxJson(tree, nxJson);
}

/**
* Initializes ESLint configuration in a workspace and adds necessary dependencies.
*/
function initEsLint(tree: Tree, options: LinterInitOptions): GeneratorCallback {
if (findEslintFile(tree)) {
return () => {};
Expand Down
Loading

1 comment on commit f4f4eb6

@vercel
Copy link

@vercel vercel bot commented on f4f4eb6 Jul 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx-five.vercel.app
nx.dev

Please sign in to comment.