source: frontend/node_modules/@rushstack/eslint-patch/lib-commonjs/eslint-bulk-suppressions/generate-patched-file.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 14.6 KB
Line 
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __importDefault = (this && this.__importDefault) || function (mod) {
5 return (mod && mod.__esModule) ? mod : { "default": mod };
6};
7Object.defineProperty(exports, "__esModule", { value: true });
8exports.generatePatchedLinterJsFileIfDoesNotExist = generatePatchedLinterJsFileIfDoesNotExist;
9const node_fs_1 = __importDefault(require("node:fs"));
10const constants_1 = require("./constants");
11/**
12 * Dynamically generate file to properly patch many versions of ESLint
13 * @param inputFilePath - Must be an iteration of https://github.com/eslint/eslint/blob/main/lib/linter/linter.js
14 * @param outputFilePath - Some small changes to linter.js
15 */
16function generatePatchedLinterJsFileIfDoesNotExist(inputFilePath, outputFilePath, eslintPackageVersion) {
17 const generateEnvVarValue = process.env[constants_1.ESLINT_BULK_FORCE_REGENERATE_PATCH_ENV_VAR_NAME];
18 if (generateEnvVarValue !== 'true' && generateEnvVarValue !== '1' && node_fs_1.default.existsSync(outputFilePath)) {
19 return;
20 }
21 const [majorVersionString, minorVersionString] = eslintPackageVersion.split('.');
22 const majorVersion = parseInt(majorVersionString, 10);
23 const minorVersion = parseInt(minorVersionString, 10);
24 const inputFile = node_fs_1.default.readFileSync(inputFilePath).toString();
25 let inputIndex = 0;
26 /**
27 * Extract from the stream until marker is reached. When matching marker,
28 * ignore whitespace in the stream and in the marker. Return the extracted text.
29 */
30 function scanUntilMarker(marker) {
31 const trimmedMarker = marker.replace(/\s/g, '');
32 let output = '';
33 let trimmed = '';
34 while (inputIndex < inputFile.length) {
35 const char = inputFile[inputIndex++];
36 output += char;
37 if (!/^\s$/.test(char)) {
38 trimmed += char;
39 }
40 if (trimmed.endsWith(trimmedMarker)) {
41 return output;
42 }
43 }
44 throw new Error('Unexpected end of input while looking for ' + JSON.stringify(marker));
45 }
46 function scanUntilNewline() {
47 let output = '';
48 while (inputIndex < inputFile.length) {
49 const char = inputFile[inputIndex++];
50 output += char;
51 if (char === '\n') {
52 return output;
53 }
54 }
55 throw new Error('Unexpected end of input while looking for new line');
56 }
57 function scanUntilEnd() {
58 const output = inputFile.substring(inputIndex);
59 inputIndex = inputFile.length;
60 return output;
61 }
62 const markerForStartOfClassMethodSpaces = '\n */\n ';
63 const markerForStartOfClassMethodTabs = '\n\t */\n\t';
64 function indexOfStartOfClassMethod(input, position) {
65 let startOfClassMethodIndex = input.indexOf(markerForStartOfClassMethodSpaces, position);
66 if (startOfClassMethodIndex === -1) {
67 startOfClassMethodIndex = input.indexOf(markerForStartOfClassMethodTabs, position);
68 if (startOfClassMethodIndex === -1) {
69 return { index: startOfClassMethodIndex };
70 }
71 return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodTabs };
72 }
73 return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodSpaces };
74 }
75 /**
76 * Returns index of next public method
77 * @param fromIndex - index of inputFile to search if public method still exists
78 * @returns -1 if public method does not exist or index of next public method
79 */
80 function getIndexOfNextMethod(fromIndex) {
81 const rest = inputFile.substring(fromIndex);
82 const endOfClassIndex = rest.indexOf('\n}');
83 const { index: startOfClassMethodIndex, marker: startOfClassMethodMarker } = indexOfStartOfClassMethod(rest);
84 if (startOfClassMethodIndex === -1 ||
85 !startOfClassMethodMarker ||
86 startOfClassMethodIndex > endOfClassIndex) {
87 return { index: -1 };
88 }
89 const afterMarkerIndex = startOfClassMethodIndex + startOfClassMethodMarker.length;
90 const isPublicMethod = rest[afterMarkerIndex] !== '_' &&
91 rest[afterMarkerIndex] !== '#' &&
92 !rest.substring(afterMarkerIndex, rest.indexOf('\n', afterMarkerIndex)).includes('static') &&
93 !rest.substring(afterMarkerIndex, rest.indexOf('\n', afterMarkerIndex)).includes('constructor');
94 return { index: fromIndex + afterMarkerIndex, isPublic: isPublicMethod };
95 }
96 function scanUntilIndex(indexToScanTo) {
97 const output = inputFile.substring(inputIndex, indexToScanTo);
98 inputIndex = indexToScanTo;
99 return output;
100 }
101 let outputFile = '';
102 // Match this:
103 // //------------------------------------------------------------------------------
104 // // Requirements
105 // //------------------------------------------------------------------------------
106 outputFile += scanUntilMarker('// Requirements');
107 outputFile += scanUntilMarker('//--');
108 outputFile += scanUntilNewline();
109 outputFile += `
110// --- BEGIN MONKEY PATCH ---
111const bulkSuppressionsPatch = require(process.env.${constants_1.ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME});
112const requireFromPathToLinterJS = bulkSuppressionsPatch.requireFromPathToLinterJS;
113`;
114 // Match this:
115 // //------------------------------------------------------------------------------
116 // // Typedefs
117 // //------------------------------------------------------------------------------
118 const requireSection = scanUntilMarker('// Typedefs');
119 // Match something like this:
120 //
121 // const path = require('path'),
122 // eslintScope = require('eslint-scope'),
123 // evk = require('eslint-visitor-keys'),
124 //
125 // Convert to something like this:
126 //
127 // const path = require('path'),
128 // eslintScope = requireFromPathToLinterJS('eslint-scope'),
129 // evk = requireFromPathToLinterJS('eslint-visitor-keys'),
130 //
131 outputFile += requireSection.replace(/require\s*\((?:'([^']+)'|"([^"]+)")\)/g, (match, p1, p2) => {
132 var _a;
133 const importPath = (_a = p1 !== null && p1 !== void 0 ? p1 : p2) !== null && _a !== void 0 ? _a : '';
134 if (importPath !== 'path') {
135 if (p1) {
136 return `requireFromPathToLinterJS('${p1}')`;
137 }
138 if (p2) {
139 return `requireFromPathToLinterJS("${p2}")`;
140 }
141 }
142 // Keep as-is
143 return match;
144 });
145 outputFile += `--- END MONKEY PATCH ---
146`;
147 if (majorVersion >= 9) {
148 if (minorVersion >= 37) {
149 outputFile += scanUntilMarker('const visitor = new SourceCodeVisitor();');
150 }
151 else {
152 outputFile += scanUntilMarker('const emitter = createEmitter();');
153 }
154 outputFile += `
155 // --- BEGIN MONKEY PATCH ---
156 let currentNode = undefined;
157 // --- END MONKEY PATCH ---`;
158 }
159 // Match this (9.25.1):
160 // ```
161 // if (reportTranslator === null) {
162 // reportTranslator = createReportTranslator({
163 // ruleId,
164 // severity,
165 // sourceCode,
166 // messageIds,
167 // disableFixes
168 // });
169 // }
170 // const problem = reportTranslator(...args);
171 //
172 // if (problem.fix && !(rule.meta && rule.meta.fixable)) {
173 // throw new Error("Fixable rules must set the `meta.fixable` property to \"code\" or \"whitespace\".");
174 // }
175 // ```
176 // Or this (9.37.0):
177 // ```
178 // const problem = report.addRuleMessage(
179 // ruleId,
180 // severity,
181 // ...args,
182 // );
183 //
184 // if (problem.fix && !(rule.meta && rule.meta.fixable)) {
185 // throw new Error(
186 // 'Fixable rules must set the `meta.fixable` property to "code" or "whitespace".',
187 // );
188 // }
189 // ```
190 //
191 // Convert to something like this (9.25.1):
192 // ```
193 // if (reportTranslator === null) {
194 // reportTranslator = createReportTranslator({
195 // ruleId,
196 // severity,
197 // sourceCode,
198 // messageIds,
199 // disableFixes
200 // });
201 // }
202 // const problem = reportTranslator(...args);
203 // // --- BEGIN MONKEY PATCH ---
204 // if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return;
205 // // --- END MONKEY PATCH ---
206 //
207 // if (problem.fix && !(rule.meta && rule.meta.fixable)) {
208 // throw new Error("Fixable rules must set the `meta.fixable` property to \"code\" or \"whitespace\".");
209 // }
210 // ```
211 // Or this (9.37.0):
212 // ```
213 // const problem = report.addRuleMessage(
214 // ruleId,
215 // severity,
216 // ...args,
217 // );
218 // // --- BEGIN MONKEY PATCH ---
219 // if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return;
220 // // --- END MONKEY PATCH ---
221 //
222 // if (problem.fix && !(rule.meta && rule.meta.fixable)) {
223 // throw new Error(
224 // 'Fixable rules must set the `meta.fixable` property to "code" or "whitespace".',
225 // );
226 // }
227 // ```
228 if (majorVersion > 9 || (majorVersion === 9 && minorVersion >= 37)) {
229 outputFile += scanUntilMarker('const problem = report.addRuleMessage(');
230 outputFile += scanUntilMarker('ruleId,');
231 outputFile += scanUntilMarker('severity,');
232 outputFile += scanUntilMarker('...args,');
233 outputFile += scanUntilMarker(');');
234 }
235 else {
236 outputFile += scanUntilMarker('const problem = reportTranslator(...args);');
237 }
238 outputFile += `
239 // --- BEGIN MONKEY PATCH ---`;
240 if (majorVersion > 9 || (majorVersion === 9 && minorVersion >= 37)) {
241 outputFile += `
242 if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) {
243 problem.suppressions ??= []; problem.suppressions.push({kind:"bulk",justification:""});
244 }`;
245 }
246 else {
247 outputFile += `
248 if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return;`;
249 }
250 outputFile += `
251 // --- END MONKEY PATCH ---`;
252 //
253 // Match this:
254 // ```
255 // Object.keys(ruleListeners).forEach(selector => {
256 // ...
257 // });
258 // ```
259 //
260 // Convert to something like this (9.25.1):
261 // ```
262 // Object.keys(ruleListeners).forEach(selector => {
263 // // --- BEGIN MONKEY PATCH ---
264 // emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; });
265 // // --- END MONKEY PATCH ---
266 // ...
267 // });
268 // ```
269 // Or this (9.37.0):
270 // ```
271 // Object.keys(ruleListeners).forEach(selector => {
272 // // --- BEGIN MONKEY PATCH ---
273 // visitor.add(selector, (...args) => { currentNode = args[args.length - 1]; });
274 // // --- END MONKEY PATCH ---
275 // ...
276 // });
277 // ```
278 if (majorVersion >= 9) {
279 outputFile += scanUntilMarker('Object.keys(ruleListeners).forEach(selector => {');
280 outputFile += `
281 // --- BEGIN MONKEY PATCH ---
282`;
283 if (minorVersion >= 37) {
284 outputFile += `visitor.add(selector, (...args) => { currentNode = args[args.length - 1]; });`;
285 }
286 else {
287 outputFile += `emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; });`;
288 }
289 outputFile += `
290 // --- END MONKEY PATCH ---`;
291 }
292 outputFile += scanUntilMarker('class Linter {');
293 outputFile += scanUntilNewline();
294 outputFile += `
295 // --- BEGIN MONKEY PATCH ---
296 /**
297 * We intercept ESLint execution at the .eslintrc.js file, but unfortunately the Linter class is
298 * initialized before the .eslintrc.js file is executed. This means the internalSlotsMap that all
299 * the patched methods refer to is not initialized. This method checks if the internalSlotsMap is
300 * initialized, and if not, initializes it.
301 */
302 _conditionallyReinitialize({ cwd, configType } = {}) {
303 if (internalSlotsMap.get(this) === undefined) {
304 internalSlotsMap.set(this, {
305 cwd: normalizeCwd(cwd),
306 flags: [],
307 lastConfigArray: null,
308 lastSourceCode: null,
309 lastSuppressedMessages: [],
310 configType, // TODO: Remove after flat config conversion
311 parserMap: new Map([['espree', espree]]),
312 ruleMap: new Rules()
313 });
314
315 this.version = pkg.version;
316 }
317 }
318 // --- END MONKEY PATCH ---
319`;
320 const privateMethodNames = [];
321 let { index: indexOfNextMethod, isPublic } = getIndexOfNextMethod(inputIndex);
322 while (indexOfNextMethod !== -1) {
323 outputFile += scanUntilIndex(indexOfNextMethod);
324 if (isPublic) {
325 // Inject the monkey patch at the start of the public method
326 outputFile += scanUntilNewline();
327 outputFile += ` // --- BEGIN MONKEY PATCH ---
328 this._conditionallyReinitialize();
329 // --- END MONKEY PATCH ---
330`;
331 }
332 else if (inputFile[inputIndex] === '#') {
333 // Replace the '#' private method with a '_' private method, so that our monkey patch
334 // can still call it. Otherwise, we get the following error during execution:
335 // TypeError: Receiver must be an instance of class Linter
336 const privateMethodName = scanUntilMarker('(');
337 // Remove the '(' at the end and stash it, since we need to escape it for the regex later
338 privateMethodNames.push(privateMethodName.slice(0, -1));
339 outputFile += `_${privateMethodName.slice(1)}`;
340 }
341 const indexResult = getIndexOfNextMethod(inputIndex);
342 indexOfNextMethod = indexResult.index;
343 isPublic = indexResult.isPublic;
344 }
345 outputFile += scanUntilEnd();
346 // Do a second pass to find and replace all calls to private methods with the patched versions.
347 if (privateMethodNames.length) {
348 const privateMethodCallRegex = new RegExp(`\.(${privateMethodNames.join('|')})\\(`, 'g');
349 outputFile = outputFile.replace(privateMethodCallRegex, (match, privateMethodName) => {
350 // Replace the leading '#' with a leading '_'
351 return `._${privateMethodName.slice(1)}(`;
352 });
353 }
354 node_fs_1.default.writeFileSync(outputFilePath, outputFile);
355}
356//# sourceMappingURL=generate-patched-file.js.map
Note: See TracBrowser for help on using the repository browser.