@etchteam/eslint-config 3.0.1 → 3.1.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/CHANGELOG.md +3 -3
- package/package.json +2 -1
- package/src/base.mjs +1 -0
- package/src/configs/environments/webc.mjs +17 -0
- package/src/configs/webc.mjs +18 -0
- package/src/index.mjs +1 -0
- package/src/plugins/webc-processor.mjs +289 -0
- package/src/types.d.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
## <small>3.
|
|
1
|
+
## <small>3.1.1 (2026-04-02)</small>
|
|
2
2
|
|
|
3
|
-
* Merge pull request #
|
|
4
|
-
* fix: Bump
|
|
3
|
+
* Merge pull request #551 from etchteam/dependabot/npm_and_yarn/npm_and_yarn-52571dc7e9 ([695b535](https://github.com/etchteam/eslint/commit/695b535)), closes [#551](https://github.com/etchteam/eslint/issues/551)
|
|
4
|
+
* fix: Bump lodash in the npm_and_yarn group across 1 directory ([2eddafe](https://github.com/etchteam/eslint/commit/2eddafe))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etchteam/eslint-config",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Etch's standard eslint config",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"./preact": "./src/configs/environments/preact.mjs",
|
|
17
17
|
"./angular": "./src/configs/environments/angular.mjs",
|
|
18
18
|
"./web-components": "./src/configs/environments/web-components.mjs",
|
|
19
|
+
"./webc": "./src/configs/environments/webc.mjs",
|
|
19
20
|
"./nestjs": "./src/configs/environments/nestjs.mjs",
|
|
20
21
|
"./package.json": "./package.json"
|
|
21
22
|
},
|
package/src/base.mjs
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import base from '../../base.mjs';
|
|
2
|
+
import json from '../json.mjs';
|
|
3
|
+
import storybook from '../storybook.mjs';
|
|
4
|
+
import webc from '../webc.mjs';
|
|
5
|
+
import yaml from '../yaml.mjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* WebC ESLint configuration.
|
|
9
|
+
* Includes: base + JSON + YAML + Storybook + WebC processor
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```js
|
|
13
|
+
* import webc from '@etchteam/eslint-config/webc';
|
|
14
|
+
* export default webc;
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export default [...base, ...json, ...yaml, ...storybook, ...webc];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import webcPlugin from '../plugins/webc-processor.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WebC template linting configuration.
|
|
5
|
+
* Provides a processor for extracting and linting inline JavaScript in .webc files.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```js
|
|
9
|
+
* import { webc } from '@etchteam/eslint-config';
|
|
10
|
+
* export default [...base, ...webc];
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export default [
|
|
14
|
+
{
|
|
15
|
+
files: ['**/*.webc'],
|
|
16
|
+
processor: webcPlugin.processors.webc,
|
|
17
|
+
},
|
|
18
|
+
];
|
package/src/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ export { default as base } from './base.mjs';
|
|
|
9
9
|
export { default as json } from './configs/json.mjs';
|
|
10
10
|
export { default as react } from './configs/react.mjs';
|
|
11
11
|
export { default as storybook } from './configs/storybook.mjs';
|
|
12
|
+
export { default as webc } from './configs/webc.mjs';
|
|
12
13
|
export { default as yaml } from './configs/yaml.mjs';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block definitions for each type of inline JavaScript in WebC files.
|
|
3
|
+
* Each entry maps an opening tag pattern to its closing pattern and type resolver.
|
|
4
|
+
*/
|
|
5
|
+
const BLOCK_DEFS = [
|
|
6
|
+
{
|
|
7
|
+
open: /^(\s*)<script(\s[^>]*)?\s*>/i,
|
|
8
|
+
close: /<\/script\s*>/i,
|
|
9
|
+
getType: (line) => (/webc:setup/.test(line) ? 'script-setup' : 'script'),
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
open: /^(\s*)<template\s+webc:type=["'](js|render)["'](\s[^>]*)?\s*>/i,
|
|
13
|
+
close: /<\/template\s*>/i,
|
|
14
|
+
getType: (_line, match) => `template-${match[2]}`,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/** @type {Map<string, Array<{ startLine: number, charOffset: number, type: string }>>} */
|
|
19
|
+
const blockMetadata = new Map();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Try to match a line against all block definitions.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} line
|
|
25
|
+
* @returns {{ match: RegExpMatchArray, close: RegExp, type: string } | null}
|
|
26
|
+
*/
|
|
27
|
+
function matchOpenTag(line) {
|
|
28
|
+
for (const def of BLOCK_DEFS) {
|
|
29
|
+
const match = def.open.exec(line);
|
|
30
|
+
|
|
31
|
+
if (match) {
|
|
32
|
+
return { match, close: def.close, type: def.getType(line, match) };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Handle a line that opens a new block. Returns the block if it's a
|
|
41
|
+
* single-line block, or the parser state for a multi-line block.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} line
|
|
44
|
+
* @param {{ match: RegExpMatchArray, close: RegExp, type: string }} opened
|
|
45
|
+
* @param {number} lineIndex
|
|
46
|
+
* @param {number} currentCharOffset
|
|
47
|
+
* @returns {{ block: object } | { state: object }}
|
|
48
|
+
*/
|
|
49
|
+
function handleOpenTag(line, opened, lineIndex, currentCharOffset) {
|
|
50
|
+
const tagEnd = line.indexOf('>', opened.match.index) + 1;
|
|
51
|
+
const closeMatch = opened.close.exec(line);
|
|
52
|
+
|
|
53
|
+
if (closeMatch && closeMatch.index > opened.match.index) {
|
|
54
|
+
return {
|
|
55
|
+
block: {
|
|
56
|
+
code: line.slice(tagEnd, closeMatch.index),
|
|
57
|
+
startLine: lineIndex + 1,
|
|
58
|
+
charOffset: currentCharOffset + tagEnd,
|
|
59
|
+
type: opened.type,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const afterTag = line.slice(tagEnd);
|
|
65
|
+
const hasContentOnSameLine = afterTag.trim();
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
state: {
|
|
69
|
+
closePattern: opened.close,
|
|
70
|
+
blockType: opened.type,
|
|
71
|
+
blockLines: hasContentOnSameLine ? [afterTag] : [],
|
|
72
|
+
blockStartLine: hasContentOnSameLine ? lineIndex + 1 : lineIndex + 2,
|
|
73
|
+
blockCharOffset: hasContentOnSameLine
|
|
74
|
+
? currentCharOffset + tagEnd
|
|
75
|
+
: currentCharOffset + line.length + 1,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle a line while inside a block. Returns the completed block if the
|
|
82
|
+
* closing tag is found, or null to continue accumulating.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} line
|
|
85
|
+
* @param {RegExp} closePattern
|
|
86
|
+
* @param {string[]} blockLines
|
|
87
|
+
* @param {number} blockStartLine
|
|
88
|
+
* @param {number} blockCharOffset
|
|
89
|
+
* @param {string} blockType
|
|
90
|
+
* @returns {{ code: string, startLine: number, charOffset: number, type: string } | null}
|
|
91
|
+
*/
|
|
92
|
+
function handleBlockLine(
|
|
93
|
+
line,
|
|
94
|
+
closePattern,
|
|
95
|
+
blockLines,
|
|
96
|
+
blockStartLine,
|
|
97
|
+
blockCharOffset,
|
|
98
|
+
blockType,
|
|
99
|
+
) {
|
|
100
|
+
const closeMatch = closePattern.exec(line);
|
|
101
|
+
|
|
102
|
+
if (!closeMatch) {
|
|
103
|
+
blockLines.push(line);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const beforeClose = line.slice(0, closeMatch.index);
|
|
108
|
+
|
|
109
|
+
if (beforeClose.trim()) {
|
|
110
|
+
blockLines.push(beforeClose);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const code = blockLines.join('\n');
|
|
114
|
+
|
|
115
|
+
if (!code.trim()) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
code: code + '\n',
|
|
121
|
+
startLine: blockStartLine,
|
|
122
|
+
charOffset: blockCharOffset,
|
|
123
|
+
type: blockType,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extract JavaScript blocks from a WebC file.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} text - The full file content
|
|
131
|
+
* @returns {Array<{ code: string, startLine: number, charOffset: number, type: string }>}
|
|
132
|
+
*/
|
|
133
|
+
function extractBlocks(text) {
|
|
134
|
+
const lines = text.split('\n');
|
|
135
|
+
const blocks = [];
|
|
136
|
+
|
|
137
|
+
let insideBlock = false;
|
|
138
|
+
let closePattern = null;
|
|
139
|
+
let blockType = '';
|
|
140
|
+
let blockLines = [];
|
|
141
|
+
let blockStartLine = 0;
|
|
142
|
+
let blockCharOffset = 0;
|
|
143
|
+
let currentCharOffset = 0;
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < lines.length; i++) {
|
|
146
|
+
const line = lines[i];
|
|
147
|
+
|
|
148
|
+
if (insideBlock) {
|
|
149
|
+
const completed = handleBlockLine(
|
|
150
|
+
line,
|
|
151
|
+
closePattern,
|
|
152
|
+
blockLines,
|
|
153
|
+
blockStartLine,
|
|
154
|
+
blockCharOffset,
|
|
155
|
+
blockType,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
if (completed) {
|
|
159
|
+
blocks.push(completed);
|
|
160
|
+
insideBlock = false;
|
|
161
|
+
closePattern = null;
|
|
162
|
+
blockLines = [];
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
const opened = matchOpenTag(line);
|
|
166
|
+
|
|
167
|
+
if (opened) {
|
|
168
|
+
const result = handleOpenTag(line, opened, i, currentCharOffset);
|
|
169
|
+
|
|
170
|
+
if (result.block) {
|
|
171
|
+
blocks.push(result.block);
|
|
172
|
+
} else {
|
|
173
|
+
insideBlock = true;
|
|
174
|
+
closePattern = result.state.closePattern;
|
|
175
|
+
blockType = result.state.blockType;
|
|
176
|
+
blockLines = result.state.blockLines;
|
|
177
|
+
blockStartLine = result.state.blockStartLine;
|
|
178
|
+
blockCharOffset = result.state.blockCharOffset;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
currentCharOffset += line.length + 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return blocks;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param {string} text
|
|
191
|
+
* @param {string} filename
|
|
192
|
+
* @returns {Array<{ text: string, filename: string }>}
|
|
193
|
+
*/
|
|
194
|
+
function preprocess(text, filename) {
|
|
195
|
+
const blocks = extractBlocks(text);
|
|
196
|
+
const metadata = [];
|
|
197
|
+
const result = [];
|
|
198
|
+
|
|
199
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
200
|
+
const block = blocks[i];
|
|
201
|
+
const virtualFilename = `${filename}/${i}.${block.type}.js`;
|
|
202
|
+
|
|
203
|
+
metadata.push({
|
|
204
|
+
startLine: block.startLine,
|
|
205
|
+
charOffset: block.charOffset,
|
|
206
|
+
type: block.type,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
result.push({
|
|
210
|
+
text: block.code,
|
|
211
|
+
filename: virtualFilename,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
blockMetadata.set(filename, metadata);
|
|
216
|
+
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @param {import('eslint').Linter.LintMessage} message
|
|
222
|
+
* @param {number} lineOffset
|
|
223
|
+
* @param {number} charOffset
|
|
224
|
+
* @returns {import('eslint').Linter.LintMessage}
|
|
225
|
+
*/
|
|
226
|
+
function adjustMessage(message, lineOffset, charOffset) {
|
|
227
|
+
return {
|
|
228
|
+
...message,
|
|
229
|
+
line: message.line + lineOffset,
|
|
230
|
+
endLine: message.endLine != null ? message.endLine + lineOffset : undefined,
|
|
231
|
+
fix: message.fix
|
|
232
|
+
? {
|
|
233
|
+
range: [
|
|
234
|
+
message.fix.range[0] + charOffset,
|
|
235
|
+
message.fix.range[1] + charOffset,
|
|
236
|
+
],
|
|
237
|
+
text: message.fix.text,
|
|
238
|
+
}
|
|
239
|
+
: undefined,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @param {Array<Array<import('eslint').Linter.LintMessage>>} messages
|
|
245
|
+
* @param {string} filename
|
|
246
|
+
* @returns {Array<import('eslint').Linter.LintMessage>}
|
|
247
|
+
*/
|
|
248
|
+
function postprocess(messages, filename) {
|
|
249
|
+
const metadata = blockMetadata.get(filename);
|
|
250
|
+
|
|
251
|
+
blockMetadata.delete(filename);
|
|
252
|
+
|
|
253
|
+
if (!metadata) {
|
|
254
|
+
return messages.flat();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const result = [];
|
|
258
|
+
|
|
259
|
+
for (let i = 0; i < messages.length; i++) {
|
|
260
|
+
const blockMessages = messages[i];
|
|
261
|
+
const meta = metadata[i];
|
|
262
|
+
|
|
263
|
+
if (meta) {
|
|
264
|
+
const lineOffset = meta.startLine - 1;
|
|
265
|
+
|
|
266
|
+
for (const message of blockMessages) {
|
|
267
|
+
result.push(adjustMessage(message, lineOffset, meta.charOffset));
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
result.push(...blockMessages);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export default {
|
|
278
|
+
meta: {
|
|
279
|
+
name: 'eslint-plugin-webc',
|
|
280
|
+
version: '1.0.0',
|
|
281
|
+
},
|
|
282
|
+
processors: {
|
|
283
|
+
webc: {
|
|
284
|
+
preprocess,
|
|
285
|
+
postprocess,
|
|
286
|
+
supportsAutofix: true,
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
};
|
package/src/types.d.ts
CHANGED
|
@@ -52,6 +52,12 @@ export declare const preact: FlatConfigArray;
|
|
|
52
52
|
*/
|
|
53
53
|
export declare const angular: FlatConfigArray;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* WebC template linting configuration.
|
|
57
|
+
* Provides a processor for extracting and linting inline JavaScript in .webc files.
|
|
58
|
+
*/
|
|
59
|
+
export declare const webc: FlatConfigArray;
|
|
60
|
+
|
|
55
61
|
/**
|
|
56
62
|
* Web Components (Lit) ESLint configuration.
|
|
57
63
|
* Includes: base + JSON + YAML + Storybook + Lit rules
|