@hublo/sentinel 1.4.0-alpha.35 → 1.4.0-alpha.37
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/dist/bin/sentinel.js +3 -3
- package/dist/{chunk-ZMUTKBZ2.js → chunk-2N5D63HC.js} +79 -303
- package/dist/{chunk-6QCKI5FD.js → chunk-H3GSGAX2.js} +4 -2
- package/dist/{chunk-6QCKI5FD.js.map → chunk-H3GSGAX2.js.map} +1 -1
- package/dist/{chunk-PWV3BMDA.js → chunk-MMKBDO5V.js} +11 -2
- package/dist/chunk-MMKBDO5V.js.map +1 -0
- package/dist/{chunk-ICXQJ4EN.js → chunk-SOCLCHJK.js} +6 -1
- package/dist/{chunk-ICXQJ4EN.js.map → chunk-SOCLCHJK.js.map} +1 -1
- package/dist/{chunk-OORAEYSU.js → chunk-YMSI5ZCB.js} +337 -1
- package/dist/index.js +2 -2
- package/dist/roles/test/nest/toolchain.js +1 -1
- package/dist/roles/test/react/toolchain.js +1 -1
- package/dist/roles/test/setup/file-boundary-close.js +1 -12
- package/dist/roles/test/setup/file-boundary-close.js.map +1 -1
- package/dist/roles/test/setup/file-boundary.js +4 -2
- package/dist/roles/test/setup/file-boundary.js.map +1 -1
- package/dist/roles/test/setup/msw-lifecycle.js +2 -1
- package/dist/roles/test/setup/msw-lifecycle.js.map +1 -1
- package/dist/roles/test/setup/msw-server.js +2 -1
- package/dist/roles/test/setup/msw-server.js.map +1 -1
- package/dist/roles/test/shared-test-config.js +1 -1
- package/dist/{validate-2RTPM5EB.js → validate-MTAEHEMX.js} +49 -10
- package/docs/test-adoption.md +97 -0
- package/package.json +1 -1
- package/dist/chunk-PWV3BMDA.js.map +0 -1
package/dist/bin/sentinel.js
CHANGED
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
palette,
|
|
16
16
|
registerAdapters,
|
|
17
17
|
resolve
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-2N5D63HC.js";
|
|
19
19
|
import {
|
|
20
20
|
resolveContext
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-YMSI5ZCB.js";
|
|
22
22
|
import "../chunk-O7REVMOC.js";
|
|
23
23
|
import "../chunk-QXFCZON7.js";
|
|
24
24
|
import {
|
|
@@ -145,7 +145,7 @@ async function runValidate(ctx) {
|
|
|
145
145
|
);
|
|
146
146
|
}
|
|
147
147
|
if (!ctx.targets.includes("test")) return 1;
|
|
148
|
-
const { validateModuleTests } = await import("../validate-
|
|
148
|
+
const { validateModuleTests } = await import("../validate-MTAEHEMX.js");
|
|
149
149
|
const outcomes = modules.flatMap(
|
|
150
150
|
(module) => validateModuleTests(module.name, module.root)
|
|
151
151
|
);
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALWAYS_LOCAL_KEYS,
|
|
3
3
|
BaseAdapter,
|
|
4
|
+
ESLINT_CONFIG_FILES,
|
|
4
5
|
FORMAT_CONFIG_FILE,
|
|
5
6
|
FORMAT_DEFAULT_IGNORES,
|
|
6
7
|
FORMAT_SCRIPT_NAME,
|
|
8
|
+
LINT_BASELINE_FILE,
|
|
9
|
+
LINT_BASELINE_SPECIFIER,
|
|
10
|
+
LINT_CONFIG_FILE,
|
|
11
|
+
LINT_MEASURE_FILE,
|
|
12
|
+
LINT_SCRIPT_NAME,
|
|
7
13
|
PERMITTED_LOCAL_KEYS,
|
|
8
14
|
PRETTIER_CONFIG_FILES,
|
|
9
15
|
PROVENANCE_KEY,
|
|
@@ -11,22 +17,43 @@ import {
|
|
|
11
17
|
adoptedPackageName,
|
|
12
18
|
binFromOwnInstall,
|
|
13
19
|
binSearchPath,
|
|
20
|
+
canRunTypeAware,
|
|
21
|
+
describeHolds,
|
|
22
|
+
extendsWithBaseline,
|
|
23
|
+
extendsWithPreset,
|
|
14
24
|
formatPresetFor,
|
|
15
25
|
formatPresetName,
|
|
16
26
|
formatTargets,
|
|
27
|
+
heldRules,
|
|
28
|
+
holdsFrom,
|
|
29
|
+
isSentinelPreset,
|
|
30
|
+
lintTarget,
|
|
31
|
+
localOnlyKeys,
|
|
17
32
|
manifestOperation,
|
|
18
33
|
moduleScripts,
|
|
19
34
|
nxTargetOperations,
|
|
20
35
|
oxfmtRejects,
|
|
36
|
+
oxlintPath,
|
|
37
|
+
oxlintSearchPath,
|
|
38
|
+
presetNameFromPath,
|
|
39
|
+
presetOfVariant,
|
|
40
|
+
presetOwnedKeys,
|
|
41
|
+
presetPath,
|
|
42
|
+
presetVariant,
|
|
21
43
|
projectTargets,
|
|
44
|
+
readAdoption,
|
|
22
45
|
readFormatAdoption,
|
|
46
|
+
readLintAdoption,
|
|
47
|
+
renderBaseline,
|
|
23
48
|
resolveBin,
|
|
49
|
+
resolveConfigTarget,
|
|
24
50
|
resolveOxfmt,
|
|
51
|
+
resolveOxlint,
|
|
25
52
|
rewriteToolchainImports,
|
|
26
53
|
selfCommand,
|
|
27
54
|
splitToolArgs,
|
|
28
55
|
walk
|
|
29
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-YMSI5ZCB.js";
|
|
30
57
|
import {
|
|
31
58
|
parseJsonc
|
|
32
59
|
} from "./chunk-QXFCZON7.js";
|
|
@@ -1897,8 +1924,8 @@ function registerFormat() {
|
|
|
1897
1924
|
|
|
1898
1925
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
1899
1926
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
1900
|
-
import { existsSync as
|
|
1901
|
-
import { join as
|
|
1927
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1928
|
+
import { join as join16 } from "node:path";
|
|
1902
1929
|
|
|
1903
1930
|
// src/core/config/deferred-rules.ts
|
|
1904
1931
|
function deferredRuleNames(rules) {
|
|
@@ -1957,104 +1984,6 @@ function describeFixOutcome(outcome, toolLabel) {
|
|
|
1957
1984
|
return `the ${toolLabel} autofix rewrote ${outcome.changed.length} file${outcome.changed.length === 1 ? "" : "s"}: ${listed}. Review the diff before committing: rules this module used to have switched off are enforced from now on.`;
|
|
1958
1985
|
}
|
|
1959
1986
|
|
|
1960
|
-
// src/roles/lint/module-baseline.ts
|
|
1961
|
-
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
1962
|
-
import { join as join12 } from "node:path";
|
|
1963
|
-
var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
|
|
1964
|
-
var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
|
|
1965
|
-
var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
|
|
1966
|
-
function holdsFrom(errorCounts) {
|
|
1967
|
-
return [...errorCounts].map(([rule, count]) => ({ rule, count })).sort((left, right) => right.count - left.count || left.rule.localeCompare(right.rule));
|
|
1968
|
-
}
|
|
1969
|
-
function renderBaseline(holds, version) {
|
|
1970
|
-
const entries = holds.map(({ rule }) => ` ${JSON.stringify(rule)}: "warn"`).join(",\n");
|
|
1971
|
-
return [
|
|
1972
|
-
"{",
|
|
1973
|
-
` // Written by @hublo/sentinel@${version}. Do not edit by hand: \`sentinel --init --lint\``,
|
|
1974
|
-
" // regenerates it from a fresh measurement, and a rule you have since fixed drops out",
|
|
1975
|
-
" // and returns to `error` on its own.",
|
|
1976
|
-
" //",
|
|
1977
|
-
" // These rules are held at `warn` because this module ALREADY violated them when it",
|
|
1978
|
-
" // adopted. They still run and still report, so the build cannot fail on them.",
|
|
1979
|
-
" //",
|
|
1980
|
-
" // The hold is per RULE, not per occurrence: oxlint cannot freeze a specific list of",
|
|
1981
|
-
" // violations, so a NEW violation of one of these rules is covered too, and will only",
|
|
1982
|
-
" // warn. That is the cost of adopting without a red build, and the reason to clear this",
|
|
1983
|
-
" // file rather than live with it.",
|
|
1984
|
-
" //",
|
|
1985
|
-
" // `sentinel --inspect --lint` lists what is held; `sentinel --run --lint --json` counts",
|
|
1986
|
-
" // what is left, per rule. Fix them and re-run `--init --lint` to get the severity back.",
|
|
1987
|
-
' "rules": {',
|
|
1988
|
-
entries,
|
|
1989
|
-
" }",
|
|
1990
|
-
"}",
|
|
1991
|
-
""
|
|
1992
|
-
].join("\n");
|
|
1993
|
-
}
|
|
1994
|
-
function extendsWithBaseline(current, hasHolds) {
|
|
1995
|
-
const withoutBaseline = current.filter((entry) => entry !== LINT_BASELINE_SPECIFIER);
|
|
1996
|
-
return hasHolds ? [...withoutBaseline, LINT_BASELINE_SPECIFIER] : withoutBaseline;
|
|
1997
|
-
}
|
|
1998
|
-
function describeHolds(holds) {
|
|
1999
|
-
if (holds.length === 0) return "";
|
|
2000
|
-
const total = holds.reduce((sum, hold) => sum + hold.count, 0);
|
|
2001
|
-
const listed = holds.slice(0, 5).map(({ rule, count }) => `${rule} (${count})`).join(", ");
|
|
2002
|
-
const rest = holds.length > 5 ? `, and ${holds.length - 5} more` : "";
|
|
2003
|
-
return `held ${holds.length} rule(s) at \`warn\` for this module, covering ${total} violation(s) that were already there: ${listed}${rest}. They are written to ${LINT_BASELINE_FILE}, they still report, and they return to \`error\` once fixed and re-initialized`;
|
|
2004
|
-
}
|
|
2005
|
-
function heldRules(cwd) {
|
|
2006
|
-
const path = join12(cwd, LINT_BASELINE_FILE);
|
|
2007
|
-
if (!existsSync9(path)) return [];
|
|
2008
|
-
try {
|
|
2009
|
-
const parsed = parseJsonc(
|
|
2010
|
-
readFileSync9(path, "utf8"),
|
|
2011
|
-
LINT_BASELINE_FILE
|
|
2012
|
-
);
|
|
2013
|
-
return Object.keys(parsed.rules ?? {});
|
|
2014
|
-
} catch {
|
|
2015
|
-
return [];
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
// src/roles/lint/config-policy.ts
|
|
2020
|
-
var LINT_CONFIG_FILE = ".oxlintrc.json";
|
|
2021
|
-
var LINT_SCRIPT_NAME = "lint";
|
|
2022
|
-
var PRESET_DIR = "./node_modules/@hublo/sentinel/lint";
|
|
2023
|
-
function presetPath(preset) {
|
|
2024
|
-
return `${PRESET_DIR}/${preset}.json`;
|
|
2025
|
-
}
|
|
2026
|
-
function isSentinelPreset(entry) {
|
|
2027
|
-
return /@hublo\/sentinel\/(lint|oxlint)\//.test(entry);
|
|
2028
|
-
}
|
|
2029
|
-
function extendsWithPreset(current, variant) {
|
|
2030
|
-
const own = presetPath(variant);
|
|
2031
|
-
const others = [...new Set(current.filter((entry) => entry !== own && !isSentinelPreset(entry)))];
|
|
2032
|
-
const baseline = others.filter((entry) => entry === LINT_BASELINE_SPECIFIER);
|
|
2033
|
-
const rest = others.filter((entry) => entry !== LINT_BASELINE_SPECIFIER);
|
|
2034
|
-
return [own, ...rest, ...baseline];
|
|
2035
|
-
}
|
|
2036
|
-
function presetVariant(preset, cwd) {
|
|
2037
|
-
const normalised = cwd.replaceAll("\\", "/");
|
|
2038
|
-
return preset === "react" && /(^|\/)libs\//.test(normalised) ? "react-lib" : preset;
|
|
2039
|
-
}
|
|
2040
|
-
function presetOfVariant(variant) {
|
|
2041
|
-
return variant === "react-lib" ? "react" : variant;
|
|
2042
|
-
}
|
|
2043
|
-
var SENTINEL_LINT_PRESET = /@hublo\/sentinel\/(?:lint|oxlint)\/([a-z-]+)\.json$/;
|
|
2044
|
-
function presetNameFromPath(preset) {
|
|
2045
|
-
return preset ? SENTINEL_LINT_PRESET.exec(preset)?.[1] ?? void 0 : void 0;
|
|
2046
|
-
}
|
|
2047
|
-
var PERMITTED_LOCAL_KEYS2 = ["extends", "ignorePatterns"];
|
|
2048
|
-
var ESLINT_CONFIG_FILES = [
|
|
2049
|
-
"eslint.config.js",
|
|
2050
|
-
"eslint.config.mjs",
|
|
2051
|
-
"eslint.config.cjs",
|
|
2052
|
-
"eslint.config.ts"
|
|
2053
|
-
];
|
|
2054
|
-
function lintTarget() {
|
|
2055
|
-
return { cache: true, inputs: ["default", "^default", `{projectRoot}/${LINT_CONFIG_FILE}`] };
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
1987
|
// src/roles/lint/presets/nest.json
|
|
2059
1988
|
var nest_default = {
|
|
2060
1989
|
plugins: ["typescript", "jest", "import", "unicorn", "oxc", "node"],
|
|
@@ -3833,14 +3762,14 @@ function downgradedRulesFor(preset) {
|
|
|
3833
3762
|
}
|
|
3834
3763
|
|
|
3835
3764
|
// src/roles/lint/extra-layers.ts
|
|
3836
|
-
import { existsSync as
|
|
3837
|
-
import { isAbsolute, join as
|
|
3765
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
3766
|
+
import { isAbsolute, join as join12, resolve as resolve2 } from "node:path";
|
|
3838
3767
|
function ruleCount(cwd, specifier) {
|
|
3839
3768
|
const path = isAbsolute(specifier) ? specifier : resolve2(cwd, specifier);
|
|
3840
|
-
if (!
|
|
3769
|
+
if (!existsSync9(path)) return void 0;
|
|
3841
3770
|
try {
|
|
3842
3771
|
const parsed = parseJsonc(
|
|
3843
|
-
|
|
3772
|
+
readFileSync9(path, "utf8"),
|
|
3844
3773
|
specifier
|
|
3845
3774
|
);
|
|
3846
3775
|
return Object.keys(parsed.rules ?? {}).length;
|
|
@@ -3860,7 +3789,7 @@ function extraLayers(cwd, extendsList) {
|
|
|
3860
3789
|
function committedExtendsList(cwd, configFile) {
|
|
3861
3790
|
try {
|
|
3862
3791
|
const parsed = parseJsonc(
|
|
3863
|
-
|
|
3792
|
+
readFileSync9(join12(cwd, configFile), "utf8"),
|
|
3864
3793
|
configFile
|
|
3865
3794
|
);
|
|
3866
3795
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -3918,166 +3847,13 @@ function errorCountsByConfigRule(stdout) {
|
|
|
3918
3847
|
return counts;
|
|
3919
3848
|
}
|
|
3920
3849
|
|
|
3921
|
-
// src/core/config/read-adoption.ts
|
|
3922
|
-
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
|
|
3923
|
-
import { join as join15 } from "node:path";
|
|
3924
|
-
|
|
3925
|
-
// src/core/config/owned-keys.ts
|
|
3926
|
-
function presetOwnedKeys(config, permitted, presetSets) {
|
|
3927
|
-
if (!config) return [];
|
|
3928
|
-
return Object.keys(config).filter(
|
|
3929
|
-
(key) => !permitted.includes(key) && (presetSets === void 0 || presetSets.includes(key))
|
|
3930
|
-
);
|
|
3931
|
-
}
|
|
3932
|
-
function localOnlyKeys(config, permitted, presetSets) {
|
|
3933
|
-
if (!config) return [];
|
|
3934
|
-
return Object.keys(config).filter((key) => !permitted.includes(key) && !presetSets.includes(key));
|
|
3935
|
-
}
|
|
3936
|
-
|
|
3937
|
-
// src/core/config/resolve-config-target.ts
|
|
3938
|
-
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "node:fs";
|
|
3939
|
-
import { join as join14 } from "node:path";
|
|
3940
|
-
function readExtends(absolutePath) {
|
|
3941
|
-
let parsed;
|
|
3942
|
-
try {
|
|
3943
|
-
parsed = parseJsonc(readFileSync11(absolutePath, "utf8"), absolutePath);
|
|
3944
|
-
} catch {
|
|
3945
|
-
return [];
|
|
3946
|
-
}
|
|
3947
|
-
if (typeof parsed.extends === "string") return [parsed.extends];
|
|
3948
|
-
if (Array.isArray(parsed.extends)) {
|
|
3949
|
-
return parsed.extends.filter((entry) => typeof entry === "string");
|
|
3950
|
-
}
|
|
3951
|
-
return [];
|
|
3952
|
-
}
|
|
3953
|
-
function resolveConfigTarget(moduleDir, { candidates, markers, fallback }) {
|
|
3954
|
-
let existing;
|
|
3955
|
-
let existingExtendsSomething = false;
|
|
3956
|
-
for (const candidate of candidates) {
|
|
3957
|
-
const absolutePath = join14(moduleDir, candidate);
|
|
3958
|
-
if (!existsSync11(absolutePath)) continue;
|
|
3959
|
-
const chain = readExtends(absolutePath);
|
|
3960
|
-
if (existing === void 0) {
|
|
3961
|
-
existing = candidate;
|
|
3962
|
-
existingExtendsSomething = chain.length > 0;
|
|
3963
|
-
}
|
|
3964
|
-
const extendsBase = chain.some((value) => markers.some((marker) => value.includes(marker)));
|
|
3965
|
-
if (extendsBase) return { path: candidate, reason: "extends-base" };
|
|
3966
|
-
}
|
|
3967
|
-
if (existing) {
|
|
3968
|
-
return { path: existing, reason: existingExtendsSomething ? "other-chain" : "no-extends" };
|
|
3969
|
-
}
|
|
3970
|
-
return { path: fallback, reason: "none" };
|
|
3971
|
-
}
|
|
3972
|
-
|
|
3973
|
-
// src/core/config/read-adoption.ts
|
|
3974
|
-
function normaliseExtends(value) {
|
|
3975
|
-
if (typeof value === "string") return [value];
|
|
3976
|
-
if (Array.isArray(value)) {
|
|
3977
|
-
return value.filter((entry) => typeof entry === "string");
|
|
3978
|
-
}
|
|
3979
|
-
return [];
|
|
3980
|
-
}
|
|
3981
|
-
var NOT_ADOPTED2 = (configFile, unreadable = null) => ({
|
|
3982
|
-
configFile,
|
|
3983
|
-
preset: null,
|
|
3984
|
-
adopted: false,
|
|
3985
|
-
conformant: false,
|
|
3986
|
-
drift: [],
|
|
3987
|
-
unreadable
|
|
3988
|
-
});
|
|
3989
|
-
function readAdoption(cwd, options) {
|
|
3990
|
-
const target = resolveConfigTarget(cwd, options);
|
|
3991
|
-
if (target.reason === "none" || !existsSync12(join15(cwd, target.path))) {
|
|
3992
|
-
return NOT_ADOPTED2(target.reason === "none" ? null : target.path);
|
|
3993
|
-
}
|
|
3994
|
-
let parsed;
|
|
3995
|
-
try {
|
|
3996
|
-
parsed = parseJsonc(readFileSync12(join15(cwd, target.path), "utf8"), target.path);
|
|
3997
|
-
} catch (error) {
|
|
3998
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
3999
|
-
return NOT_ADOPTED2(target.path, `${target.path} could not be parsed (${reason})`);
|
|
4000
|
-
}
|
|
4001
|
-
const preset = normaliseExtends(parsed.extends).find((entry) => options.presetPattern.test(entry)) ?? null;
|
|
4002
|
-
if (preset === null) return NOT_ADOPTED2(target.path);
|
|
4003
|
-
const settings = options.settingsKey === null ? parsed : parsed[options.settingsKey];
|
|
4004
|
-
const drift = presetOwnedKeys(settings, options.permitted, options.presetOwns?.(preset));
|
|
4005
|
-
return {
|
|
4006
|
-
configFile: target.path,
|
|
4007
|
-
preset,
|
|
4008
|
-
adopted: true,
|
|
4009
|
-
conformant: drift.length === 0,
|
|
4010
|
-
drift,
|
|
4011
|
-
unreadable: null
|
|
4012
|
-
};
|
|
4013
|
-
}
|
|
4014
|
-
|
|
4015
|
-
// src/roles/lint/read-adoption.ts
|
|
4016
|
-
function readLintAdoption(cwd) {
|
|
4017
|
-
return readAdoption(cwd, {
|
|
4018
|
-
candidates: [LINT_CONFIG_FILE],
|
|
4019
|
-
markers: ["@hublo/sentinel/oxlint/"],
|
|
4020
|
-
fallback: LINT_CONFIG_FILE,
|
|
4021
|
-
presetPattern: SENTINEL_LINT_PRESET,
|
|
4022
|
-
// Drift for lint is any TOP-LEVEL key beyond `extends`, so the settings the shared
|
|
4023
|
-
// reader inspects are the config's own root rather than a nested options object.
|
|
4024
|
-
settingsKey: null,
|
|
4025
|
-
permitted: PERMITTED_LOCAL_KEYS2
|
|
4026
|
-
});
|
|
4027
|
-
}
|
|
4028
|
-
|
|
4029
|
-
// src/roles/lint/resolve-oxlint.ts
|
|
4030
|
-
import { existsSync as existsSync13 } from "node:fs";
|
|
4031
|
-
import { createRequire } from "node:module";
|
|
4032
|
-
import { delimiter, dirname, join as join16 } from "node:path";
|
|
4033
|
-
import { fileURLToPath } from "node:url";
|
|
4034
|
-
var PACKAGE_OF = {
|
|
4035
|
-
oxlint: "oxlint",
|
|
4036
|
-
tsgolint: "oxlint-tsgolint"
|
|
4037
|
-
};
|
|
4038
|
-
var require2 = createRequire(import.meta.url);
|
|
4039
|
-
function resolveOxlint(cwd, name = "oxlint") {
|
|
4040
|
-
return resolveBin(cwd, name) ?? binFromOwnInstall(PACKAGE_OF[name], name);
|
|
4041
|
-
}
|
|
4042
|
-
function canRunTypeAware(cwd) {
|
|
4043
|
-
return tsgolintShim(cwd) !== void 0;
|
|
4044
|
-
}
|
|
4045
|
-
function oxlintPath(cwd, env) {
|
|
4046
|
-
const shim = tsgolintShim(cwd);
|
|
4047
|
-
if (!shim) return env.PATH;
|
|
4048
|
-
return [dirname(shim), env.PATH].filter(Boolean).join(delimiter);
|
|
4049
|
-
}
|
|
4050
|
-
function tsgolintShim(cwd) {
|
|
4051
|
-
const fromModule = resolveBin(cwd, "tsgolint");
|
|
4052
|
-
if (fromModule) return fromModule;
|
|
4053
|
-
const candidates = [];
|
|
4054
|
-
for (const owner of ["oxlint-tsgolint", "oxlint"]) {
|
|
4055
|
-
try {
|
|
4056
|
-
const packageDir = dirname(require2.resolve(`${owner}/package.json`));
|
|
4057
|
-
candidates.push(join16(packageDir, "node_modules", ".bin", "tsgolint"));
|
|
4058
|
-
candidates.push(join16(packageDir, "..", ".bin", "tsgolint"));
|
|
4059
|
-
} catch {
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
try {
|
|
4063
|
-
const ownRoot = dirname(require2.resolve("@hublo/sentinel/package.json"));
|
|
4064
|
-
candidates.push(join16(ownRoot, "node_modules", ".bin", "tsgolint"));
|
|
4065
|
-
} catch {
|
|
4066
|
-
candidates.push(resolveBin(dirname(fileURLToPath(import.meta.url)), "tsgolint") ?? "");
|
|
4067
|
-
}
|
|
4068
|
-
return candidates.find((candidate) => candidate !== "" && existsSync13(candidate));
|
|
4069
|
-
}
|
|
4070
|
-
function oxlintSearchPath(cwd) {
|
|
4071
|
-
return binSearchPath(cwd);
|
|
4072
|
-
}
|
|
4073
|
-
|
|
4074
3850
|
// src/roles/lint/adapters/oxlint/plan.ts
|
|
4075
|
-
import { existsSync as
|
|
4076
|
-
import { join as
|
|
3851
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "node:fs";
|
|
3852
|
+
import { join as join15 } from "node:path";
|
|
4077
3853
|
|
|
4078
3854
|
// src/roles/lint/eslint-ignores.ts
|
|
4079
|
-
import { existsSync as
|
|
4080
|
-
import { join as
|
|
3855
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
|
|
3856
|
+
import { join as join13 } from "node:path";
|
|
4081
3857
|
import { parseSync } from "oxc-parser";
|
|
4082
3858
|
var OPAQUE_SOURCE = /\b(includeIgnoreFile)\s*\([^)]*\)/g;
|
|
4083
3859
|
function toOxlintPattern(pattern) {
|
|
@@ -4089,11 +3865,11 @@ function readRootEslintIgnores(root) {
|
|
|
4089
3865
|
return { patterns: patterns.filter((pattern) => pattern.startsWith("**/")), unresolved };
|
|
4090
3866
|
}
|
|
4091
3867
|
function readEslintIgnores(cwd) {
|
|
4092
|
-
const config = ESLINT_CONFIG_FILES.map((name) =>
|
|
3868
|
+
const config = ESLINT_CONFIG_FILES.map((name) => join13(cwd, name)).find((path) => existsSync10(path));
|
|
4093
3869
|
if (!config) return { patterns: [], unresolved: [] };
|
|
4094
3870
|
let source;
|
|
4095
3871
|
try {
|
|
4096
|
-
source =
|
|
3872
|
+
source = readFileSync10(config, "utf8");
|
|
4097
3873
|
} catch {
|
|
4098
3874
|
return { patterns: [], unresolved: [] };
|
|
4099
3875
|
}
|
|
@@ -4184,8 +3960,8 @@ function lintPresetFor(preset) {
|
|
|
4184
3960
|
}
|
|
4185
3961
|
|
|
4186
3962
|
// src/roles/lint/rename-suppressions.ts
|
|
4187
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
4188
|
-
import { join as
|
|
3963
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
|
|
3964
|
+
import { join as join14, relative as relative3 } from "node:path";
|
|
4189
3965
|
var SOURCE_EXTENSIONS = [
|
|
4190
3966
|
".ts",
|
|
4191
3967
|
".tsx",
|
|
@@ -4232,7 +4008,7 @@ function* sourceFiles(dir) {
|
|
|
4232
4008
|
return;
|
|
4233
4009
|
}
|
|
4234
4010
|
for (const entry of entries) {
|
|
4235
|
-
const full =
|
|
4011
|
+
const full = join14(dir, entry);
|
|
4236
4012
|
let isDirectory;
|
|
4237
4013
|
try {
|
|
4238
4014
|
isDirectory = statSync3(full).isDirectory();
|
|
@@ -4252,7 +4028,7 @@ function findSuppressionRenames(cwd, renames) {
|
|
|
4252
4028
|
for (const file of sourceFiles(cwd)) {
|
|
4253
4029
|
let content;
|
|
4254
4030
|
try {
|
|
4255
|
-
content =
|
|
4031
|
+
content = readFileSync11(file, "utf8");
|
|
4256
4032
|
} catch {
|
|
4257
4033
|
continue;
|
|
4258
4034
|
}
|
|
@@ -4380,7 +4156,7 @@ function plan2(context) {
|
|
|
4380
4156
|
keys: removableDeps
|
|
4381
4157
|
});
|
|
4382
4158
|
}
|
|
4383
|
-
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) =>
|
|
4159
|
+
const eslintConfigs = ESLINT_CONFIG_FILES.filter((name) => existsSync11(join15(context.cwd, name)));
|
|
4384
4160
|
for (const name of eslintConfigs) operations.push({ kind: "delete", path: name });
|
|
4385
4161
|
operations.push(
|
|
4386
4162
|
...nxTargetOperations({
|
|
@@ -4478,7 +4254,7 @@ function lintScripts(cwd) {
|
|
|
4478
4254
|
function committedExtends(cwd) {
|
|
4479
4255
|
try {
|
|
4480
4256
|
const parsed = parseJsonc(
|
|
4481
|
-
|
|
4257
|
+
readFileSync12(join15(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4482
4258
|
LINT_CONFIG_FILE
|
|
4483
4259
|
);
|
|
4484
4260
|
return Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4489,7 +4265,7 @@ function committedExtends(cwd) {
|
|
|
4489
4265
|
function committedIgnorePatterns(cwd) {
|
|
4490
4266
|
try {
|
|
4491
4267
|
const parsed = parseJsonc(
|
|
4492
|
-
|
|
4268
|
+
readFileSync12(join15(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4493
4269
|
LINT_CONFIG_FILE
|
|
4494
4270
|
);
|
|
4495
4271
|
return Array.isArray(parsed.ignorePatterns) ? parsed.ignorePatterns.filter((entry) => typeof entry === "string") : [];
|
|
@@ -4501,7 +4277,7 @@ function moduleEslintDependencies(cwd) {
|
|
|
4501
4277
|
const isEslintPackage = (name) => name === "eslint" || name === "@types/eslint" || name === "typescript-eslint" || name.startsWith("@typescript-eslint/") || name.startsWith("eslint-plugin-") || name.startsWith("eslint-config-") || name.startsWith("@eslint/");
|
|
4502
4278
|
let manifest;
|
|
4503
4279
|
try {
|
|
4504
|
-
manifest = JSON.parse(
|
|
4280
|
+
manifest = JSON.parse(readFileSync12(join15(cwd, "package.json"), "utf8"));
|
|
4505
4281
|
} catch {
|
|
4506
4282
|
return [];
|
|
4507
4283
|
}
|
|
@@ -4635,20 +4411,20 @@ ${result.stderr ?? ""}`);
|
|
|
4635
4411
|
* build the developer can see, rather than a silent half-adoption they cannot.
|
|
4636
4412
|
*/
|
|
4637
4413
|
writeModuleBaseline(ctx, oxlint, env) {
|
|
4638
|
-
const configPath =
|
|
4639
|
-
const baselinePath =
|
|
4640
|
-
if (!
|
|
4414
|
+
const configPath = join16(ctx.cwd, LINT_CONFIG_FILE);
|
|
4415
|
+
const baselinePath = join16(ctx.cwd, LINT_BASELINE_FILE);
|
|
4416
|
+
if (!existsSync12(configPath)) return;
|
|
4641
4417
|
let config;
|
|
4642
4418
|
try {
|
|
4643
4419
|
config = parseJsonc(
|
|
4644
|
-
|
|
4420
|
+
readFileSync13(configPath, "utf8"),
|
|
4645
4421
|
LINT_CONFIG_FILE
|
|
4646
4422
|
);
|
|
4647
4423
|
} catch {
|
|
4648
4424
|
return;
|
|
4649
4425
|
}
|
|
4650
4426
|
const current = Array.isArray(config.extends) ? config.extends : [];
|
|
4651
|
-
const measurePath =
|
|
4427
|
+
const measurePath = join16(ctx.cwd, LINT_MEASURE_FILE);
|
|
4652
4428
|
let measured;
|
|
4653
4429
|
try {
|
|
4654
4430
|
writeFileSync2(
|
|
@@ -4696,7 +4472,7 @@ ${result.stderr ?? ""}`);
|
|
|
4696
4472
|
}
|
|
4697
4473
|
}
|
|
4698
4474
|
async run(ctx) {
|
|
4699
|
-
if (!
|
|
4475
|
+
if (!existsSync12(join16(ctx.cwd, LINT_CONFIG_FILE))) {
|
|
4700
4476
|
process.stderr.write(
|
|
4701
4477
|
`sentinel lint(oxlint): no ${LINT_CONFIG_FILE} in this module; run \`sentinel --init --lint\` to adopt.
|
|
4702
4478
|
`
|
|
@@ -4844,7 +4620,7 @@ ${result.stderr ?? ""}`);
|
|
|
4844
4620
|
let stub;
|
|
4845
4621
|
try {
|
|
4846
4622
|
stub = parseJsonc(
|
|
4847
|
-
|
|
4623
|
+
readFileSync13(join16(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4848
4624
|
LINT_CONFIG_FILE
|
|
4849
4625
|
);
|
|
4850
4626
|
} catch {
|
|
@@ -4854,7 +4630,7 @@ ${result.stderr ?? ""}`);
|
|
|
4854
4630
|
for (const entry of stub.extends ?? []) {
|
|
4855
4631
|
try {
|
|
4856
4632
|
const preset = parseJsonc(
|
|
4857
|
-
|
|
4633
|
+
readFileSync13(join16(cwd, entry), "utf8"),
|
|
4858
4634
|
entry
|
|
4859
4635
|
);
|
|
4860
4636
|
for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
|
|
@@ -4924,14 +4700,14 @@ ${result.stderr ?? ""}`);
|
|
|
4924
4700
|
let parsed;
|
|
4925
4701
|
try {
|
|
4926
4702
|
parsed = parseJsonc(
|
|
4927
|
-
|
|
4703
|
+
readFileSync13(join16(cwd, LINT_CONFIG_FILE), "utf8"),
|
|
4928
4704
|
LINT_CONFIG_FILE
|
|
4929
4705
|
);
|
|
4930
4706
|
} catch {
|
|
4931
4707
|
return void 0;
|
|
4932
4708
|
}
|
|
4933
4709
|
const targets = Array.isArray(parsed.extends) ? parsed.extends.filter((entry) => typeof entry === "string") : typeof parsed.extends === "string" ? [parsed.extends] : [];
|
|
4934
|
-
return targets.find((target) => !
|
|
4710
|
+
return targets.find((target) => !existsSync12(join16(cwd, target)));
|
|
4935
4711
|
}
|
|
4936
4712
|
/** Announce what is not enforced, so reduced coverage is never silent. */
|
|
4937
4713
|
announceDisabled(preset) {
|
|
@@ -4969,9 +4745,9 @@ function registerTest() {
|
|
|
4969
4745
|
|
|
4970
4746
|
// src/roles/typescript/adapters/tsc/tsc.adapter.ts
|
|
4971
4747
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4972
|
-
import { existsSync as
|
|
4973
|
-
import { createRequire
|
|
4974
|
-
import { join as
|
|
4748
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
|
|
4749
|
+
import { createRequire } from "node:module";
|
|
4750
|
+
import { join as join18 } from "node:path";
|
|
4975
4751
|
|
|
4976
4752
|
// src/roles/typescript/presets/nest.json
|
|
4977
4753
|
var nest_default2 = {
|
|
@@ -5152,8 +4928,8 @@ function readTsconfigAdoption(cwd) {
|
|
|
5152
4928
|
}
|
|
5153
4929
|
|
|
5154
4930
|
// src/roles/typescript/adapters/tsc/plan.ts
|
|
5155
|
-
import { readFileSync as
|
|
5156
|
-
import { join as
|
|
4931
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
4932
|
+
import { join as join17 } from "node:path";
|
|
5157
4933
|
|
|
5158
4934
|
// src/roles/typescript/typecheck-script.ts
|
|
5159
4935
|
var SENTINEL_TYPECHECK_COMMAND = "sentinel --run --typescript";
|
|
@@ -5246,7 +5022,7 @@ function planAdoption(context) {
|
|
|
5246
5022
|
};
|
|
5247
5023
|
}
|
|
5248
5024
|
const existing = parseJsonc(
|
|
5249
|
-
|
|
5025
|
+
readFileSync14(join17(context.cwd, target.path), "utf8"),
|
|
5250
5026
|
target.path
|
|
5251
5027
|
);
|
|
5252
5028
|
const extendsChain = composeExtends(existing.extends, preset);
|
|
@@ -5384,7 +5160,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5384
5160
|
let chain;
|
|
5385
5161
|
try {
|
|
5386
5162
|
const parsed = parseJsonc(
|
|
5387
|
-
|
|
5163
|
+
readFileSync15(join18(cwd, target.path), "utf8"),
|
|
5388
5164
|
target.path
|
|
5389
5165
|
);
|
|
5390
5166
|
chain = parsed.extends;
|
|
@@ -5397,7 +5173,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5397
5173
|
);
|
|
5398
5174
|
if (preset === void 0) return void 0;
|
|
5399
5175
|
try {
|
|
5400
|
-
|
|
5176
|
+
createRequire(join18(cwd, "noop.js")).resolve(preset);
|
|
5401
5177
|
return void 0;
|
|
5402
5178
|
} catch {
|
|
5403
5179
|
return preset;
|
|
@@ -5493,7 +5269,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5493
5269
|
referencedProjects(cwd, config) {
|
|
5494
5270
|
try {
|
|
5495
5271
|
const parsed = parseJsonc(
|
|
5496
|
-
|
|
5272
|
+
readFileSync15(join18(cwd, config), "utf8"),
|
|
5497
5273
|
config
|
|
5498
5274
|
);
|
|
5499
5275
|
const referenced = (parsed.references ?? []).map((reference) => reference.path).filter((path) => typeof path === "string" && path.length > 0);
|
|
@@ -5509,7 +5285,7 @@ var TscAdapter = class _TscAdapter extends BaseAdapter {
|
|
|
5509
5285
|
* check.
|
|
5510
5286
|
*/
|
|
5511
5287
|
typecheckTarget(cwd) {
|
|
5512
|
-
if (
|
|
5288
|
+
if (existsSync13(join18(cwd, "tsconfig.json"))) return "tsconfig.json";
|
|
5513
5289
|
const target = resolveTsconfigTarget(cwd);
|
|
5514
5290
|
return target.reason === "none" ? null : target.path;
|
|
5515
5291
|
}
|
|
@@ -5714,7 +5490,7 @@ function replaceLines(current, replacements) {
|
|
|
5714
5490
|
}
|
|
5715
5491
|
|
|
5716
5492
|
// src/core/apply-plan.ts
|
|
5717
|
-
import { existsSync as
|
|
5493
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5718
5494
|
import { resolve as resolve3, sep } from "node:path";
|
|
5719
5495
|
import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser";
|
|
5720
5496
|
|
|
@@ -5733,7 +5509,7 @@ function resolveWithinRoot(cwd, relativePath) {
|
|
|
5733
5509
|
return absolutePath;
|
|
5734
5510
|
}
|
|
5735
5511
|
function readIfExists(absolutePath) {
|
|
5736
|
-
return
|
|
5512
|
+
return existsSync14(absolutePath) ? readFileSync16(absolutePath, "utf8") : void 0;
|
|
5737
5513
|
}
|
|
5738
5514
|
function* leaves(value, prefix = []) {
|
|
5739
5515
|
for (const [key, keyValue] of Object.entries(value)) {
|
|
@@ -5851,8 +5627,8 @@ function applyPlan(cwd, plan3) {
|
|
|
5851
5627
|
}
|
|
5852
5628
|
|
|
5853
5629
|
// src/core/config/preset-evidence.ts
|
|
5854
|
-
import { existsSync as
|
|
5855
|
-
import { join as
|
|
5630
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync17 } from "node:fs";
|
|
5631
|
+
import { join as join19 } from "node:path";
|
|
5856
5632
|
var PATH_SIGNALS = [
|
|
5857
5633
|
{
|
|
5858
5634
|
preset: "nest",
|
|
@@ -5886,12 +5662,12 @@ function sourceImports(cwd, pattern) {
|
|
|
5886
5662
|
}
|
|
5887
5663
|
for (const entry of entries) {
|
|
5888
5664
|
if (entry.isDirectory()) {
|
|
5889
|
-
if (!NOT_SCANNED.has(entry.name)) stack.push(
|
|
5665
|
+
if (!NOT_SCANNED.has(entry.name)) stack.push(join19(dir, entry.name));
|
|
5890
5666
|
continue;
|
|
5891
5667
|
}
|
|
5892
5668
|
if (!SOURCE_FILE.test(entry.name)) continue;
|
|
5893
5669
|
try {
|
|
5894
|
-
if (pattern.test(
|
|
5670
|
+
if (pattern.test(readFileSync17(join19(dir, entry.name), "utf8"))) return true;
|
|
5895
5671
|
} catch {
|
|
5896
5672
|
continue;
|
|
5897
5673
|
}
|
|
@@ -5900,11 +5676,11 @@ function sourceImports(cwd, pattern) {
|
|
|
5900
5676
|
return false;
|
|
5901
5677
|
}
|
|
5902
5678
|
function dependencyNames(cwd) {
|
|
5903
|
-
const path =
|
|
5904
|
-
if (!
|
|
5679
|
+
const path = join19(cwd, "package.json");
|
|
5680
|
+
if (!existsSync15(path)) return [];
|
|
5905
5681
|
try {
|
|
5906
5682
|
const manifest = parseJsonc(
|
|
5907
|
-
|
|
5683
|
+
readFileSync17(path, "utf8"),
|
|
5908
5684
|
path
|
|
5909
5685
|
);
|
|
5910
5686
|
return [
|
|
@@ -5927,7 +5703,7 @@ function declaresJsx(cwd) {
|
|
|
5927
5703
|
for (const name of entries) {
|
|
5928
5704
|
try {
|
|
5929
5705
|
const config = parseJsonc(
|
|
5930
|
-
|
|
5706
|
+
readFileSync17(join19(cwd, name), "utf8"),
|
|
5931
5707
|
name
|
|
5932
5708
|
);
|
|
5933
5709
|
if (config.compilerOptions?.jsx !== void 0) return true;
|