eslint-plugin-weld 0.0.4-rc.2 → 0.2.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 +66 -12
- package/dist/index.d.ts +7 -33
- package/dist/index.js +873 -5
- package/dist/index.js.map +1 -1
- package/package.json +14 -1
package/dist/index.js
CHANGED
|
@@ -1,22 +1,890 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
+
import createDebug from 'debug';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import path2 from 'path';
|
|
5
|
+
import { getTsconfig } from 'get-tsconfig';
|
|
2
6
|
|
|
3
7
|
// src/index.ts
|
|
8
|
+
var PACKAGE_NAME = "eslint-plugin-weld";
|
|
9
|
+
function createLogger(scope) {
|
|
10
|
+
return createDebug(`${PACKAGE_NAME}:${scope}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/imports/nodes.ts
|
|
14
|
+
function isSpecifierNode(node) {
|
|
15
|
+
return node != null && node.type === "Literal" && typeof node.value === "string";
|
|
16
|
+
}
|
|
17
|
+
function createSpecifierVisitor(onSpecifier) {
|
|
18
|
+
return {
|
|
19
|
+
"ImportDeclaration, ImportExpression"(node) {
|
|
20
|
+
if (isSpecifierNode(node.source)) {
|
|
21
|
+
onSpecifier(node.source);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
CallExpression(node) {
|
|
25
|
+
if (node.callee.type !== "Identifier" || node.callee.name !== "require") {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const arg = node.arguments[0];
|
|
29
|
+
if (isSpecifierNode(arg)) {
|
|
30
|
+
onSpecifier(arg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function replaceSpecifier(node, specifier) {
|
|
36
|
+
const quote = node.raw[0] ?? "'";
|
|
37
|
+
return (fixer) => fixer.replaceTextRange(node.range, `${quote}${specifier}${quote}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/extensions.ts
|
|
41
|
+
var ENTRY_BASENAME = "index";
|
|
42
|
+
var ENTRY_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"];
|
|
43
|
+
var MODULE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"];
|
|
44
|
+
var ENTRY_EXTENSION_SET = new Set(ENTRY_EXTENSIONS);
|
|
45
|
+
var MODULE_EXTENSION_SET = new Set(MODULE_EXTENSIONS);
|
|
46
|
+
function isEntryExtension(ext) {
|
|
47
|
+
return ENTRY_EXTENSION_SET.has(ext);
|
|
48
|
+
}
|
|
49
|
+
function entryFileName(ext) {
|
|
50
|
+
return `${ENTRY_BASENAME}.${ext}`;
|
|
51
|
+
}
|
|
52
|
+
var ENTRY_FILE_NAMES = ENTRY_EXTENSIONS.map(entryFileName);
|
|
53
|
+
function isEntryFileName(name, ext) {
|
|
54
|
+
return isEntryBasename(name) && isEntryExtension(ext);
|
|
55
|
+
}
|
|
56
|
+
function isEntryBasename(name) {
|
|
57
|
+
return name === ENTRY_BASENAME;
|
|
58
|
+
}
|
|
59
|
+
function isModuleExtension(ext) {
|
|
60
|
+
return MODULE_EXTENSION_SET.has(ext);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/path/posix.ts
|
|
64
|
+
function toPosix(path3) {
|
|
65
|
+
return path3.replace(/\\/g, "/");
|
|
66
|
+
}
|
|
67
|
+
function segments(path3) {
|
|
68
|
+
return path3.split("/").filter(Boolean);
|
|
69
|
+
}
|
|
70
|
+
function joinSegments(segs) {
|
|
71
|
+
return segs.length === 0 ? "/" : `/${segs.join("/")}`;
|
|
72
|
+
}
|
|
73
|
+
function joinPath(dir, segment) {
|
|
74
|
+
return dir === "/" ? `/${segment}` : `${dir}/${segment}`;
|
|
75
|
+
}
|
|
76
|
+
function basename(path3) {
|
|
77
|
+
return path3.slice(path3.lastIndexOf("/") + 1);
|
|
78
|
+
}
|
|
79
|
+
function splitExtension(base) {
|
|
80
|
+
const dot = base.lastIndexOf(".");
|
|
81
|
+
if (dot <= 0 || dot === base.length - 1) {
|
|
82
|
+
return { name: base, ext: "" };
|
|
83
|
+
}
|
|
84
|
+
return { name: base.slice(0, dot), ext: base.slice(dot + 1) };
|
|
85
|
+
}
|
|
86
|
+
function dirname(path3) {
|
|
87
|
+
const segs = segments(path3);
|
|
88
|
+
if (segs.length === 0) {
|
|
89
|
+
return "/";
|
|
90
|
+
}
|
|
91
|
+
return joinSegments(segs.slice(0, -1));
|
|
92
|
+
}
|
|
93
|
+
function resolvePath(base, path3) {
|
|
94
|
+
const segs = path3.startsWith("/") ? [] : segments(base);
|
|
95
|
+
for (const part of path3.split("/")) {
|
|
96
|
+
if (part === "" || part === ".") {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (part === "..") {
|
|
100
|
+
if (segs.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
segs.pop();
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
segs.push(part);
|
|
107
|
+
}
|
|
108
|
+
return joinSegments(segs);
|
|
109
|
+
}
|
|
110
|
+
function commonPrefixLength(segsA, segsB) {
|
|
111
|
+
let i = 0;
|
|
112
|
+
while (i < segsA.length && i < segsB.length && segsA[i] === segsB[i]) {
|
|
113
|
+
i += 1;
|
|
114
|
+
}
|
|
115
|
+
return i;
|
|
116
|
+
}
|
|
117
|
+
function commonDepth(a, b) {
|
|
118
|
+
return commonPrefixLength(segments(a), segments(b));
|
|
119
|
+
}
|
|
120
|
+
function relativePath(from, to) {
|
|
121
|
+
const segsFrom = segments(from);
|
|
122
|
+
const segsTo = segments(to);
|
|
123
|
+
const i = commonPrefixLength(segsFrom, segsTo);
|
|
124
|
+
const ups = Array(segsFrom.length - i).fill("..");
|
|
125
|
+
const downs = segsTo.slice(i);
|
|
126
|
+
return [...ups, ...downs].join("/");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/imports/specifier.ts
|
|
130
|
+
function parseSpecifier(specifier, fromDir, aliases) {
|
|
131
|
+
if (specifier.includes("?") || specifier.includes("!")) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (isRelativeSpecifier(specifier)) {
|
|
135
|
+
return finalize(resolvePath(fromDir, specifier), { kind: "relative" });
|
|
136
|
+
}
|
|
137
|
+
const alias = matchAlias(specifier, aliases);
|
|
138
|
+
if (alias !== null) {
|
|
139
|
+
const suffix = specifier.slice(alias.prefix.length).replace(/^\/+/, "");
|
|
140
|
+
return finalize(resolvePath(alias.anchor, suffix), { kind: "alias", alias });
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function isRelativeSpecifier(specifier) {
|
|
145
|
+
return specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../");
|
|
146
|
+
}
|
|
147
|
+
function pickLongest(aliases, key) {
|
|
148
|
+
let best = null;
|
|
149
|
+
let bestLength = -1;
|
|
150
|
+
for (const alias of aliases) {
|
|
151
|
+
const matched = key(alias);
|
|
152
|
+
if (matched === null || matched.length <= bestLength) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
best = alias;
|
|
156
|
+
bestLength = matched.length;
|
|
157
|
+
}
|
|
158
|
+
return best;
|
|
159
|
+
}
|
|
160
|
+
function matchAlias(specifier, aliases) {
|
|
161
|
+
return pickLongest(aliases, (alias) => covers(alias.prefix, specifier) ? alias.prefix : null);
|
|
162
|
+
}
|
|
163
|
+
function finalize(path3, form) {
|
|
164
|
+
if (path3 === null) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const { ext } = splitExtension(basename(path3));
|
|
168
|
+
if (ext !== "" && !isModuleExtension(ext)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return { path: path3, form, extension: ext === "" ? null : ext };
|
|
172
|
+
}
|
|
173
|
+
function renderSpecifier(form, fromDir, barrier, aliases, extension = null) {
|
|
174
|
+
const dir = renderDirectory(form, fromDir, barrier, aliases);
|
|
175
|
+
return extension === null ? dir : withEntry(dir, extension);
|
|
176
|
+
}
|
|
177
|
+
function renderDirectory(form, fromDir, barrier, aliases) {
|
|
178
|
+
if (form.kind === "relative") {
|
|
179
|
+
return renderRelative(fromDir, barrier);
|
|
180
|
+
}
|
|
181
|
+
if (covers(form.alias.anchor, barrier)) {
|
|
182
|
+
return renderAlias(form.alias, barrier);
|
|
183
|
+
}
|
|
184
|
+
const longest = longestCoveringAlias(aliases, barrier);
|
|
185
|
+
return longest === null ? renderRelative(fromDir, barrier) : renderAlias(longest, barrier);
|
|
186
|
+
}
|
|
187
|
+
function withEntry(dir, extension) {
|
|
188
|
+
const separator = dir.endsWith("/") ? "" : "/";
|
|
189
|
+
return `${dir}${separator}${entryFileName(extension)}`;
|
|
190
|
+
}
|
|
191
|
+
function covers(prefix, value) {
|
|
192
|
+
return value === prefix || value.startsWith(`${prefix}/`);
|
|
193
|
+
}
|
|
194
|
+
function longestCoveringAlias(aliases, dir) {
|
|
195
|
+
return pickLongest(aliases, (alias) => covers(alias.anchor, dir) ? alias.anchor : null);
|
|
196
|
+
}
|
|
197
|
+
function renderAlias(alias, barrier) {
|
|
198
|
+
if (barrier === alias.anchor) {
|
|
199
|
+
return alias.prefix;
|
|
200
|
+
}
|
|
201
|
+
return `${alias.prefix}/${relativePath(alias.anchor, barrier)}`;
|
|
202
|
+
}
|
|
203
|
+
function renderRelative(fromDir, barrier) {
|
|
204
|
+
const rel = relativePath(fromDir, barrier);
|
|
205
|
+
return rel.startsWith("./") || rel.startsWith("../") ? rel : `./${rel}`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/settings/aliases.ts
|
|
209
|
+
var debug = createLogger("aliases");
|
|
210
|
+
function parseAliases(rawAliases, aliasesBaseUrl, source) {
|
|
211
|
+
if (typeof rawAliases !== "object" || rawAliases === null || Array.isArray(rawAliases)) {
|
|
212
|
+
throw new Error(`${source} must be an object`);
|
|
213
|
+
}
|
|
214
|
+
const base = resolvePath("/", aliasesBaseUrl) ?? "/";
|
|
215
|
+
const result = [];
|
|
216
|
+
const seen = /* @__PURE__ */ new Set();
|
|
217
|
+
for (const [key, rawValue] of Object.entries(rawAliases)) {
|
|
218
|
+
if (!hasValidStarShape(key)) {
|
|
219
|
+
debug('%s: dropped %s \u2014 key must have no "*" or end with "/*"', source, key);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const anchorsRaw = toAnchorList(key, rawValue, source);
|
|
223
|
+
const prefix = stripStarSuffix(key);
|
|
224
|
+
for (const anchorRaw of anchorsRaw) {
|
|
225
|
+
if (!hasValidStarShape(anchorRaw)) {
|
|
226
|
+
debug(
|
|
227
|
+
'%s: dropped %s -> %s \u2014 target must have no "*" or end with "/*"',
|
|
228
|
+
source,
|
|
229
|
+
key,
|
|
230
|
+
anchorRaw
|
|
231
|
+
);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const anchor = normalizeAnchor(anchorRaw, base);
|
|
235
|
+
if (anchor === null) {
|
|
236
|
+
debug(
|
|
237
|
+
"%s: dropped %s -> %s \u2014 not a directory (only index.<ext> stands for one) or above the repository root",
|
|
238
|
+
source,
|
|
239
|
+
key,
|
|
240
|
+
anchorRaw
|
|
241
|
+
);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const dedupeKey = `${prefix}\0${anchor}`;
|
|
245
|
+
if (seen.has(dedupeKey)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
seen.add(dedupeKey);
|
|
249
|
+
result.push({ prefix, anchor });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (debug.enabled) {
|
|
253
|
+
debug(
|
|
254
|
+
`${source} (aliasesBaseUrl ${aliasesBaseUrl}) resolved to:
|
|
255
|
+
${formatAliases(result)}`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
function formatAliases(aliases) {
|
|
261
|
+
if (aliases.length === 0) {
|
|
262
|
+
return "(none)";
|
|
263
|
+
}
|
|
264
|
+
return aliases.map(({ prefix, anchor }) => `${prefix} - ${anchor}`).join("\n");
|
|
265
|
+
}
|
|
266
|
+
function toAnchorList(key, value, source) {
|
|
267
|
+
if (typeof value === "string") {
|
|
268
|
+
return [value];
|
|
269
|
+
}
|
|
270
|
+
if (Array.isArray(value)) {
|
|
271
|
+
for (const item of value) {
|
|
272
|
+
if (typeof item !== "string") {
|
|
273
|
+
throw new Error(`${source}['${key}'] must contain only strings`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
throw new Error(`${source}['${key}'] must be a string or an array of strings`);
|
|
279
|
+
}
|
|
280
|
+
function hasValidStarShape(value) {
|
|
281
|
+
const starCount = value.split("*").length - 1;
|
|
282
|
+
if (starCount > 1 || starCount === 1 && !value.endsWith("/*")) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
return stripStarSuffix(value) !== "";
|
|
286
|
+
}
|
|
287
|
+
function stripStarSuffix(value) {
|
|
288
|
+
return value.endsWith("/*") ? value.slice(0, -2) : value;
|
|
289
|
+
}
|
|
290
|
+
function normalizeAnchor(anchorRaw, base) {
|
|
291
|
+
if (anchorRaw.endsWith("/*")) {
|
|
292
|
+
return resolvePath(base, anchorRaw.slice(0, -2));
|
|
293
|
+
}
|
|
294
|
+
const lastSegment = basename(anchorRaw);
|
|
295
|
+
const { name, ext } = splitExtension(lastSegment);
|
|
296
|
+
if (ext === "") {
|
|
297
|
+
return resolvePath(base, anchorRaw);
|
|
298
|
+
}
|
|
299
|
+
if (!isEntryFileName(name, ext)) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return resolvePath(base, anchorRaw.slice(0, -lastSegment.length));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/settings/weld.ts
|
|
306
|
+
var DEFAULT_ALIASES_BASE_URL = ".";
|
|
307
|
+
function getWeldSettings(settings) {
|
|
308
|
+
if (typeof settings !== "object" || settings === null) {
|
|
309
|
+
return void 0;
|
|
310
|
+
}
|
|
311
|
+
const weld = settings.weld;
|
|
312
|
+
if (weld === void 0) {
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
if (typeof weld !== "object" || weld === null) {
|
|
316
|
+
throw new Error("settings.weld must be an object");
|
|
317
|
+
}
|
|
318
|
+
return weld;
|
|
319
|
+
}
|
|
320
|
+
function requireString(value, source) {
|
|
321
|
+
if (typeof value !== "string") {
|
|
322
|
+
throw new Error(`${source} must be a string, got ${typeof value}`);
|
|
323
|
+
}
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
function getRepoRoot(settings, override) {
|
|
327
|
+
if (override !== void 0) {
|
|
328
|
+
return requireString(override, "options.repoRoot");
|
|
329
|
+
}
|
|
330
|
+
const repoRoot = getWeldSettings(settings)?.repoRoot;
|
|
331
|
+
if (repoRoot === void 0) {
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
return requireString(repoRoot, "settings.weld.repoRoot");
|
|
335
|
+
}
|
|
336
|
+
function getAliasesBaseUrl(settings, override) {
|
|
337
|
+
if (override !== void 0) {
|
|
338
|
+
return requireString(override, "options.aliasesBaseUrl");
|
|
339
|
+
}
|
|
340
|
+
const aliasesBaseUrl = getWeldSettings(settings)?.aliasesBaseUrl;
|
|
341
|
+
if (aliasesBaseUrl === void 0) {
|
|
342
|
+
return DEFAULT_ALIASES_BASE_URL;
|
|
343
|
+
}
|
|
344
|
+
return requireString(aliasesBaseUrl, "settings.weld.aliasesBaseUrl");
|
|
345
|
+
}
|
|
346
|
+
function readAliases(settings, override, baseUrlOverride) {
|
|
347
|
+
const fromOverride = override !== void 0;
|
|
348
|
+
const rawAliases = fromOverride ? override : getWeldSettings(settings)?.aliases;
|
|
349
|
+
if (rawAliases === void 0) {
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
const source = fromOverride ? "options.aliases" : "settings.weld.aliases";
|
|
353
|
+
return parseAliases(rawAliases, getAliasesBaseUrl(settings, baseUrlOverride), source);
|
|
354
|
+
}
|
|
355
|
+
var TTL_MS = 6e4;
|
|
356
|
+
var aliasesCache = [];
|
|
357
|
+
function sameKey(left, right) {
|
|
358
|
+
return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
|
|
359
|
+
}
|
|
360
|
+
function cachedAliases(key, parse) {
|
|
361
|
+
const time = Date.now();
|
|
362
|
+
for (const entry of aliasesCache) {
|
|
363
|
+
if (entry.expiresAt > time && sameKey(entry.key, key)) {
|
|
364
|
+
return entry.result;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const result = parse();
|
|
368
|
+
aliasesCache = aliasesCache.filter(
|
|
369
|
+
(entry) => entry.expiresAt > time && !sameKey(entry.key, key)
|
|
370
|
+
);
|
|
371
|
+
aliasesCache.push({ key, result, expiresAt: time + TTL_MS });
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
function getAliases(settings, override, baseUrlOverride) {
|
|
375
|
+
return cachedAliases(
|
|
376
|
+
[settings, override, baseUrlOverride],
|
|
377
|
+
() => readAliases(settings, override, baseUrlOverride)
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
function getAliasesFromPaths(paths, virtualBase, source) {
|
|
381
|
+
return cachedAliases(
|
|
382
|
+
[paths, virtualBase, source],
|
|
383
|
+
() => parseAliases(paths, virtualBase, source)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
function hasAliases(settings, override) {
|
|
387
|
+
if (override !== void 0) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
return getWeldSettings(settings)?.aliases !== void 0;
|
|
391
|
+
}
|
|
392
|
+
var DRIVE_PREFIX = /^[A-Za-z]:/;
|
|
393
|
+
function isDriveRoot(realPath) {
|
|
394
|
+
return realPath.length === 2 && DRIVE_PREFIX.test(realPath);
|
|
395
|
+
}
|
|
396
|
+
function isAbsoluteRealPath(realPath) {
|
|
397
|
+
return realPath.startsWith("/") || DRIVE_PREFIX.test(realPath);
|
|
398
|
+
}
|
|
399
|
+
function normalizeRoot(root) {
|
|
400
|
+
const posix = toPosix(root);
|
|
401
|
+
if (posix === "/") {
|
|
402
|
+
return "";
|
|
403
|
+
}
|
|
404
|
+
return posix.endsWith("/") ? posix.slice(0, -1) : posix;
|
|
405
|
+
}
|
|
406
|
+
function joinReal(dir, name) {
|
|
407
|
+
return dir.endsWith("/") ? `${dir}${name}` : `${dir}/${name}`;
|
|
408
|
+
}
|
|
409
|
+
function parentOf(dir) {
|
|
410
|
+
const idx = dir.lastIndexOf("/");
|
|
411
|
+
if (idx === -1) {
|
|
412
|
+
return dir;
|
|
413
|
+
}
|
|
414
|
+
const head = dir.slice(0, idx);
|
|
415
|
+
if (head === "") {
|
|
416
|
+
return "/";
|
|
417
|
+
}
|
|
418
|
+
if (isDriveRoot(head)) {
|
|
419
|
+
return `${head}/`;
|
|
420
|
+
}
|
|
421
|
+
return head;
|
|
422
|
+
}
|
|
423
|
+
function sameSegment(left, right, index) {
|
|
424
|
+
if (index === 0 && DRIVE_PREFIX.test(left) && DRIVE_PREFIX.test(right)) {
|
|
425
|
+
return left.toUpperCase() === right.toUpperCase();
|
|
426
|
+
}
|
|
427
|
+
return left === right;
|
|
428
|
+
}
|
|
429
|
+
function commonRealDirectory(left, right) {
|
|
430
|
+
const prefixOf = (candidate) => DRIVE_PREFIX.exec(candidate)?.[0] ?? "";
|
|
431
|
+
const prefix = prefixOf(left);
|
|
432
|
+
const rightPrefix = prefixOf(right);
|
|
433
|
+
if (!sameSegment(prefix, rightPrefix, 0)) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
const leftRest = left.slice(prefix.length);
|
|
437
|
+
const depth = commonDepth(leftRest, right.slice(rightPrefix.length));
|
|
438
|
+
return `${prefix}${joinSegments(segments(leftRest).slice(0, depth))}`;
|
|
439
|
+
}
|
|
440
|
+
function resolveRealPath(base, target) {
|
|
441
|
+
const posixTarget = toPosix(target);
|
|
442
|
+
if (isAbsoluteRealPath(posixTarget)) {
|
|
443
|
+
return posixTarget;
|
|
444
|
+
}
|
|
445
|
+
const posixBase = toPosix(base);
|
|
446
|
+
const resolveBase = isDriveRoot(posixBase) ? `${posixBase}/` : posixBase;
|
|
447
|
+
const resolve = DRIVE_PREFIX.test(resolveBase) ? path2.win32.resolve : path2.posix.resolve;
|
|
448
|
+
return toPosix(resolve(resolveBase, posixTarget));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// src/host/root.ts
|
|
452
|
+
var debug2 = createLogger("root");
|
|
453
|
+
function findRepoRoot(startDir, exists) {
|
|
454
|
+
let current = toPosix(startDir);
|
|
455
|
+
let found = null;
|
|
456
|
+
debug2("searching repo root from %s", current);
|
|
457
|
+
for (; ; ) {
|
|
458
|
+
if (exists(joinReal(current, "package.json"))) {
|
|
459
|
+
found = current;
|
|
460
|
+
debug2("found package.json at %s", current);
|
|
461
|
+
}
|
|
462
|
+
if (exists(joinReal(current, ".git"))) {
|
|
463
|
+
debug2("found .git at %s, stopping", current);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
const parent = parentOf(current);
|
|
467
|
+
if (parent === current) {
|
|
468
|
+
debug2("reached filesystem root at %s", current);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
current = parent;
|
|
472
|
+
}
|
|
473
|
+
debug2("resolved repo root: %o", found);
|
|
474
|
+
return found;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/host/fs.ts
|
|
478
|
+
var debug3 = createLogger("fs");
|
|
479
|
+
var TTL_MS2 = 6e5;
|
|
480
|
+
var NEGATIVE_TTL_MS = 5e3;
|
|
481
|
+
function peek(cache2, key, time) {
|
|
482
|
+
const entry = cache2.get(key);
|
|
483
|
+
return entry !== void 0 && entry.expiresAt > time ? entry.value : void 0;
|
|
484
|
+
}
|
|
485
|
+
function remember(cache2, key, time, value) {
|
|
486
|
+
cache2.set(key, { value, expiresAt: time + (value ? TTL_MS2 : NEGATIVE_TTL_MS) });
|
|
487
|
+
return value;
|
|
488
|
+
}
|
|
489
|
+
function createFsHost(root, options = {}) {
|
|
490
|
+
const normalizedRoot = normalizeRoot(root);
|
|
491
|
+
const rootSegments = segments(normalizedRoot);
|
|
492
|
+
const now = options.now ?? Date.now;
|
|
493
|
+
const nativeExists = options.exists ?? existsSync;
|
|
494
|
+
const entryPointCache = /* @__PURE__ */ new Map();
|
|
495
|
+
const directoryCache = /* @__PURE__ */ new Map();
|
|
496
|
+
function toRealDir(dir) {
|
|
497
|
+
return `${normalizedRoot}${dir}`;
|
|
498
|
+
}
|
|
499
|
+
function toVirtual(realPath) {
|
|
500
|
+
const posixPath = toPosix(realPath);
|
|
501
|
+
if (!isAbsoluteRealPath(posixPath)) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
const pathSegments = segments(posixPath);
|
|
505
|
+
if (pathSegments.length < rootSegments.length) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
for (let i = 0; i < rootSegments.length; i += 1) {
|
|
509
|
+
if (!sameSegment(pathSegments[i], rootSegments[i], i)) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return joinSegments(pathSegments.slice(rootSegments.length));
|
|
514
|
+
}
|
|
515
|
+
function directoryExists(dir) {
|
|
516
|
+
const time = now();
|
|
517
|
+
const cached = peek(directoryCache, dir, time);
|
|
518
|
+
if (cached !== void 0) {
|
|
519
|
+
return cached;
|
|
520
|
+
}
|
|
521
|
+
if (missingParent(dir, time)) {
|
|
522
|
+
return remember(directoryCache, dir, time, false);
|
|
523
|
+
}
|
|
524
|
+
const found = nativeExists(toRealDir(dir));
|
|
525
|
+
debug3("check directory %s: %o", dir, found);
|
|
526
|
+
return remember(directoryCache, dir, time, found);
|
|
527
|
+
}
|
|
528
|
+
function missingParent(dir, time) {
|
|
529
|
+
const parent = dirname(dir);
|
|
530
|
+
if (parent === dir) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
return peek(directoryCache, parent, time) === false;
|
|
534
|
+
}
|
|
535
|
+
function hasEntryPoint(dir) {
|
|
536
|
+
if (!dir.startsWith("/")) {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
const time = now();
|
|
540
|
+
const cached = peek(entryPointCache, dir, time);
|
|
541
|
+
if (cached !== void 0) {
|
|
542
|
+
return cached;
|
|
543
|
+
}
|
|
544
|
+
if (!directoryExists(dir)) {
|
|
545
|
+
return remember(entryPointCache, dir, time, false);
|
|
546
|
+
}
|
|
547
|
+
const realDir = toRealDir(dir);
|
|
548
|
+
const found = ENTRY_FILE_NAMES.some((name) => nativeExists(joinReal(realDir, name)));
|
|
549
|
+
debug3("check entry point %s: %o", dir, found);
|
|
550
|
+
return remember(entryPointCache, dir, time, found);
|
|
551
|
+
}
|
|
552
|
+
return { hasEntryPoint, toVirtual };
|
|
553
|
+
}
|
|
554
|
+
var instanceCache = /* @__PURE__ */ new Map();
|
|
555
|
+
var repoRootCache = /* @__PURE__ */ new Map();
|
|
556
|
+
function findRepoRootCached(cwd) {
|
|
557
|
+
const cached = repoRootCache.get(cwd);
|
|
558
|
+
if (cached !== void 0) {
|
|
559
|
+
return cached;
|
|
560
|
+
}
|
|
561
|
+
const found = findRepoRoot(cwd, existsSync);
|
|
562
|
+
repoRootCache.set(cwd, found);
|
|
563
|
+
return found;
|
|
564
|
+
}
|
|
565
|
+
function resolveRoot(settings, cwd, repoRootOverride, coverDirs) {
|
|
566
|
+
const explicitRoot = getRepoRoot(settings, repoRootOverride);
|
|
567
|
+
if (explicitRoot !== void 0) {
|
|
568
|
+
return resolveRealPath(cwd, explicitRoot);
|
|
569
|
+
}
|
|
570
|
+
const base = toPosix(findRepoRootCached(cwd) ?? cwd);
|
|
571
|
+
let root = base;
|
|
572
|
+
for (const coverDir of coverDirs) {
|
|
573
|
+
const combined = commonRealDirectory(root, toPosix(coverDir));
|
|
574
|
+
if (combined === null) {
|
|
575
|
+
debug3(
|
|
576
|
+
"resolveRoot: cover dir %s shares no prefix with root %s, ignoring",
|
|
577
|
+
coverDir,
|
|
578
|
+
root
|
|
579
|
+
);
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
root = combined;
|
|
583
|
+
}
|
|
584
|
+
if (root !== base) {
|
|
585
|
+
debug3("resolveRoot: auto root raised from %s to %s to cover requested dirs", base, root);
|
|
586
|
+
}
|
|
587
|
+
return root;
|
|
588
|
+
}
|
|
589
|
+
function getFsHost(settings, cwd, repoRootOverride, coverDirs = []) {
|
|
590
|
+
const root = resolveRoot(settings, cwd, repoRootOverride, coverDirs);
|
|
591
|
+
const cached = instanceCache.get(root);
|
|
592
|
+
if (cached) {
|
|
593
|
+
return cached;
|
|
594
|
+
}
|
|
595
|
+
debug3("getFsHost: creating instance for root %s", root);
|
|
596
|
+
const created = createFsHost(root);
|
|
597
|
+
instanceCache.set(root, created);
|
|
598
|
+
return created;
|
|
599
|
+
}
|
|
600
|
+
var debug4 = createLogger("tsconfig");
|
|
601
|
+
var TTL_MS3 = 6e5;
|
|
602
|
+
var NEGATIVE_TTL_MS2 = 5e3;
|
|
603
|
+
var cache = /* @__PURE__ */ new Map();
|
|
604
|
+
function loadTsconfigPaths(realFilePath, options = {}) {
|
|
605
|
+
if (!path2.isAbsolute(realFilePath)) {
|
|
606
|
+
debug4("skip tsconfig lookup: file path is not absolute: %s", realFilePath);
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
const dir = path2.dirname(realFilePath);
|
|
610
|
+
const now = options.now ?? Date.now;
|
|
611
|
+
const time = now();
|
|
612
|
+
const entry = cache.get(dir);
|
|
613
|
+
if (entry && entry.expiresAt > time) {
|
|
614
|
+
return entry.value;
|
|
615
|
+
}
|
|
616
|
+
const readCache = /* @__PURE__ */ new Map();
|
|
617
|
+
const read = options.read ?? ((searchDir) => getTsconfig(searchDir, "tsconfig.json", readCache));
|
|
618
|
+
const value = readTsconfigPaths(dir, read);
|
|
619
|
+
cache.set(dir, { value, expiresAt: time + (value ? TTL_MS3 : NEGATIVE_TTL_MS2) });
|
|
620
|
+
return value;
|
|
621
|
+
}
|
|
622
|
+
function readTsconfigPaths(dir, read) {
|
|
623
|
+
let found;
|
|
624
|
+
try {
|
|
625
|
+
found = read(dir);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
debug4("failed to read tsconfig near %s: %s", dir, error);
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
if (!found) {
|
|
631
|
+
debug4("no tsconfig found upwards from %s", dir);
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
const configPath = found.path;
|
|
635
|
+
const compilerOptions = found.config.compilerOptions ?? {};
|
|
636
|
+
const paths = compilerOptions["paths"];
|
|
637
|
+
if (typeof paths !== "object" || paths === null || Object.keys(paths).length === 0) {
|
|
638
|
+
debug4("tsconfig %s has no compilerOptions.paths", configPath);
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
const baseUrl = typeof compilerOptions["baseUrl"] === "string" ? compilerOptions["baseUrl"] : void 0;
|
|
642
|
+
const realBaseDir = baseUrl !== void 0 ? path2.resolve(path2.dirname(configPath), baseUrl) : getImplicitBaseUrl(compilerOptions) ?? path2.dirname(configPath);
|
|
643
|
+
const record = relativizePathsValues(paths, realBaseDir);
|
|
644
|
+
debug4("found paths in %s, base dir %s", configPath, realBaseDir);
|
|
645
|
+
return {
|
|
646
|
+
paths: record,
|
|
647
|
+
realBaseDir,
|
|
648
|
+
configPath,
|
|
649
|
+
realAnchors: collectRealAnchors(record, realBaseDir)
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function getImplicitBaseUrl(compilerOptions) {
|
|
653
|
+
const symbol = Object.getOwnPropertySymbols(compilerOptions).find(
|
|
654
|
+
(candidate) => candidate.description === "implicitBaseUrl"
|
|
655
|
+
);
|
|
656
|
+
const value = symbol === void 0 ? void 0 : compilerOptions[symbol];
|
|
657
|
+
return typeof value === "string" ? value : void 0;
|
|
658
|
+
}
|
|
659
|
+
var ABSOLUTE_REAL_PATH = /^(?:\/|[A-Za-z]:)/;
|
|
660
|
+
function relativizePathsValues(paths, realBaseDir) {
|
|
661
|
+
const relativize = (item) => {
|
|
662
|
+
if (typeof item !== "string" || !ABSOLUTE_REAL_PATH.test(item)) {
|
|
663
|
+
return item;
|
|
664
|
+
}
|
|
665
|
+
const relative = toPosix(path2.relative(realBaseDir, item));
|
|
666
|
+
if (relative === "") {
|
|
667
|
+
return ".";
|
|
668
|
+
}
|
|
669
|
+
return relative.startsWith("*") ? `./${relative}` : relative;
|
|
670
|
+
};
|
|
671
|
+
const result = {};
|
|
672
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
673
|
+
result[key] = Array.isArray(value) ? value.map(relativize) : relativize(value);
|
|
674
|
+
}
|
|
675
|
+
return result;
|
|
676
|
+
}
|
|
677
|
+
function collectRealAnchors(paths, realBaseDir) {
|
|
678
|
+
const anchors = /* @__PURE__ */ new Set();
|
|
679
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
680
|
+
if (!hasValidStarShape(key)) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
const items = Array.isArray(value) ? value : [value];
|
|
684
|
+
for (const item of items) {
|
|
685
|
+
if (typeof item !== "string" || !hasValidStarShape(item)) {
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
const anchor = path2.resolve(realBaseDir, stripStarSuffix(item));
|
|
689
|
+
if (segments(toPosix(anchor)).includes("node_modules")) {
|
|
690
|
+
debug4("anchor %s is inside node_modules, excluded from root computation", anchor);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
anchors.add(anchor);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return [...anchors];
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/rules/context.ts
|
|
700
|
+
var debug5 = createLogger("rules");
|
|
701
|
+
var WELD_OPTION_PROPERTIES = {
|
|
702
|
+
repoRoot: { type: "string" },
|
|
703
|
+
aliasesBaseUrl: { type: "string" },
|
|
704
|
+
aliases: { type: "object" }
|
|
705
|
+
};
|
|
706
|
+
function resolveWeldContext(context, fsHostOverride) {
|
|
707
|
+
const options = context.options[0] ?? {};
|
|
708
|
+
const { fsHost, aliases } = resolveHostAndAliases(context, options, fsHostOverride);
|
|
709
|
+
const fromFile = fsHost.toVirtual(context.filename);
|
|
710
|
+
if (fromFile === null) {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
return { fromFile, aliases, fsHost, options };
|
|
714
|
+
}
|
|
715
|
+
function resolveHostAndAliases(context, options, fsHostOverride) {
|
|
716
|
+
if (hasAliases(context.settings, options.aliases)) {
|
|
717
|
+
return {
|
|
718
|
+
fsHost: fsHostOverride ?? getFsHost(context.settings, context.cwd, options.repoRoot),
|
|
719
|
+
aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl)
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
const found = loadTsconfigPaths(context.filename);
|
|
723
|
+
const hasExplicitRoot = getRepoRoot(context.settings, options.repoRoot) !== void 0;
|
|
724
|
+
const fsHost = getFsHost(
|
|
725
|
+
context.settings,
|
|
726
|
+
context.cwd,
|
|
727
|
+
options.repoRoot,
|
|
728
|
+
found?.realAnchors ?? []
|
|
729
|
+
);
|
|
730
|
+
return { fsHost, aliases: aliasesFromTsconfig(found, fsHost, hasExplicitRoot) };
|
|
731
|
+
}
|
|
732
|
+
function aliasesFromTsconfig(found, fsHost, hasExplicitRoot) {
|
|
733
|
+
if (found === null) {
|
|
734
|
+
return [];
|
|
735
|
+
}
|
|
736
|
+
if (hasExplicitRoot && fsHost.toVirtual(found.configPath) === null) {
|
|
737
|
+
debug5("discarding tsconfig %s: it is outside the explicit root", found.configPath);
|
|
738
|
+
return [];
|
|
739
|
+
}
|
|
740
|
+
const virtualBase = fsHost.toVirtual(found.realBaseDir);
|
|
741
|
+
if (virtualBase === null) {
|
|
742
|
+
debug5(
|
|
743
|
+
"discarding tsconfig %s: base dir %s is outside the root",
|
|
744
|
+
found.configPath,
|
|
745
|
+
found.realBaseDir
|
|
746
|
+
);
|
|
747
|
+
return [];
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
return getAliasesFromPaths(found.paths, virtualBase, found.configPath);
|
|
751
|
+
} catch (error) {
|
|
752
|
+
debug5("discarding tsconfig %s: %s", found.configPath, error);
|
|
753
|
+
return [];
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/rules/no-barrel-bypass/core/barrier.ts
|
|
758
|
+
function isIndexFile(targetPath) {
|
|
759
|
+
return isEntryBasename(splitExtension(basename(targetPath)).name);
|
|
760
|
+
}
|
|
761
|
+
function findBarrier(fromDir, targetPath, hasEntryPoint) {
|
|
762
|
+
const targetDir = dirname(targetPath);
|
|
763
|
+
const targetDirSegments = segments(targetDir);
|
|
764
|
+
const depth = commonDepth(fromDir, targetDir);
|
|
765
|
+
let dir = joinSegments(targetDirSegments.slice(0, depth));
|
|
766
|
+
for (let i = depth; i < targetDirSegments.length; i += 1) {
|
|
767
|
+
dir = joinPath(dir, targetDirSegments[i]);
|
|
768
|
+
if (hasEntryPoint(dir)) {
|
|
769
|
+
const atTargetDir = i === targetDirSegments.length - 1;
|
|
770
|
+
return atTargetDir && isIndexFile(targetPath) ? null : dir;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/rules/no-barrel-bypass/pipeline.ts
|
|
777
|
+
var debug6 = createLogger("no-barrel-bypass");
|
|
778
|
+
function createChecker({ fromFile, aliases }, host) {
|
|
779
|
+
const fromDir = dirname(fromFile);
|
|
780
|
+
return function checkImport(specifier) {
|
|
781
|
+
const target = parseSpecifier(specifier, fromDir, aliases);
|
|
782
|
+
if (target === null) {
|
|
783
|
+
debug6("%s: %s external dependency", fromFile, specifier);
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);
|
|
787
|
+
if (barrier === null) {
|
|
788
|
+
debug6("%s: %s -> %s, no barrier on the way", fromFile, specifier, target.path);
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
return renderSpecifier(target.form, fromDir, barrier, aliases, target.extension);
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/rules/no-barrel-bypass/index.ts
|
|
796
|
+
var debug7 = createLogger("no-barrel-bypass");
|
|
797
|
+
var messages = {
|
|
798
|
+
bypass: "Import bypasses the module barrel. Use '{{suggestion}}' instead of '{{original}}'.",
|
|
799
|
+
useBarrel: "Import through '{{suggestion}}'."
|
|
800
|
+
};
|
|
801
|
+
function createRule(fsHost) {
|
|
802
|
+
return {
|
|
803
|
+
meta: {
|
|
804
|
+
type: "problem",
|
|
805
|
+
docs: {
|
|
806
|
+
description: "disallow imports that reach into a module past its barrel",
|
|
807
|
+
url: "https://github.com/jt4d-lab/weld/blob/master/docs/rules/no-barrel-bypass.md"
|
|
808
|
+
},
|
|
809
|
+
fixable: "code",
|
|
810
|
+
hasSuggestions: true,
|
|
811
|
+
schema: [
|
|
812
|
+
{
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: {
|
|
815
|
+
fix: { type: "boolean" },
|
|
816
|
+
...WELD_OPTION_PROPERTIES
|
|
817
|
+
},
|
|
818
|
+
additionalProperties: false
|
|
819
|
+
}
|
|
820
|
+
],
|
|
821
|
+
messages
|
|
822
|
+
},
|
|
823
|
+
create(context) {
|
|
824
|
+
const weld = resolveWeldContext(context, fsHost);
|
|
825
|
+
if (weld === null) {
|
|
826
|
+
return {};
|
|
827
|
+
}
|
|
828
|
+
const { fromFile } = weld;
|
|
829
|
+
const applyFix = weld.options.fix ?? true;
|
|
830
|
+
const checkImport = createChecker(weld, weld.fsHost);
|
|
831
|
+
return createSpecifierVisitor((sourceNode) => {
|
|
832
|
+
const original = sourceNode.value;
|
|
833
|
+
const suggestion = checkImport(original);
|
|
834
|
+
if (suggestion === null) {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
debug7(
|
|
838
|
+
"%s: crosses barrier, %s -> %s (fix=%o)",
|
|
839
|
+
fromFile,
|
|
840
|
+
original,
|
|
841
|
+
suggestion,
|
|
842
|
+
applyFix
|
|
843
|
+
);
|
|
844
|
+
const fix = replaceSpecifier(sourceNode, suggestion);
|
|
845
|
+
context.report({
|
|
846
|
+
node: sourceNode,
|
|
847
|
+
messageId: "bypass",
|
|
848
|
+
data: { suggestion, original },
|
|
849
|
+
...applyFix ? { fix } : { suggest: [{ messageId: "useBarrel", data: { suggestion }, fix }] }
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
}
|
|
4
855
|
|
|
5
856
|
// src/rules/index.ts
|
|
6
|
-
var rules = {
|
|
857
|
+
var rules = {
|
|
858
|
+
"no-barrel-bypass": createRule()
|
|
859
|
+
};
|
|
7
860
|
|
|
8
861
|
// src/index.ts
|
|
9
|
-
var {
|
|
862
|
+
var { version } = createRequire(import.meta.url)("../package.json");
|
|
10
863
|
var recommended = {
|
|
11
864
|
name: "weld/recommended",
|
|
12
|
-
rules: {
|
|
865
|
+
rules: {
|
|
866
|
+
"weld/no-barrel-bypass": "error"
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
var strict = {
|
|
870
|
+
name: "weld/strict",
|
|
871
|
+
rules: {
|
|
872
|
+
"weld/no-barrel-bypass": "error"
|
|
873
|
+
}
|
|
13
874
|
};
|
|
14
875
|
var plugin = {
|
|
15
|
-
meta: {
|
|
876
|
+
meta: {
|
|
877
|
+
name: PACKAGE_NAME,
|
|
878
|
+
version
|
|
879
|
+
},
|
|
16
880
|
rules,
|
|
17
|
-
configs: {
|
|
881
|
+
configs: {
|
|
882
|
+
recommended,
|
|
883
|
+
strict
|
|
884
|
+
}
|
|
18
885
|
};
|
|
19
886
|
recommended.plugins = { weld: plugin };
|
|
887
|
+
strict.plugins = { weld: plugin };
|
|
20
888
|
var index_default = plugin;
|
|
21
889
|
|
|
22
890
|
export { index_default as default };
|