@grafana/react-detect 0.2.1 → 0.3.0-canary.2395.21023152965.0

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.
@@ -5,25 +5,34 @@ import { jsonReporter } from '../reporters/json.js';
5
5
  import { consoleReporter } from '../reporters/console.js';
6
6
  import { extractAllSources } from '../source-extractor.js';
7
7
  import { analyzeSourceFiles } from '../analyzer.js';
8
+ import { output } from '../utils/output.js';
8
9
 
9
10
  async function detect19(argv) {
10
- const pluginRoot = argv.pluginRoot || process.cwd();
11
- const allMatches = await getAllMatches(pluginRoot);
12
- const depContext = new DependencyContext();
13
- await depContext.loadDependencies(pluginRoot);
14
- const matchesWithRootDependency = allMatches.map((match) => {
15
- if (match.type === "dependency" && match.packageName) {
16
- return { ...match, rootDependency: depContext.findRootDependency(match.packageName) };
11
+ try {
12
+ const pluginRoot = argv.pluginRoot || process.cwd();
13
+ const allMatches = await getAllMatches(pluginRoot);
14
+ const depContext = new DependencyContext();
15
+ await depContext.loadDependencies(pluginRoot);
16
+ const matchesWithRootDependency = allMatches.map((match) => {
17
+ if (match.type === "dependency" && match.packageName) {
18
+ return { ...match, rootDependency: depContext.findRootDependency(match.packageName) };
19
+ }
20
+ return match;
21
+ });
22
+ const results = generateAnalysisResults(matchesWithRootDependency, pluginRoot, depContext);
23
+ if (argv.json) {
24
+ jsonReporter(results);
25
+ } else {
26
+ consoleReporter(results);
17
27
  }
18
- return match;
19
- });
20
- const results = generateAnalysisResults(matchesWithRootDependency, pluginRoot, depContext);
21
- if (argv.json) {
22
- jsonReporter(results);
23
- } else {
24
- consoleReporter(results);
28
+ process.exit(results.summary.totalIssues > 0 ? 1 : 0);
29
+ } catch (error) {
30
+ output.error({
31
+ title: "Error during detection",
32
+ body: [error.message]
33
+ });
34
+ process.exit(1);
25
35
  }
26
- process.exit(results.summary.totalIssues > 0 ? 1 : 0);
27
36
  }
28
37
  async function getAllMatches(pluginRoot) {
29
38
  const sourcemapPaths = await findSourceMapFiles(pluginRoot);
@@ -37,7 +37,7 @@ function analyzeComponentType(ast) {
37
37
  function hasReactComponent(ast) {
38
38
  let found = false;
39
39
  walk(ast, (node) => {
40
- if (node.type === "ClassDeclaration" && node.superClass?.type === "MemberExpression" && node.superClass.object.type === "Identifier" && node.superClass.object.name === "React" && node.superClass.property.type === "Identifier" && (node.superClass.property.name === "Component" || node.superClass.property.name === "PureComponent")) {
40
+ if (node && node.type === "ClassDeclaration" && node.superClass?.type === "MemberExpression" && node.superClass.object.type === "Identifier" && node.superClass.object.name === "React" && node.superClass.property.type === "Identifier" && (node.superClass.property.name === "Component" || node.superClass.property.name === "PureComponent")) {
41
41
  found = true;
42
42
  }
43
43
  });
@@ -51,7 +51,7 @@ function hasFunctionComponentPattern(ast) {
51
51
  }
52
52
  let found = false;
53
53
  walk(ast, (node) => {
54
- if (node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression") {
54
+ if (node && (node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression")) {
55
55
  found = true;
56
56
  }
57
57
  });
@@ -60,7 +60,7 @@ function hasFunctionComponentPattern(ast) {
60
60
  function hasReactImport(ast) {
61
61
  let found = false;
62
62
  walk(ast, (node) => {
63
- if (node.type === "ImportDeclaration" && node.source.type === "Literal" && (node.source.value === "react" || node.source.value === "react-dom")) {
63
+ if (node && node.type === "ImportDeclaration" && node.source.type === "Literal" && (node.source.value === "react" || node.source.value === "react-dom")) {
64
64
  found = true;
65
65
  }
66
66
  });
@@ -69,7 +69,7 @@ function hasReactImport(ast) {
69
69
  function hasJSX(ast) {
70
70
  let found = false;
71
71
  walk(ast, (node) => {
72
- if (node.type === "JSXElement" || node.type === "JSXFragment") {
72
+ if (node && (node.type === "JSXElement" || node.type === "JSXFragment")) {
73
73
  found = true;
74
74
  }
75
75
  });
@@ -78,7 +78,7 @@ function hasJSX(ast) {
78
78
  function hasHooks(ast) {
79
79
  let found = false;
80
80
  walk(ast, (node) => {
81
- if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name.startsWith("use")) {
81
+ if (node && node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name.startsWith("use")) {
82
82
  found = true;
83
83
  }
84
84
  });
@@ -1,5 +1,5 @@
1
1
  import { join } from 'node:path';
2
- import { parsePnpmProject, parseNpmLockV2Project, parseYarnLockV2Project } from 'snyk-nodejs-lockfile-parser';
2
+ import { parsePnpmProject, parseNpmLockV2Project, parseYarnLockV1Project, parseYarnLockV2Project } from 'snyk-nodejs-lockfile-parser';
3
3
  import { readFileSync, existsSync } from 'node:fs';
4
4
  import { readJsonFile } from './plugin.js';
5
5
 
@@ -34,12 +34,22 @@ class DependencyContext {
34
34
  pruneCycles: false
35
35
  });
36
36
  } else if (lockfile === "yarn.lock") {
37
- this.depGraph = await parseYarnLockV2Project(pkgJsonContentString, lockfileContent, {
38
- includeDevDeps: true,
39
- includeOptionalDeps: true,
40
- strictOutOfSync: false,
41
- pruneWithinTopLevelDeps: true
42
- });
37
+ if (/#\s*yarn lockfile v1/i.test(lockfileContent)) {
38
+ this.depGraph = await parseYarnLockV1Project(pkgJsonContentString, lockfileContent, {
39
+ includeDevDeps: true,
40
+ includeOptionalDeps: true,
41
+ includePeerDeps: true,
42
+ strictOutOfSync: false,
43
+ pruneLevel: "withinTopLevelDeps"
44
+ });
45
+ } else {
46
+ this.depGraph = await parseYarnLockV2Project(pkgJsonContentString, lockfileContent, {
47
+ includeDevDeps: true,
48
+ includeOptionalDeps: true,
49
+ strictOutOfSync: false,
50
+ pruneWithinTopLevelDeps: true
51
+ });
52
+ }
43
53
  }
44
54
  if (pkgJsonContent.dependencies) {
45
55
  Object.entries(pkgJsonContent.dependencies).forEach(([name, version]) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@grafana/react-detect",
3
3
  "description": "Run various checks to detect if a Grafana plugin is compatible with React.",
4
- "version": "0.2.1",
4
+ "version": "0.3.0-canary.2395.21023152965.0",
5
5
  "repository": {
6
6
  "directory": "packages/react-detect",
7
7
  "url": "https://github.com/grafana/plugin-tools"
@@ -40,5 +40,5 @@
40
40
  "engines": {
41
41
  "node": ">=20"
42
42
  },
43
- "gitHead": "31a4b153d8fa990eece9a5a58d6e29df5d801428"
43
+ "gitHead": "2d906bece6457dd89becbc6c62a78ccfbdedd8f7"
44
44
  }
@@ -6,32 +6,41 @@ import { jsonReporter } from '../reporters/json.js';
6
6
  import { consoleReporter } from '../reporters/console.js';
7
7
  import { extractAllSources } from '../source-extractor.js';
8
8
  import { analyzeSourceFiles } from '../analyzer.js';
9
+ import { output } from '../utils/output.js';
9
10
  /**
10
11
  * Main detect command for finding React 19 breaking changes
11
12
  */
12
13
  export async function detect19(argv: minimist.ParsedArgs) {
13
- const pluginRoot = argv.pluginRoot || process.cwd();
14
-
15
- const allMatches = await getAllMatches(pluginRoot);
16
- const depContext = new DependencyContext();
17
- await depContext.loadDependencies(pluginRoot);
18
-
19
- const matchesWithRootDependency = allMatches.map((match) => {
20
- if (match.type === 'dependency' && match.packageName) {
21
- return { ...match, rootDependency: depContext.findRootDependency(match.packageName) };
14
+ try {
15
+ const pluginRoot = argv.pluginRoot || process.cwd();
16
+
17
+ const allMatches = await getAllMatches(pluginRoot);
18
+ const depContext = new DependencyContext();
19
+ await depContext.loadDependencies(pluginRoot);
20
+
21
+ const matchesWithRootDependency = allMatches.map((match) => {
22
+ if (match.type === 'dependency' && match.packageName) {
23
+ return { ...match, rootDependency: depContext.findRootDependency(match.packageName) };
24
+ }
25
+ return match;
26
+ });
27
+
28
+ const results = generateAnalysisResults(matchesWithRootDependency, pluginRoot, depContext);
29
+
30
+ if (argv.json) {
31
+ jsonReporter(results);
32
+ } else {
33
+ consoleReporter(results);
22
34
  }
23
- return match;
24
- });
25
35
 
26
- const results = generateAnalysisResults(matchesWithRootDependency, pluginRoot, depContext);
27
-
28
- if (argv.json) {
29
- jsonReporter(results);
30
- } else {
31
- consoleReporter(results);
36
+ process.exit(results.summary.totalIssues > 0 ? 1 : 0);
37
+ } catch (error) {
38
+ output.error({
39
+ title: 'Error during detection',
40
+ body: [(error as Error).message],
41
+ });
42
+ process.exit(1);
32
43
  }
33
-
34
- process.exit(results.summary.totalIssues > 0 ? 1 : 0);
35
44
  }
36
45
 
37
46
  async function getAllMatches(pluginRoot: string) {
@@ -47,6 +47,7 @@ export function hasReactComponent(ast: TSESTree.Program): boolean {
47
47
 
48
48
  walk(ast, (node) => {
49
49
  if (
50
+ node &&
50
51
  node.type === 'ClassDeclaration' &&
51
52
  node.superClass?.type === 'MemberExpression' &&
52
53
  node.superClass.object.type === 'Identifier' &&
@@ -71,7 +72,7 @@ export function hasFunctionComponentPattern(ast: TSESTree.Program): boolean {
71
72
 
72
73
  let found = false;
73
74
  walk(ast, (node) => {
74
- if (node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') {
75
+ if (node && (node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')) {
75
76
  found = true;
76
77
  }
77
78
  });
@@ -84,6 +85,7 @@ function hasReactImport(ast: TSESTree.Program): boolean {
84
85
 
85
86
  walk(ast, (node) => {
86
87
  if (
88
+ node &&
87
89
  node.type === 'ImportDeclaration' &&
88
90
  node.source.type === 'Literal' &&
89
91
  (node.source.value === 'react' || node.source.value === 'react-dom')
@@ -99,7 +101,7 @@ function hasJSX(ast: TSESTree.Program): boolean {
99
101
  let found = false;
100
102
 
101
103
  walk(ast, (node) => {
102
- if (node.type === 'JSXElement' || node.type === 'JSXFragment') {
104
+ if (node && (node.type === 'JSXElement' || node.type === 'JSXFragment')) {
103
105
  found = true;
104
106
  }
105
107
  });
@@ -111,7 +113,12 @@ function hasHooks(ast: TSESTree.Program): boolean {
111
113
  let found = false;
112
114
 
113
115
  walk(ast, (node) => {
114
- if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name.startsWith('use')) {
116
+ if (
117
+ node &&
118
+ node.type === 'CallExpression' &&
119
+ node.callee.type === 'Identifier' &&
120
+ node.callee.name.startsWith('use')
121
+ ) {
115
122
  found = true;
116
123
  }
117
124
  });
@@ -1,5 +1,10 @@
1
1
  import { join } from 'node:path';
2
- import { parsePnpmProject, parseNpmLockV2Project, parseYarnLockV2Project } from 'snyk-nodejs-lockfile-parser';
2
+ import {
3
+ parsePnpmProject,
4
+ parseNpmLockV2Project,
5
+ parseYarnLockV1Project,
6
+ parseYarnLockV2Project,
7
+ } from 'snyk-nodejs-lockfile-parser';
3
8
  import type { DepGraph } from '@snyk/dep-graph';
4
9
  import { existsSync, readFileSync } from 'node:fs';
5
10
  import { readJsonFile } from './plugin.js';
@@ -32,12 +37,22 @@ export class DependencyContext {
32
37
  pruneCycles: false,
33
38
  });
34
39
  } else if (lockfile === 'yarn.lock') {
35
- this.depGraph = await parseYarnLockV2Project(pkgJsonContentString, lockfileContent, {
36
- includeDevDeps: true,
37
- includeOptionalDeps: true,
38
- strictOutOfSync: false,
39
- pruneWithinTopLevelDeps: true,
40
- });
40
+ if (/#\s*yarn lockfile v1/i.test(lockfileContent)) {
41
+ this.depGraph = await parseYarnLockV1Project(pkgJsonContentString, lockfileContent, {
42
+ includeDevDeps: true,
43
+ includeOptionalDeps: true,
44
+ includePeerDeps: true,
45
+ strictOutOfSync: false,
46
+ pruneLevel: 'withinTopLevelDeps',
47
+ });
48
+ } else {
49
+ this.depGraph = await parseYarnLockV2Project(pkgJsonContentString, lockfileContent, {
50
+ includeDevDeps: true,
51
+ includeOptionalDeps: true,
52
+ strictOutOfSync: false,
53
+ pruneWithinTopLevelDeps: true,
54
+ });
55
+ }
41
56
  }
42
57
 
43
58
  if (pkgJsonContent.dependencies) {