@oxlint/migrate 1.42.0 → 1.43.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/README.md +1 -0
- package/dist/bin/oxlint-migrate.mjs +135 -23
- package/dist/src/index.d.mts +2670 -6
- package/dist/src/index.mjs +1 -1
- package/dist/{src-BiX8O61Z.mjs → src-BMWXAQvA.mjs} +524 -504
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ When no config file is provided, the script searches for the default ESLint conf
|
|
|
24
24
|
| `--type-aware` | Include type aware rules, which are supported with `oxlint --type-aware` and [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) |
|
|
25
25
|
| `--with-nursery` | Include oxlint rules which are currently under development |
|
|
26
26
|
| `--js-plugins` | \*\* Include ESLint plugins via `jsPlugins` key. |
|
|
27
|
+
| `--details` | List rules that could not be migrated to oxlint |
|
|
27
28
|
| `--output-file <file>` | The oxlint configuration file where ESLint v9 rules will be written to, default: `.oxlintrc.json` |
|
|
28
29
|
| `--replace-eslint-comments` | Search in the project files for ESLint comments and replaces them with oxlint. Some ESLint comments are not supported and will be reported. |
|
|
29
30
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as rules_exports, i as nurseryRules, n as preFixForJsPlugins, r as isOffValue, t as src_default } from "../src-BMWXAQvA.mjs";
|
|
3
3
|
import { program } from "commander";
|
|
4
4
|
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
@@ -17,9 +17,9 @@ const FLAT_CONFIG_FILENAMES = [
|
|
|
17
17
|
"eslint.config.mts",
|
|
18
18
|
"eslint.config.cts"
|
|
19
19
|
];
|
|
20
|
-
const getAutodetectedEslintConfigName = (cwd
|
|
20
|
+
const getAutodetectedEslintConfigName = (cwd) => {
|
|
21
21
|
for (const filename of FLAT_CONFIG_FILENAMES) {
|
|
22
|
-
const filePath = path.join(cwd
|
|
22
|
+
const filePath = path.join(cwd, filename);
|
|
23
23
|
if (existsSync(filePath)) return filePath;
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -40,7 +40,7 @@ const loadESLintConfig = async (filePath) => {
|
|
|
40
40
|
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region package.json
|
|
43
|
-
var version = "1.
|
|
43
|
+
var version = "1.43.0";
|
|
44
44
|
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region src/walker/comments/replaceRuleDirectiveComment.ts
|
|
@@ -211,8 +211,8 @@ function partialAstroSourceTextLoader(sourceText) {
|
|
|
211
211
|
pos = frontmatterEndDelimiter + 3;
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
-
results.push(...extractScriptBlocks(sourceText, pos, Number.MAX_SAFE_INTEGER, false).map((sourceText
|
|
215
|
-
return Object.assign(sourceText
|
|
214
|
+
results.push(...extractScriptBlocks(sourceText, pos, Number.MAX_SAFE_INTEGER, false).map((sourceText) => {
|
|
215
|
+
return Object.assign(sourceText, {
|
|
216
216
|
lang: `ts`,
|
|
217
217
|
sourceType: `module`
|
|
218
218
|
});
|
|
@@ -227,7 +227,7 @@ const getComments = (absoluteFilePath, partialSourceText, options) => {
|
|
|
227
227
|
lang: partialSourceText.lang,
|
|
228
228
|
sourceType: partialSourceText.sourceType
|
|
229
229
|
});
|
|
230
|
-
if (parserResult.errors.length > 0) options.reporter?.
|
|
230
|
+
if (parserResult.errors.length > 0) options.reporter?.addWarning(`${absoluteFilePath}: failed to parse`);
|
|
231
231
|
return parserResult.comments;
|
|
232
232
|
};
|
|
233
233
|
function replaceCommentsInSourceText(absoluteFilePath, partialSourceText, options) {
|
|
@@ -241,7 +241,7 @@ function replaceCommentsInSourceText(absoluteFilePath, partialSourceText, option
|
|
|
241
241
|
}
|
|
242
242
|
} catch (error) {
|
|
243
243
|
if (error instanceof Error) {
|
|
244
|
-
options.reporter?.
|
|
244
|
+
options.reporter?.addWarning(`${absoluteFilePath}, char offset ${comment.start + partialSourceText.offset}: ${error.message}`);
|
|
245
245
|
continue;
|
|
246
246
|
}
|
|
247
247
|
throw error;
|
|
@@ -258,13 +258,13 @@ function replaceCommentsInFile(absoluteFilePath, fileContent, options) {
|
|
|
258
258
|
|
|
259
259
|
//#endregion
|
|
260
260
|
//#region src/walker/index.ts
|
|
261
|
-
const walkAndReplaceProjectFiles = (projectFiles, readFileSync
|
|
261
|
+
const walkAndReplaceProjectFiles = (projectFiles, readFileSync, writeFile, options) => {
|
|
262
262
|
return Promise.all(projectFiles.map((file) => {
|
|
263
|
-
const sourceText = readFileSync
|
|
263
|
+
const sourceText = readFileSync(file);
|
|
264
264
|
if (!sourceText) return Promise.resolve();
|
|
265
265
|
const newSourceText = replaceCommentsInFile(file, sourceText, options);
|
|
266
266
|
if (newSourceText === sourceText) return Promise.resolve();
|
|
267
|
-
return writeFile
|
|
267
|
+
return writeFile(file, newSourceText);
|
|
268
268
|
}));
|
|
269
269
|
};
|
|
270
270
|
|
|
@@ -281,20 +281,18 @@ const getAllProjectFiles = () => {
|
|
|
281
281
|
//#endregion
|
|
282
282
|
//#region src/reporter.ts
|
|
283
283
|
var DefaultReporter = class {
|
|
284
|
-
|
|
284
|
+
warnings = /* @__PURE__ */ new Set();
|
|
285
285
|
skippedRules = new Map([
|
|
286
286
|
["nursery", /* @__PURE__ */ new Set()],
|
|
287
287
|
["type-aware", /* @__PURE__ */ new Set()],
|
|
288
|
-
["unsupported", /* @__PURE__ */ new Set()]
|
|
288
|
+
["unsupported", /* @__PURE__ */ new Set()],
|
|
289
|
+
["js-plugins", /* @__PURE__ */ new Set()]
|
|
289
290
|
]);
|
|
290
|
-
|
|
291
|
-
this.
|
|
291
|
+
addWarning(message) {
|
|
292
|
+
this.warnings.add(message);
|
|
292
293
|
}
|
|
293
|
-
|
|
294
|
-
this.
|
|
295
|
-
}
|
|
296
|
-
getReports() {
|
|
297
|
-
return Array.from(this.reports);
|
|
294
|
+
getWarnings() {
|
|
295
|
+
return Array.from(this.warnings);
|
|
298
296
|
}
|
|
299
297
|
markSkipped(rule, category) {
|
|
300
298
|
this.skippedRules.get(category)?.add(rule);
|
|
@@ -306,6 +304,7 @@ var DefaultReporter = class {
|
|
|
306
304
|
const result = {
|
|
307
305
|
nursery: [],
|
|
308
306
|
"type-aware": [],
|
|
307
|
+
"js-plugins": [],
|
|
309
308
|
unsupported: []
|
|
310
309
|
};
|
|
311
310
|
for (const [category, rules] of this.skippedRules) result[category] = Array.from(rules);
|
|
@@ -313,6 +312,92 @@ var DefaultReporter = class {
|
|
|
313
312
|
}
|
|
314
313
|
};
|
|
315
314
|
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region bin/output-formatter.ts
|
|
317
|
+
const CATEGORY_METADATA = {
|
|
318
|
+
nursery: {
|
|
319
|
+
label: "Nursery",
|
|
320
|
+
description: "Experimental:"
|
|
321
|
+
},
|
|
322
|
+
"type-aware": {
|
|
323
|
+
label: "Type-aware",
|
|
324
|
+
description: "Requires TS info:"
|
|
325
|
+
},
|
|
326
|
+
"js-plugins": {
|
|
327
|
+
label: "JS Plugins",
|
|
328
|
+
description: "Requires JS plugins:"
|
|
329
|
+
},
|
|
330
|
+
unsupported: { label: "Unsupported" }
|
|
331
|
+
};
|
|
332
|
+
const MAX_LABEL_LENGTH = Math.max(...Object.values(CATEGORY_METADATA).map((meta) => meta.label.length));
|
|
333
|
+
/**
|
|
334
|
+
* Formats a category summary as either inline (with example) or vertical list
|
|
335
|
+
*/
|
|
336
|
+
function formatCategorySummary(count, category, rules, showAll) {
|
|
337
|
+
const meta = CATEGORY_METADATA[category];
|
|
338
|
+
if (!showAll) {
|
|
339
|
+
const maxRules = 3;
|
|
340
|
+
const exampleList = rules.slice(0, maxRules).join(", ");
|
|
341
|
+
const suffix = count > maxRules ? ", and more" : "";
|
|
342
|
+
const prefix = meta.description ? `${meta.description} ` : "";
|
|
343
|
+
return ` - ${String(count).padStart(3)} ${meta.label.padEnd(MAX_LABEL_LENGTH)} (${prefix}${exampleList}${suffix})\n`;
|
|
344
|
+
}
|
|
345
|
+
let output = ` - ${count} ${meta.label}\n`;
|
|
346
|
+
for (const rule of rules) output += ` - ${rule}\n`;
|
|
347
|
+
return output;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Detects which CLI flags are missing and could enable more rules
|
|
351
|
+
*/
|
|
352
|
+
function detectMissingFlags(byCategory, cliOptions) {
|
|
353
|
+
const missingFlags = [];
|
|
354
|
+
if (byCategory.nursery.length > 0 && !cliOptions.withNursery) missingFlags.push("--with-nursery");
|
|
355
|
+
if (byCategory["type-aware"].length > 0 && !cliOptions.typeAware) missingFlags.push("--type-aware");
|
|
356
|
+
if (byCategory["js-plugins"].length > 0 && !cliOptions.jsPlugins) missingFlags.push("--js-plugins");
|
|
357
|
+
return missingFlags;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Formats the complete migration output message
|
|
361
|
+
*/
|
|
362
|
+
function formatMigrationOutput(data) {
|
|
363
|
+
let output = "";
|
|
364
|
+
const showAll = data.cliOptions.details || false;
|
|
365
|
+
if (data.enabledRulesCount === 0) output += `\n⚠️ ${data.outputFileName} created with no rules enabled.\n`;
|
|
366
|
+
else output += `\n✨ ${data.outputFileName} created with ${data.enabledRulesCount} rules.\n`;
|
|
367
|
+
const byCategory = data.skippedRulesByCategory;
|
|
368
|
+
const nurseryCount = byCategory.nursery.length;
|
|
369
|
+
const typeAwareCount = byCategory["type-aware"].length;
|
|
370
|
+
const unsupportedCount = byCategory.unsupported.length;
|
|
371
|
+
const jsPluginsCount = byCategory["js-plugins"].length;
|
|
372
|
+
const totalSkipped = nurseryCount + typeAwareCount + unsupportedCount + jsPluginsCount;
|
|
373
|
+
if (totalSkipped > 0) {
|
|
374
|
+
output += `\n Skipped ${totalSkipped} rules:\n`;
|
|
375
|
+
if (nurseryCount > 0) output += formatCategorySummary(nurseryCount, "nursery", byCategory.nursery, showAll);
|
|
376
|
+
if (typeAwareCount > 0) output += formatCategorySummary(typeAwareCount, "type-aware", byCategory["type-aware"], showAll);
|
|
377
|
+
if (jsPluginsCount > 0) output += formatCategorySummary(jsPluginsCount, "js-plugins", byCategory["js-plugins"], showAll);
|
|
378
|
+
if (unsupportedCount > 0) output += formatCategorySummary(unsupportedCount, "unsupported", byCategory.unsupported, showAll);
|
|
379
|
+
if (!showAll) {
|
|
380
|
+
const maxExamples = 3;
|
|
381
|
+
if (nurseryCount > maxExamples || typeAwareCount > maxExamples || unsupportedCount > maxExamples || jsPluginsCount > maxExamples) output += `\n Tip: Use --details to see the full list.\n`;
|
|
382
|
+
}
|
|
383
|
+
const missingFlags = detectMissingFlags(byCategory, data.cliOptions);
|
|
384
|
+
if (missingFlags.length > 0) {
|
|
385
|
+
const eslintConfigArg = data.eslintConfigPath ? ` ${path.basename(data.eslintConfigPath)}` : "";
|
|
386
|
+
output += `\n👉 Re-run with flags to include more:\n`;
|
|
387
|
+
output += ` npx @oxlint/migrate${eslintConfigArg} ${missingFlags.join(" ")}\n`;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (data.enabledRulesCount > 0) {
|
|
391
|
+
output += `\n🚀 Next:\n`;
|
|
392
|
+
output += ` npx oxlint .\n`;
|
|
393
|
+
}
|
|
394
|
+
return output;
|
|
395
|
+
}
|
|
396
|
+
function displayMigrationResult(outputMessage, warnings) {
|
|
397
|
+
console.log(outputMessage);
|
|
398
|
+
for (const warning of warnings) console.warn(warning);
|
|
399
|
+
}
|
|
400
|
+
|
|
316
401
|
//#endregion
|
|
317
402
|
//#region bin/oxlint-migrate.ts
|
|
318
403
|
const cwd = process.cwd();
|
|
@@ -323,7 +408,22 @@ const getFileContent = (absoluteFilePath) => {
|
|
|
323
408
|
return;
|
|
324
409
|
}
|
|
325
410
|
};
|
|
326
|
-
|
|
411
|
+
/**
|
|
412
|
+
* Count enabled rules (excluding "off" rules) from both rules and overrides
|
|
413
|
+
*/
|
|
414
|
+
const countEnabledRules = (config) => {
|
|
415
|
+
const enabledRules = /* @__PURE__ */ new Set();
|
|
416
|
+
if (config.rules) {
|
|
417
|
+
for (const [ruleName, ruleValue] of Object.entries(config.rules)) if (!isOffValue(ruleValue)) enabledRules.add(ruleName);
|
|
418
|
+
}
|
|
419
|
+
if (config.overrides && Array.isArray(config.overrides)) {
|
|
420
|
+
for (const override of config.overrides) if (override.rules) {
|
|
421
|
+
for (const [ruleName, ruleValue] of Object.entries(override.rules)) if (!isOffValue(ruleValue)) enabledRules.add(ruleName);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return enabledRules.size;
|
|
425
|
+
};
|
|
426
|
+
program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The path to the eslint v9 config file").option("--output-file <file>", "The oxlint configuration file where to eslint v9 rules will be written to", ".oxlintrc.json").option("--merge", "Merge eslint configuration with an existing .oxlintrc.json configuration", false).option("--with-nursery", "Include oxlint rules which are currently under development", false).option("--replace-eslint-comments", "Search in the project files for eslint comments and replaces them with oxlint. Some eslint comments are not supported and will be reported.").option("--type-aware", "Includes supported type-aware rules. Needs the same flag in `oxlint` to enable it.").option("--js-plugins", "Tries to convert unsupported oxlint plugins with `jsPlugins`.").option("--details", "List rules that could not be migrated to oxlint.", false).action(async (filePath) => {
|
|
327
427
|
const cliOptions = program.opts();
|
|
328
428
|
const oxlintFilePath = path.join(cwd, cliOptions.outputFile);
|
|
329
429
|
const reporter = new DefaultReporter();
|
|
@@ -335,7 +435,7 @@ program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The
|
|
|
335
435
|
jsPlugins: !!cliOptions.jsPlugins
|
|
336
436
|
};
|
|
337
437
|
if (cliOptions.replaceEslintComments) {
|
|
338
|
-
await walkAndReplaceProjectFiles(await getAllProjectFiles(), (filePath
|
|
438
|
+
await walkAndReplaceProjectFiles(await getAllProjectFiles(), (filePath) => getFileContent(filePath), (filePath, content) => writeFile(filePath, content, "utf-8"), options);
|
|
339
439
|
return;
|
|
340
440
|
}
|
|
341
441
|
if (filePath === void 0) filePath = getAutodetectedEslintConfigName(cwd);
|
|
@@ -352,7 +452,19 @@ program.name("oxlint-migrate").version(version).argument("[eslint-config]", "The
|
|
|
352
452
|
const oxlintConfig = "default" in eslintConfigs ? await src_default(eslintConfigs.default, config, options) : await src_default(eslintConfigs, config, options);
|
|
353
453
|
if (existsSync(oxlintFilePath)) renameSync(oxlintFilePath, `${oxlintFilePath}.bak`);
|
|
354
454
|
writeFileSync(oxlintFilePath, JSON.stringify(oxlintConfig, null, 2));
|
|
355
|
-
|
|
455
|
+
const enabledRulesCount = countEnabledRules(oxlintConfig);
|
|
456
|
+
displayMigrationResult(formatMigrationOutput({
|
|
457
|
+
outputFileName: cliOptions.outputFile,
|
|
458
|
+
enabledRulesCount,
|
|
459
|
+
skippedRulesByCategory: reporter.getSkippedRulesByCategory(),
|
|
460
|
+
cliOptions: {
|
|
461
|
+
withNursery: !!cliOptions.withNursery,
|
|
462
|
+
typeAware: !!cliOptions.typeAware,
|
|
463
|
+
details: !!cliOptions.details,
|
|
464
|
+
jsPlugins: !!cliOptions.jsPlugins
|
|
465
|
+
},
|
|
466
|
+
eslintConfigPath: filePath
|
|
467
|
+
}), reporter.getWarnings());
|
|
356
468
|
});
|
|
357
469
|
program.parse();
|
|
358
470
|
|