@nx/js 23.2.0-beta.6 → 23.2.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,8 @@ export { getProjectPackageManagerWorkspaceState, getProjectPackageManagerWorkspa
21
21
  export { findNpmDependencies } from './src/utils/find-npm-dependencies';
22
22
  export { addBuildAndWatchDepsTargets, isValidPackageJsonBuildConfig, } from './src/plugins/typescript/util';
23
23
  export { normalizeLinterOption, normalizeUnitTestRunnerOption, } from './src/utils/generator-prompts';
24
+ export { detectLinters } from './src/utils/linter';
25
+ export { addLintingToProject, type AddLintingToProjectOptions, } from './src/utils/add-linting-to-project';
24
26
  export { createGlobPatternsForDependencies } from './src/utils/generate-globs';
25
27
  export { getImportPath } from './src/utils/get-import-path';
26
28
  export { stripGlobToBaseDir } from './src/utils/strip-glob-to-base-dir';
package/dist/internal.js CHANGED
@@ -5,8 +5,8 @@
5
5
  // internal consumers and may change without semver protection. Consider it
6
6
  // the @nx/js equivalent of `@nx/devkit/internal`.
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.addLocalRegistryScripts = exports.stripGlobToBaseDir = exports.getImportPath = exports.createGlobPatternsForDependencies = exports.normalizeUnitTestRunnerOption = exports.normalizeLinterOption = exports.isValidPackageJsonBuildConfig = exports.addBuildAndWatchDepsTargets = exports.findNpmDependencies = exports.getProjectPackageManagerWorkspaceStateWarningTask = exports.getProjectPackageManagerWorkspaceState = exports.getNpmScope = exports.sortPackageJsonFields = exports.CopyAssetsHandler = exports.addSwcRegisterDependencies = exports.addSwcDependencies = exports.addSwcTestConfig = exports.addSwcConfig = exports.createTmpTsConfig = exports.computeCompilerOptionsPaths = exports.calculateProjectDependencies = exports.calculateProjectBuildableDependencies = exports.compileTypeScript = exports.getNeededCompilerOptionOverrides = exports.isTypescriptVersionAtLeast = exports.getTsConfigModuleResolution = exports.getRangeMinimum = exports.ensureTypescript = exports.createTreeParseConfigHost = exports.getTsConfigBaseOptions = exports.updateTsconfigFiles = exports.shouldConfigureTsSolutionSetup = exports.isUsingTsSolutionSetup = exports.getProjectType = exports.getProjectSourceRoot = exports.getDefinedCustomConditionName = exports.findRuntimeTsConfigName = exports.assertNotUsingTsSolutionSetup = exports.addProjectToTsSolutionWorkspace = exports.TS_SOLUTION_SETUP_TSCONFIG_INPUT = exports.isEsmProject = exports.walkTsconfigExtendsChain = exports.findProjectsNpmDependencies = exports.isBuiltinModuleImport = exports.TargetProjectLocator = exports.requireWithTsconfigFallback = exports.registerTsConfigPaths = exports.registerTsProject = exports.loadTsFile = exports.forceRegisterEsmLoader = void 0;
9
- exports.typesNodeVersion = exports.tsLibVersion = exports.swcNodeVersion = exports.swcHelpersVersion = exports.swcCoreVersion = exports.swcCliVersion = exports.prettierVersion = exports.esbuildVersion = exports.nxVersion = exports.releaseTasks = exports.addReleaseConfigForTsSolution = exports.addReleaseConfigForNonTsSolution = void 0;
8
+ exports.getImportPath = exports.createGlobPatternsForDependencies = exports.addLintingToProject = exports.detectLinters = exports.normalizeUnitTestRunnerOption = exports.normalizeLinterOption = exports.isValidPackageJsonBuildConfig = exports.addBuildAndWatchDepsTargets = exports.findNpmDependencies = exports.getProjectPackageManagerWorkspaceStateWarningTask = exports.getProjectPackageManagerWorkspaceState = exports.getNpmScope = exports.sortPackageJsonFields = exports.CopyAssetsHandler = exports.addSwcRegisterDependencies = exports.addSwcDependencies = exports.addSwcTestConfig = exports.addSwcConfig = exports.createTmpTsConfig = exports.computeCompilerOptionsPaths = exports.calculateProjectDependencies = exports.calculateProjectBuildableDependencies = exports.compileTypeScript = exports.getNeededCompilerOptionOverrides = exports.isTypescriptVersionAtLeast = exports.getTsConfigModuleResolution = exports.getRangeMinimum = exports.ensureTypescript = exports.createTreeParseConfigHost = exports.getTsConfigBaseOptions = exports.updateTsconfigFiles = exports.shouldConfigureTsSolutionSetup = exports.isUsingTsSolutionSetup = exports.getProjectType = exports.getProjectSourceRoot = exports.getDefinedCustomConditionName = exports.findRuntimeTsConfigName = exports.assertNotUsingTsSolutionSetup = exports.addProjectToTsSolutionWorkspace = exports.TS_SOLUTION_SETUP_TSCONFIG_INPUT = exports.isEsmProject = exports.walkTsconfigExtendsChain = exports.findProjectsNpmDependencies = exports.isBuiltinModuleImport = exports.TargetProjectLocator = exports.requireWithTsconfigFallback = exports.registerTsConfigPaths = exports.registerTsProject = exports.loadTsFile = exports.forceRegisterEsmLoader = void 0;
9
+ exports.typesNodeVersion = exports.tsLibVersion = exports.swcNodeVersion = exports.swcHelpersVersion = exports.swcCoreVersion = exports.swcCliVersion = exports.prettierVersion = exports.esbuildVersion = exports.nxVersion = exports.releaseTasks = exports.addReleaseConfigForTsSolution = exports.addReleaseConfigForNonTsSolution = exports.addLocalRegistryScripts = exports.stripGlobToBaseDir = void 0;
10
10
  // Re-exports of nx-source internals.
11
11
  var internal_1 = require("@nx/devkit/internal");
12
12
  Object.defineProperty(exports, "forceRegisterEsmLoader", { enumerable: true, get: function () { return internal_1.forceRegisterEsmLoader; } });
@@ -89,6 +89,10 @@ Object.defineProperty(exports, "isValidPackageJsonBuildConfig", { enumerable: tr
89
89
  var generator_prompts_1 = require("./src/utils/generator-prompts");
90
90
  Object.defineProperty(exports, "normalizeLinterOption", { enumerable: true, get: function () { return generator_prompts_1.normalizeLinterOption; } });
91
91
  Object.defineProperty(exports, "normalizeUnitTestRunnerOption", { enumerable: true, get: function () { return generator_prompts_1.normalizeUnitTestRunnerOption; } });
92
+ var linter_1 = require("./src/utils/linter");
93
+ Object.defineProperty(exports, "detectLinters", { enumerable: true, get: function () { return linter_1.detectLinters; } });
94
+ var add_linting_to_project_1 = require("./src/utils/add-linting-to-project");
95
+ Object.defineProperty(exports, "addLintingToProject", { enumerable: true, get: function () { return add_linting_to_project_1.addLintingToProject; } });
92
96
  var generate_globs_1 = require("./src/utils/generate-globs");
93
97
  Object.defineProperty(exports, "createGlobPatternsForDependencies", { enumerable: true, get: function () { return generate_globs_1.createGlobPatternsForDependencies; } });
94
98
  var get_import_path_1 = require("./src/utils/get-import-path");
@@ -7,6 +7,7 @@ const tslib_1 = require("tslib");
7
7
  const internal_1 = require("@nx/devkit/internal");
8
8
  const devkit_1 = require("@nx/devkit");
9
9
  const path_1 = require("path");
10
+ const add_linting_to_project_1 = require("../../utils/add-linting-to-project");
10
11
  const assert_supported_typescript_version_1 = require("../../utils/assert-supported-typescript-version");
11
12
  const generator_prompts_1 = require("../../utils/generator-prompts");
12
13
  const sort_fields_1 = require("../../utils/package-json/sort-fields");
@@ -248,6 +249,17 @@ async function configureProject(tree, options) {
248
249
  }
249
250
  }
250
251
  async function addLint(tree, options) {
252
+ // Everything below reaches into `@nx/eslint`'s config utilities, which have
253
+ // no equivalent for other linters. Dispatch before touching any of it.
254
+ if (options.linter !== 'eslint') {
255
+ return (0, add_linting_to_project_1.addLintingToProject)(tree, {
256
+ linter: options.linter,
257
+ project: options.name,
258
+ addPlugin: options.addPlugin,
259
+ rootProject: options.rootProject,
260
+ unitTestRunner: options.unitTestRunner,
261
+ });
262
+ }
251
263
  const { lintProjectGenerator } = (0, devkit_1.ensurePackage)('@nx/eslint', versions_1.nxVersion);
252
264
  const { addOverrideToLintConfig, lintConfigHasOverride, isEslintConfigSupported, updateOverrideInLintConfig, addIgnoresToLintConfig, isTypedLintingEnabled,
253
265
  // nx-ignore-next-line
@@ -638,6 +650,10 @@ async function normalizeOptions(tree, options) {
638
650
  isUsingTsSolutionConfig;
639
651
  return {
640
652
  ...options,
653
+ // Read from `options`, not a hoisted local: the `npm-scripts` block resets
654
+ // it to 'none' after `normalizeLinterOption` runs. Naming the key also
655
+ // satisfies the normalized type, since the spread carries `linter?`.
656
+ linter: options.linter,
641
657
  fileName,
642
658
  name: isUsingTsSolutionConfig && !options.name ? importPath : projectName,
643
659
  projectNames,
@@ -1,6 +1,5 @@
1
1
  import type { ProjectNameAndRootOptions } from '@nx/devkit/internal';
2
- // nx-ignore-next-line
3
- const { Linter, LinterType } = require('@nx/eslint'); // use require to import to avoid circular dependency
2
+ import type { LinterType } from '../../utils/linter';
4
3
  import type { ProjectPackageManagerWorkspaceState } from '../../utils/package-manager-workspaces';
5
4
 
6
5
  export type Compiler = 'tsc' | 'swc';
@@ -15,7 +14,7 @@ export interface LibraryGeneratorSchema {
15
14
  skipPackageJson?: boolean;
16
15
  includeBabelRc?: boolean;
17
16
  unitTestRunner?: 'jest' | 'vitest' | 'none';
18
- linter?: Linter | LinterType;
17
+ linter?: LinterType;
19
18
  testEnvironment?: 'jsdom' | 'node';
20
19
  importPath?: string;
21
20
  js?: boolean;
@@ -42,6 +41,8 @@ export interface LibraryGeneratorSchema {
42
41
  export interface NormalizedLibraryGeneratorOptions
43
42
  extends LibraryGeneratorSchema {
44
43
  name: string;
44
+ /** `normalizeOptions` always resolves this, so it is no longer optional. */
45
+ linter: LinterType;
45
46
  projectNames: ProjectNameAndRootOptions['names'];
46
47
  fileName: string;
47
48
  projectRoot: string;
@@ -29,9 +29,9 @@
29
29
  "x-priority": "important"
30
30
  },
31
31
  "linter": {
32
- "description": "The tool to use for running lint checks.",
32
+ "description": "The tool to use for running lint checks. Defaults to the linter the workspace already uses.",
33
33
  "type": "string",
34
- "enum": ["none", "eslint"],
34
+ "enum": ["none", "eslint", "oxlint"],
35
35
  "x-priority": "important"
36
36
  },
37
37
  "unitTestRunner": {
@@ -16,5 +16,6 @@ export { initGenerator } from './generators/init/init';
16
16
  export { setupPrettierGenerator } from './generators/setup-prettier/generator';
17
17
  export { setupVerdaccio } from './generators/setup-verdaccio/generator';
18
18
  export { isValidVariable } from './utils/is-valid-variable';
19
+ export { type LinterType } from './utils/linter';
19
20
  export { createLockFile, getLockFileName } from '@nx/devkit/internal';
20
21
  export { createPackageJson } from '@nx/devkit/internal';
@@ -0,0 +1,46 @@
1
+ import { GeneratorCallback, Tree } from '@nx/devkit';
2
+ import { type LinterType } from './linter';
3
+ export interface AddLintingToProjectOptions {
4
+ /** Required, but `undefined` still reaches here — see the normalization below. */
5
+ linter: LinterType | undefined;
6
+ project: string;
7
+ skipPackageJson?: boolean;
8
+ keepExistingVersions?: boolean;
9
+ addPlugin?: boolean;
10
+ /** ESLint-only. Ignored by other linters. */
11
+ tsConfigPaths?: string[];
12
+ unitTestRunner?: string;
13
+ /** ESLint-only. Ignored by other linters. */
14
+ rootProject?: boolean;
15
+ /** ESLint-only. Ignored by other linters. */
16
+ enableTypedLinting?: boolean;
17
+ /** ESLint-only. Oxlint is inference-only, so it writes no explicit target. */
18
+ addExplicitTargets?: boolean;
19
+ /**
20
+ * ESLint-only. The `@nx/dependency-checks` rule lints `package.json`, which
21
+ * Oxlint cannot read.
22
+ */
23
+ addPackageJsonDependencyChecks?: boolean;
24
+ /**
25
+ * Oxlint plugins to enable for this project, e.g. `['react', 'jsx-a11y']`.
26
+ * Ignored by other linters, which express framework presets differently.
27
+ */
28
+ oxlintPlugins?: string[];
29
+ }
30
+ /**
31
+ * Sets a project's linter up, dispatching to whichever linter the workspace
32
+ * asked for. Consumers call this instead of importing `lintProjectGenerator`
33
+ * from `@nx/eslint` directly, so adding a linter does not mean editing every
34
+ * generator. The two arms name different generators on purpose: `@nx/eslint`
35
+ * writes a lint target, while `@nx/oxlint` is inference-only and only
36
+ * configures the project.
37
+ *
38
+ * Scope is deliberately narrow: registering the linter and its project target.
39
+ * Linter-specific config shaping — ESLint framework presets, `extends`, ignore
40
+ * entries — stays at the call site guarded on `linter`, because none of it has
41
+ * a cross-linter equivalent.
42
+ *
43
+ * Both plugins are loaded through `ensurePackage`; a static import would be
44
+ * circular, since both depend on `@nx/js`.
45
+ */
46
+ export declare function addLintingToProject(tree: Tree, options: AddLintingToProjectOptions): Promise<GeneratorCallback>;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addLintingToProject = addLintingToProject;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const internal_1 = require("@nx/devkit/internal");
6
+ const versions_1 = require("./versions");
7
+ const linter_1 = require("./linter");
8
+ /**
9
+ * Sets a project's linter up, dispatching to whichever linter the workspace
10
+ * asked for. Consumers call this instead of importing `lintProjectGenerator`
11
+ * from `@nx/eslint` directly, so adding a linter does not mean editing every
12
+ * generator. The two arms name different generators on purpose: `@nx/eslint`
13
+ * writes a lint target, while `@nx/oxlint` is inference-only and only
14
+ * configures the project.
15
+ *
16
+ * Scope is deliberately narrow: registering the linter and its project target.
17
+ * Linter-specific config shaping — ESLint framework presets, `extends`, ignore
18
+ * entries — stays at the call site guarded on `linter`, because none of it has
19
+ * a cross-linter equivalent.
20
+ *
21
+ * Both plugins are loaded through `ensurePackage`; a static import would be
22
+ * circular, since both depend on `@nx/js`.
23
+ */
24
+ async function addLintingToProject(tree, options) {
25
+ // Callers should resolve this themselves, since their own guards need a
26
+ // concrete value. Resolving again here keeps a caller that forgets from
27
+ // silently getting ESLint in a workspace that uses something else.
28
+ const linter = options.linter ?? (0, linter_1.detectLinters)(tree)[0] ?? 'none';
29
+ if (linter === 'none') {
30
+ return () => { };
31
+ }
32
+ if (linter === 'oxlint') {
33
+ // `ensurePackage` installs by package name, so the subpath is required
34
+ // separately. `nx-ignore-next-line` keeps this out of the import graph, so
35
+ // the dependency stays a devDependency of this package rather than a runtime
36
+ // one. That devDependency is what makes the `js` <-> `@nx/oxlint` cycle,
37
+ // which is deliberate and recorded in `ignoredCircularDependencies` in the
38
+ // root eslint config.
39
+ (0, devkit_1.ensurePackage)('@nx/oxlint', versions_1.nxVersion);
40
+ // `@nx/oxlint` is the only ESM package under `packages/` and its
41
+ // `./generators` subpath has no `require` condition, so a bare `require()`
42
+ // throws ERR_REQUIRE_ESM below Node 20.19 / 22.12.
43
+ //
44
+ // Resolve to an absolute path before importing. `ensurePackage` exposes an
45
+ // on-demand install by adding a temp dir to NODE_PATH, which `require.resolve`
46
+ // honours and the ESM loader does not — so handing `import()` a bare specifier
47
+ // fails with ERR_MODULE_NOT_FOUND on exactly the Node band this indirection
48
+ // exists for. `load-resolved-plugin.ts` calls `handleImport` the same way.
49
+ // nx-ignore-next-line
50
+ const generatorsPath = require.resolve('@nx/oxlint/generators', {
51
+ paths: [devkit_1.workspaceRoot, __dirname],
52
+ });
53
+ const { configurationGenerator } = await (0, internal_1.handleImport)(generatorsPath);
54
+ return configurationGenerator(tree, {
55
+ project: options.project,
56
+ plugins: [
57
+ ...(options.oxlintPlugins ?? []),
58
+ ...oxlintTestPlugins(options.unitTestRunner),
59
+ ],
60
+ skipFormat: true,
61
+ skipPackageJson: options.skipPackageJson,
62
+ keepExistingVersions: options.keepExistingVersions,
63
+ // No `addPlugin`: `@nx/oxlint` is inference-only, so it always registers.
64
+ });
65
+ }
66
+ if (linter === 'eslint') {
67
+ const { lintProjectGenerator } = (0, devkit_1.ensurePackage)('@nx/eslint', versions_1.nxVersion);
68
+ return lintProjectGenerator(tree, {
69
+ linter,
70
+ project: options.project,
71
+ tsConfigPaths: options.tsConfigPaths,
72
+ unitTestRunner: options.unitTestRunner,
73
+ rootProject: options.rootProject,
74
+ enableTypedLinting: options.enableTypedLinting,
75
+ addExplicitTargets: options.addExplicitTargets,
76
+ addPackageJsonDependencyChecks: options.addPackageJsonDependencyChecks,
77
+ skipFormat: true,
78
+ skipPackageJson: options.skipPackageJson,
79
+ keepExistingVersions: options.keepExistingVersions,
80
+ addPlugin: options.addPlugin,
81
+ });
82
+ }
83
+ // Spelling ESLint out as its own branch rather than letting it be the
84
+ // fallthrough: a new `LinterType` member then fails to compile here instead of
85
+ // silently getting an ESLint setup.
86
+ const unhandled = linter;
87
+ throw new Error(`Unsupported linter: ${unhandled}`);
88
+ }
89
+ /**
90
+ * Which Oxlint plugins a test runner needs is the runner package's knowledge,
91
+ * so it is read from there rather than hardcoded here — this only maps the
92
+ * runner name to its package. Matched loosely because runners arrive with
93
+ * suffixes, e.g. Angular's `vitest-analog`. Both packages are already installed
94
+ * by the time the runner's own generator runs; `ensurePackage` keeps them out
95
+ * of the import graph, since both depend on `@nx/js`.
96
+ */
97
+ function oxlintTestPlugins(unitTestRunner) {
98
+ const pkg = unitTestRunner?.includes('vitest')
99
+ ? '@nx/vitest'
100
+ : unitTestRunner?.includes('jest')
101
+ ? '@nx/jest'
102
+ : undefined;
103
+ if (!pkg) {
104
+ return [];
105
+ }
106
+ // `ensurePackage` installs by package name, so the `internal` subpath is
107
+ // required separately. Plain `require` holds only while both runners are CJS;
108
+ // `@nx/oxlint` is ESM, which is why the arm above needs `handleImport`.
109
+ (0, devkit_1.ensurePackage)(pkg, versions_1.nxVersion);
110
+ const { oxlintPlugins } = require(`${pkg}/internal`);
111
+ return oxlintPlugins ?? [];
112
+ }
@@ -1,3 +1,4 @@
1
1
  import type { Tree } from '@nx/devkit';
2
- export declare function normalizeLinterOption(tree: Tree, linter: undefined | 'none' | 'eslint'): Promise<'none' | 'eslint'>;
2
+ import { type LinterType } from './linter';
3
+ export declare function normalizeLinterOption(tree: Tree, linter: undefined | LinterType): Promise<LinterType>;
3
4
  export declare function normalizeUnitTestRunnerOption<T extends 'none' | 'jest' | 'vitest'>(tree: Tree, unitTestRunner: undefined | T, testRunners?: Array<'jest' | 'vitest'>): Promise<T>;
@@ -4,22 +4,27 @@ exports.normalizeLinterOption = normalizeLinterOption;
4
4
  exports.normalizeUnitTestRunnerOption = normalizeUnitTestRunnerOption;
5
5
  const internal_1 = require("@nx/devkit/internal");
6
6
  const ts_solution_setup_1 = require("./typescript/ts-solution-setup");
7
+ const linter_1 = require("./linter");
7
8
  async function normalizeLinterOption(tree, linter) {
8
9
  if (linter) {
9
10
  return linter;
10
11
  }
11
- const isTsSolutionSetup = (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree);
12
- const choices = isTsSolutionSetup
13
- ? [{ name: 'none' }, { name: 'eslint' }]
14
- : [{ name: 'eslint' }, { name: 'none' }];
15
- const defaultValue = isTsSolutionSetup ? 'none' : 'eslint';
12
+ // Following the workspace is not a question. When it already has a linter,
13
+ // use it — including in a hybrid workspace, where `detectLinters` puts the
14
+ // one being migrated *to* first. Pass `--linter` to choose something else.
15
+ const [detected] = (0, linter_1.detectLinters)(tree);
16
+ if (detected) {
17
+ return detected;
18
+ }
19
+ // Nothing to follow, so this is a real choice. `{ linter: 'none' }` is the
20
+ // non-interactive answer; `none` leads so the interactive default matches it.
16
21
  return await (0, internal_1.promptWhenInteractive)({
17
22
  type: 'autocomplete',
18
23
  name: 'linter',
19
24
  message: `Which linter would you like to use?`,
20
- choices,
25
+ choices: [{ name: 'none' }, { name: 'eslint' }, { name: 'oxlint' }],
21
26
  initial: 0,
22
- }, { linter: defaultValue }).then(({ linter }) => linter);
27
+ }, { linter: 'none' }).then(({ linter }) => linter);
23
28
  }
24
29
  async function normalizeUnitTestRunnerOption(tree, unitTestRunner, testRunners = ['jest', 'vitest']) {
25
30
  if (unitTestRunner) {
@@ -0,0 +1,30 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ /**
3
+ * The linters Nx generators can set up for a project.
4
+ *
5
+ * Canonical home. `@nx/eslint` re-exports this for back-compat. Two standalone
6
+ * copies exist because neither package can depend on `@nx/js`: `@nx/workspace`
7
+ * (which `@nx/js` itself depends on) and `LINTERS` in `create-nx-workspace`.
8
+ * The assertion below pins the first; `create-nx-workspace` has no edge to
9
+ * assert across, so that one is still kept in sync by hand.
10
+ */
11
+ export type LinterType = 'eslint' | 'oxlint' | 'none';
12
+ /**
13
+ * Every linter the workspace has installed, most-preferred first — so `[0]` is
14
+ * the one a generator should follow when it was not told which to set up, and
15
+ * `.includes(x)` answers whether the workspace uses `x` at all. Those differ in
16
+ * a workspace part-way through a migration: it has both, and a project
17
+ * generated for Oxlint still lives under the root ESLint config.
18
+ *
19
+ * Oxlint sorts first when both are present. A workspace with both has adopted
20
+ * Oxlint alongside its existing ESLint setup, and new projects should follow
21
+ * the direction of travel rather than the setup being migrated away from.
22
+ *
23
+ * Empty means the workspace opted out of linting; callers that need a concrete
24
+ * answer should read that as `none` rather than inferring ESLint for it.
25
+ *
26
+ * Reads the tree, never the generator's own module resolution: `eslint` is a
27
+ * peer dependency of several first-party plugins, so it resolves from disk in
28
+ * workspaces that do not use it.
29
+ */
30
+ export declare function detectLinters(tree: Tree): Exclude<LinterType, 'none'>[];
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectLinters = detectLinters;
4
+ const devkit_1 = require("@nx/devkit");
5
+ function hasPlugin(tree, plugin) {
6
+ const nxJson = (0, devkit_1.readNxJson)(tree);
7
+ return !!nxJson?.plugins?.some((p) => typeof p === 'string' ? p === plugin : p.plugin === plugin);
8
+ }
9
+ function hasAnyDependency(tree, packages) {
10
+ return packages.some((pkg) => Boolean((0, devkit_1.getDependencyVersionFromPackageJson)(tree, pkg)));
11
+ }
12
+ /**
13
+ * Every linter the workspace has installed, most-preferred first — so `[0]` is
14
+ * the one a generator should follow when it was not told which to set up, and
15
+ * `.includes(x)` answers whether the workspace uses `x` at all. Those differ in
16
+ * a workspace part-way through a migration: it has both, and a project
17
+ * generated for Oxlint still lives under the root ESLint config.
18
+ *
19
+ * Oxlint sorts first when both are present. A workspace with both has adopted
20
+ * Oxlint alongside its existing ESLint setup, and new projects should follow
21
+ * the direction of travel rather than the setup being migrated away from.
22
+ *
23
+ * Empty means the workspace opted out of linting; callers that need a concrete
24
+ * answer should read that as `none` rather than inferring ESLint for it.
25
+ *
26
+ * Reads the tree, never the generator's own module resolution: `eslint` is a
27
+ * peer dependency of several first-party plugins, so it resolves from disk in
28
+ * workspaces that do not use it.
29
+ */
30
+ function detectLinters(tree) {
31
+ const linters = [];
32
+ if (hasPlugin(tree, '@nx/oxlint') ||
33
+ hasAnyDependency(tree, ['@nx/oxlint', 'oxlint'])) {
34
+ linters.push('oxlint');
35
+ }
36
+ // `@nx/eslint` registers its inference plugin as `@nx/eslint/plugin`, unlike
37
+ // `@nx/oxlint`, which registers under its bare package name.
38
+ if (hasPlugin(tree, '@nx/eslint/plugin') ||
39
+ hasAnyDependency(tree, ['@nx/eslint', 'eslint'])) {
40
+ linters.push('eslint');
41
+ }
42
+ return linters;
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "23.2.0-beta.6",
3
+ "version": "23.2.0-beta.7",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -147,11 +147,18 @@
147
147
  "source-map-support": "0.5.19",
148
148
  "tinyglobby": "^0.2.12",
149
149
  "tslib": "^2.3.0",
150
- "@nx/devkit": "23.2.0-beta.6",
151
- "@nx/workspace": "23.2.0-beta.6"
150
+ "@nx/devkit": "23.2.0-beta.7",
151
+ "@nx/workspace": "23.2.0-beta.7"
152
152
  },
153
153
  "devDependencies": {
154
- "nx": "23.2.0-beta.6"
154
+ "@nx/esbuild": "23.2.0-beta.7",
155
+ "@nx/jest": "23.2.0-beta.7",
156
+ "@nx/eslint": "23.2.0-beta.7",
157
+ "@nx/oxlint": "23.2.0-beta.7",
158
+ "@nx/rollup": "23.2.0-beta.7",
159
+ "@nx/vite": "23.2.0-beta.7",
160
+ "@nx/vitest": "23.2.0-beta.7",
161
+ "nx": "23.2.0-beta.7"
155
162
  },
156
163
  "peerDependencies": {
157
164
  "@swc/cli": ">=0.6.0 <0.9.0",