@isentinel/eslint-config 6.2.0 → 6.4.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 +29 -19
- package/dist/cli.mjs +6 -6
- package/dist/index.d.mts +946 -162
- package/dist/index.mjs +784 -490
- package/dist/lint-cli.mjs +64 -54
- package/dist/oxfmt.mjs +61 -22
- package/dist/oxlint.d.mts +1007 -97
- package/dist/oxlint.mjs +236 -136
- package/package.json +16 -16
package/dist/lint-cli.mjs
CHANGED
|
@@ -42,7 +42,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
42
42
|
}) : target, mod));
|
|
43
43
|
//#endregion
|
|
44
44
|
//#region package.json
|
|
45
|
-
var version = "6.
|
|
45
|
+
var version = "6.4.0";
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/lint-cli/lib/cli/types.ts
|
|
48
48
|
/**
|
|
@@ -1512,6 +1512,65 @@ function quotePosix(token) {
|
|
|
1512
1512
|
return `'${token.replaceAll("'", "'\\''")}'`;
|
|
1513
1513
|
}
|
|
1514
1514
|
//#endregion
|
|
1515
|
+
//#region src/hybrid-status.ts
|
|
1516
|
+
/**
|
|
1517
|
+
* Set by `isentinel-lint` in its ESLint children. Only those evaluations load
|
|
1518
|
+
* the config the lint run uses, so only they may write the status: tests,
|
|
1519
|
+
* scripts and generators that build other configs in the project root would
|
|
1520
|
+
* otherwise overwrite it.
|
|
1521
|
+
*/
|
|
1522
|
+
const HYBRID_STATUS_ENV = "ISENTINEL_WRITE_HYBRID_STATUS";
|
|
1523
|
+
/**
|
|
1524
|
+
* Absolute path to the hybrid-status file for a project root.
|
|
1525
|
+
*
|
|
1526
|
+
* @param cwd - The project root.
|
|
1527
|
+
* @returns The absolute status-file path.
|
|
1528
|
+
*/
|
|
1529
|
+
function hybridStatusPath(cwd) {
|
|
1530
|
+
return statePath(cwd, "hybrid-status");
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Read the persisted hybrid status, or `undefined` when the file is missing,
|
|
1534
|
+
* unreadable or malformed (the CLI then treats the status as unknown).
|
|
1535
|
+
*
|
|
1536
|
+
* @param cwd - The project root.
|
|
1537
|
+
* @returns The parsed status, or `undefined`.
|
|
1538
|
+
*/
|
|
1539
|
+
function readHybridStatus(cwd) {
|
|
1540
|
+
const stored = readState(hybridStatusPath(cwd));
|
|
1541
|
+
return typeof stored?.oxlint === "boolean" ? stored : void 0;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Passively record whether the resolved ESLint config runs in hybrid mode.
|
|
1545
|
+
* Called from the factory on every config evaluation in a lint child, so an
|
|
1546
|
+
* unchanged status is touched rather than rewritten: the file must not churn
|
|
1547
|
+
* as both lint passes re-evaluate the config, yet its mtime must stay ahead of
|
|
1548
|
+
* the config's or the CLI re-runs its ~3s probe every lint. Every failure is
|
|
1549
|
+
* swallowed — config evaluation must never throw for this, and the CLI treats
|
|
1550
|
+
* a missing or stale file as "unknown" and re-probes.
|
|
1551
|
+
*
|
|
1552
|
+
* Skipped entirely when `node_modules` is absent (nothing installed, so no
|
|
1553
|
+
* cache home and no CLI to read it).
|
|
1554
|
+
*
|
|
1555
|
+
* @param cwd - The project root.
|
|
1556
|
+
* @param oxlint - Whether the config enabled hybrid mode.
|
|
1557
|
+
*/
|
|
1558
|
+
function writeHybridStatus(cwd, oxlint) {
|
|
1559
|
+
let installed;
|
|
1560
|
+
try {
|
|
1561
|
+
installed = fs.existsSync(path.resolve(cwd, "node_modules"));
|
|
1562
|
+
} catch {
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
if (!installed) return;
|
|
1566
|
+
const filePath = hybridStatusPath(cwd);
|
|
1567
|
+
if (readHybridStatus(cwd)?.oxlint === oxlint) {
|
|
1568
|
+
touchState(filePath);
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
writeState(filePath, { oxlint });
|
|
1572
|
+
}
|
|
1573
|
+
//#endregion
|
|
1515
1574
|
//#region src/lint-cli/lib/plan/command.ts
|
|
1516
1575
|
/**
|
|
1517
1576
|
* Compose the oxlint child command.
|
|
@@ -1578,7 +1637,10 @@ function composeEslintCommand(options, context) {
|
|
|
1578
1637
|
if (options.agents) args.push("--format", context.agentsFormatterPath);
|
|
1579
1638
|
if (context.fix) args.push("--fix");
|
|
1580
1639
|
args.push(...options.eslintArgs, ...pathArguments(context.paths));
|
|
1581
|
-
const environment = {
|
|
1640
|
+
const environment = {
|
|
1641
|
+
ESLINT_TYPE_AWARE: context.typeAwareEnv,
|
|
1642
|
+
[HYBRID_STATUS_ENV]: "1"
|
|
1643
|
+
};
|
|
1582
1644
|
if (context.quietWarnings === true) environment[QUIET_WARNINGS_ENV] = "1";
|
|
1583
1645
|
if (context.heap?.nodeOptions !== void 0) environment["NODE_OPTIONS"] = context.heap.nodeOptions;
|
|
1584
1646
|
return {
|
|
@@ -4965,58 +5027,6 @@ function queryIgnoredFiles(cwd, targets) {
|
|
|
4965
5027
|
}
|
|
4966
5028
|
}
|
|
4967
5029
|
//#endregion
|
|
4968
|
-
//#region src/hybrid-status.ts
|
|
4969
|
-
/**
|
|
4970
|
-
* Absolute path to the hybrid-status file for a project root.
|
|
4971
|
-
*
|
|
4972
|
-
* @param cwd - The project root.
|
|
4973
|
-
* @returns The absolute status-file path.
|
|
4974
|
-
*/
|
|
4975
|
-
function hybridStatusPath(cwd) {
|
|
4976
|
-
return statePath(cwd, "hybrid-status");
|
|
4977
|
-
}
|
|
4978
|
-
/**
|
|
4979
|
-
* Read the persisted hybrid status, or `undefined` when the file is missing,
|
|
4980
|
-
* unreadable or malformed (the CLI then treats the status as unknown).
|
|
4981
|
-
*
|
|
4982
|
-
* @param cwd - The project root.
|
|
4983
|
-
* @returns The parsed status, or `undefined`.
|
|
4984
|
-
*/
|
|
4985
|
-
function readHybridStatus(cwd) {
|
|
4986
|
-
const stored = readState(hybridStatusPath(cwd));
|
|
4987
|
-
return typeof stored?.oxlint === "boolean" ? stored : void 0;
|
|
4988
|
-
}
|
|
4989
|
-
/**
|
|
4990
|
-
* Passively record whether the resolved ESLint config runs in hybrid mode.
|
|
4991
|
-
* Called from the factory on every config evaluation, so an unchanged status is
|
|
4992
|
-
* touched rather than rewritten: the file must not churn as editors and both
|
|
4993
|
-
* lint passes re-evaluate the config, yet its mtime must stay ahead of the
|
|
4994
|
-
* config's or the CLI re-runs its ~3s probe every lint. Every failure is
|
|
4995
|
-
* swallowed — config evaluation must never throw for this, and the CLI treats a
|
|
4996
|
-
* missing or stale file as "unknown" and re-probes.
|
|
4997
|
-
*
|
|
4998
|
-
* Skipped entirely when `node_modules` is absent (nothing installed, so no
|
|
4999
|
-
* cache home and no CLI to read it).
|
|
5000
|
-
*
|
|
5001
|
-
* @param cwd - The project root.
|
|
5002
|
-
* @param oxlint - Whether the config enabled hybrid mode.
|
|
5003
|
-
*/
|
|
5004
|
-
function writeHybridStatus(cwd, oxlint) {
|
|
5005
|
-
let installed;
|
|
5006
|
-
try {
|
|
5007
|
-
installed = fs.existsSync(path.resolve(cwd, "node_modules"));
|
|
5008
|
-
} catch {
|
|
5009
|
-
return;
|
|
5010
|
-
}
|
|
5011
|
-
if (!installed) return;
|
|
5012
|
-
const filePath = hybridStatusPath(cwd);
|
|
5013
|
-
if (readHybridStatus(cwd)?.oxlint === oxlint) {
|
|
5014
|
-
touchState(filePath);
|
|
5015
|
-
return;
|
|
5016
|
-
}
|
|
5017
|
-
writeState(filePath, { oxlint });
|
|
5018
|
-
}
|
|
5019
|
-
//#endregion
|
|
5020
5030
|
//#region src/lint-cli/lib/hybrid/probe.ts
|
|
5021
5031
|
/**
|
|
5022
5032
|
* Read the hybrid marker from `eslint --print-config` stdout. Config evaluation
|
package/dist/oxfmt.mjs
CHANGED
|
@@ -315,15 +315,15 @@ function parseJsonLike(contents) {
|
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
-
function isRecord(value) {
|
|
318
|
+
function isRecord$1(value) {
|
|
319
319
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
320
320
|
}
|
|
321
321
|
function readPackageJsonPrettierKey(filePath) {
|
|
322
322
|
const contents = readFileOrUndefined(filePath);
|
|
323
323
|
if (contents === void 0) return;
|
|
324
324
|
const parsed = parseJsonLike(contents);
|
|
325
|
-
const prettierKey = isRecord(parsed) ? parsed["prettier"] : void 0;
|
|
326
|
-
return isRecord(prettierKey) ? prettierKey : void 0;
|
|
325
|
+
const prettierKey = isRecord$1(parsed) ? parsed["prettier"] : void 0;
|
|
326
|
+
return isRecord$1(prettierKey) ? prettierKey : void 0;
|
|
327
327
|
}
|
|
328
328
|
/**
|
|
329
329
|
* Load a JS Prettier config. Node's `require` handles ESM entry points
|
|
@@ -336,8 +336,8 @@ function readPackageJsonPrettierKey(filePath) {
|
|
|
336
336
|
function requireConfigModule(filePath) {
|
|
337
337
|
try {
|
|
338
338
|
const loaded = createRequire(filePath)(filePath);
|
|
339
|
-
const value = isRecord(loaded) && "default" in loaded ? loaded["default"] : loaded;
|
|
340
|
-
return isRecord(value) ? value : void 0;
|
|
339
|
+
const value = isRecord$1(loaded) && "default" in loaded ? loaded["default"] : loaded;
|
|
340
|
+
return isRecord$1(value) ? value : void 0;
|
|
341
341
|
} catch {
|
|
342
342
|
return;
|
|
343
343
|
}
|
|
@@ -355,7 +355,7 @@ function readConfigFile(filePath) {
|
|
|
355
355
|
const contents = readFileOrUndefined(filePath);
|
|
356
356
|
if (contents === void 0) return;
|
|
357
357
|
const parsed = parseJsonLike(contents) ?? parseYamlLike(contents);
|
|
358
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
358
|
+
return isRecord$1(parsed) ? parsed : void 0;
|
|
359
359
|
}
|
|
360
360
|
function ancestorDirectories(cwd) {
|
|
361
361
|
const directories = [];
|
|
@@ -481,6 +481,17 @@ function resolveEditorConfigOptions(cwd) {
|
|
|
481
481
|
return editorConfigToPrettier(properties);
|
|
482
482
|
}
|
|
483
483
|
/**
|
|
484
|
+
* Read a numeric `.editorconfig` value. An absent or empty value sets nothing.
|
|
485
|
+
*
|
|
486
|
+
* @param value - The raw property value.
|
|
487
|
+
* @returns The finite number, or `undefined`.
|
|
488
|
+
*/
|
|
489
|
+
function editorConfigNumber(value) {
|
|
490
|
+
if (value === void 0 || value === "") return;
|
|
491
|
+
const parsed = Number(value);
|
|
492
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
484
495
|
* Map one `.editorconfig` section to oxfmt options, as the rule's config
|
|
485
496
|
* loader does.
|
|
486
497
|
*
|
|
@@ -495,17 +506,34 @@ function editorConfigToOxfmt(properties) {
|
|
|
495
506
|
if (indentStyle === "tab" || indentStyle === "space") options["useTabs"] = indentStyle === "tab";
|
|
496
507
|
const quoteType = properties["quote_type"];
|
|
497
508
|
if (quoteType === "single" || quoteType === "double") options["singleQuote"] = quoteType === "single";
|
|
498
|
-
const indentSize =
|
|
499
|
-
const tabWidth =
|
|
500
|
-
if (
|
|
501
|
-
else if (
|
|
502
|
-
const printWidth =
|
|
503
|
-
if (
|
|
509
|
+
const indentSize = editorConfigNumber(properties["indent_size"]);
|
|
510
|
+
const tabWidth = editorConfigNumber(properties["tab_width"]);
|
|
511
|
+
if (indentSize !== void 0 && options["useTabs"] === false) options["tabWidth"] = indentSize;
|
|
512
|
+
else if (tabWidth !== void 0) options["tabWidth"] = tabWidth;
|
|
513
|
+
const printWidth = editorConfigNumber(properties["max_line_length"]);
|
|
514
|
+
if (printWidth !== void 0) options["printWidth"] = printWidth;
|
|
504
515
|
const finalNewline = properties["insert_final_newline"];
|
|
505
516
|
if (finalNewline === "true" || finalNewline === "false") options["insertFinalNewline"] = finalNewline === "true";
|
|
506
517
|
return options;
|
|
507
518
|
}
|
|
508
519
|
//#endregion
|
|
520
|
+
//#region src/guards.ts
|
|
521
|
+
/**
|
|
522
|
+
* Internal runtime type guards. Prefer these over `as` assertions so values
|
|
523
|
+
* crossing untyped boundaries (`JSON.parse`, dynamic `import`, plugin objects)
|
|
524
|
+
* are validated at runtime rather than asserted away.
|
|
525
|
+
*/
|
|
526
|
+
/**
|
|
527
|
+
* Whether a value is a non-null, non-array object usable as a string-keyed
|
|
528
|
+
* record. Narrows `unknown` without an assertion.
|
|
529
|
+
*
|
|
530
|
+
* @param value - The value to test.
|
|
531
|
+
* @returns Whether the value is a plain object.
|
|
532
|
+
*/
|
|
533
|
+
function isRecord(value) {
|
|
534
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
509
537
|
//#region src/rules/oxfmt.ts
|
|
510
538
|
const UNSUPPORTED_PRETTIER_KEYS = /* @__PURE__ */ new Set([
|
|
511
539
|
"experimentalOperatorPosition",
|
|
@@ -572,12 +600,12 @@ function migratePrettierOptions(prettierOptions) {
|
|
|
572
600
|
function migratePrettierOverrides(overrides) {
|
|
573
601
|
if (!Array.isArray(overrides)) return [];
|
|
574
602
|
return overrides.flatMap((override) => {
|
|
575
|
-
if (
|
|
603
|
+
if (!isRecord(override)) return [];
|
|
576
604
|
const { excludeFiles, files, options } = override;
|
|
577
605
|
return [{
|
|
578
606
|
...excludeFiles === void 0 ? {} : { excludeFiles: stringsOf(excludeFiles) },
|
|
579
607
|
files: stringsOf(files),
|
|
580
|
-
options:
|
|
608
|
+
options: isRecord(options) ? migratePrettierOptions(options) : {}
|
|
581
609
|
}];
|
|
582
610
|
});
|
|
583
611
|
}
|
|
@@ -592,17 +620,11 @@ function migratePrettierOverrides(overrides) {
|
|
|
592
620
|
* @returns The overrides with anchored globs.
|
|
593
621
|
*/
|
|
594
622
|
function anchorOverrideGlobs(overrides) {
|
|
595
|
-
function anchor(glob) {
|
|
596
|
-
const negated = glob.startsWith("!");
|
|
597
|
-
const body = negated ? glob.slice(1) : glob;
|
|
598
|
-
const anchored = body.includes("/") ? body : `./${body}`;
|
|
599
|
-
return negated ? `!${anchored}` : anchored;
|
|
600
|
-
}
|
|
601
623
|
return overrides.map((override) => {
|
|
602
624
|
return {
|
|
603
625
|
...override,
|
|
604
|
-
...override.excludeFiles ? { excludeFiles: override.excludeFiles.map(
|
|
605
|
-
files: override.files.map(
|
|
626
|
+
...override.excludeFiles ? { excludeFiles: override.excludeFiles.map(anchorGlob) } : {},
|
|
627
|
+
files: override.files.map(anchorGlob)
|
|
606
628
|
};
|
|
607
629
|
});
|
|
608
630
|
}
|
|
@@ -627,11 +649,16 @@ function buildOxfmtOptions({ oxfmtConfigOptions = {}, oxfmtOptions, prettierOpti
|
|
|
627
649
|
* ESLint config and the standalone oxfmt config so both format Markdown the
|
|
628
650
|
* same way.
|
|
629
651
|
*
|
|
652
|
+
* Oxfmt would format code blocks with these options too, at the prose width.
|
|
653
|
+
* It leaves them alone instead, and ESLint formats each block with the options
|
|
654
|
+
* of its own language (see `isentinel/oxfmt/markdown-code/*`).
|
|
655
|
+
*
|
|
630
656
|
* @param jsdocPrintWidth - Width for Markdown prose; 80 when unset.
|
|
631
657
|
* @returns Options applied on top of the base for Markdown files.
|
|
632
658
|
*/
|
|
633
659
|
function markdownOxfmtOptions(jsdocPrintWidth) {
|
|
634
660
|
return {
|
|
661
|
+
embeddedLanguageFormatting: "off",
|
|
635
662
|
printWidth: Number(jsdocPrintWidth) || 80,
|
|
636
663
|
proseWrap: "always",
|
|
637
664
|
tabWidth: 2
|
|
@@ -651,6 +678,18 @@ const YAML_OXFMT_OPTIONS = {
|
|
|
651
678
|
tabWidth: 2,
|
|
652
679
|
useTabs: false
|
|
653
680
|
};
|
|
681
|
+
/**
|
|
682
|
+
* Anchor one slash-less glob to the config directory, keeping its negation.
|
|
683
|
+
*
|
|
684
|
+
* @param glob - An override glob.
|
|
685
|
+
* @returns The anchored glob.
|
|
686
|
+
*/
|
|
687
|
+
function anchorGlob(glob) {
|
|
688
|
+
const negated = glob.startsWith("!");
|
|
689
|
+
const body = negated ? glob.slice(1) : glob;
|
|
690
|
+
const anchored = body.includes("/") ? body : `./${body}`;
|
|
691
|
+
return negated ? `!${anchored}` : anchored;
|
|
692
|
+
}
|
|
654
693
|
//#endregion
|
|
655
694
|
//#region src/oxfmt/index.ts
|
|
656
695
|
/**
|