@grafana/react-detect 0.6.2 → 0.6.3-canary.2558.23891449991.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.
- package/dist/bin/run.js +1 -1
- package/dist/commands/detect19.js +7 -5
- package/dist/file-scanner.js +3 -5
- package/dist/results.js +2 -2
- package/dist/utils/plugin.js +2 -3
- package/package.json +2 -2
- package/src/bin/run.ts +1 -1
- package/src/commands/detect19.ts +7 -5
- package/src/file-scanner.test.ts +36 -0
- package/src/file-scanner.ts +3 -6
- package/src/results.test.ts +7 -7
- package/src/results.ts +2 -2
- package/src/utils/plugin.test.ts +32 -0
- package/src/utils/plugin.ts +6 -3
package/dist/bin/run.js
CHANGED
|
@@ -5,7 +5,7 @@ import { detect19 } from '../commands/detect19.js';
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
const argv = minimist(args, {
|
|
7
7
|
boolean: ["json", "skipBuildTooling", "skipDependencies", "noErrorExitCode"],
|
|
8
|
-
string: ["pluginRoot"],
|
|
8
|
+
string: ["pluginRoot", "distDir"],
|
|
9
9
|
default: {
|
|
10
10
|
json: false,
|
|
11
11
|
skipBuildTooling: false,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { findSourceMapFiles } from '../file-scanner.js';
|
|
2
3
|
import { generateAnalysisResults } from '../results.js';
|
|
3
4
|
import { DependencyContext } from '../utils/dependencies.js';
|
|
@@ -10,10 +11,11 @@ import { output } from '../utils/output.js';
|
|
|
10
11
|
async function detect19(argv) {
|
|
11
12
|
try {
|
|
12
13
|
const pluginRoot = argv.pluginRoot || process.cwd();
|
|
14
|
+
const distDir = argv.distDir || join(pluginRoot, "dist");
|
|
13
15
|
const skipDependencies = argv.skipDependencies || false;
|
|
14
16
|
const skipBuildTooling = argv.skipBuildTooling || false;
|
|
15
17
|
const jsonOutput = argv.json || false;
|
|
16
|
-
const allMatches = await getAllMatches(
|
|
18
|
+
const allMatches = await getAllMatches(distDir);
|
|
17
19
|
let depContext = null;
|
|
18
20
|
if (!skipDependencies) {
|
|
19
21
|
depContext = new DependencyContext();
|
|
@@ -39,7 +41,7 @@ async function detect19(argv) {
|
|
|
39
41
|
}
|
|
40
42
|
return match;
|
|
41
43
|
});
|
|
42
|
-
const results = generateAnalysisResults(matchesWithRootDependency,
|
|
44
|
+
const results = generateAnalysisResults(matchesWithRootDependency, distDir, depContext, {
|
|
43
45
|
skipBuildTooling,
|
|
44
46
|
skipDependencies
|
|
45
47
|
});
|
|
@@ -61,10 +63,10 @@ async function detect19(argv) {
|
|
|
61
63
|
process.exit(1);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
|
-
async function getAllMatches(
|
|
65
|
-
const sourcemapPaths = await findSourceMapFiles(
|
|
66
|
+
async function getAllMatches(distDir) {
|
|
67
|
+
const sourcemapPaths = await findSourceMapFiles(distDir);
|
|
66
68
|
if (sourcemapPaths.length === 0) {
|
|
67
|
-
throw new Error(
|
|
69
|
+
throw new Error(`No source map files found in "${distDir}". Make sure to build your plugin first.`);
|
|
68
70
|
}
|
|
69
71
|
const sources = await extractAllSources(sourcemapPaths);
|
|
70
72
|
if (sources.length === 0) {
|
package/dist/file-scanner.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import fg from 'fast-glob';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
2
|
|
|
4
|
-
async function findSourceMapFiles(
|
|
5
|
-
const distDirectory = join(directory, "dist");
|
|
3
|
+
async function findSourceMapFiles(distDir) {
|
|
6
4
|
try {
|
|
7
5
|
const files = await fg("**/*.js.map", {
|
|
8
|
-
cwd:
|
|
6
|
+
cwd: distDir,
|
|
9
7
|
absolute: true,
|
|
10
8
|
ignore: ["**/node_modules/**"]
|
|
11
9
|
});
|
|
12
10
|
return files;
|
|
13
11
|
} catch (error) {
|
|
14
12
|
throw new Error(
|
|
15
|
-
`Error finding source map files in ${
|
|
13
|
+
`Error finding source map files in "${distDir}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
16
14
|
);
|
|
17
15
|
}
|
|
18
16
|
}
|
package/dist/results.js
CHANGED
|
@@ -3,9 +3,9 @@ import { getPluginJson, hasExternalisedJsxRuntime } from './utils/plugin.js';
|
|
|
3
3
|
import { isExternal } from './utils/dependencies.js';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
|
-
function generateAnalysisResults(matches,
|
|
6
|
+
function generateAnalysisResults(matches, distDir, depContext, options = { skipBuildTooling: false, skipDependencies: false }) {
|
|
7
7
|
const filtered = filterMatches(matches, options.skipBuildTooling);
|
|
8
|
-
const pluginJson = getPluginJson(
|
|
8
|
+
const pluginJson = getPluginJson(distDir);
|
|
9
9
|
const filteredWithoutExternals = filtered.filter((match) => shouldIncludeDependencyMatch(match));
|
|
10
10
|
const sourceMatches = filteredWithoutExternals.filter((m) => m.type === "source");
|
|
11
11
|
const dependencyMatches = filteredWithoutExternals.filter((m) => m.type === "dependency");
|
package/dist/utils/plugin.js
CHANGED
|
@@ -4,12 +4,11 @@ import { parseFile } from '../parser.js';
|
|
|
4
4
|
import { walk } from './ast.js';
|
|
5
5
|
|
|
6
6
|
let cachedPluginJson = null;
|
|
7
|
-
function getPluginJson(
|
|
7
|
+
function getPluginJson(distDir) {
|
|
8
8
|
if (cachedPluginJson) {
|
|
9
9
|
return cachedPluginJson;
|
|
10
10
|
}
|
|
11
|
-
const
|
|
12
|
-
const pluginJsonPath = path.join(srcPath, "plugin.json");
|
|
11
|
+
const pluginJsonPath = path.join(distDir, "plugin.json");
|
|
13
12
|
cachedPluginJson = readJsonFile(pluginJsonPath);
|
|
14
13
|
return cachedPluginJson;
|
|
15
14
|
}
|
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.6.
|
|
4
|
+
"version": "0.6.3-canary.2558.23891449991.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"directory": "packages/react-detect",
|
|
7
7
|
"url": "https://github.com/grafana/plugin-tools"
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=20"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "2cb04b2c307bd2e796b10e3cb1b672801c06ec57"
|
|
45
45
|
}
|
package/src/bin/run.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { detect19 } from '../commands/detect19.js';
|
|
|
6
6
|
const args = process.argv.slice(2);
|
|
7
7
|
const argv = minimist(args, {
|
|
8
8
|
boolean: ['json', 'skipBuildTooling', 'skipDependencies', 'noErrorExitCode'],
|
|
9
|
-
string: ['pluginRoot'],
|
|
9
|
+
string: ['pluginRoot', 'distDir'],
|
|
10
10
|
default: {
|
|
11
11
|
json: false,
|
|
12
12
|
skipBuildTooling: false,
|
package/src/commands/detect19.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import minimist from 'minimist';
|
|
2
|
+
import { join } from 'node:path';
|
|
2
3
|
import { findSourceMapFiles } from '../file-scanner.js';
|
|
3
4
|
import { generateAnalysisResults } from '../results.js';
|
|
4
5
|
import { DependencyContext } from '../utils/dependencies.js';
|
|
@@ -13,11 +14,12 @@ import { output } from '../utils/output.js';
|
|
|
13
14
|
export async function detect19(argv: minimist.ParsedArgs) {
|
|
14
15
|
try {
|
|
15
16
|
const pluginRoot = argv.pluginRoot || process.cwd();
|
|
17
|
+
const distDir = argv.distDir || join(pluginRoot, 'dist');
|
|
16
18
|
const skipDependencies = argv.skipDependencies || false;
|
|
17
19
|
const skipBuildTooling = argv.skipBuildTooling || false;
|
|
18
20
|
const jsonOutput = argv.json || false;
|
|
19
21
|
|
|
20
|
-
const allMatches = await getAllMatches(
|
|
22
|
+
const allMatches = await getAllMatches(distDir);
|
|
21
23
|
|
|
22
24
|
// Conditionally load dependencies
|
|
23
25
|
let depContext: DependencyContext | null = null;
|
|
@@ -52,7 +54,7 @@ export async function detect19(argv: minimist.ParsedArgs) {
|
|
|
52
54
|
return match;
|
|
53
55
|
});
|
|
54
56
|
|
|
55
|
-
const results = generateAnalysisResults(matchesWithRootDependency,
|
|
57
|
+
const results = generateAnalysisResults(matchesWithRootDependency, distDir, depContext, {
|
|
56
58
|
skipBuildTooling,
|
|
57
59
|
skipDependencies,
|
|
58
60
|
});
|
|
@@ -77,11 +79,11 @@ export async function detect19(argv: minimist.ParsedArgs) {
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
async function getAllMatches(
|
|
81
|
-
const sourcemapPaths = await findSourceMapFiles(
|
|
82
|
+
async function getAllMatches(distDir: string) {
|
|
83
|
+
const sourcemapPaths = await findSourceMapFiles(distDir);
|
|
82
84
|
|
|
83
85
|
if (sourcemapPaths.length === 0) {
|
|
84
|
-
throw new Error(
|
|
86
|
+
throw new Error(`No source map files found in "${distDir}". Make sure to build your plugin first.`);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
const sources = await extractAllSources(sourcemapPaths);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { findSourceMapFiles } from './file-scanner.js';
|
|
3
|
+
import { mkdtempSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
describe('findSourceMapFiles', () => {
|
|
8
|
+
it('searches the given directory directly without appending a dist subdirectory', async () => {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), 'react-detect-test-'));
|
|
10
|
+
writeFileSync(join(dir, 'module.js.map'), '{}');
|
|
11
|
+
|
|
12
|
+
const files = await findSourceMapFiles(dir);
|
|
13
|
+
|
|
14
|
+
expect(files).toHaveLength(1);
|
|
15
|
+
expect(files[0]).toContain('module.js.map');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('finds source map files recursively within the given directory', async () => {
|
|
19
|
+
const dir = mkdtempSync(join(tmpdir(), 'react-detect-test-'));
|
|
20
|
+
mkdirSync(join(dir, 'nested'));
|
|
21
|
+
writeFileSync(join(dir, 'a.js.map'), '{}');
|
|
22
|
+
writeFileSync(join(dir, 'nested', 'b.js.map'), '{}');
|
|
23
|
+
|
|
24
|
+
const files = await findSourceMapFiles(dir);
|
|
25
|
+
|
|
26
|
+
expect(files).toHaveLength(2);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns empty array when no source map files exist', async () => {
|
|
30
|
+
const dir = mkdtempSync(join(tmpdir(), 'react-detect-test-'));
|
|
31
|
+
|
|
32
|
+
const files = await findSourceMapFiles(dir);
|
|
33
|
+
|
|
34
|
+
expect(files).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/src/file-scanner.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import fg from 'fast-glob';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
export async function findSourceMapFiles(directory: string): Promise<string[]> {
|
|
5
|
-
const distDirectory = join(directory, 'dist');
|
|
6
2
|
|
|
3
|
+
export async function findSourceMapFiles(distDir: string): Promise<string[]> {
|
|
7
4
|
try {
|
|
8
5
|
const files = await fg('**/*.js.map', {
|
|
9
|
-
cwd:
|
|
6
|
+
cwd: distDir,
|
|
10
7
|
absolute: true,
|
|
11
8
|
ignore: ['**/node_modules/**'],
|
|
12
9
|
});
|
|
13
10
|
return files;
|
|
14
11
|
} catch (error) {
|
|
15
12
|
throw new Error(
|
|
16
|
-
`Error finding source map files in ${
|
|
13
|
+
`Error finding source map files in "${distDir}": ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
17
14
|
);
|
|
18
15
|
}
|
|
19
16
|
}
|
package/src/results.test.ts
CHANGED
|
@@ -36,7 +36,7 @@ describe('generateAnalysisResults', () => {
|
|
|
36
36
|
bundledFilePath: `node_modules/${packageName}/index.js`,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
const
|
|
39
|
+
const distDir = process.cwd();
|
|
40
40
|
const depContext = new DependencyContext();
|
|
41
41
|
const options: AnalysisOptions = {
|
|
42
42
|
skipBuildTooling: true,
|
|
@@ -64,7 +64,7 @@ describe('generateAnalysisResults', () => {
|
|
|
64
64
|
bundledFilePath: 'src/components/MyComponent.tsx',
|
|
65
65
|
};
|
|
66
66
|
const matches: AnalyzedMatch[] = [sourceMatch, createDependencyMatch('react', 'react')];
|
|
67
|
-
const results = generateAnalysisResults(matches,
|
|
67
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
68
68
|
|
|
69
69
|
expect(results.summary.sourceIssuesCount).toBe(1);
|
|
70
70
|
expect(results.summary.dependencyIssuesCount).toBe(0);
|
|
@@ -75,7 +75,7 @@ describe('generateAnalysisResults', () => {
|
|
|
75
75
|
createDependencyMatch('lodash', 'lodash'), // lodash is externalized by Grafana
|
|
76
76
|
createDependencyMatch('axios', 'axios'),
|
|
77
77
|
];
|
|
78
|
-
const results = generateAnalysisResults(matches,
|
|
78
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
79
79
|
|
|
80
80
|
expect(results.issues.dependencies).toHaveLength(1);
|
|
81
81
|
expect(results.issues.dependencies[0].packageName).toBe('axios');
|
|
@@ -87,7 +87,7 @@ describe('generateAnalysisResults', () => {
|
|
|
87
87
|
createDependencyMatch('@grafana/ui', '@grafana/ui'),
|
|
88
88
|
createDependencyMatch('@custom/package', '@custom/package'),
|
|
89
89
|
];
|
|
90
|
-
const results = generateAnalysisResults(matches,
|
|
90
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
91
91
|
|
|
92
92
|
expect(results.issues.dependencies).toHaveLength(1);
|
|
93
93
|
expect(results.issues.dependencies[0].packageName).toBe('@custom/package');
|
|
@@ -98,7 +98,7 @@ describe('generateAnalysisResults', () => {
|
|
|
98
98
|
createDependencyMatch('@grafana/data/utils', '@grafana/data'),
|
|
99
99
|
createDependencyMatch('@custom/package/utils', '@custom/package'),
|
|
100
100
|
];
|
|
101
|
-
const results = generateAnalysisResults(matches,
|
|
101
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
102
102
|
|
|
103
103
|
expect(results.issues.dependencies).toHaveLength(1);
|
|
104
104
|
expect(results.issues.dependencies[0].packageName).toBe('@custom/package/utils');
|
|
@@ -111,7 +111,7 @@ describe('generateAnalysisResults', () => {
|
|
|
111
111
|
createDependencyMatch('scheduler', 'react'),
|
|
112
112
|
createDependencyMatch('debug', 'axios'), // Non-externalized root dependency
|
|
113
113
|
];
|
|
114
|
-
const results = generateAnalysisResults(matches,
|
|
114
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
115
115
|
|
|
116
116
|
expect(results.issues.dependencies).toHaveLength(1);
|
|
117
117
|
expect(results.issues.dependencies[0].packageName).toBe('debug');
|
|
@@ -125,7 +125,7 @@ describe('generateAnalysisResults', () => {
|
|
|
125
125
|
createDependencyMatch('@grafana/data', '@grafana/data'),
|
|
126
126
|
createDependencyMatch('axios', 'axios'),
|
|
127
127
|
];
|
|
128
|
-
const results = generateAnalysisResults(matches,
|
|
128
|
+
const results = generateAnalysisResults(matches, distDir, depContext, options);
|
|
129
129
|
// Filter to only critical dep issues
|
|
130
130
|
const reportedDeps = results.issues.critical.filter((i) => i.location.type === 'dependency');
|
|
131
131
|
expect(reportedDeps).toHaveLength(1);
|
package/src/results.ts
CHANGED
|
@@ -12,12 +12,12 @@ export interface AnalysisOptions {
|
|
|
12
12
|
|
|
13
13
|
export function generateAnalysisResults(
|
|
14
14
|
matches: AnalyzedMatch[],
|
|
15
|
-
|
|
15
|
+
distDir: string,
|
|
16
16
|
depContext: DependencyContext | null,
|
|
17
17
|
options: AnalysisOptions = { skipBuildTooling: false, skipDependencies: false }
|
|
18
18
|
): PluginAnalysisResults {
|
|
19
19
|
const filtered = filterMatches(matches, options.skipBuildTooling);
|
|
20
|
-
const pluginJson = getPluginJson(
|
|
20
|
+
const pluginJson = getPluginJson(distDir);
|
|
21
21
|
|
|
22
22
|
// Filter out externalized dependencies
|
|
23
23
|
const filteredWithoutExternals = filtered.filter((match) => shouldIncludeDependencyMatch(match, depContext));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
// Reset module cache between tests since getPluginJson caches internally
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
const { resetPluginJsonCache } = await import('./plugin.js');
|
|
9
|
+
resetPluginJsonCache();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('getPluginJson', () => {
|
|
13
|
+
it('reads plugin.json from the given distDir directly without appending /dist', async () => {
|
|
14
|
+
const distDir = mkdtempSync(join(tmpdir(), 'react-detect-plugin-test-'));
|
|
15
|
+
const pluginJson = { id: 'my-plugin', name: 'My Plugin', type: 'app', info: { version: '2.0.0' } };
|
|
16
|
+
writeFileSync(join(distDir, 'plugin.json'), JSON.stringify(pluginJson));
|
|
17
|
+
|
|
18
|
+
const { getPluginJson } = await import('./plugin.js');
|
|
19
|
+
const result = getPluginJson(distDir);
|
|
20
|
+
|
|
21
|
+
expect(result?.id).toBe('my-plugin');
|
|
22
|
+
expect(result?.info.version).toBe('2.0.0');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('throws when plugin.json does not exist in the given distDir', async () => {
|
|
26
|
+
const distDir = mkdtempSync(join(tmpdir(), 'react-detect-plugin-test-'));
|
|
27
|
+
|
|
28
|
+
const { getPluginJson } = await import('./plugin.js');
|
|
29
|
+
|
|
30
|
+
expect(() => getPluginJson(distDir)).toThrow('plugin.json');
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/utils/plugin.ts
CHANGED
|
@@ -14,13 +14,16 @@ interface PluginJson {
|
|
|
14
14
|
|
|
15
15
|
let cachedPluginJson: PluginJson | null = null;
|
|
16
16
|
|
|
17
|
-
export function
|
|
17
|
+
export function resetPluginJsonCache() {
|
|
18
|
+
cachedPluginJson = null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getPluginJson(distDir: string) {
|
|
18
22
|
if (cachedPluginJson) {
|
|
19
23
|
return cachedPluginJson;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
const
|
|
23
|
-
const pluginJsonPath = path.join(srcPath, 'plugin.json');
|
|
26
|
+
const pluginJsonPath = path.join(distDir, 'plugin.json');
|
|
24
27
|
cachedPluginJson = readJsonFile(pluginJsonPath);
|
|
25
28
|
return cachedPluginJson;
|
|
26
29
|
}
|