projscan 2.0.0 → 2.1.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.
Files changed (114) hide show
  1. package/README.md +15 -7
  2. package/dist/cli/_shared.d.ts +1 -0
  3. package/dist/cli/_shared.js +19 -3
  4. package/dist/cli/_shared.js.map +1 -1
  5. package/dist/cli/commands/analyze.js +7 -3
  6. package/dist/cli/commands/analyze.js.map +1 -1
  7. package/dist/cli/commands/applyFix.js +2 -2
  8. package/dist/cli/commands/applyFix.js.map +1 -1
  9. package/dist/cli/commands/audit.js +2 -2
  10. package/dist/cli/commands/audit.js.map +1 -1
  11. package/dist/cli/commands/badge.js +2 -1
  12. package/dist/cli/commands/badge.js.map +1 -1
  13. package/dist/cli/commands/ci.js +3 -3
  14. package/dist/cli/commands/ci.js.map +1 -1
  15. package/dist/cli/commands/coupling.js +2 -2
  16. package/dist/cli/commands/coupling.js.map +1 -1
  17. package/dist/cli/commands/coverage.js +2 -2
  18. package/dist/cli/commands/coverage.js.map +1 -1
  19. package/dist/cli/commands/dependencies.js +2 -2
  20. package/dist/cli/commands/dependencies.js.map +1 -1
  21. package/dist/cli/commands/diagram.js +2 -2
  22. package/dist/cli/commands/diagram.js.map +1 -1
  23. package/dist/cli/commands/diff.js +2 -2
  24. package/dist/cli/commands/diff.js.map +1 -1
  25. package/dist/cli/commands/doctor.js +3 -3
  26. package/dist/cli/commands/doctor.js.map +1 -1
  27. package/dist/cli/commands/explain.js +2 -2
  28. package/dist/cli/commands/explain.js.map +1 -1
  29. package/dist/cli/commands/explainIssue.js +2 -2
  30. package/dist/cli/commands/explainIssue.js.map +1 -1
  31. package/dist/cli/commands/file.js +2 -2
  32. package/dist/cli/commands/file.js.map +1 -1
  33. package/dist/cli/commands/fix.js +2 -1
  34. package/dist/cli/commands/fix.js.map +1 -1
  35. package/dist/cli/commands/fixSuggest.js +2 -2
  36. package/dist/cli/commands/fixSuggest.js.map +1 -1
  37. package/dist/cli/commands/help.js +2 -1
  38. package/dist/cli/commands/help.js.map +1 -1
  39. package/dist/cli/commands/hotspots.js +2 -2
  40. package/dist/cli/commands/hotspots.js.map +1 -1
  41. package/dist/cli/commands/impact.js +2 -2
  42. package/dist/cli/commands/impact.js.map +1 -1
  43. package/dist/cli/commands/init.js +2 -1
  44. package/dist/cli/commands/init.js.map +1 -1
  45. package/dist/cli/commands/installHook.js +2 -1
  46. package/dist/cli/commands/installHook.js.map +1 -1
  47. package/dist/cli/commands/mcp.js +2 -1
  48. package/dist/cli/commands/mcp.js.map +1 -1
  49. package/dist/cli/commands/memory.js +5 -4
  50. package/dist/cli/commands/memory.js.map +1 -1
  51. package/dist/cli/commands/outdated.js +2 -2
  52. package/dist/cli/commands/outdated.js.map +1 -1
  53. package/dist/cli/commands/plugin.js +83 -3
  54. package/dist/cli/commands/plugin.js.map +1 -1
  55. package/dist/cli/commands/prDiff.js +2 -2
  56. package/dist/cli/commands/prDiff.js.map +1 -1
  57. package/dist/cli/commands/preflight.d.ts +1 -0
  58. package/dist/cli/commands/preflight.js +80 -0
  59. package/dist/cli/commands/preflight.js.map +1 -0
  60. package/dist/cli/commands/review.js +2 -2
  61. package/dist/cli/commands/review.js.map +1 -1
  62. package/dist/cli/commands/search.js +2 -2
  63. package/dist/cli/commands/search.js.map +1 -1
  64. package/dist/cli/commands/session.js +5 -5
  65. package/dist/cli/commands/session.js.map +1 -1
  66. package/dist/cli/commands/structure.js +2 -2
  67. package/dist/cli/commands/structure.js.map +1 -1
  68. package/dist/cli/commands/taint.js +2 -2
  69. package/dist/cli/commands/taint.js.map +1 -1
  70. package/dist/cli/commands/upgrade.js +2 -2
  71. package/dist/cli/commands/upgrade.js.map +1 -1
  72. package/dist/cli/commands/watch.js +2 -1
  73. package/dist/cli/commands/watch.js.map +1 -1
  74. package/dist/cli/commands/workspace.js +4 -2
  75. package/dist/cli/commands/workspace.js.map +1 -1
  76. package/dist/cli/commands/workspaces.js +2 -2
  77. package/dist/cli/commands/workspaces.js.map +1 -1
  78. package/dist/cli/index.js +2 -0
  79. package/dist/cli/index.js.map +1 -1
  80. package/dist/core/pluginDx.d.ts +14 -0
  81. package/dist/core/pluginDx.js +298 -0
  82. package/dist/core/pluginDx.js.map +1 -0
  83. package/dist/core/plugins.d.ts +5 -6
  84. package/dist/core/plugins.js +99 -13
  85. package/dist/core/plugins.js.map +1 -1
  86. package/dist/core/preflight.d.ts +11 -0
  87. package/dist/core/preflight.js +416 -0
  88. package/dist/core/preflight.js.map +1 -0
  89. package/dist/core/review.js +101 -1
  90. package/dist/core/review.js.map +1 -1
  91. package/dist/core/sessionResources.d.ts +6 -0
  92. package/dist/core/sessionResources.js +216 -0
  93. package/dist/core/sessionResources.js.map +1 -0
  94. package/dist/mcp/resources.js +25 -0
  95. package/dist/mcp/resources.js.map +1 -1
  96. package/dist/mcp/tools/preflight.d.ts +2 -0
  97. package/dist/mcp/tools/preflight.js +51 -0
  98. package/dist/mcp/tools/preflight.js.map +1 -0
  99. package/dist/mcp/tools.js +2 -0
  100. package/dist/mcp/tools.js.map +1 -1
  101. package/dist/reporters/htmlReporter.d.ts +2 -1
  102. package/dist/reporters/htmlReporter.js +70 -0
  103. package/dist/reporters/htmlReporter.js.map +1 -1
  104. package/dist/tool-manifest.json +33 -3
  105. package/dist/types.d.ts +137 -0
  106. package/dist/utils/banner.js +14 -4
  107. package/dist/utils/banner.js.map +1 -1
  108. package/dist/utils/fileWalker.js +4 -0
  109. package/dist/utils/fileWalker.js.map +1 -1
  110. package/dist/utils/formatSupport.d.ts +58 -0
  111. package/dist/utils/formatSupport.js +63 -0
  112. package/dist/utils/formatSupport.js.map +1 -0
  113. package/docs/PLUGIN-AUTHORING.md +30 -0
  114. package/package.json +4 -2
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { program, getRootPath, getFormat, setupLogLevel, maybeCompactBanner } from '../_shared.js';
2
+ import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
3
3
  import { addRepo, loadOrCreateWorkspace, loadWorkspace, removeRepo, saveWorkspace, } from '../../core/workspace.js';
4
4
  /**
5
5
  * `projscan workspace` (1.6+) — register and inspect cross-repo
@@ -42,7 +42,7 @@ async function runList() {
42
42
  setupLogLevel();
43
43
  maybeCompactBanner();
44
44
  const rootPath = getRootPath();
45
- const format = getFormat();
45
+ const format = assertFormatSupported('workspace list');
46
46
  try {
47
47
  const w = await loadWorkspace(rootPath);
48
48
  if (format === 'json') {
@@ -59,6 +59,7 @@ async function runList() {
59
59
  async function runAdd(repoPath, name) {
60
60
  setupLogLevel();
61
61
  maybeCompactBanner();
62
+ assertFormatSupported('workspace add');
62
63
  const rootPath = getRootPath();
63
64
  try {
64
65
  const w = await loadOrCreateWorkspace(rootPath);
@@ -75,6 +76,7 @@ async function runAdd(repoPath, name) {
75
76
  async function runRemove(pathOrName) {
76
77
  setupLogLevel();
77
78
  maybeCompactBanner();
79
+ assertFormatSupported('workspace remove');
78
80
  const rootPath = getRootPath();
79
81
  try {
80
82
  const w = await loadWorkspace(rootPath);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { program, getFormat, getRootPath, setupLogLevel, maybeCompactBanner } from '../_shared.js';
2
+ import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
3
3
  import { detectWorkspaces } from '../../core/monorepo.js';
4
4
  import { reportWorkspaces } from '../../reporters/consoleReporter.js';
5
5
  import { reportWorkspacesJson } from '../../reporters/jsonReporter.js';
@@ -12,7 +12,7 @@ export function registerWorkspaces() {
12
12
  setupLogLevel();
13
13
  maybeCompactBanner();
14
14
  const rootPath = getRootPath();
15
- const format = getFormat();
15
+ const format = assertFormatSupported('workspaces');
16
16
  try {
17
17
  const info = await detectWorkspaces(rootPath);
18
18
  switch (format) {
@@ -1 +1 @@
1
- {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/cli/commands/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,MAAM,UAAU,kBAAkB;IAChC,OAAO;SACJ,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,sGAAsG,CAAC;SACnH,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC9C,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,MAAM;oBACT,oBAAoB,CAAC,IAAI,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,UAAU;oBACb,wBAAwB,CAAC,IAAI,CAAC,CAAC;oBAC/B,MAAM;gBACR;oBACE,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/cli/commands/workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,MAAM,UAAU,kBAAkB;IAChC,OAAO;SACJ,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,sGAAsG,CAAC;SACnH,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC9C,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,MAAM;oBACT,oBAAoB,CAAC,IAAI,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,UAAU;oBACb,wBAAwB,CAAC,IAAI,CAAC,CAAC;oBAC/B,MAAM;gBACR;oBACE,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
package/dist/cli/index.js CHANGED
@@ -34,6 +34,7 @@ import { registerInstallHook } from './commands/installHook.js';
34
34
  import { registerTaint } from './commands/taint.js';
35
35
  import { registerBadge } from './commands/badge.js';
36
36
  import { registerPlugin } from './commands/plugin.js';
37
+ import { registerPreflight } from './commands/preflight.js';
37
38
  import { registerHelp } from './commands/help.js';
38
39
  registerAnalyze();
39
40
  registerDoctor();
@@ -69,6 +70,7 @@ registerInstallHook();
69
70
  registerTaint();
70
71
  registerBadge();
71
72
  registerPlugin();
73
+ registerPreflight();
72
74
  registerHelp();
73
75
  program.parse();
74
76
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { PluginTestResult } from '../types.js';
2
+ export interface InitPluginOptions {
3
+ kind?: 'analyzer' | 'reporter';
4
+ name?: string;
5
+ }
6
+ export interface InitPluginResult {
7
+ manifestPath: string;
8
+ modulePath: string;
9
+ }
10
+ export interface TestPluginOptions {
11
+ fixtureRoot?: string;
12
+ }
13
+ export declare function initPlugin(rootPath: string, options?: InitPluginOptions): Promise<InitPluginResult>;
14
+ export declare function testPlugin(manifestPath: string, options?: TestPluginOptions): Promise<PluginTestResult>;
@@ -0,0 +1,298 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { scanRepository } from './repositoryScanner.js';
5
+ import { PLUGIN_DIR, PLUGIN_MANIFEST_EXT, PLUGIN_REPORTER_COMMANDS, readPluginManifestFile, } from './plugins.js';
6
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
7
+ export async function initPlugin(rootPath, options = {}) {
8
+ const kind = options.kind ?? 'analyzer';
9
+ const name = options.name ?? (kind === 'analyzer' ? 'policy' : 'team-report');
10
+ const fileName = safePluginFileName(name);
11
+ const pluginDir = path.join(rootPath, PLUGIN_DIR);
12
+ const manifestPath = path.join(pluginDir, `${fileName}${PLUGIN_MANIFEST_EXT}`);
13
+ const modulePath = path.join(pluginDir, `${fileName}.mjs`);
14
+ await fs.mkdir(pluginDir, { recursive: true });
15
+ await assertDoesNotExist(manifestPath);
16
+ await assertDoesNotExist(modulePath);
17
+ const manifest = kind === 'analyzer'
18
+ ? analyzerManifest(name, `./${fileName}.mjs`)
19
+ : reporterManifest(name, `./${fileName}.mjs`);
20
+ const moduleSource = kind === 'analyzer' ? analyzerTemplate(name) : reporterTemplate(name);
21
+ await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { flag: 'wx' });
22
+ await fs.writeFile(modulePath, moduleSource, { flag: 'wx' });
23
+ return { manifestPath, modulePath };
24
+ }
25
+ export async function testPlugin(manifestPath, options = {}) {
26
+ const resolvedManifestPath = path.resolve(manifestPath);
27
+ const manifestResult = await readPluginManifestFile(resolvedManifestPath);
28
+ if (!manifestResult.ok) {
29
+ return failResult(resolvedManifestPath, {
30
+ code: manifestResult.diagnostic.code,
31
+ severity: 'error',
32
+ message: manifestResult.diagnostic.message,
33
+ });
34
+ }
35
+ const manifest = manifestResult.manifest;
36
+ const modulePath = path.resolve(path.dirname(resolvedManifestPath), manifest.module);
37
+ let exportsObj;
38
+ try {
39
+ const mod = await importPluginModule(modulePath);
40
+ exportsObj = (mod.default ?? mod);
41
+ }
42
+ catch (err) {
43
+ return failResult(resolvedManifestPath, {
44
+ code: 'module-load-error',
45
+ severity: 'error',
46
+ message: `plugin module failed to load: ${err instanceof Error ? err.message : String(err)}`,
47
+ });
48
+ }
49
+ return manifest.kind === 'analyzer'
50
+ ? testAnalyzerPlugin(resolvedManifestPath, manifest, exportsObj, options)
51
+ : testReporterPlugin(resolvedManifestPath, manifest, exportsObj, options);
52
+ }
53
+ function analyzerManifest(name, modulePath) {
54
+ return {
55
+ schemaVersion: 1,
56
+ name,
57
+ kind: 'analyzer',
58
+ module: modulePath,
59
+ category: 'policy',
60
+ description: 'Example local policy analyzer.',
61
+ };
62
+ }
63
+ function reporterManifest(name, modulePath) {
64
+ return {
65
+ schemaVersion: 1,
66
+ name,
67
+ kind: 'reporter',
68
+ module: modulePath,
69
+ commands: [...PLUGIN_REPORTER_COMMANDS],
70
+ description: 'Example local reporter.',
71
+ };
72
+ }
73
+ function analyzerTemplate(name) {
74
+ return `import { readFile } from 'node:fs/promises';
75
+
76
+ export default {
77
+ async check(_rootPath, files) {
78
+ const issues = [];
79
+ for (const file of files) {
80
+ if (file.relativePath.startsWith('.projscan-plugins/')) continue;
81
+ let text = '';
82
+ try {
83
+ text = await readFile(file.absolutePath, 'utf-8');
84
+ } catch {
85
+ continue;
86
+ }
87
+ if (!text.includes('PROJSCAN_PLUGIN_EXAMPLE')) continue;
88
+ issues.push({
89
+ id: 'example-marker',
90
+ title: '${escapeForSingleQuotedJs(name)} example marker found',
91
+ description: 'Remove PROJSCAN_PLUGIN_EXAMPLE before committing production code.',
92
+ severity: 'info',
93
+ category: 'policy',
94
+ fixAvailable: false,
95
+ locations: [{ file: file.relativePath, line: 1 }],
96
+ });
97
+ }
98
+ return issues;
99
+ },
100
+ };
101
+ `;
102
+ }
103
+ function reporterTemplate(name) {
104
+ return `export default {
105
+ render({ command, payload }) {
106
+ const issueCount = Array.isArray(payload?.issues)
107
+ ? payload.issues.length
108
+ : Array.isArray(payload?.ci?.issues)
109
+ ? payload.ci.issues.length
110
+ : 0;
111
+ const score = payload?.health?.score ?? payload?.ci?.score ?? 'n/a';
112
+ return '${escapeForSingleQuotedJs(name)}:' + command + ': score=' + score + ', issues=' + issueCount;
113
+ },
114
+ };
115
+ `;
116
+ }
117
+ async function testAnalyzerPlugin(manifestPath, manifest, exportsObj, options) {
118
+ if (typeof exportsObj.check !== 'function') {
119
+ return failResult(manifestPath, {
120
+ code: 'invalid-analyzer-export',
121
+ severity: 'error',
122
+ message: `analyzer plugin "${manifest.name}" missing required export "check"`,
123
+ });
124
+ }
125
+ const rootPath = options.fixtureRoot ?? path.dirname(path.dirname(manifestPath));
126
+ const scan = await scanRepository(rootPath);
127
+ let rawIssues;
128
+ try {
129
+ rawIssues = await exportsObj.check(rootPath, scan.files);
130
+ }
131
+ catch (err) {
132
+ return failResult(manifestPath, {
133
+ code: 'analyzer-runtime-error',
134
+ severity: 'error',
135
+ message: `analyzer plugin "${manifest.name}" threw: ${err instanceof Error ? err.message : String(err)}`,
136
+ });
137
+ }
138
+ if (!Array.isArray(rawIssues)) {
139
+ return failResult(manifestPath, {
140
+ code: 'invalid-analyzer-result',
141
+ severity: 'error',
142
+ message: `analyzer plugin "${manifest.name}" returned ${typeof rawIssues}; expected Issue[]`,
143
+ });
144
+ }
145
+ const issues = rawIssues;
146
+ const malformed = issues.find((issue) => !isIssueShape(issue));
147
+ if (malformed) {
148
+ return failResult(manifestPath, {
149
+ code: 'invalid-analyzer-issue',
150
+ severity: 'error',
151
+ message: `analyzer plugin "${manifest.name}" returned a malformed issue`,
152
+ });
153
+ }
154
+ return {
155
+ schemaVersion: 1,
156
+ manifestPath,
157
+ ok: true,
158
+ diagnostics: [],
159
+ analyzer: { issues },
160
+ };
161
+ }
162
+ async function testReporterPlugin(manifestPath, manifest, exportsObj, options) {
163
+ if (typeof exportsObj.render !== 'function') {
164
+ return failResult(manifestPath, {
165
+ code: 'invalid-reporter-export',
166
+ severity: 'error',
167
+ message: `reporter plugin "${manifest.name}" missing required export "render"`,
168
+ });
169
+ }
170
+ const rootPath = options.fixtureRoot ?? path.dirname(path.dirname(manifestPath));
171
+ const outputs = [];
172
+ for (const command of manifest.commands) {
173
+ const context = {
174
+ command,
175
+ rootPath,
176
+ manifest,
177
+ payload: sampleReporterPayload(command),
178
+ };
179
+ let text;
180
+ try {
181
+ text = await exportsObj.render(context);
182
+ }
183
+ catch (err) {
184
+ return failResult(manifestPath, {
185
+ code: 'reporter-render-error',
186
+ severity: 'error',
187
+ message: `reporter plugin "${manifest.name}" threw for ${command}: ${err instanceof Error ? err.message : String(err)}`,
188
+ });
189
+ }
190
+ if (typeof text !== 'string') {
191
+ return failResult(manifestPath, {
192
+ code: 'reporter-render-error',
193
+ severity: 'error',
194
+ message: `reporter plugin "${manifest.name}" returned ${typeof text}; expected string`,
195
+ });
196
+ }
197
+ outputs.push({ command, text });
198
+ }
199
+ return {
200
+ schemaVersion: 1,
201
+ manifestPath,
202
+ ok: true,
203
+ diagnostics: [],
204
+ reporter: { outputs },
205
+ };
206
+ }
207
+ function sampleReporterPayload(command) {
208
+ switch (command) {
209
+ case 'ci':
210
+ return { ci: { score: 100, grade: 'A', issues: [] } };
211
+ case 'analyze':
212
+ return { projectName: 'fixture', issues: [] };
213
+ case 'doctor':
214
+ default:
215
+ return { health: { score: 100, grade: 'A', errors: 0, warnings: 0, infos: 0 }, issues: [] };
216
+ }
217
+ }
218
+ function failResult(manifestPath, diagnostic) {
219
+ return {
220
+ schemaVersion: 1,
221
+ manifestPath,
222
+ ok: false,
223
+ diagnostics: [diagnostic],
224
+ };
225
+ }
226
+ async function assertDoesNotExist(filePath) {
227
+ try {
228
+ await fs.access(filePath);
229
+ throw new Error(`${filePath} already exists`);
230
+ }
231
+ catch (err) {
232
+ if (err instanceof Error && err.message.includes('already exists'))
233
+ throw err;
234
+ }
235
+ }
236
+ function safePluginFileName(name) {
237
+ const safe = name
238
+ .trim()
239
+ .toLowerCase()
240
+ .replace(/[^a-z0-9._-]+/g, '-')
241
+ .replace(/^-+|-+$/g, '');
242
+ return safe || 'plugin';
243
+ }
244
+ function isIssueShape(value) {
245
+ if (!value || typeof value !== 'object')
246
+ return false;
247
+ const issue = value;
248
+ return (typeof issue.id === 'string' &&
249
+ typeof issue.title === 'string' &&
250
+ typeof issue.description === 'string' &&
251
+ (issue.severity === 'info' || issue.severity === 'warning' || issue.severity === 'error') &&
252
+ typeof issue.category === 'string' &&
253
+ typeof issue.fixAvailable === 'boolean');
254
+ }
255
+ function importPluginModule(modulePath) {
256
+ return dynamicImport(pathToFileURL(modulePath).href).catch(async (err) => {
257
+ if (!(err instanceof TypeError) || !err.message.includes('dynamic import callback was not specified')) {
258
+ throw err;
259
+ }
260
+ return importPluginModuleFromSource(modulePath);
261
+ });
262
+ }
263
+ async function importPluginModuleFromSource(modulePath) {
264
+ const originalSource = await fs.readFile(modulePath, 'utf-8');
265
+ const { source, bindings } = stripSupportedImports(originalSource);
266
+ const defaultMatch = source.match(/^\s*export\s+default\s+([\s\S]*?)\s*;?\s*$/);
267
+ if (defaultMatch) {
268
+ const expression = defaultMatch[1].trim().replace(/;$/, '');
269
+ return {
270
+ default: new Function(...Object.keys(bindings), `return (${expression});`)(...Object.values(bindings)),
271
+ };
272
+ }
273
+ const names = [];
274
+ let transformed = source.replace(/\bexport\s+(async\s+function|function)\s+([A-Za-z_$][\w$]*)/g, (_m, kind, name) => {
275
+ names.push(String(name));
276
+ return `${kind} ${name}`;
277
+ });
278
+ transformed = transformed.replace(/\bexport\s+const\s+([A-Za-z_$][\w$]*)\s*=/g, (_m, name) => {
279
+ names.push(String(name));
280
+ return `const ${name} =`;
281
+ });
282
+ if (names.length === 0) {
283
+ throw new Error('unsupported module syntax in Vitest VM fallback');
284
+ }
285
+ return new Function(`${transformed}\nreturn { ${names.join(', ')} };`)();
286
+ }
287
+ function stripSupportedImports(source) {
288
+ const bindings = {};
289
+ const stripped = source.replace(/^\s*import\s+\{\s*readFile\s*\}\s+from\s+['"]node:fs\/promises['"];\s*/m, () => {
290
+ bindings.readFile = fs.readFile;
291
+ return '';
292
+ });
293
+ return { source: stripped, bindings };
294
+ }
295
+ function escapeForSingleQuotedJs(value) {
296
+ return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
297
+ }
298
+ //# sourceMappingURL=pluginDx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginDx.js","sourceRoot":"","sources":["../../src/core/pluginDx.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,11 @@
1
1
  import type { FileEntry, Issue } from '../types.js';
2
2
  /**
3
- * Plugin API preview (1.10+).
3
+ * Stable local plugin API (2.0+).
4
4
  *
5
- * Gated behind PROJSCAN_PLUGINS_PREVIEW=1. The shape declared here is what
6
- * 2.0 will commit to; until 2.0 ships, the schema, the discovery path,
7
- * and the dispatch lifecycle may change between minors. Without the env
8
- * flag, `loadPlugins()` returns [] and projscan ignores any manifests on
9
- * disk — so a half-written plugin can't accidentally light up.
5
+ * Gated behind PROJSCAN_PLUGINS_PREVIEW=1 as an explicit local-code trust
6
+ * boundary. Without the env flag, `loadPlugins()` returns [] and projscan
7
+ * ignores manifests on disk, so a half-written plugin cannot accidentally
8
+ * affect a scan.
10
9
  *
11
10
  * Goal for 2.0: third parties write `.projscan-plugin.json` declaring an
12
11
  * analyzer that produces Issue[], projscan dispatches to it during
@@ -2,13 +2,12 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  /**
5
- * Plugin API preview (1.10+).
5
+ * Stable local plugin API (2.0+).
6
6
  *
7
- * Gated behind PROJSCAN_PLUGINS_PREVIEW=1. The shape declared here is what
8
- * 2.0 will commit to; until 2.0 ships, the schema, the discovery path,
9
- * and the dispatch lifecycle may change between minors. Without the env
10
- * flag, `loadPlugins()` returns [] and projscan ignores any manifests on
11
- * disk — so a half-written plugin can't accidentally light up.
7
+ * Gated behind PROJSCAN_PLUGINS_PREVIEW=1 as an explicit local-code trust
8
+ * boundary. Without the env flag, `loadPlugins()` returns [] and projscan
9
+ * ignores manifests on disk, so a half-written plugin cannot accidentally
10
+ * affect a scan.
12
11
  *
13
12
  * Goal for 2.0: third parties write `.projscan-plugin.json` declaring an
14
13
  * analyzer that produces Issue[], projscan dispatches to it during
@@ -20,6 +19,8 @@ export const PLUGIN_PREVIEW_FLAG = 'PROJSCAN_PLUGINS_PREVIEW';
20
19
  export const PLUGIN_SCHEMA_VERSION = 1;
21
20
  export const PLUGIN_DIR = '.projscan-plugins';
22
21
  export const PLUGIN_MANIFEST_EXT = '.projscan-plugin.json';
22
+ // Keep arbitrary plugin file URLs out of Vite/Vitest's static import transform.
23
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
23
24
  export const PLUGIN_REPORTER_COMMANDS = ['doctor', 'analyze', 'ci'];
24
25
  export function pluginsEnabled() {
25
26
  const v = process.env[PLUGIN_PREVIEW_FLAG];
@@ -116,10 +117,11 @@ export async function loadPlugins(rootPath) {
116
117
  continue;
117
118
  const modulePath = path.resolve(path.dirname(entry.manifestPath), entry.manifest.module);
118
119
  try {
119
- const mod = (await import(pathToFileURL(modulePath).href));
120
+ await assertPluginModuleReadable(entry.manifest.module, modulePath);
121
+ const mod = await importPluginModule(modulePath);
120
122
  const exportsObj = (mod.default ?? mod);
121
123
  if (typeof exportsObj.check !== 'function') {
122
- process.stderr.write(`[projscan] plugin "${entry.manifest.name}" missing required export "check"; skipped.\n`);
124
+ process.stderr.write(`[projscan] plugin "${entry.manifest.name}" missing required export "check"; export default { check(rootPath, files) { ... } } or export a named check function. skipped.\n`);
123
125
  continue;
124
126
  }
125
127
  loaded.push({
@@ -130,7 +132,8 @@ export async function loadPlugins(rootPath) {
130
132
  });
131
133
  }
132
134
  catch (err) {
133
- process.stderr.write(`[projscan] plugin "${entry.manifest.name}" failed to load: ${err instanceof Error ? err.message : String(err)}. skipped.\n`);
135
+ const detail = describePluginModuleLoadError(err, entry.manifest.module, modulePath, 'manifest');
136
+ process.stderr.write(`[projscan] plugin "${entry.manifest.name}" failed to load: ${detail.message}${detail.hint ? ` ${detail.hint}` : ''}. skipped.\n`);
134
137
  }
135
138
  }
136
139
  return loaded;
@@ -185,13 +188,14 @@ export async function renderReporterPlugin(plugin, context) {
185
188
  async function loadReporterPlugin(manifest, manifestPath) {
186
189
  const modulePath = path.resolve(path.dirname(manifestPath), manifest.module);
187
190
  try {
188
- const mod = (await import(pathToFileURL(modulePath).href));
191
+ await assertPluginModuleReadable(manifest.module, modulePath);
192
+ const mod = await importPluginModule(modulePath);
189
193
  const exportsObj = (mod.default ?? mod);
190
194
  if (typeof exportsObj.render !== 'function') {
191
195
  return pluginRuntimeFail({
192
196
  code: 'invalid-reporter-export',
193
197
  message: `reporter plugin "${manifest.name}" missing required export "render"`,
194
- hint: 'Export `render(context)` as the default export or a named export.',
198
+ hint: 'Use export default { render(context) { ... } } or export a named render function.',
195
199
  });
196
200
  }
197
201
  return {
@@ -205,13 +209,95 @@ async function loadReporterPlugin(manifest, manifestPath) {
205
209
  };
206
210
  }
207
211
  catch (err) {
212
+ const detail = describePluginModuleLoadError(err, manifest.module, modulePath, 'reporter manifest');
208
213
  return pluginRuntimeFail({
209
214
  code: 'reporter-load-error',
210
- message: `reporter plugin "${manifest.name}" failed to load: ${formatError(err)}`,
211
- hint: 'Check the reporter module path and module syntax.',
215
+ message: `reporter plugin "${manifest.name}" failed to load: ${detail.message}`,
216
+ hint: detail.hint ?? 'Check the reporter module path and module syntax.',
212
217
  });
213
218
  }
214
219
  }
220
+ class PluginModuleMissingError extends Error {
221
+ manifestModule;
222
+ modulePath;
223
+ constructor(manifestModule, modulePath) {
224
+ super(`module "${manifestModule}" was not found at ${modulePath}`);
225
+ this.manifestModule = manifestModule;
226
+ this.modulePath = modulePath;
227
+ }
228
+ }
229
+ class PluginModuleReadError extends Error {
230
+ manifestModule;
231
+ modulePath;
232
+ constructor(manifestModule, modulePath, err) {
233
+ super(`module "${manifestModule}" could not be read at ${modulePath}: ${formatError(err)}`);
234
+ this.manifestModule = manifestModule;
235
+ this.modulePath = modulePath;
236
+ }
237
+ }
238
+ async function assertPluginModuleReadable(manifestModule, modulePath) {
239
+ try {
240
+ await fs.access(modulePath);
241
+ }
242
+ catch (err) {
243
+ const code = typeof err === 'object' && err !== null && 'code' in err ? String(err.code) : '';
244
+ if (code === 'ENOENT')
245
+ throw new PluginModuleMissingError(manifestModule, modulePath);
246
+ throw new PluginModuleReadError(manifestModule, modulePath, err);
247
+ }
248
+ }
249
+ function describePluginModuleLoadError(err, manifestModule, modulePath, manifestLabel) {
250
+ if (err instanceof PluginModuleMissingError) {
251
+ return {
252
+ message: err.message,
253
+ hint: `Check the ${manifestLabel} "module" path.`,
254
+ };
255
+ }
256
+ if (err instanceof PluginModuleReadError) {
257
+ return {
258
+ message: err.message,
259
+ hint: `Check file permissions for the ${manifestLabel} "module" path.`,
260
+ };
261
+ }
262
+ if (err instanceof SyntaxError) {
263
+ return {
264
+ message: `syntax error in module "${manifestModule}": ${formatError(err)}`,
265
+ hint: `Run node "${modulePath}" to reproduce the syntax error.`,
266
+ };
267
+ }
268
+ return { message: formatError(err) };
269
+ }
270
+ function importPluginModule(modulePath) {
271
+ return dynamicImport(pathToFileURL(modulePath).href).catch(async (err) => {
272
+ if (!isMissingDynamicImportCallback(err))
273
+ throw err;
274
+ return importPluginModuleFromSource(modulePath);
275
+ });
276
+ }
277
+ function isMissingDynamicImportCallback(err) {
278
+ return err instanceof TypeError && err.message.includes('dynamic import callback was not specified');
279
+ }
280
+ async function importPluginModuleFromSource(modulePath) {
281
+ const source = await fs.readFile(modulePath, 'utf-8');
282
+ const defaultMatch = source.match(/^\s*export\s+default\s+([\s\S]*?)\s*;?\s*$/);
283
+ if (defaultMatch) {
284
+ const expression = defaultMatch[1].trim().replace(/;$/, '');
285
+ return { default: new Function(`return (${expression});`)() };
286
+ }
287
+ const names = [];
288
+ let transformed = source.replace(/\bexport\s+(async\s+function|function)\s+([A-Za-z_$][\w$]*)/g, (_m, kind, name) => {
289
+ names.push(String(name));
290
+ return `${kind} ${name}`;
291
+ });
292
+ transformed = transformed.replace(/\bexport\s+const\s+([A-Za-z_$][\w$]*)\s*=/g, (_m, name) => {
293
+ names.push(String(name));
294
+ return `const ${name} =`;
295
+ });
296
+ if (names.length === 0) {
297
+ throw new Error('unsupported module syntax in Vitest VM fallback');
298
+ }
299
+ return new Function(`${transformed}\nreturn { ${names.join(', ')} };`)();
300
+ }
215
301
  /**
216
302
  * Run every loaded analyzer plugin against `files`. Issues that don't pass
217
303
  * a tight shape check are dropped so a malformed plugin can't poison the