@nx/eslint-plugin 23.0.0-beta.18 → 23.0.0-beta.19

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.
@@ -1,5 +1,6 @@
1
1
  import type { TSESLint } from '@typescript-eslint/utils';
2
2
  import type { AST } from 'jsonc-eslint-parser';
3
+ import { ProjectConfiguration } from '@nx/devkit';
3
4
  export type Options = [
4
5
  {
5
6
  generatorsJson?: string;
@@ -10,21 +11,17 @@ export type Options = [
10
11
  tsConfig?: string;
11
12
  }
12
13
  ];
13
- type NormalizedOptions = Options[0] & {
14
- rootDir?: string;
15
- outDir?: string;
16
- };
17
14
  export type MessageIds = 'missingRequiredSchema' | 'invalidSchemaPath' | 'missingImplementation' | 'invalidImplementationPath' | 'invalidImplementationModule' | 'unableToReadImplementationExports' | 'invalidPromptPath' | 'invalidVersion' | 'missingVersion' | 'noGeneratorsOrSchematicsFound' | 'noExecutorsOrBuildersFound' | 'valueShouldBeObject';
18
15
  export declare const RULE_NAME = "nx-plugin-checks";
19
16
  declare const _default: TSESLint.RuleModule<MessageIds, Options, unknown, TSESLint.RuleListener> & {
20
17
  name: string;
21
18
  };
22
19
  export default _default;
23
- export declare function checkCollectionFileNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, options: NormalizedOptions): void;
24
- export declare function checkCollectionNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, options: NormalizedOptions): void;
25
- export declare function validateEntry(baseNode: AST.JSONObjectExpression, key: string, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, options: NormalizedOptions): void;
26
- export declare function validateImplementationNode(implementationNode: AST.JSONProperty, key: string, context: TSESLint.RuleContext<MessageIds, Options>, options: NormalizedOptions): void;
27
- export declare function validatePromptNode(promptNode: AST.JSONProperty, key: string, context: TSESLint.RuleContext<MessageIds, Options>, options: NormalizedOptions): void;
20
+ export declare function checkCollectionFileNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, projects: Record<string, ProjectConfiguration>): void;
21
+ export declare function checkCollectionNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, projects: Record<string, ProjectConfiguration>): void;
22
+ export declare function validateEntry(baseNode: AST.JSONObjectExpression, key: string, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>, projects: Record<string, ProjectConfiguration>): void;
23
+ export declare function validateImplementationNode(implementationNode: AST.JSONProperty, key: string, context: TSESLint.RuleContext<MessageIds, Options>, projects: Record<string, ProjectConfiguration>): void;
24
+ export declare function validatePromptNode(promptNode: AST.JSONProperty, key: string, context: TSESLint.RuleContext<MessageIds, Options>): void;
28
25
  export declare function validatePackageGroup(baseNode: AST.JSONObjectExpression, context: TSESLint.RuleContext<MessageIds, Options>): void;
29
26
  export declare function validateVersionJsonExpression(node: AST.JSONExpression, context: TSESLint.RuleContext<MessageIds, Options>): string | boolean;
30
27
  export declare function checkIfIdentifierIsFunction(filePath: string, identifier: string): boolean;
@@ -15,6 +15,7 @@ const fs_1 = require("fs");
15
15
  const tsquery_1 = require("@phenomnomnominal/tsquery");
16
16
  const devkit_1 = require("@nx/devkit");
17
17
  const internal_1 = require("@nx/js/internal");
18
+ const internal_2 = require("@nx/devkit/internal");
18
19
  const path = tslib_1.__importStar(require("path"));
19
20
  const semver_1 = require("semver");
20
21
  const project_graph_utils_1 = require("../utils/project-graph-utils");
@@ -90,6 +91,10 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => ``)({
90
91
  return {};
91
92
  }
92
93
  const { projectGraph, projectRootMappings } = (0, project_graph_utils_1.readProjectGraph)(exports.RULE_NAME);
94
+ const projects = {};
95
+ for (const [projectName, node] of Object.entries(projectGraph.nodes)) {
96
+ projects[projectName] = node.data;
97
+ }
93
98
  const sourceFilePath = (0, runtime_lint_utils_1.getSourceFilePath)(context.filename ?? context.getFilename(), devkit_1.workspaceRoot);
94
99
  const sourceProject = (0, runtime_lint_utils_1.findProject)(projectGraph, projectRootMappings, sourceFilePath);
95
100
  // If source is not part of an nx workspace, return.
@@ -105,13 +110,13 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => ``)({
105
110
  return {
106
111
  ['JSONExpressionStatement > JSONObjectExpression'](node) {
107
112
  if (sourceFilePath === generatorsJson) {
108
- checkCollectionFileNode(node, 'generator', context, options);
113
+ checkCollectionFileNode(node, 'generator', context, projects);
109
114
  }
110
115
  else if (sourceFilePath === migrationsJson) {
111
- checkCollectionFileNode(node, 'migration', context, options);
116
+ checkCollectionFileNode(node, 'migration', context, projects);
112
117
  }
113
118
  else if (sourceFilePath === executorsJson) {
114
- checkCollectionFileNode(node, 'executor', context, options);
119
+ checkCollectionFileNode(node, 'executor', context, projects);
115
120
  }
116
121
  else if (sourceFilePath === packageJson) {
117
122
  validatePackageGroup(node, context);
@@ -121,29 +126,7 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => ``)({
121
126
  },
122
127
  });
123
128
  function normalizeOptions(sourceProject, options) {
124
- let rootDir;
125
- let outDir;
126
129
  const base = { ...DEFAULT_OPTIONS, ...options };
127
- let runtimeTsConfig;
128
- if (sourceProject.data.targets?.build?.executor === '@nx/js:tsc') {
129
- rootDir = sourceProject.data.targets.build.options.rootDir;
130
- outDir = sourceProject.data.targets.build.options.outputPath;
131
- }
132
- if (!rootDir && !outDir) {
133
- try {
134
- runtimeTsConfig = require.resolve(path.join(devkit_1.workspaceRoot, sourceProject.data.root, base.tsConfig));
135
- const tsConfig = (0, devkit_1.readJsonFile)(runtimeTsConfig);
136
- rootDir ??= tsConfig.compilerOptions?.rootDir
137
- ? path.join(sourceProject.data.root, tsConfig.compilerOptions.rootDir)
138
- : undefined;
139
- outDir ??= tsConfig.compilerOptions?.outDir
140
- ? path.join(sourceProject.data.root, tsConfig.compilerOptions.outDir)
141
- : undefined;
142
- }
143
- catch {
144
- // nothing
145
- }
146
- }
147
130
  const pathPrefix = sourceProject.data.root !== '.' ? `${sourceProject.data.root}/` : '';
148
131
  return {
149
132
  ...base,
@@ -159,11 +142,9 @@ function normalizeOptions(sourceProject, options) {
159
142
  packageJson: base.packageJson
160
143
  ? `${pathPrefix}${base.packageJson}`
161
144
  : undefined,
162
- rootDir,
163
- outDir,
164
145
  };
165
146
  }
166
- function checkCollectionFileNode(baseNode, mode, context, options) {
147
+ function checkCollectionFileNode(baseNode, mode, context, projects) {
167
148
  const schematicsRootNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' && x.key.value === 'schematics');
168
149
  const generatorsRootNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' && x.key.value === 'generators');
169
150
  const executorsRootNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' && x.key.value === 'executors');
@@ -197,11 +178,11 @@ function checkCollectionFileNode(baseNode, mode, context, options) {
197
178
  });
198
179
  }
199
180
  else {
200
- checkCollectionNode(collectionNode.value, mode, context, options);
181
+ checkCollectionNode(collectionNode.value, mode, context, projects);
201
182
  }
202
183
  }
203
184
  }
204
- function checkCollectionNode(baseNode, mode, context, options) {
185
+ function checkCollectionNode(baseNode, mode, context, projects) {
205
186
  const entries = baseNode.properties;
206
187
  for (const entryNode of entries) {
207
188
  if (entryNode.value.type !== 'JSONObjectExpression') {
@@ -212,11 +193,11 @@ function checkCollectionNode(baseNode, mode, context, options) {
212
193
  });
213
194
  }
214
195
  else if (entryNode.key.type === 'JSONLiteral') {
215
- validateEntry(entryNode.value, entryNode.key.value.toString(), mode, context, options);
196
+ validateEntry(entryNode.value, entryNode.key.value.toString(), mode, context, projects);
216
197
  }
217
198
  }
218
199
  }
219
- function validateEntry(baseNode, key, mode, context, options) {
200
+ function validateEntry(baseNode, key, mode, context, projects) {
220
201
  const schemaNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' && x.key.value === 'schema');
221
202
  if (mode !== 'migration' && !schemaNode) {
222
203
  context.report({
@@ -236,28 +217,20 @@ function validateEntry(baseNode, key, mode, context, options) {
236
217
  });
237
218
  }
238
219
  else {
239
- let validJsonFound = false;
240
- const schemaFilePath = path.join(path.dirname(context.filename ?? context.getFilename()), schemaNode.value.value);
241
220
  try {
242
- (0, devkit_1.readJsonFile)(schemaFilePath);
243
- validJsonFound = true;
221
+ (0, internal_2.resolveSchema)(schemaNode.value.value, path.dirname(context.filename ?? context.getFilename()), context.filename ?? context.getFilename(), projects);
244
222
  }
245
- catch {
246
- try {
247
- // Try to map back to source, which will be the case with TS solution setup.
248
- (0, devkit_1.readJsonFile)(schemaFilePath.replace(options.outDir, options.rootDir));
249
- validJsonFound = true;
223
+ catch (e) {
224
+ if (e instanceof internal_2.SchemaResolutionError) {
225
+ context.report({
226
+ messageId: 'invalidSchemaPath',
227
+ node: schemaNode.value,
228
+ });
250
229
  }
251
- catch {
252
- // nothing, will be reported below
230
+ else {
231
+ throw e;
253
232
  }
254
233
  }
255
- if (!validJsonFound) {
256
- context.report({
257
- messageId: 'invalidSchemaPath',
258
- node: schemaNode.value,
259
- });
260
- }
261
234
  }
262
235
  }
263
236
  const implementationNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' &&
@@ -273,10 +246,10 @@ function validateEntry(baseNode, key, mode, context, options) {
273
246
  });
274
247
  }
275
248
  else if (implementationNode) {
276
- validateImplementationNode(implementationNode, key, context, options);
249
+ validateImplementationNode(implementationNode, key, context, projects);
277
250
  }
278
251
  if (mode === 'migration' && promptNode) {
279
- validatePromptNode(promptNode, key, context, options);
252
+ validatePromptNode(promptNode, key, context);
280
253
  }
281
254
  if (mode === 'migration') {
282
255
  const versionNode = baseNode.properties.find((x) => x.key.type === 'JSONLiteral' && x.key.value === 'version');
@@ -313,27 +286,7 @@ function validateEntry(baseNode, key, mode, context, options) {
313
286
  }
314
287
  }
315
288
  }
316
- // Under Node's native TS strip, swc-node isn't registered, so `require.resolve`
317
- // no longer auto-tries `.ts` extensions. Try them explicitly before giving up.
318
- const TS_EXTENSIONS = ['.ts', '.tsx', '.cts', '.mts'];
319
- function tryResolveImplementation(modulePath) {
320
- try {
321
- return require.resolve(modulePath);
322
- }
323
- catch {
324
- // fall through
325
- }
326
- for (const ext of TS_EXTENSIONS) {
327
- try {
328
- return require.resolve(`${modulePath}${ext}`);
329
- }
330
- catch {
331
- // try next
332
- }
333
- }
334
- return undefined;
335
- }
336
- function validateImplementationNode(implementationNode, key, context, options) {
289
+ function validateImplementationNode(implementationNode, key, context, projects) {
337
290
  if (implementationNode.value.type !== 'JSONLiteral' ||
338
291
  typeof implementationNode.value.value !== 'string') {
339
292
  context.report({
@@ -347,21 +300,24 @@ function validateImplementationNode(implementationNode, key, context, options) {
347
300
  else {
348
301
  const [implementationPath, identifier] = implementationNode.value.value.split('#');
349
302
  let resolvedPath;
350
- const modulePath = path.join(path.dirname(context.filename ?? context.getFilename()), implementationPath);
351
- resolvedPath = tryResolveImplementation(modulePath);
352
- if (!resolvedPath && options.outDir && options.rootDir) {
353
- resolvedPath = tryResolveImplementation(modulePath.replace(options.outDir, options.rootDir));
303
+ try {
304
+ resolvedPath = (0, internal_2.resolveImplementation)(implementationPath, path.dirname(context.filename ?? context.getFilename()), context.filename ?? context.getFilename(), projects);
354
305
  }
355
- if (!resolvedPath) {
356
- context.report({
357
- messageId: 'invalidImplementationPath',
358
- data: {
359
- key,
360
- },
361
- node: implementationNode.value,
362
- });
306
+ catch (e) {
307
+ if (e instanceof internal_2.ImplementationResolutionError) {
308
+ context.report({
309
+ messageId: 'invalidImplementationPath',
310
+ data: {
311
+ key,
312
+ },
313
+ node: implementationNode.value,
314
+ });
315
+ }
316
+ else {
317
+ throw e;
318
+ }
363
319
  }
364
- if (identifier) {
320
+ if (resolvedPath && identifier) {
365
321
  try {
366
322
  if (!checkIfIdentifierIsFunction(resolvedPath, identifier)) {
367
323
  context.report({
@@ -387,7 +343,7 @@ function validateImplementationNode(implementationNode, key, context, options) {
387
343
  }
388
344
  }
389
345
  }
390
- function validatePromptNode(promptNode, key, context, options) {
346
+ function validatePromptNode(promptNode, key, context) {
391
347
  if (promptNode.value.type !== 'JSONLiteral' ||
392
348
  typeof promptNode.value.value !== 'string') {
393
349
  context.report({
@@ -397,20 +353,13 @@ function validatePromptNode(promptNode, key, context, options) {
397
353
  },
398
354
  node: promptNode.value,
399
355
  });
356
+ return;
400
357
  }
401
- else {
402
- const promptPath = path.join(path.dirname(context.filename ?? context.getFilename()), promptNode.value.value);
403
- let resolvedPath;
404
- if ((0, fs_1.existsSync)(promptPath)) {
405
- resolvedPath = promptPath;
406
- }
407
- else if (options.outDir && options.rootDir) {
408
- const mapped = promptPath.replace(options.outDir, options.rootDir);
409
- if ((0, fs_1.existsSync)(mapped)) {
410
- resolvedPath = mapped;
411
- }
412
- }
413
- if (!resolvedPath) {
358
+ try {
359
+ (0, internal_2.resolvePrompt)(promptNode.value.value, path.dirname(context.filename ?? context.getFilename()));
360
+ }
361
+ catch (e) {
362
+ if (e instanceof internal_2.PromptResolutionError) {
414
363
  context.report({
415
364
  messageId: 'invalidPromptPath',
416
365
  data: {
@@ -419,6 +368,9 @@ function validatePromptNode(promptNode, key, context, options) {
419
368
  node: promptNode.value,
420
369
  });
421
370
  }
371
+ else {
372
+ throw e;
373
+ }
422
374
  }
423
375
  }
424
376
  function validatePackageGroup(baseNode, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/eslint-plugin",
3
- "version": "23.0.0-beta.18",
3
+ "version": "23.0.0-beta.19",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -98,11 +98,11 @@
98
98
  "jsonc-eslint-parser": "^2.1.0",
99
99
  "semver": "^7.6.3",
100
100
  "tslib": "^2.3.0",
101
- "@nx/js": "23.0.0-beta.18",
102
- "@nx/devkit": "23.0.0-beta.18"
101
+ "@nx/devkit": "23.0.0-beta.19",
102
+ "@nx/js": "23.0.0-beta.19"
103
103
  },
104
104
  "devDependencies": {
105
- "nx": "23.0.0-beta.18"
105
+ "nx": "23.0.0-beta.19"
106
106
  },
107
107
  "publishConfig": {
108
108
  "access": "public"