@useinsider/eslint-config 3.0.2-beta.1 → 4.0.0-beta.1
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 +285 -64
- package/dist/builders/configFiles.d.ts +13 -0
- package/dist/builders/oxlint.d.ts +51 -0
- package/dist/builders/oxlintOwnership.d.ts +43 -0
- package/dist/cli.cjs +584 -27
- package/dist/cli.cjs.map +1 -1
- package/dist/configs/dependencies.d.ts +3 -0
- package/dist/{javascript-VBXqjVi8.cjs → javascript-DH8-N38w.cjs} +5 -5
- package/dist/{javascript-VBXqjVi8.cjs.map → javascript-DH8-N38w.cjs.map} +1 -1
- package/dist/{javascript-dom-5jRL_yKP.cjs → javascript-dom-CvtQ6_9W.cjs} +4 -4
- package/dist/{javascript-dom-5jRL_yKP.cjs.map → javascript-dom-CvtQ6_9W.cjs.map} +1 -1
- package/dist/{javascript-node-C1AU_4X2.cjs → javascript-node-BNsy3loY.cjs} +4 -4
- package/dist/{javascript-node-C1AU_4X2.cjs.map → javascript-node-BNsy3loY.cjs.map} +1 -1
- package/dist/{jest-CHzjzBtf.cjs → jest-4Es9Ggaa.cjs} +3 -3
- package/dist/{jest-CHzjzBtf.cjs.map → jest-4Es9Ggaa.cjs.map} +1 -1
- package/dist/main.cjs +452 -73
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.ts +9 -2
- package/dist/main.js +435 -56
- package/dist/main.js.map +1 -1
- package/dist/oxlintOwnership-7hpdB3d1.cjs +199 -0
- package/dist/oxlintOwnership-7hpdB3d1.cjs.map +1 -0
- package/dist/oxlintOwnership-Cx0hgA_J.js +127 -0
- package/dist/oxlintOwnership-Cx0hgA_J.js.map +1 -0
- package/dist/removePluginsFromConfigs-D0oSUj2C.cjs.map +1 -1
- package/dist/removePluginsFromConfigs-DzqX2Ju2.js.map +1 -1
- package/dist/{stylistic-Dio0AM2E.cjs → stylistic-CoP3MT1Y.cjs} +4 -4
- package/dist/{stylistic-Dio0AM2E.cjs.map → stylistic-CoP3MT1Y.cjs.map} +1 -1
- package/dist/{typescript-C1icmOxY.cjs → typescript-D3VeLnpn.cjs} +7 -7
- package/dist/{typescript-C1icmOxY.cjs.map → typescript-D3VeLnpn.cjs.map} +1 -1
- package/dist/{typescript-dom-RUJn2WCZ.cjs → typescript-dom-iB_xBTAD.cjs} +4 -4
- package/dist/{typescript-dom-RUJn2WCZ.cjs.map → typescript-dom-iB_xBTAD.cjs.map} +1 -1
- package/dist/{typescript-node-CBEzcc56.cjs → typescript-node-Cq6qhj53.cjs} +4 -4
- package/dist/{typescript-node-CBEzcc56.cjs.map → typescript-node-Cq6qhj53.cjs.map} +1 -1
- package/dist/utils/ensureConfigStructure.d.ts +9 -4
- package/dist/utils/ensureOxlintConfig.d.ts +9 -0
- package/dist/utils/silenceRules.d.ts +9 -0
- package/dist/{vitest-BomI8gtu.cjs → vitest-BlGQswK2.cjs} +3 -3
- package/dist/{vitest-BomI8gtu.cjs.map → vitest-BlGQswK2.cjs.map} +1 -1
- package/dist/{vue2-azKwRgaJ.cjs → vue2-Da1i_oVn.cjs} +8 -8
- package/dist/{vue2-azKwRgaJ.cjs.map → vue2-Da1i_oVn.cjs.map} +1 -1
- package/dist/{vue2-options-api-ma3J-bvM.cjs → vue2-options-api-BSdrUm8H.cjs} +8 -8
- package/dist/{vue2-options-api-ma3J-bvM.cjs.map → vue2-options-api-BSdrUm8H.cjs.map} +1 -1
- package/dist/{vue2-typescript-CZSHzp7I.cjs → vue2-typescript-dXLRSGNP.cjs} +10 -10
- package/dist/{vue2-typescript-CZSHzp7I.cjs.map → vue2-typescript-dXLRSGNP.cjs.map} +1 -1
- package/dist/{vue2-typescript-options-api-B8OzZlje.cjs → vue2-typescript-options-api-LM7U5dWq.cjs} +10 -10
- package/dist/{vue2-typescript-options-api-B8OzZlje.cjs.map → vue2-typescript-options-api-LM7U5dWq.cjs.map} +1 -1
- package/dist/{vue3-Df5UQ1Ys.cjs → vue3-621EM2Ii.cjs} +8 -8
- package/dist/{vue3-Df5UQ1Ys.cjs.map → vue3-621EM2Ii.cjs.map} +1 -1
- package/dist/{vue3-options-api-Bx7yYHza.cjs → vue3-options-api-CC9_fAiq.cjs} +8 -8
- package/dist/{vue3-options-api-Bx7yYHza.cjs.map → vue3-options-api-CC9_fAiq.cjs.map} +1 -1
- package/dist/{vue3-typescript-C0O1JZlk.cjs → vue3-typescript-DONe-IKz.cjs} +10 -10
- package/dist/{vue3-typescript-C0O1JZlk.cjs.map → vue3-typescript-DONe-IKz.cjs.map} +1 -1
- package/dist/{vue3-typescript-options-api-cPal4O-K.cjs → vue3-typescript-options-api-D4pqpDFs.cjs} +10 -10
- package/dist/{vue3-typescript-options-api-cPal4O-K.cjs.map → vue3-typescript-options-api-D4pqpDFs.cjs.map} +1 -1
- package/package.json +8 -5
- package/dist/rolldown-runtime-VH7oDXx4.cjs +0 -28
package/dist/main.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_oxlintOwnership = require("./oxlintOwnership-7hpdB3d1.cjs");
|
|
3
3
|
const require_merge = require("./merge-DJQU2y2U.cjs");
|
|
4
4
|
const require_mergeAllConfig = require("./mergeAllConfig-DjMWjBNg.cjs");
|
|
5
5
|
let eslint_config = require("eslint/config");
|
|
6
6
|
let globals = require("globals");
|
|
7
|
-
globals =
|
|
7
|
+
globals = require_oxlintOwnership.__toESM(globals, 1);
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
let node_module = require("node:module");
|
|
10
10
|
let node_path = require("node:path");
|
|
@@ -12,7 +12,7 @@ let node_url = require("node:url");
|
|
|
12
12
|
let _typescript_eslint_types = require("@typescript-eslint/types");
|
|
13
13
|
let _antfu_install_pkg = require("@antfu/install-pkg");
|
|
14
14
|
let semver = require("semver");
|
|
15
|
-
semver =
|
|
15
|
+
semver = require_oxlintOwnership.__toESM(semver, 1);
|
|
16
16
|
let node_console = require("node:console");
|
|
17
17
|
//#region src/builders/configFiles.ts
|
|
18
18
|
/**
|
|
@@ -33,6 +33,33 @@ var configFileRules = {
|
|
|
33
33
|
"import-x/no-extraneous-dependencies": "off",
|
|
34
34
|
"import-x/prefer-default-export": "off"
|
|
35
35
|
};
|
|
36
|
+
var LINT_TOPOLOGY_NAME = "insider/lint-topology";
|
|
37
|
+
/**
|
|
38
|
+
* The three files `useInsider()` requires a consumer to own. They are always linted with
|
|
39
|
+
* {@link buildLintTopologyBlocks}, independently of the `configFiles` option.
|
|
40
|
+
*/
|
|
41
|
+
var LINT_TOPOLOGY_GLOBS = [
|
|
42
|
+
"**/insider.lint.{ts,mts,cts,js,mjs,cjs}",
|
|
43
|
+
"**/eslint.config.{ts,mts,cts,js,mjs,cjs}",
|
|
44
|
+
"**/oxlint.config.{ts,mts,cts,js,mjs,cjs}"
|
|
45
|
+
];
|
|
46
|
+
var lintTopologyRules = {
|
|
47
|
+
...configFileRules,
|
|
48
|
+
"import-x/extensions": "off"
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Relaxations the mandated three-file layout needs in order to lint clean. Emitted unconditionally
|
|
52
|
+
* because the layout exists whether or not the consumer passes `configFiles`.
|
|
53
|
+
* @example
|
|
54
|
+
* buildLintTopologyBlocks()[0].rules['import-x/extensions']; // 'off'
|
|
55
|
+
*/
|
|
56
|
+
function buildLintTopologyBlocks() {
|
|
57
|
+
return [{
|
|
58
|
+
name: LINT_TOPOLOGY_NAME,
|
|
59
|
+
files: [...LINT_TOPOLOGY_GLOBS],
|
|
60
|
+
rules: { ...lintTopologyRules }
|
|
61
|
+
}];
|
|
62
|
+
}
|
|
36
63
|
function mergeGlobs(userGlobs) {
|
|
37
64
|
const seen = /* @__PURE__ */ new Set();
|
|
38
65
|
const merged = [];
|
|
@@ -62,17 +89,148 @@ function buildConfigFilesBlocks(value) {
|
|
|
62
89
|
function buildJqueryBlocks(entries) {
|
|
63
90
|
if (!entries || entries.length === 0) return [];
|
|
64
91
|
return entries.map((entry) => ({
|
|
65
|
-
files: entry.files,
|
|
92
|
+
files: [...entry.files],
|
|
66
93
|
languageOptions: { globals: { ...globals.default.jquery } }
|
|
67
94
|
}));
|
|
68
95
|
}
|
|
69
96
|
//#endregion
|
|
97
|
+
//#region src/builders/oxlint.ts
|
|
98
|
+
var OXLINT_SUPPRESSION_NAME = "insider/oxlint-suppression";
|
|
99
|
+
var OXLINT_SFC_EXCEPTION_NAME = "insider/oxlint-suppression-sfc-exception";
|
|
100
|
+
var UNUSED_DIRECTIVE_BLOCK_NAME = "insider/unused-disable-directives";
|
|
101
|
+
var UNUSED_DIRECTIVE_SEVERITY = "warn";
|
|
102
|
+
var OXLINT_DIRECTIVE_OPTIONS = {
|
|
103
|
+
respectEslintDisableDirectives: false,
|
|
104
|
+
reportUnusedDisableDirectives: UNUSED_DIRECTIVE_SEVERITY
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The always-on ESLint block that makes a stale `eslint-disable` visible. ESLint 9 defaults this to
|
|
108
|
+
* `warn`, but the split relies on it, so it is pinned rather than inherited.
|
|
109
|
+
* @example
|
|
110
|
+
* buildUnusedDirectiveBlocks()[0].linterOptions; // { reportUnusedDisableDirectives: 'warn' }
|
|
111
|
+
*/
|
|
112
|
+
function buildUnusedDirectiveBlocks() {
|
|
113
|
+
return [{
|
|
114
|
+
name: UNUSED_DIRECTIVE_BLOCK_NAME,
|
|
115
|
+
linterOptions: { reportUnusedDisableDirectives: UNUSED_DIRECTIVE_SEVERITY }
|
|
116
|
+
}];
|
|
117
|
+
}
|
|
118
|
+
function ownedRuleNames(rules) {
|
|
119
|
+
return Object.keys(rules).filter((name) => require_oxlintOwnership.isOxlintOwned(name) && !require_oxlintOwnership.isRuleDisabled(rules[name]));
|
|
120
|
+
}
|
|
121
|
+
function translatableRuleNames(rules) {
|
|
122
|
+
return Object.keys(rules).filter(require_oxlintOwnership.isOxlintOwned);
|
|
123
|
+
}
|
|
124
|
+
function toOffRecord(ruleNames) {
|
|
125
|
+
return Object.fromEntries(ruleNames.map((name) => [name, "off"]));
|
|
126
|
+
}
|
|
127
|
+
function suppressionFor(block) {
|
|
128
|
+
if (!block.rules) return [];
|
|
129
|
+
const owned = ownedRuleNames(block.rules);
|
|
130
|
+
if (owned.length === 0) return [];
|
|
131
|
+
const files = block.files ? { files: [...block.files] } : {};
|
|
132
|
+
const blockIgnores = block.ignores ? [...block.ignores] : [];
|
|
133
|
+
const vueSafe = owned.filter((name) => !require_oxlintOwnership.isVueUnsafeRule(name));
|
|
134
|
+
const vueUnsafe = owned.filter((name) => require_oxlintOwnership.isVueUnsafeRule(name));
|
|
135
|
+
const suppressions = [];
|
|
136
|
+
if (vueSafe.length > 0) suppressions.push({
|
|
137
|
+
name: OXLINT_SUPPRESSION_NAME,
|
|
138
|
+
...files,
|
|
139
|
+
...blockIgnores.length > 0 ? { ignores: blockIgnores } : {},
|
|
140
|
+
rules: toOffRecord(vueSafe)
|
|
141
|
+
});
|
|
142
|
+
if (vueUnsafe.length > 0) suppressions.push({
|
|
143
|
+
name: OXLINT_SFC_EXCEPTION_NAME,
|
|
144
|
+
...files,
|
|
145
|
+
ignores: [...blockIgnores, ...require_oxlintOwnership.VUE_LIKE_GLOBS],
|
|
146
|
+
rules: toOffRecord(vueUnsafe)
|
|
147
|
+
});
|
|
148
|
+
return suppressions;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* ESLint blocks that turn off every rule the emitted oxlint config takes over. Rules oxlint cannot
|
|
152
|
+
* check inside an SFC are suppressed everywhere except `.vue`/`.svelte`/`.astro`, so a Vue consumer
|
|
153
|
+
* keeps them.
|
|
154
|
+
* @example
|
|
155
|
+
* buildOxlintSuppressionBlocks([{ files: ['src/**\/*.ts'], rules: { 'no-var': 'error' } }]);
|
|
156
|
+
* // [{ files: ['src/**\/*.ts'], rules: { 'no-var': 'off' } }]
|
|
157
|
+
*/
|
|
158
|
+
function buildOxlintSuppressionBlocks(blocks) {
|
|
159
|
+
return blocks.flatMap(suppressionFor);
|
|
160
|
+
}
|
|
161
|
+
function translateRules(rules, plugins) {
|
|
162
|
+
return translatableRuleNames(rules).reduce((accumulator, name) => {
|
|
163
|
+
plugins.add(require_oxlintOwnership.toOxlintPluginName(name));
|
|
164
|
+
accumulator[require_oxlintOwnership.toOxlintRuleName(name)] = rules[name];
|
|
165
|
+
return accumulator;
|
|
166
|
+
}, {});
|
|
167
|
+
}
|
|
168
|
+
function anchorOxlintGlob(glob) {
|
|
169
|
+
return glob.startsWith("**") || glob.startsWith("./") || glob.startsWith("/") ? glob : `./${glob}`;
|
|
170
|
+
}
|
|
171
|
+
function anchorGlobs(globs) {
|
|
172
|
+
return globs.map(anchorOxlintGlob);
|
|
173
|
+
}
|
|
174
|
+
function anchorOxlintIgnorePattern(pattern) {
|
|
175
|
+
const negated = pattern.startsWith("!");
|
|
176
|
+
const body = negated ? pattern.slice(1) : pattern;
|
|
177
|
+
const anchored = body.startsWith("/") || body.startsWith("**") ? body : `/${body}`;
|
|
178
|
+
return negated ? `!${anchored}` : anchored;
|
|
179
|
+
}
|
|
180
|
+
function overrideFor(block, plugins) {
|
|
181
|
+
if (!block.files || !block.rules) return [];
|
|
182
|
+
const translated = translateRules(block.rules, plugins);
|
|
183
|
+
if (Object.keys(translated).length === 0) return [];
|
|
184
|
+
return [{
|
|
185
|
+
files: anchorGlobs(block.files),
|
|
186
|
+
...block.ignores && block.ignores.length > 0 ? { excludeFiles: anchorGlobs(block.ignores) } : {},
|
|
187
|
+
rules: translated
|
|
188
|
+
}];
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The oxlint half of the split, derived from the same emitted ESLint blocks the suppression layer
|
|
192
|
+
* reads. Every oxlint category is pinned off so the config enables exactly the preset's own rules
|
|
193
|
+
* and nothing oxlint would otherwise turn on by default.
|
|
194
|
+
* @example
|
|
195
|
+
* buildOxlintConfig([{ files: ['src/**\/*.ts'], rules: { 'no-var': 'error' } }], ['dist']);
|
|
196
|
+
* // { plugins: ['eslint'], categories: { correctness: 'off', ... }, ignorePatterns: ['/dist'], ... }
|
|
197
|
+
*/
|
|
198
|
+
function buildOxlintConfig(blocks, ignores) {
|
|
199
|
+
const plugins = /* @__PURE__ */ new Set(["eslint"]);
|
|
200
|
+
const overrides = blocks.flatMap((block) => overrideFor(block, plugins));
|
|
201
|
+
const globalRules = blocks.filter((block) => !block.files && block.rules).reduce((accumulator, block) => ({
|
|
202
|
+
...accumulator,
|
|
203
|
+
...translateRules(block.rules, plugins)
|
|
204
|
+
}), {});
|
|
205
|
+
return {
|
|
206
|
+
plugins: [...plugins].sort((left, right) => left.localeCompare(right)),
|
|
207
|
+
categories: Object.fromEntries(require_oxlintOwnership.OXLINT_CATEGORIES.map((name) => [name, "off"])),
|
|
208
|
+
...ignores && ignores.length > 0 ? { ignorePatterns: ignores.map(anchorOxlintIgnorePattern) } : {},
|
|
209
|
+
options: { ...OXLINT_DIRECTIVE_OPTIONS },
|
|
210
|
+
rules: globalRules,
|
|
211
|
+
overrides
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
70
215
|
//#region src/utils/ensureConfigStructure.ts
|
|
71
|
-
var
|
|
72
|
-
var OPT_OUT_ENV = "INSIDER_SKIP_CONFIG_VALIDATION";
|
|
216
|
+
var ANCHOR_FILE_REGEX = /(?:insider\.lint|eslint\.config)\.[mc]?[jt]s(?:$|\?)/;
|
|
217
|
+
var OPT_OUT_ENV$1 = "INSIDER_SKIP_CONFIG_VALIDATION";
|
|
73
218
|
var FILE_URL_PREFIX = "file://";
|
|
74
219
|
var TARGET_CALLEE_NAME = "useInsider";
|
|
220
|
+
var ESLINT_RENDERER_NAME = "useInsiderESLint";
|
|
221
|
+
var OXC_RENDERER_NAME = "useInsiderOXC";
|
|
75
222
|
var INSIDER_IMPORT_SOURCE = "@useinsider/eslint-config";
|
|
223
|
+
var MODULE_EXTENSIONS = [
|
|
224
|
+
"ts",
|
|
225
|
+
"mts",
|
|
226
|
+
"cts",
|
|
227
|
+
"js",
|
|
228
|
+
"mjs",
|
|
229
|
+
"cjs"
|
|
230
|
+
];
|
|
231
|
+
var RENDERER_EXPORT = `export const ${`{ ${ESLINT_RENDERER_NAME}, ${OXC_RENDERER_NAME} }`} = ${TARGET_CALLEE_NAME}({ ... })`;
|
|
232
|
+
var LINT_MODULE_FILENAMES = MODULE_EXTENSIONS.map((ext) => `insider.lint.${ext}`);
|
|
233
|
+
var ESLINT_CONFIG_FILENAMES = MODULE_EXTENSIONS.map((ext) => `eslint.config.${ext}`);
|
|
76
234
|
var bypassWarnEmitted = false;
|
|
77
235
|
function computeSelfModulePath() {
|
|
78
236
|
const raw = (0, node_url.fileURLToPath)({}.url);
|
|
@@ -102,24 +260,24 @@ function locOf(node) {
|
|
|
102
260
|
column: node.loc.start.column
|
|
103
261
|
};
|
|
104
262
|
}
|
|
105
|
-
function validateCallExpression(callExpr) {
|
|
263
|
+
function validateCallExpression(callExpr, expectedCallee) {
|
|
106
264
|
const { callee } = callExpr;
|
|
107
|
-
if (callee.type !== _typescript_eslint_types.AST_NODE_TYPES.Identifier || callee.name !==
|
|
265
|
+
if (callee.type !== _typescript_eslint_types.AST_NODE_TYPES.Identifier || callee.name !== expectedCallee) return [{
|
|
108
266
|
...locOf(callExpr),
|
|
109
|
-
reason:
|
|
267
|
+
reason: `Default export must call \`${expectedCallee}(...)\` directly.`
|
|
110
268
|
}];
|
|
111
269
|
return [];
|
|
112
270
|
}
|
|
113
|
-
function validateExportedCall(callee, fallbackLoc) {
|
|
271
|
+
function validateExportedCall(callee, fallbackLoc, expectedCallee) {
|
|
114
272
|
let target = callee;
|
|
115
273
|
if (target.type === _typescript_eslint_types.AST_NODE_TYPES.AwaitExpression) target = target.argument;
|
|
116
|
-
if (target.type === _typescript_eslint_types.AST_NODE_TYPES.CallExpression) return validateCallExpression(target);
|
|
274
|
+
if (target.type === _typescript_eslint_types.AST_NODE_TYPES.CallExpression) return validateCallExpression(target, expectedCallee);
|
|
117
275
|
if (target.type === _typescript_eslint_types.AST_NODE_TYPES.ArrayExpression) return [{
|
|
118
276
|
...locOf(target),
|
|
119
|
-
reason:
|
|
277
|
+
reason: `Default export must not wrap \`${expectedCallee}(...)\` in an array.`
|
|
120
278
|
}];
|
|
121
279
|
if (target.type === _typescript_eslint_types.AST_NODE_TYPES.LogicalExpression || target.type === _typescript_eslint_types.AST_NODE_TYPES.ConditionalExpression) {
|
|
122
|
-
const reason = [
|
|
280
|
+
const reason = [`Default export must not gate \`${expectedCallee}(...)\` behind a logical/conditional expression.`, "Use the typed options object instead."].join(" ");
|
|
123
281
|
return [{
|
|
124
282
|
...locOf(target),
|
|
125
283
|
reason
|
|
@@ -127,7 +285,7 @@ function validateExportedCall(callee, fallbackLoc) {
|
|
|
127
285
|
}
|
|
128
286
|
return [{
|
|
129
287
|
...fallbackLoc,
|
|
130
|
-
reason:
|
|
288
|
+
reason: `Default export must be a direct call to \`${expectedCallee}(...)\`.`
|
|
131
289
|
}];
|
|
132
290
|
}
|
|
133
291
|
function collectInsiderBindings(node, bindings, isInsiderSource) {
|
|
@@ -148,7 +306,7 @@ function classifyExpressionStatement(node, insiderBindings) {
|
|
|
148
306
|
}
|
|
149
307
|
};
|
|
150
308
|
}
|
|
151
|
-
function classifyStatement(node, exportSeen, insiderBindings) {
|
|
309
|
+
function classifyStatement(node, exportSeen, insiderBindings, expectedCallee) {
|
|
152
310
|
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ImportDeclaration || node.type === _typescript_eslint_types.AST_NODE_TYPES.VariableDeclaration) return { type: "ignore" };
|
|
153
311
|
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ExpressionStatement) return classifyExpressionStatement(node, insiderBindings);
|
|
154
312
|
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
@@ -162,13 +320,13 @@ function classifyStatement(node, exportSeen, insiderBindings) {
|
|
|
162
320
|
const declaration = node.declaration;
|
|
163
321
|
return {
|
|
164
322
|
type: "export",
|
|
165
|
-
violations: validateExportedCall(declaration, locOf(node))
|
|
323
|
+
violations: validateExportedCall(declaration, locOf(node), expectedCallee)
|
|
166
324
|
};
|
|
167
325
|
}
|
|
168
326
|
const reason = [
|
|
169
327
|
`Disallowed top-level statement \`${node.type}\`.`,
|
|
170
328
|
"Only imports, variable declarations, helper calls from `@useinsider/eslint-config`,",
|
|
171
|
-
|
|
329
|
+
`and one \`export default ${expectedCallee}()\` are allowed.`
|
|
172
330
|
].join(" ");
|
|
173
331
|
return {
|
|
174
332
|
type: "disallowed",
|
|
@@ -183,18 +341,112 @@ function validateProgramStructure(program, configPath = "", options = {}) {
|
|
|
183
341
|
const insiderBindings = /* @__PURE__ */ new Set();
|
|
184
342
|
const resolver = options.isInsiderImportSource ?? defaultIsInsiderImportSource;
|
|
185
343
|
const isInsiderSource = (source) => resolver(source, configPath);
|
|
344
|
+
const expectedCallee = options.expectedCallee ?? ESLINT_RENDERER_NAME;
|
|
186
345
|
let exportSeen = false;
|
|
187
346
|
program.body.forEach((node) => {
|
|
188
347
|
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ImportDeclaration) collectInsiderBindings(node, insiderBindings, isInsiderSource);
|
|
189
|
-
const result = classifyStatement(node, exportSeen, insiderBindings);
|
|
348
|
+
const result = classifyStatement(node, exportSeen, insiderBindings, expectedCallee);
|
|
190
349
|
if (result.type === "ignore") return;
|
|
191
350
|
if (result.type === "export") {
|
|
192
351
|
exportSeen = true;
|
|
193
352
|
violations.push(...result.violations);
|
|
194
353
|
return;
|
|
195
354
|
}
|
|
196
|
-
|
|
197
|
-
|
|
355
|
+
violations.push(result.violation);
|
|
356
|
+
});
|
|
357
|
+
if (!exportSeen) violations.push({
|
|
358
|
+
line: 1,
|
|
359
|
+
column: 0,
|
|
360
|
+
reason: `Missing \`export default ${options.expectedCallee ?? ESLINT_RENDERER_NAME}()\`.`
|
|
361
|
+
});
|
|
362
|
+
return violations;
|
|
363
|
+
}
|
|
364
|
+
function validateRendererPattern(pattern) {
|
|
365
|
+
const bound = /* @__PURE__ */ new Set();
|
|
366
|
+
const violations = [];
|
|
367
|
+
pattern.properties.forEach((property) => {
|
|
368
|
+
if (property.type !== _typescript_eslint_types.AST_NODE_TYPES.Property || property.computed || !property.shorthand) {
|
|
369
|
+
violations.push({
|
|
370
|
+
...locOf(property),
|
|
371
|
+
reason: `Destructure \`${ESLINT_RENDERER_NAME}\` and \`${OXC_RENDERER_NAME}\` by name, without renaming or spreading.`
|
|
372
|
+
});
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
bound.add(property.key.name);
|
|
376
|
+
});
|
|
377
|
+
[ESLINT_RENDERER_NAME, OXC_RENDERER_NAME].forEach((name) => {
|
|
378
|
+
if (!bound.has(name)) violations.push({
|
|
379
|
+
...locOf(pattern),
|
|
380
|
+
reason: `Missing \`${name}\` in the destructured \`${TARGET_CALLEE_NAME}(...)\` result.`
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
return violations;
|
|
384
|
+
}
|
|
385
|
+
function validateRendererExport(node) {
|
|
386
|
+
const { declaration } = node;
|
|
387
|
+
if (!declaration || declaration.type !== _typescript_eslint_types.AST_NODE_TYPES.VariableDeclaration || declaration.kind !== "const") return [{
|
|
388
|
+
...locOf(node),
|
|
389
|
+
reason: `Renderer export must be \`${RENDERER_EXPORT};\`.`
|
|
390
|
+
}];
|
|
391
|
+
if (declaration.declarations.length !== 1) return [{
|
|
392
|
+
...locOf(declaration),
|
|
393
|
+
reason: "Renderer export must declare exactly one binding."
|
|
394
|
+
}];
|
|
395
|
+
const [declarator] = declaration.declarations;
|
|
396
|
+
if (declarator.id.type !== _typescript_eslint_types.AST_NODE_TYPES.ObjectPattern) return [{
|
|
397
|
+
...locOf(declarator.id),
|
|
398
|
+
reason: `Renderer export must destructure \`${ESLINT_RENDERER_NAME}\` and \`${OXC_RENDERER_NAME}\`.`
|
|
399
|
+
}];
|
|
400
|
+
const patternViolations = validateRendererPattern(declarator.id);
|
|
401
|
+
const { init } = declarator;
|
|
402
|
+
if (!init) return [...patternViolations, {
|
|
403
|
+
...locOf(declarator),
|
|
404
|
+
reason: `Renderer export must be initialized by a direct \`${TARGET_CALLEE_NAME}(...)\` call.`
|
|
405
|
+
}];
|
|
406
|
+
return [...patternViolations, ...validateExportedCall(init, locOf(declarator), TARGET_CALLEE_NAME)];
|
|
407
|
+
}
|
|
408
|
+
function classifyLintModuleStatement(node, exportSeen, insiderBindings) {
|
|
409
|
+
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ImportDeclaration || node.type === _typescript_eslint_types.AST_NODE_TYPES.VariableDeclaration) return { type: "ignore" };
|
|
410
|
+
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ExpressionStatement) return classifyExpressionStatement(node, insiderBindings);
|
|
411
|
+
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
412
|
+
if (exportSeen) return {
|
|
413
|
+
type: "duplicateExport",
|
|
414
|
+
violation: {
|
|
415
|
+
...locOf(node),
|
|
416
|
+
reason: "Only one export is allowed."
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
return {
|
|
420
|
+
type: "export",
|
|
421
|
+
violations: validateRendererExport(node)
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const reason = [
|
|
425
|
+
`Disallowed top-level statement \`${node.type}\`.`,
|
|
426
|
+
"Only imports, variable declarations, helper calls from `@useinsider/eslint-config`,",
|
|
427
|
+
`and one \`${RENDERER_EXPORT}\` are allowed.`
|
|
428
|
+
].join(" ");
|
|
429
|
+
return {
|
|
430
|
+
type: "disallowed",
|
|
431
|
+
violation: {
|
|
432
|
+
...locOf(node),
|
|
433
|
+
reason
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function validateLintModuleStructure(program, configPath = "", options = {}) {
|
|
438
|
+
const violations = [];
|
|
439
|
+
const insiderBindings = /* @__PURE__ */ new Set();
|
|
440
|
+
const resolver = options.isInsiderImportSource ?? defaultIsInsiderImportSource;
|
|
441
|
+
const isInsiderSource = (source) => resolver(source, configPath);
|
|
442
|
+
let exportSeen = false;
|
|
443
|
+
program.body.forEach((node) => {
|
|
444
|
+
if (node.type === _typescript_eslint_types.AST_NODE_TYPES.ImportDeclaration) collectInsiderBindings(node, insiderBindings, isInsiderSource);
|
|
445
|
+
const result = classifyLintModuleStatement(node, exportSeen, insiderBindings);
|
|
446
|
+
if (result.type === "ignore") return;
|
|
447
|
+
if (result.type === "export") {
|
|
448
|
+
exportSeen = true;
|
|
449
|
+
violations.push(...result.violations);
|
|
198
450
|
return;
|
|
199
451
|
}
|
|
200
452
|
violations.push(result.violation);
|
|
@@ -202,7 +454,7 @@ function validateProgramStructure(program, configPath = "", options = {}) {
|
|
|
202
454
|
if (!exportSeen) violations.push({
|
|
203
455
|
line: 1,
|
|
204
456
|
column: 0,
|
|
205
|
-
reason:
|
|
457
|
+
reason: `Missing \`${RENDERER_EXPORT};\`.`
|
|
206
458
|
});
|
|
207
459
|
return violations;
|
|
208
460
|
}
|
|
@@ -226,7 +478,7 @@ function extractMatchesFromStack(frames) {
|
|
|
226
478
|
const matches = /* @__PURE__ */ new Set();
|
|
227
479
|
frames.forEach((frame) => {
|
|
228
480
|
const name = normalizeFrameFilename(frame.getFileName());
|
|
229
|
-
if (name &&
|
|
481
|
+
if (name && ANCHOR_FILE_REGEX.test(name)) matches.add(name);
|
|
230
482
|
});
|
|
231
483
|
return Array.from(matches);
|
|
232
484
|
}
|
|
@@ -239,13 +491,16 @@ function argvCandidateAt(argv, index) {
|
|
|
239
491
|
function extractConfigFromArgv(argv) {
|
|
240
492
|
for (let i = 0; i < argv.length; i += 1) {
|
|
241
493
|
const candidate = argvCandidateAt(argv, i);
|
|
242
|
-
if (candidate &&
|
|
494
|
+
if (candidate && ANCHOR_FILE_REGEX.test(candidate)) return candidate;
|
|
243
495
|
}
|
|
244
496
|
return null;
|
|
245
497
|
}
|
|
498
|
+
var LINT_MODULE_REGEX = /insider\.lint\.[mc]?[jt]s(?:$|\?)/;
|
|
246
499
|
function getConsumerConfigPath(frames = readStackFrames(), argv = process.argv) {
|
|
247
500
|
const stackMatches = extractMatchesFromStack(frames);
|
|
248
501
|
if (stackMatches.length === 1) return stackMatches[0];
|
|
502
|
+
const lintModules = stackMatches.filter((match) => LINT_MODULE_REGEX.test(match));
|
|
503
|
+
if (lintModules.length === 1) return lintModules[0];
|
|
249
504
|
const fromArgv = extractConfigFromArgv(argv);
|
|
250
505
|
if (fromArgv) return fromArgv;
|
|
251
506
|
return null;
|
|
@@ -255,27 +510,46 @@ function captureConsumerConfigDir(configPath) {
|
|
|
255
510
|
consumerConfigDir = configPath === null ? null : (0, node_path.dirname)((0, node_path.resolve)(configPath));
|
|
256
511
|
}
|
|
257
512
|
/**
|
|
258
|
-
* Directory holding the consumer's `
|
|
259
|
-
* This is the anchor `tsconfigPaths` entries are written against
|
|
260
|
-
*
|
|
513
|
+
* Directory holding the consumer's `insider.lint.*` module and its sibling linter configs, captured
|
|
514
|
+
* by {@link ensureConfigStructure}. This is the anchor `tsconfigPaths` entries are written against,
|
|
515
|
+
* and where {@link import('./ensureOxlintConfig').ensureOxlintConfig} looks for the oxlint config;
|
|
516
|
+
* it falls back to `process.cwd()` when the module could not be located.
|
|
261
517
|
* @example
|
|
262
|
-
* // consumer
|
|
518
|
+
* // consumer module at /repo/insider.lint.ts, `eslint` launched from /repo/app
|
|
263
519
|
* getConsumerConfigDir(); // '/repo'
|
|
264
520
|
*/
|
|
265
521
|
function getConsumerConfigDir() {
|
|
266
522
|
return consumerConfigDir ?? process.cwd();
|
|
267
523
|
}
|
|
524
|
+
var EXPECTED_LAYOUT = [
|
|
525
|
+
"Expected layout (three sibling files):",
|
|
526
|
+
"",
|
|
527
|
+
" // insider.lint.ts",
|
|
528
|
+
" import { useInsider } from '@useinsider/eslint-config';",
|
|
529
|
+
"",
|
|
530
|
+
` ${RENDERER_EXPORT};`,
|
|
531
|
+
"",
|
|
532
|
+
" // eslint.config.ts",
|
|
533
|
+
` import { ${ESLINT_RENDERER_NAME} } from './insider.lint.ts';`,
|
|
534
|
+
"",
|
|
535
|
+
` export default ${ESLINT_RENDERER_NAME}();`,
|
|
536
|
+
"",
|
|
537
|
+
" // oxlint.config.ts",
|
|
538
|
+
` import { ${OXC_RENDERER_NAME} } from './insider.lint.ts';`,
|
|
539
|
+
"",
|
|
540
|
+
` export default await ${OXC_RENDERER_NAME}();`
|
|
541
|
+
].join("\n");
|
|
268
542
|
function formatReport(configPath, violations) {
|
|
269
543
|
const header = ["@useinsider/eslint-config: protected configuration structure violated in:", ` ${configPath}`].join("\n");
|
|
270
544
|
const lines = violations.map((violation) => ` - [${violation.line}:${violation.column}] ${violation.reason}`);
|
|
271
545
|
const footer = [
|
|
272
546
|
"",
|
|
273
|
-
"
|
|
274
|
-
"
|
|
275
|
-
" - const/let/var declarations",
|
|
276
|
-
" - exactly one `export default useInsider({ ... })`",
|
|
547
|
+
"Alongside the export, only import statements, const/let/var declarations and helper calls",
|
|
548
|
+
"imported from `@useinsider/eslint-config` are allowed.",
|
|
277
549
|
"",
|
|
278
|
-
|
|
550
|
+
EXPECTED_LAYOUT,
|
|
551
|
+
"",
|
|
552
|
+
`Emergency bypass: set ${OPT_OUT_ENV$1}=true (do not commit).`
|
|
279
553
|
].join("\n");
|
|
280
554
|
return `${header}\n${lines.join("\n")}\n${footer}`;
|
|
281
555
|
}
|
|
@@ -316,16 +590,50 @@ function parseConsumerSource(configPath, source) {
|
|
|
316
590
|
return fail(`@useinsider/eslint-config: failed to parse consumer config at ${configPath}: ${message}`);
|
|
317
591
|
}
|
|
318
592
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
593
|
+
function resolveSibling(directory, filenames) {
|
|
594
|
+
const found = filenames.find((filename) => (0, node_fs.existsSync)((0, node_path.resolve)(directory, filename)));
|
|
595
|
+
return found ? (0, node_path.resolve)(directory, found) : null;
|
|
596
|
+
}
|
|
597
|
+
function validateFile(filePath, validator, options) {
|
|
598
|
+
const violations = validator(parseConsumerSource(filePath, readConsumerSource(filePath)), filePath, options);
|
|
322
599
|
if (violations.length === 0) return;
|
|
323
|
-
fail(formatReport(
|
|
600
|
+
fail(formatReport(filePath, violations));
|
|
601
|
+
}
|
|
602
|
+
function runValidation(anchorPath) {
|
|
603
|
+
if (!anchorPath) fail([
|
|
604
|
+
"@useinsider/eslint-config: unable to locate the consumer `insider.lint.*` module for structure validation.",
|
|
605
|
+
"",
|
|
606
|
+
EXPECTED_LAYOUT,
|
|
607
|
+
"",
|
|
608
|
+
`If this is an unexpected environment, set ${OPT_OUT_ENV$1}=true (do not commit).`
|
|
609
|
+
].join("\n"));
|
|
610
|
+
const directory = (0, node_path.dirname)((0, node_path.resolve)(anchorPath));
|
|
611
|
+
const lintModulePath = resolveSibling(directory, LINT_MODULE_FILENAMES);
|
|
612
|
+
if (!lintModulePath) fail([
|
|
613
|
+
`@useinsider/eslint-config: no \`insider.lint.*\` module found in ${directory}.`,
|
|
614
|
+
"",
|
|
615
|
+
`\`${TARGET_CALLEE_NAME}()\` now returns the two renderers instead of a flat-config array,`,
|
|
616
|
+
"so the config it is called from must be a module both linters can import.",
|
|
617
|
+
"",
|
|
618
|
+
EXPECTED_LAYOUT,
|
|
619
|
+
"",
|
|
620
|
+
`Emergency bypass: set ${OPT_OUT_ENV$1}=true (do not commit).`
|
|
621
|
+
].join("\n"));
|
|
622
|
+
validateFile(lintModulePath, validateLintModuleStructure, {});
|
|
623
|
+
const eslintConfigPath = resolveSibling(directory, ESLINT_CONFIG_FILENAMES);
|
|
624
|
+
if (!eslintConfigPath) fail([
|
|
625
|
+
`@useinsider/eslint-config: no \`eslint.config.*\` file found in ${directory}.`,
|
|
626
|
+
"",
|
|
627
|
+
EXPECTED_LAYOUT,
|
|
628
|
+
"",
|
|
629
|
+
`Emergency bypass: set ${OPT_OUT_ENV$1}=true (do not commit).`
|
|
630
|
+
].join("\n"));
|
|
631
|
+
validateFile(eslintConfigPath, validateProgramStructure, { expectedCallee: ESLINT_RENDERER_NAME });
|
|
324
632
|
}
|
|
325
633
|
function ensureConfigStructure(overridePath) {
|
|
326
634
|
const configPath = overridePath === void 0 ? getConsumerConfigPath() : overridePath;
|
|
327
635
|
captureConsumerConfigDir(configPath);
|
|
328
|
-
if (process.env[OPT_OUT_ENV] === "true") {
|
|
636
|
+
if (process.env[OPT_OUT_ENV$1] === "true") {
|
|
329
637
|
emitBypassWarning();
|
|
330
638
|
return;
|
|
331
639
|
}
|
|
@@ -366,52 +674,52 @@ function applyVueOverrides(block, input) {
|
|
|
366
674
|
//#region src/builders/scopeBlocks.ts
|
|
367
675
|
var scopeMap = {
|
|
368
676
|
async javascript() {
|
|
369
|
-
return Promise.resolve().then(() => require("./javascript-
|
|
677
|
+
return Promise.resolve().then(() => require("./javascript-DH8-N38w.cjs"));
|
|
370
678
|
},
|
|
371
679
|
async javascriptDom() {
|
|
372
|
-
return Promise.resolve().then(() => require("./javascript-dom-
|
|
680
|
+
return Promise.resolve().then(() => require("./javascript-dom-CvtQ6_9W.cjs"));
|
|
373
681
|
},
|
|
374
682
|
async javascriptNode() {
|
|
375
|
-
return Promise.resolve().then(() => require("./javascript-node-
|
|
683
|
+
return Promise.resolve().then(() => require("./javascript-node-BNsy3loY.cjs"));
|
|
376
684
|
},
|
|
377
685
|
async typescript() {
|
|
378
|
-
return Promise.resolve().then(() => require("./typescript-
|
|
686
|
+
return Promise.resolve().then(() => require("./typescript-D3VeLnpn.cjs"));
|
|
379
687
|
},
|
|
380
688
|
async typescriptDom() {
|
|
381
|
-
return Promise.resolve().then(() => require("./typescript-dom-
|
|
689
|
+
return Promise.resolve().then(() => require("./typescript-dom-iB_xBTAD.cjs"));
|
|
382
690
|
},
|
|
383
691
|
async typescriptNode() {
|
|
384
|
-
return Promise.resolve().then(() => require("./typescript-node-
|
|
692
|
+
return Promise.resolve().then(() => require("./typescript-node-Cq6qhj53.cjs"));
|
|
385
693
|
},
|
|
386
694
|
async vue2() {
|
|
387
|
-
return Promise.resolve().then(() => require("./vue2-
|
|
695
|
+
return Promise.resolve().then(() => require("./vue2-Da1i_oVn.cjs"));
|
|
388
696
|
},
|
|
389
697
|
async vue3() {
|
|
390
|
-
return Promise.resolve().then(() => require("./vue3-
|
|
698
|
+
return Promise.resolve().then(() => require("./vue3-621EM2Ii.cjs"));
|
|
391
699
|
},
|
|
392
700
|
async vue2Typescript() {
|
|
393
|
-
return Promise.resolve().then(() => require("./vue2-typescript-
|
|
701
|
+
return Promise.resolve().then(() => require("./vue2-typescript-dXLRSGNP.cjs"));
|
|
394
702
|
},
|
|
395
703
|
async vue3Typescript() {
|
|
396
|
-
return Promise.resolve().then(() => require("./vue3-typescript-
|
|
704
|
+
return Promise.resolve().then(() => require("./vue3-typescript-DONe-IKz.cjs"));
|
|
397
705
|
},
|
|
398
706
|
async vue2OptionsApi() {
|
|
399
|
-
return Promise.resolve().then(() => require("./vue2-options-api-
|
|
707
|
+
return Promise.resolve().then(() => require("./vue2-options-api-BSdrUm8H.cjs"));
|
|
400
708
|
},
|
|
401
709
|
async vue3OptionsApi() {
|
|
402
|
-
return Promise.resolve().then(() => require("./vue3-options-api-
|
|
710
|
+
return Promise.resolve().then(() => require("./vue3-options-api-CC9_fAiq.cjs"));
|
|
403
711
|
},
|
|
404
712
|
async vue2TypescriptOptionsApi() {
|
|
405
|
-
return Promise.resolve().then(() => require("./vue2-typescript-options-api-
|
|
713
|
+
return Promise.resolve().then(() => require("./vue2-typescript-options-api-LM7U5dWq.cjs"));
|
|
406
714
|
},
|
|
407
715
|
async vue3TypescriptOptionsApi() {
|
|
408
|
-
return Promise.resolve().then(() => require("./vue3-typescript-options-api-
|
|
716
|
+
return Promise.resolve().then(() => require("./vue3-typescript-options-api-D4pqpDFs.cjs"));
|
|
409
717
|
},
|
|
410
718
|
async jest() {
|
|
411
|
-
return Promise.resolve().then(() => require("./jest-
|
|
719
|
+
return Promise.resolve().then(() => require("./jest-4Es9Ggaa.cjs"));
|
|
412
720
|
},
|
|
413
721
|
async vitest() {
|
|
414
|
-
return Promise.resolve().then(() => require("./vitest-
|
|
722
|
+
return Promise.resolve().then(() => require("./vitest-BlGQswK2.cjs"));
|
|
415
723
|
},
|
|
416
724
|
async jsdocBasedTs() {
|
|
417
725
|
return Promise.resolve().then(() => require("./jsdoc-based-ts-Cfahz0nn.cjs"));
|
|
@@ -469,7 +777,7 @@ async function buildScopeBlocks(scope, entries) {
|
|
|
469
777
|
const preset = (await scopeMap[scope]()).default;
|
|
470
778
|
return entries.map((entry) => {
|
|
471
779
|
const block = clonePreset(preset);
|
|
472
|
-
block.files = entry.files;
|
|
780
|
+
block.files = [...entry.files];
|
|
473
781
|
if (tsScopes.has(scope)) {
|
|
474
782
|
const { tsconfigPaths } = entry;
|
|
475
783
|
if (tsconfigPaths) applyTypescriptOverrides(block, { tsconfigPaths });
|
|
@@ -503,7 +811,7 @@ async function buildStrictBlocks(value) {
|
|
|
503
811
|
const { default: strictConfig } = await Promise.resolve().then(() => require("./strict-DnJFBY_B.cjs"));
|
|
504
812
|
const clone = { ...strictConfig };
|
|
505
813
|
if (clone.rules) clone.rules = { ...clone.rules };
|
|
506
|
-
if (isFileScoped) clone.files = value;
|
|
814
|
+
if (isFileScoped) clone.files = [...value];
|
|
507
815
|
else delete clone.files;
|
|
508
816
|
return [clone];
|
|
509
817
|
}
|
|
@@ -512,7 +820,11 @@ async function buildStrictBlocks(value) {
|
|
|
512
820
|
var turnOff = false;
|
|
513
821
|
var scanned = false;
|
|
514
822
|
var silencedDependencies = /* @__PURE__ */ new Set();
|
|
515
|
-
var fixedDependencies = [
|
|
823
|
+
var fixedDependencies = [
|
|
824
|
+
"eslint",
|
|
825
|
+
"globals",
|
|
826
|
+
"oxlint"
|
|
827
|
+
];
|
|
516
828
|
var require$1 = (0, node_module.createRequire)({}.url);
|
|
517
829
|
function getPackageJSONPath(currentDir) {
|
|
518
830
|
const fileLocation = (0, node_path.resolve)(currentDir, "package.json");
|
|
@@ -568,6 +880,46 @@ function silenceDependencyWarning(input) {
|
|
|
568
880
|
input.forEach((dependency) => silencedDependencies.add(String(dependency)));
|
|
569
881
|
}
|
|
570
882
|
//#endregion
|
|
883
|
+
//#region src/utils/ensureOxlintConfig.ts
|
|
884
|
+
var OPT_OUT_ENV = "INSIDER_SKIP_CONFIG_VALIDATION";
|
|
885
|
+
var OXLINT_CONFIG_FILENAMES = ["oxlint.config.ts", "oxlint.config.mts"];
|
|
886
|
+
function lintModuleSpecifier(directory) {
|
|
887
|
+
return `./${LINT_MODULE_FILENAMES.find((filename) => (0, node_fs.existsSync)((0, node_path.join)(directory, filename))) ?? "insider.lint.ts"}`;
|
|
888
|
+
}
|
|
889
|
+
function formatMessage(directory) {
|
|
890
|
+
return [
|
|
891
|
+
"@useinsider/eslint-config: no oxlint config found in",
|
|
892
|
+
` ${directory}`,
|
|
893
|
+
"",
|
|
894
|
+
"useInsiderESLint() turns off every rule oxlint owns, so running ESLint alone would silently",
|
|
895
|
+
"drop those checks. Create the sibling oxlint config:",
|
|
896
|
+
"",
|
|
897
|
+
" // oxlint.config.ts",
|
|
898
|
+
` import { useInsiderOXC } from '${lintModuleSpecifier(directory)}';`,
|
|
899
|
+
"",
|
|
900
|
+
" export default await useInsiderOXC();",
|
|
901
|
+
"",
|
|
902
|
+
"and run both linters:",
|
|
903
|
+
"",
|
|
904
|
+
" \"lint\": \"oxlint --disable-nested-config && eslint .\"",
|
|
905
|
+
"",
|
|
906
|
+
`Expected one of: ${OXLINT_CONFIG_FILENAMES.join(", ")}.`,
|
|
907
|
+
`Emergency bypass: set ${OPT_OUT_ENV}=true (do not commit).`
|
|
908
|
+
].join("\n");
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Fails when the consumer enabled the ESLint-side oxlint suppression without a sibling oxlint
|
|
912
|
+
* config to run the suppressed rules. Resolved against the directory
|
|
913
|
+
* {@link getConsumerConfigDir} captured during `useInsider()`.
|
|
914
|
+
* @example
|
|
915
|
+
* ensureOxlintConfig(); // throws unless oxlint.config.{ts,mts} sits beside insider.lint.*
|
|
916
|
+
*/
|
|
917
|
+
function ensureOxlintConfig(directory = getConsumerConfigDir()) {
|
|
918
|
+
if (process.env[OPT_OUT_ENV] === "true") return;
|
|
919
|
+
if (OXLINT_CONFIG_FILENAMES.some((filename) => (0, node_fs.existsSync)((0, node_path.join)(directory, filename)))) return;
|
|
920
|
+
throw new Error(formatMessage(directory));
|
|
921
|
+
}
|
|
922
|
+
//#endregion
|
|
571
923
|
//#region src/utils/silenceRules.ts
|
|
572
924
|
var globallySilencedRules = /* @__PURE__ */ new Set();
|
|
573
925
|
/**
|
|
@@ -587,14 +939,25 @@ function silenceRule(config) {
|
|
|
587
939
|
const [, ...restConfig] = Array.isArray(config) ? config : [];
|
|
588
940
|
return ["warn", ...restConfig];
|
|
589
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Reports a silenced rule name that no emitted block declares, which is how a typo in
|
|
944
|
+
* `silenceRules` stays visible. The caller passes the union of every block's rule keys: a single
|
|
945
|
+
* block is not evidence, because the emission is many narrow blocks and a correctly spelled rule is
|
|
946
|
+
* absent from nearly all of them.
|
|
947
|
+
* @example
|
|
948
|
+
* warnUnknownSilencedRules(['no-console'], ['no-unknown-rule']); // warns once
|
|
949
|
+
*/
|
|
950
|
+
function warnUnknownSilencedRules(knownRules, silencedRules) {
|
|
951
|
+
const known = new Set(knownRules);
|
|
952
|
+
new Set(silencedRules).forEach((rule) => {
|
|
953
|
+
if (known.has(rule)) return;
|
|
954
|
+
(0, node_console.warn)(`Unknown rule "${rule}". Please remove it from the silenced rules list.`);
|
|
955
|
+
});
|
|
956
|
+
}
|
|
590
957
|
function filterSilencedRules(rules, silencedRules) {
|
|
591
958
|
let targetSilencedRules = silencedRules || globallySilencedRules;
|
|
592
959
|
if (Array.isArray(targetSilencedRules)) targetSilencedRules = new Set(targetSilencedRules);
|
|
593
960
|
if (!rules || !targetSilencedRules.size) return rules;
|
|
594
|
-
if (silencedRules) targetSilencedRules.forEach((rule) => {
|
|
595
|
-
if (rules[rule]) return;
|
|
596
|
-
(0, node_console.warn)(`Unknown rule "${rule}". Please remove it from the silenced rules list.`);
|
|
597
|
-
});
|
|
598
961
|
return Object.entries(rules).reduce((newRules, [key, value]) => {
|
|
599
962
|
if (value) newRules[key] = targetSilencedRules.has(key) && !isDisabled(value) ? silenceRule(value) : value;
|
|
600
963
|
return newRules;
|
|
@@ -630,6 +993,7 @@ async function collectScopeBlocks(config) {
|
|
|
630
993
|
}, Promise.resolve([]));
|
|
631
994
|
}
|
|
632
995
|
function applySilenceRules(blocks, perCallSilence) {
|
|
996
|
+
if (perCallSilence) warnUnknownSilencedRules(blocks.flatMap((block) => Object.keys(block.rules ?? {})), perCallSilence);
|
|
633
997
|
blocks.forEach((block) => {
|
|
634
998
|
if (!block.rules) return;
|
|
635
999
|
block.rules = perCallSilence ? filterSilencedRules(block.rules, perCallSilence) : filterSilencedRules(block.rules);
|
|
@@ -639,29 +1003,44 @@ function buildIgnoresBlocks(ignores) {
|
|
|
639
1003
|
if (!ignores || ignores.length === 0) return [];
|
|
640
1004
|
return [{ ignores: [...ignores] }];
|
|
641
1005
|
}
|
|
642
|
-
async function
|
|
643
|
-
assertNoRemovedKeys(config);
|
|
644
|
-
ensureConfigStructure();
|
|
645
|
-
await ensureDependencies();
|
|
646
|
-
const ignoresBlocks = buildIgnoresBlocks(config.ignores);
|
|
1006
|
+
async function buildBaseBlocks(config) {
|
|
647
1007
|
const scopeBlocks = await collectScopeBlocks(config);
|
|
648
|
-
const jqueryBlocks = buildJqueryBlocks(config.jquery);
|
|
649
|
-
const configFilesBlocks = buildConfigFilesBlocks(config.configFiles);
|
|
650
1008
|
const strictBlocks = await buildStrictBlocks(config.strict);
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
...
|
|
1009
|
+
return [
|
|
1010
|
+
...buildIgnoresBlocks(config.ignores),
|
|
1011
|
+
...buildUnusedDirectiveBlocks(),
|
|
654
1012
|
...scopeBlocks,
|
|
655
|
-
...
|
|
656
|
-
...
|
|
1013
|
+
...buildJqueryBlocks(config.jquery),
|
|
1014
|
+
...buildConfigFilesBlocks(config.configFiles),
|
|
657
1015
|
...strictBlocks,
|
|
658
|
-
...
|
|
1016
|
+
...buildLintTopologyBlocks(),
|
|
1017
|
+
...buildSpellCheckerBlocks(config.spellChecker)
|
|
659
1018
|
];
|
|
1019
|
+
}
|
|
1020
|
+
async function renderEslint(config) {
|
|
1021
|
+
await ensureDependencies();
|
|
1022
|
+
ensureOxlintConfig();
|
|
1023
|
+
const baseBlocks = await buildBaseBlocks(config);
|
|
1024
|
+
const blocks = [...baseBlocks, ...buildOxlintSuppressionBlocks(baseBlocks)];
|
|
660
1025
|
applySilenceRules(blocks, config.silenceRules);
|
|
661
1026
|
return (0, eslint_config.defineConfig)(blocks);
|
|
662
1027
|
}
|
|
1028
|
+
async function renderOxlint(config) {
|
|
1029
|
+
const baseBlocks = await buildBaseBlocks(config);
|
|
1030
|
+
applySilenceRules(baseBlocks, config.silenceRules);
|
|
1031
|
+
return buildOxlintConfig(baseBlocks, config.ignores);
|
|
1032
|
+
}
|
|
1033
|
+
function useInsider(config) {
|
|
1034
|
+
assertNoRemovedKeys(config);
|
|
1035
|
+
ensureConfigStructure();
|
|
1036
|
+
return {
|
|
1037
|
+
useInsiderESLint: () => renderEslint(config),
|
|
1038
|
+
useInsiderOXC: () => renderOxlint(config)
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
663
1041
|
//#endregion
|
|
664
1042
|
exports.ensureConfigStructure = ensureConfigStructure;
|
|
1043
|
+
exports.ensureOxlintConfig = ensureOxlintConfig;
|
|
665
1044
|
exports.merge = require_merge.merge;
|
|
666
1045
|
exports.mergeAll = require_mergeAllConfig.mergeAll;
|
|
667
1046
|
exports.silenceDependencyWarning = silenceDependencyWarning;
|