phpstan-mcp-mx 0.18.0 → 0.20.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.
@@ -0,0 +1,101 @@
1
+ import { contains } from './project.js';
2
+ import { displayPath } from './report.js';
3
+ /** Hoeveel groepen een lijst (opgelost of nieuw) hoogstens noemt; de rest als aantal. */
4
+ export const MAX_COMPARED_ENTRIES = 20;
5
+ /** Hoeveel bestanden de melding over niet-vergeleken bevindingen hoogstens noemt. */
6
+ const MAX_NOT_COMPARED_FILES = 10;
7
+ /**
8
+ * Legt een analyse naast een eerdere en telt wat er opgelost, nieuw en nog open is.
9
+ *
10
+ * Gekoppeld op bestand, identifier en melding — niet op het regelnummer: een reparatie
11
+ * verschuift regels, en dan zou elke bevinding eronder als opgelost én nieuw tellen.
12
+ * Om die reden negeert PHPStans eigen baseline de regel ook. Nagemeten: drie lege regels
13
+ * bovenin het drukste bestand van `lasouris-lease-shared` verschoven alle elf bevindingen
14
+ * en veranderden geen enkele melding.
15
+ *
16
+ * Een melding die zelf een regelnummer bevat (een anonieme klasse heet
17
+ * `class@anonymous/pad.php:12`) telt na zo'n verschuiving wel als opgelost én nieuw. Geen
18
+ * eigen normalisering daarvoor: die zou bij elke PHPStan-release verder afdrijven.
19
+ *
20
+ * Alleen bestanden die beide analyses dekten doen mee. Bij `analyse_changed` volgt de
21
+ * lijst uit git: wie tijdens een reparatie een tweede bestand aanraakt, krijgt dat bestand
22
+ * erbij, en diens bestaande fouten zouden anders als nieuw tellen — een antwoord dat klopt
23
+ * maar misleidt.
24
+ */
25
+ export function compareRuns(project, before, after, meta) {
26
+ const sameScope = equalScopes(before.scope, after.scope);
27
+ const inBoth = (file) => sameScope || (inScope(file, before.scope) && inScope(file, after.scope));
28
+ const groups = new Map();
29
+ const notComparedFiles = new Set();
30
+ let notCompared = 0;
31
+ const add = (finding, side) => {
32
+ if (!inBoth(finding.file)) {
33
+ notCompared++;
34
+ notComparedFiles.add(displayPath(project, finding.file));
35
+ return;
36
+ }
37
+ const key = JSON.stringify([finding.file, finding.identifier, finding.message]);
38
+ const group = groups.get(key) ?? { finding, before: [], after: [] };
39
+ group[side].push(finding.line);
40
+ groups.set(key, group);
41
+ };
42
+ for (const finding of before.result.findings)
43
+ add(finding, 'before');
44
+ for (const finding of after.result.findings)
45
+ add(finding, 'after');
46
+ const fixed = [];
47
+ const added = [];
48
+ let remaining = 0;
49
+ for (const group of groups.values()) {
50
+ remaining += Math.min(group.before.length, group.after.length);
51
+ const entry = (count, lines) => ({
52
+ file: displayPath(project, group.finding.file),
53
+ identifier: group.finding.identifier,
54
+ message: group.finding.message,
55
+ count,
56
+ lines: [...lines].sort(byLine),
57
+ });
58
+ if (group.before.length > group.after.length)
59
+ fixed.push(entry(group.before.length - group.after.length, group.before));
60
+ if (group.after.length > group.before.length)
61
+ added.push(entry(group.after.length - group.before.length, group.after));
62
+ }
63
+ fixed.sort(byPlace);
64
+ added.sort(byPlace);
65
+ return {
66
+ comparedTo: meta.comparedTo,
67
+ ageSeconds: meta.ageSeconds,
68
+ fixed: sum(fixed),
69
+ new: sum(added),
70
+ remaining,
71
+ fixedFindings: fixed.slice(0, MAX_COMPARED_ENTRIES),
72
+ newFindings: added.slice(0, MAX_COMPARED_ENTRIES),
73
+ omitted: {
74
+ fixed: Math.max(fixed.length - MAX_COMPARED_ENTRIES, 0),
75
+ new: Math.max(added.length - MAX_COMPARED_ENTRIES, 0),
76
+ },
77
+ notCompared: { findings: notCompared, files: [...notComparedFiles].sort().slice(0, MAX_NOT_COMPARED_FILES) },
78
+ };
79
+ }
80
+ function equalScopes(a, b) {
81
+ if (a === null || b === null)
82
+ return a === b;
83
+ if (a.length !== b.length)
84
+ return false;
85
+ const set = new Set(a);
86
+ return b.every((path) => set.has(path));
87
+ }
88
+ function inScope(file, scope) {
89
+ return scope === null || scope.some((path) => contains(path, file));
90
+ }
91
+ function sum(entries) {
92
+ return entries.reduce((total, entry) => total + entry.count, 0);
93
+ }
94
+ /** Een bevinding zonder regel achteraan: die hoort bij het bestand, niet bij een plek. */
95
+ function byLine(a, b) {
96
+ return (a ?? Number.MAX_SAFE_INTEGER) - (b ?? Number.MAX_SAFE_INTEGER);
97
+ }
98
+ function byPlace(a, b) {
99
+ return a.file.localeCompare(b.file) || byLine(a.lines[0] ?? null, b.lines[0] ?? null) || a.message.localeCompare(b.message);
100
+ }
101
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /** Een Composer-script dat PHPStan start, met waarin het afwijkt van deze server. */
2
+ export interface ComposerScript {
3
+ readonly name: string;
4
+ /** De aanroep zelf, vanaf de PHPStan-binary. */
5
+ readonly command: string;
6
+ /** Per verschil één zin, met wat je meegeeft om hetzelfde te analyseren. Leeg: geen verschil. */
7
+ readonly differences: readonly string[];
8
+ }
9
+ /** Hoe deze server PHPStan aanroept; zie `serverInvocation` in phpstan.ts. */
10
+ export interface ServerInvocation {
11
+ /** Relatief aan de projectroot of absoluut; null als PHPStan hem zelf zoekt en er geen is. */
12
+ readonly config: string | null;
13
+ readonly memoryLimit: string | null;
14
+ }
15
+ /** Wat een aanroep van `phpstan analyse` meegeeft, voor zover het de uitslag verandert. */
16
+ export interface Invocation {
17
+ readonly configuration: string | null;
18
+ readonly level: string | null;
19
+ readonly memoryLimit: string | null;
20
+ readonly autoloadFile: string | null;
21
+ readonly paths: readonly string[];
22
+ /** `--generate-baseline` of `--fix`: dan is het geen analyse om mee te vergelijken. */
23
+ readonly writes: boolean;
24
+ }
25
+ /**
26
+ * Leest de Composer-scripts van een project en geeft die die PHPStan analyseren, met hoe ze
27
+ * afwijken van deze server.
28
+ *
29
+ * Nagemeten op de echte projecten: het script heet nergens `phpstan` maar `code:analyze`
30
+ * en `code:analyze:strict`, is een lijst met `Composer\Config::disableProcessTimeout`
31
+ * ervoor, en schrijft `--memory-limit 1G` met een spatie. Herkennen gaat dus op het
32
+ * commando, niet op de naam. Een script via `make` of via een ander script (`@naam`) is
33
+ * niet te lezen zonder het uit te voeren, en dat gebeurt hier nooit — die vallen weg.
34
+ */
35
+ export declare function readComposerScripts(project: string, server: ServerInvocation): {
36
+ composerScripts: ComposerScript[] | null;
37
+ composerScriptsProblem: string | null;
38
+ };
39
+ /** Knipt een commando op `&&`, `||`, `;` en `|` buiten quotes: elk deel is een eigen aanroep. */
40
+ export declare function splitCommands(command: string): string[];
41
+ /** Splitst één aanroep in woorden, met quotes zoals een shell ze leest (zonder expansie). */
42
+ export declare function tokenize(segment: string): string[];
43
+ /** Leest de argumenten van `phpstan analyse`, voor zover ze de uitslag veranderen. */
44
+ export declare function parseInvocation(args: readonly string[]): Invocation;
45
+ /**
46
+ * Waarin een aanroep afwijkt van deze server, als zinnen met de uitweg erbij. Alleen wat de
47
+ * uitslag verandert; `--no-progress` of `--error-format` zegt niets over wat er gevonden wordt.
48
+ */
49
+ export declare function compareWithServer(project: string, invocation: Invocation, server: ServerInvocation): string[];
50
+ /** De scripts als tekst, onder de rest van de diagnose. Leeg als er geen enkel is. */
51
+ export declare function renderComposerScripts(scripts: readonly ComposerScript[] | null, problem: string | null): string;
52
+ /** Een framework in het project waarvan de PHPStan-extensie niet geladen is. */
53
+ export interface MissingExtension {
54
+ readonly framework: string;
55
+ readonly extension: string;
56
+ /** Of de extensie wél in composer.lock staat: dan is ze geïnstalleerd maar niet ingesloten. */
57
+ readonly installed: boolean;
58
+ readonly sees: string;
59
+ }
60
+ /**
61
+ * Welke frameworks uit `composer.lock` hun extensie missen. Wat geladen is, komt uit PHPStans
62
+ * eigen diagnose ("Included configs from Composer packages"): die noemt ook een extensie die
63
+ * zonder extension-installer met de hand in de config staat. Is de uitvoer niet te lezen —
64
+ * een andere PHPStan-versie — dan null: niet te zeggen, en een vals alarm is erger dan stilte.
65
+ */
66
+ export declare function findMissingExtensions(project: string, diagnoseOutput: string): MissingExtension[] | null;
67
+ /** De ontbrekende extensies als tekst; leeg als er niets ontbreekt of het niet te zeggen is. */
68
+ export declare function renderMissingExtensions(missing: readonly MissingExtension[] | null): string;
@@ -0,0 +1,313 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { isAbsolute, join, normalize } from 'node:path';
3
+ /** Opties die een waarde nemen, met de naam waaronder die telt. */
4
+ const VALUE_OPTIONS = {
5
+ '-c': 'configuration',
6
+ '--configuration': 'configuration',
7
+ '-l': 'level',
8
+ '--level': 'level',
9
+ '--memory-limit': 'memoryLimit',
10
+ '-a': 'autoloadFile',
11
+ '--autoload-file': 'autoloadFile',
12
+ '--error-format': null,
13
+ '--tmp-file': null,
14
+ '--instead-of': null,
15
+ };
16
+ /** De configs die PHPStan zelf zoekt als er geen `--configuration` is, in zijn volgorde. */
17
+ const DISCOVERED = ['phpstan.neon', 'phpstan.neon.dist', 'phpstan.dist.neon'];
18
+ /**
19
+ * Leest de Composer-scripts van een project en geeft die die PHPStan analyseren, met hoe ze
20
+ * afwijken van deze server.
21
+ *
22
+ * Nagemeten op de echte projecten: het script heet nergens `phpstan` maar `code:analyze`
23
+ * en `code:analyze:strict`, is een lijst met `Composer\Config::disableProcessTimeout`
24
+ * ervoor, en schrijft `--memory-limit 1G` met een spatie. Herkennen gaat dus op het
25
+ * commando, niet op de naam. Een script via `make` of via een ander script (`@naam`) is
26
+ * niet te lezen zonder het uit te voeren, en dat gebeurt hier nooit — die vallen weg.
27
+ */
28
+ export function readComposerScripts(project, server) {
29
+ let scripts;
30
+ try {
31
+ scripts = JSON.parse(readFileSync(join(project, 'composer.json'), 'utf8')).scripts;
32
+ }
33
+ catch (error) {
34
+ return {
35
+ composerScripts: null,
36
+ composerScriptsProblem: `composer.json could not be read: ${error instanceof Error ? error.message : String(error)}`,
37
+ };
38
+ }
39
+ if (typeof scripts !== 'object' || scripts === null)
40
+ return { composerScripts: [], composerScriptsProblem: null };
41
+ const found = [];
42
+ for (const [name, value] of Object.entries(scripts)) {
43
+ const commands = typeof value === 'string' ? [value] : Array.isArray(value) ? value.filter((v) => typeof v === 'string') : [];
44
+ for (const command of commands) {
45
+ for (const segment of splitCommands(command)) {
46
+ const tokens = tokenize(segment);
47
+ const start = analyseStart(tokens);
48
+ if (start === null)
49
+ continue;
50
+ const invocation = parseInvocation(tokens.slice(start.args));
51
+ found.push({
52
+ name,
53
+ command: tokens.slice(start.binary).join(' '),
54
+ differences: compareWithServer(project, invocation, server),
55
+ });
56
+ }
57
+ }
58
+ }
59
+ return { composerScripts: found, composerScriptsProblem: null };
60
+ }
61
+ /** Knipt een commando op `&&`, `||`, `;` en `|` buiten quotes: elk deel is een eigen aanroep. */
62
+ export function splitCommands(command) {
63
+ const parts = [];
64
+ let current = '';
65
+ let quote = null;
66
+ for (let i = 0; i < command.length; i++) {
67
+ const char = command[i] ?? '';
68
+ if (quote !== null) {
69
+ if (char === quote)
70
+ quote = null;
71
+ current += char;
72
+ continue;
73
+ }
74
+ if (char === '"' || char === "'") {
75
+ quote = char;
76
+ current += char;
77
+ continue;
78
+ }
79
+ if (char === ';' || char === '|' || (char === '&' && command[i + 1] === '&')) {
80
+ parts.push(current);
81
+ current = '';
82
+ if (command[i + 1] === char)
83
+ i++;
84
+ continue;
85
+ }
86
+ current += char;
87
+ }
88
+ parts.push(current);
89
+ return parts.map((part) => part.trim()).filter((part) => part !== '');
90
+ }
91
+ /** Splitst één aanroep in woorden, met quotes zoals een shell ze leest (zonder expansie). */
92
+ export function tokenize(segment) {
93
+ const tokens = [];
94
+ let current = '';
95
+ let started = false;
96
+ let quote = null;
97
+ for (const char of segment) {
98
+ if (quote !== null) {
99
+ if (char === quote)
100
+ quote = null;
101
+ else
102
+ current += char;
103
+ continue;
104
+ }
105
+ if (char === '"' || char === "'") {
106
+ quote = char;
107
+ started = true;
108
+ }
109
+ else if (/\s/.test(char)) {
110
+ if (started)
111
+ tokens.push(current);
112
+ current = '';
113
+ started = false;
114
+ }
115
+ else {
116
+ current += char;
117
+ started = true;
118
+ }
119
+ }
120
+ if (started)
121
+ tokens.push(current);
122
+ return tokens;
123
+ }
124
+ /**
125
+ * Waar in de woorden de PHPStan-binary staat en waar de argumenten van `analyse` beginnen,
126
+ * of null als dit geen analyse is. `@php vendor/bin/phpstan`, `php phpstan.phar` en een
127
+ * variabele ervoor (`XDEBUG_MODE=off vendor/bin/phpstan`) tellen allemaal.
128
+ */
129
+ function analyseStart(tokens) {
130
+ const binary = tokens.findIndex((token) => /(^|[/\\])phpstan(\.phar)?$/.test(token));
131
+ if (binary === -1)
132
+ return null;
133
+ const sub = tokens[binary + 1];
134
+ return sub === 'analyse' || sub === 'analyze' ? { binary, args: binary + 2 } : null;
135
+ }
136
+ /** Leest de argumenten van `phpstan analyse`, voor zover ze de uitslag veranderen. */
137
+ export function parseInvocation(args) {
138
+ const found = {
139
+ configuration: null,
140
+ level: null,
141
+ memoryLimit: null,
142
+ autoloadFile: null,
143
+ paths: [],
144
+ writes: false,
145
+ };
146
+ const paths = [];
147
+ for (let i = 0; i < args.length; i++) {
148
+ const arg = args[i] ?? '';
149
+ if (arg === '--') {
150
+ paths.push(...args.slice(i + 1));
151
+ break;
152
+ }
153
+ if (arg.startsWith('--generate-baseline') || arg === '--fix') {
154
+ found.writes = true;
155
+ continue;
156
+ }
157
+ if (!arg.startsWith('-')) {
158
+ paths.push(arg);
159
+ continue;
160
+ }
161
+ // `--optie=waarde`, `--optie waarde`, `-c waarde` en `-cwaarde`.
162
+ const eq = arg.indexOf('=');
163
+ const name = arg.startsWith('--') ? (eq === -1 ? arg : arg.slice(0, eq)) : arg.slice(0, 2);
164
+ if (!(name in VALUE_OPTIONS))
165
+ continue;
166
+ const field = VALUE_OPTIONS[name] ?? null;
167
+ let value;
168
+ if (arg.startsWith('--') && eq !== -1)
169
+ value = arg.slice(eq + 1);
170
+ else if (!arg.startsWith('--') && arg.length > 2)
171
+ value = arg.slice(2);
172
+ else
173
+ value = args[++i];
174
+ if (field !== null && field !== 'paths' && field !== 'writes' && value !== undefined)
175
+ found[field] = value;
176
+ }
177
+ found.paths = paths;
178
+ return found;
179
+ }
180
+ /**
181
+ * Waarin een aanroep afwijkt van deze server, als zinnen met de uitweg erbij. Alleen wat de
182
+ * uitslag verandert; `--no-progress` of `--error-format` zegt niets over wat er gevonden wordt.
183
+ */
184
+ export function compareWithServer(project, invocation, server) {
185
+ if (invocation.writes) {
186
+ return ['writes a baseline or fixes code, so it is not an analysis to compare with'];
187
+ }
188
+ const differences = [];
189
+ const hier = (path) => normalize(isAbsolute(path) ? path : join(project, path));
190
+ // Wat er aan beide kanten echt gebruikt wordt: zonder --configuration zoekt PHPStan zelf, en
191
+ // zonder --memory-limit geldt die van PHP. Alleen de expliciete waarden vergelijken miste het
192
+ // geval dat de server een standaard zet (PHPSTAN_MCP_PROJECTS) en het script niet.
193
+ const discovered = DISCOVERED.find((name) => existsSync(join(project, name))) ?? null;
194
+ const scriptConfig = invocation.configuration ?? discovered;
195
+ const serverConfig = server.config ?? discovered;
196
+ const configNaam = (config, eigen) => config === null ? 'none' : eigen ? config : `${config} (found by PHPStan itself)`;
197
+ if ((scriptConfig === null) !== (serverConfig === null) || (scriptConfig !== null && serverConfig !== null && hier(scriptConfig) !== hier(serverConfig))) {
198
+ differences.push(`uses the config ${configNaam(scriptConfig, invocation.configuration !== null)}, the server ` +
199
+ `${configNaam(serverConfig, server.config !== null)}` +
200
+ (scriptConfig === null ? '' : `; pass config: "${scriptConfig}" to analyse the same way`));
201
+ }
202
+ const scriptMemory = invocation.memoryLimit?.toLowerCase() ?? null;
203
+ if (scriptMemory !== (server.memoryLimit?.toLowerCase() ?? null)) {
204
+ differences.push(`sets ${invocation.memoryLimit === null ? "no --memory-limit (PHP's own limit)" : `--memory-limit ${invocation.memoryLimit}`}, ` +
205
+ `the server ${server.memoryLimit ?? "none (PHP's own limit)"}` +
206
+ (invocation.memoryLimit === null
207
+ ? ''
208
+ : `; pass memoryLimit: "${invocation.memoryLimit}", or set it for this project in PHPSTAN_MCP_PROJECTS`));
209
+ }
210
+ if (invocation.level !== null) {
211
+ differences.push(`sets level ${invocation.level}, the server uses the level from the config; pass level: "${invocation.level}" to match`);
212
+ }
213
+ if (invocation.paths.length > 0) {
214
+ differences.push(`analyses ${invocation.paths.join(', ')}, the server the paths from the config; pass paths to analyse_project to match`);
215
+ }
216
+ if (invocation.autoloadFile !== null) {
217
+ differences.push(`loads ${invocation.autoloadFile} with --autoload-file; the server cannot pass that, so its analysis can differ`);
218
+ }
219
+ return differences;
220
+ }
221
+ /** De scripts als tekst, onder de rest van de diagnose. Leeg als er geen enkel is. */
222
+ export function renderComposerScripts(scripts, problem) {
223
+ if (scripts === null)
224
+ return `Composer scripts: not available. ${problem ?? ''}`.trim();
225
+ if (scripts.length === 0)
226
+ return '';
227
+ const lines = ['Composer scripts that run PHPStan:'];
228
+ for (const script of scripts) {
229
+ lines.push(`- ${script.name}: ${script.command}`);
230
+ if (script.differences.length === 0)
231
+ lines.push(' runs PHPStan the same way as this server');
232
+ for (const difference of script.differences)
233
+ lines.push(` ${difference}`);
234
+ }
235
+ return lines.join('\n');
236
+ }
237
+ /**
238
+ * Frameworks waarvan PHPStan de magie niet ziet zonder extensie — en die dan vals-positieven
239
+ * geven (`Access to an undefined property App\Models\User::$name`) — met de extensie die
240
+ * daarvoor bestaat. Nagemeten op `cms/app`, level 5: zonder Larastan 187 bevindingen tegen 147
241
+ * met, vooral `property.notFound` (+16) en `method.notFound` (+18). Een eigen lijst, maar een kleine en stabiele: pakketnamen,
242
+ * geen uitleg die bij een release afdrijft.
243
+ *
244
+ * Bewust zonder testtools (`phpunit`, `mockery`, `webmozart/assert`). Nagemeten op vijf echte
245
+ * projecten: die staan in elk Laravel-project, vaak als afhankelijkheid van een
246
+ * afhankelijkheid, en hun extensie ontbrak overal — een melding bij elk project is ruis, en
247
+ * geen van de bevindingen daar had er iets mee te maken.
248
+ */
249
+ const FRAMEWORK_EXTENSIONS = [
250
+ {
251
+ framework: 'laravel/framework',
252
+ extensions: ['larastan/larastan', 'nunomaduro/larastan'],
253
+ sees: "Laravel's magic (Eloquent properties and relations, facades, the container)",
254
+ },
255
+ { framework: 'doctrine/orm', extensions: ['phpstan/phpstan-doctrine'], sees: 'Doctrine repositories, entities and DQL' },
256
+ {
257
+ framework: 'symfony/framework-bundle',
258
+ extensions: ['phpstan/phpstan-symfony'],
259
+ sees: 'the Symfony container, services and request attributes',
260
+ },
261
+ ];
262
+ /**
263
+ * Welke frameworks uit `composer.lock` hun extensie missen. Wat geladen is, komt uit PHPStans
264
+ * eigen diagnose ("Included configs from Composer packages"): die noemt ook een extensie die
265
+ * zonder extension-installer met de hand in de config staat. Is de uitvoer niet te lezen —
266
+ * een andere PHPStan-versie — dan null: niet te zeggen, en een vals alarm is erger dan stilte.
267
+ */
268
+ export function findMissingExtensions(project, diagnoseOutput) {
269
+ // Nagemeten: zonder één enkele config uit een pakket laat PHPStan de hele sectie weg — juist
270
+ // het geval waar het om gaat. De regel "Extension installer:" komt uit hetzelfde deel van de
271
+ // diagnose en staat er dan wél; alleen als ook die ontbreekt, is het een andere uitvoer.
272
+ const section = /Included configs from Composer packages:\n([\s\S]*?)(?:\n\s*\n|$)/.exec(diagnoseOutput);
273
+ if (section === null && !/^Extension installer:/m.test(diagnoseOutput))
274
+ return null;
275
+ const loaded = new Set((section?.[1] ?? '')
276
+ .split('\n')
277
+ .map((line) => line.trim().split(/\s+/)[0] ?? '')
278
+ .filter((name) => name.includes('/')));
279
+ let installed;
280
+ try {
281
+ const lock = JSON.parse(readFileSync(join(project, 'composer.lock'), 'utf8'));
282
+ installed = new Set([...(lock.packages ?? []), ...(lock['packages-dev'] ?? [])]
283
+ .map((pkg) => pkg.name)
284
+ .filter((name) => typeof name === 'string'));
285
+ }
286
+ catch {
287
+ // Zonder lock valt niet te zeggen wat er geïnstalleerd is; geen melding.
288
+ return null;
289
+ }
290
+ return FRAMEWORK_EXTENSIONS.filter((entry) => installed.has(entry.framework) && !entry.extensions.some((extension) => loaded.has(extension))).map((entry) => {
291
+ const present = entry.extensions.find((extension) => installed.has(extension));
292
+ return {
293
+ framework: entry.framework,
294
+ extension: present ?? entry.extensions[0] ?? '',
295
+ installed: present !== undefined,
296
+ sees: entry.sees,
297
+ };
298
+ });
299
+ }
300
+ /** De ontbrekende extensies als tekst; leeg als er niets ontbreekt of het niet te zeggen is. */
301
+ export function renderMissingExtensions(missing) {
302
+ if (missing === null || missing.length === 0)
303
+ return '';
304
+ return [
305
+ 'Missing PHPStan extensions:',
306
+ ...missing.map((entry) => `- ${entry.framework} is installed, but ${entry.extension} is not loaded, so PHPStan does not see ` +
307
+ `${entry.sees}. That shows up as false positives, such as undefined properties and methods. ` +
308
+ (entry.installed
309
+ ? `${entry.extension} is installed but not included: add its neon to the config's includes, or install phpstan/extension-installer.`
310
+ : `Install ${entry.extension} as a dev dependency.`)),
311
+ ].join('\n');
312
+ }
313
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA6BxD,mEAAmE;AACnE,MAAM,aAAa,GAA4C;IAC7D,IAAI,EAAE,eAAe;IACrB,iBAAiB,EAAE,eAAe;IAClC,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,OAAO;IAClB,gBAAgB,EAAE,aAAa;IAC/B,IAAI,EAAE,cAAc;IACpB,iBAAiB,EAAE,cAAc;IACjC,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,IAAI;CACrB,CAAC;AAEF,4FAA4F;AAC5F,MAAM,UAAU,GAAG,CAAC,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,CAAC,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,MAAwB;IAExB,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAA2B,CAAC,OAAO,CAAC;IAChH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,eAAe,EAAE,IAAI;YACrB,sBAAsB,EAAE,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;SACrH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC;IAElH,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9H,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,KAAK,MAAM,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACjC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;gBACnC,IAAI,KAAK,KAAK,IAAI;oBAAE,SAAS;gBAC7B,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC7D,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI;oBACJ,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;oBAC7C,WAAW,EAAE,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC;iBAC5D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,IAAI,KAAK,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAC;YACjC,OAAO,IAAI,IAAI,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,OAAO,IAAI,IAAI,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YAC7E,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACjC,SAAS;QACX,CAAC;QACD,OAAO,IAAI,IAAI,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,IAAI,KAAK,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAC;;gBAC5B,OAAO,IAAI,IAAI,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,IAAI,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IACD,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAAyB;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACrF,IAAI,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtF,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,IAAuB;IACrD,MAAM,KAAK,GAAyD;QAClE,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,KAAK;KACd,CAAC;IACF,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM;QACR,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,qBAAqB,CAAC,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YAC7D,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,SAAS;QACX,CAAC;QAED,iEAAiE;QACjE,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3F,IAAI,CAAC,CAAC,IAAI,IAAI,aAAa,CAAC;YAAE,SAAS;QACvC,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC1C,IAAI,KAAyB,CAAC;QAC9B,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;aAC5D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;;YAClE,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEvB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IAC7G,CAAC;IAED,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,UAAsB,EAAE,MAAwB;IACjG,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,2EAA2E,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACxF,6FAA6F;IAC7F,8FAA8F;IAC9F,mFAAmF;IACnF,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACtF,MAAM,YAAY,GAAG,UAAU,CAAC,aAAa,IAAI,UAAU,CAAC;IAC5D,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,IAAI,UAAU,CAAC;IACjD,MAAM,UAAU,GAAG,CAAC,MAAqB,EAAE,KAAc,EAAE,EAAE,CAC3D,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,4BAA4B,CAAC;IAEpF,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACzJ,WAAW,CAAC,IAAI,CACd,mBAAmB,UAAU,CAAC,YAAY,EAAE,UAAU,CAAC,aAAa,KAAK,IAAI,CAAC,eAAe;YAC3F,GAAG,UAAU,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE;YACrD,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,YAAY,2BAA2B,CAAC,CAC5F,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC;IACnE,IAAI,YAAY,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACjE,WAAW,CAAC,IAAI,CACd,QAAQ,UAAU,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,kBAAkB,UAAU,CAAC,WAAW,EAAE,IAAI;YAC9H,cAAc,MAAM,CAAC,WAAW,IAAI,wBAAwB,EAAE;YAC9D,CAAC,UAAU,CAAC,WAAW,KAAK,IAAI;gBAC9B,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,wBAAwB,UAAU,CAAC,WAAW,uDAAuD,CAAC,CAC7G,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC9B,WAAW,CAAC,IAAI,CACd,cAAc,UAAU,CAAC,KAAK,6DAA6D,UAAU,CAAC,KAAK,YAAY,CACxH,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,WAAW,CAAC,IAAI,CACd,YAAY,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gFAAgF,CACxH,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACrC,WAAW,CAAC,IAAI,CACd,SAAS,UAAU,CAAC,YAAY,gFAAgF,CACjH,CAAC;IACJ,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,qBAAqB,CAAC,OAAyC,EAAE,OAAsB;IACrG,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,oCAAoC,OAAO,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IACxF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAG,CAAC,oCAAoC,CAAC,CAAC;IACrD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAChG,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,oBAAoB,GAAkF;IAC1G;QACE,SAAS,EAAE,mBAAmB;QAC9B,UAAU,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;QACxD,IAAI,EAAE,6EAA6E;KACpF;IACD,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,yCAAyC,EAAE;IACxH;QACE,SAAS,EAAE,0BAA0B;QACrC,UAAU,EAAE,CAAC,yBAAyB,CAAC;QACvC,IAAI,EAAE,wDAAwD;KAC/D;CACF,CAAC;AAWF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,cAAsB;IAC3E,6FAA6F;IAC7F,6FAA6F;IAC7F,yFAAyF;IACzF,MAAM,OAAO,GAAG,mEAAmE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzG,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,cAAc,CAAC;QAAE,OAAO,IAAI,CAAC;IACpF,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACjB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAChD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CACxC,CAAC;IAEF,IAAI,SAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAG3E,CAAC;QACF,SAAS,GAAG,IAAI,GAAG,CACjB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;aACxD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;aACtB,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAC9D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,oBAAoB,CAAC,MAAM,CAChC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAC1G,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACd,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/E,OAAO;YACL,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE;YAC/C,SAAS,EAAE,OAAO,KAAK,SAAS;YAChC,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,uBAAuB,CAAC,OAA2C;IACjF,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACxD,OAAO;QACL,6BAA6B;QAC7B,GAAG,OAAO,CAAC,GAAG,CACZ,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,KAAK,CAAC,SAAS,sBAAsB,KAAK,CAAC,SAAS,0CAA0C;YACnG,GAAG,KAAK,CAAC,IAAI,gFAAgF;YAC7F,CAAC,KAAK,CAAC,SAAS;gBACd,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,gHAAgH;gBACpI,CAAC,CAAC,WAAW,KAAK,CAAC,SAAS,uBAAuB,CAAC,CACzD;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,86 @@
1
+ import type { AnalyseOptions, AnalysisResult } from './phpstan.js';
2
+ import type { ResolvedProject } from './project.js';
3
+ /** Hoeveel expressies één aanroep hoogstens meeneemt. */
4
+ export declare const MAX_EXPRESSIONS = 10;
5
+ /** Hoe lang één expressie mag zijn; een dump is voor een waarde, niet voor een blok code. */
6
+ export declare const MAX_EXPRESSION_CHARS = 500;
7
+ /** Het invoegen van de dumps, met wat nodig is om de uitslag terug te lezen. */
8
+ export interface Probe {
9
+ /** De inhoud van het bestand met de ingevoegde regels. */
10
+ readonly content: string;
11
+ /** De regel waarvóór is ingevoegd; de dumps staan op deze regel en de volgende. */
12
+ readonly line: number;
13
+ /** De expressies zoals ze ingevoegd zijn, na het opschonen. */
14
+ readonly expressions: readonly string[];
15
+ }
16
+ /** Het type van één expressie, of waarom er geen is. */
17
+ export interface ProbedType {
18
+ readonly expression: string;
19
+ /** Het type zoals PHPStan het afleidt; null als PHPStan er geen rapporteerde. */
20
+ readonly type: string | null;
21
+ /** Andere meldingen op de regel van deze dump, zoals `variable.undefined` bij `*ERROR*`. */
22
+ readonly problems: readonly {
23
+ identifier: string | null;
24
+ message: string;
25
+ }[];
26
+ }
27
+ /** Een parse-fout, met het regelnummer van het oorspronkelijke bestand. */
28
+ export interface ParseProblem {
29
+ readonly line: number | null;
30
+ /** Of de fout op een van de ingevoegde regels staat. */
31
+ readonly inserted: boolean;
32
+ readonly message: string;
33
+ }
34
+ /** Het antwoord van `inspect_type`. */
35
+ export interface TypeReport {
36
+ readonly project: string;
37
+ /** Pad relatief aan de projectroot. */
38
+ readonly file: string;
39
+ readonly line: number;
40
+ readonly types: readonly ProbedType[];
41
+ readonly parseErrors: readonly ParseProblem[];
42
+ readonly generalErrors: readonly string[];
43
+ readonly warnings: readonly string[];
44
+ readonly omitted: {
45
+ readonly parseErrors: number;
46
+ readonly generalErrors: number;
47
+ readonly warnings: number;
48
+ };
49
+ }
50
+ /** Wat een aanroep vraagt. */
51
+ export interface TypeRequest {
52
+ readonly path: string;
53
+ readonly line: number;
54
+ readonly expressions: readonly string[];
55
+ }
56
+ /**
57
+ * Laat zien welk type PHPStan afleidt voor een of meer expressies, vlak vóór `line`.
58
+ *
59
+ * PHPStans eigen begeleiding zegt: overschrijf het afgeleide type niet met `assert()` of
60
+ * een inline `@var`. Dan moet het model dat type wel kunnen zien, en tot nu toe kon dat
61
+ * alleen door het bestand te bewerken, te analyseren en weer terug te draaien. Hier gaat
62
+ * dat via een kopie buiten het project (`analyseEdited`), dus dit blijft read-only.
63
+ */
64
+ export declare function inspectType(project: ResolvedProject, request: TypeRequest, options?: Pick<AnalyseOptions, 'config' | 'memoryLimit' | 'timeoutMs' | 'signal'>): Promise<TypeReport>;
65
+ /**
66
+ * Voegt vóór `line` één regel `\PHPStan\dumpType(<expressie>);` per expressie in.
67
+ *
68
+ * Elke expressie op een eigen regel, want PHPStan rapporteert twee dumps op één regel
69
+ * niet in een vaste volgorde (nagemeten) — het regelnummer is dan het enige dat een type
70
+ * aan zijn expressie koppelt. Een regeleinde in een expressie zou die telling verschuiven,
71
+ * en wordt daarom geweigerd.
72
+ */
73
+ export declare function insertProbes(source: string, line: number, expressions: readonly string[]): Probe;
74
+ /**
75
+ * Leest de uitslag van een analyse met ingevoegde dumps terug.
76
+ *
77
+ * Alleen wat op de ingevoegde regels staat hoort bij een expressie; de gewone bevindingen
78
+ * van het bestand zijn niet gevraagd (daar is `analyse_file` voor). Parse-fouten wél,
79
+ * waar ze ook staan: een regel op een plek waar geen statement kan (midden in een
80
+ * meerregelige expressie, in een klassebody) breekt het hele bestand, en nagemeten staan
81
+ * de meeste fouten dan ná de ingevoegde regel. Hun regelnummers worden teruggerekend naar
82
+ * het oorspronkelijke bestand, anders wijzen ze een of meer regels te ver.
83
+ */
84
+ export declare function readProbes(project: string, file: string, probe: Probe, result: AnalysisResult): TypeReport;
85
+ /** Schrijft het antwoord uit als tekst voor het model. */
86
+ export declare function renderTypes(report: TypeReport): string;