@deslop/coding-standards 0.0.0-stage → 0.1.761
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MutableRef-DT4DAKeI.mjs +5851 -0
- package/dist/install.d.ts +1 -0
- package/dist/install.js +24769 -0
- package/dist/oxlint.d.ts +4669 -0
- package/dist/oxlint.js +3076 -0
- package/package.json +62 -3
- package/skills/design/SKILL.md +107 -0
- package/skills/engineering/SKILL.md +562 -0
- package/skills/testing/SKILL.md +63 -0
- package/src/css-properties.d.ts +9 -0
- package/tsconfig.json +34 -0
- package/README.md +0 -3
package/dist/oxlint.js
ADDED
|
@@ -0,0 +1,3076 @@
|
|
|
1
|
+
import { $n as isReadonlyObject, An as symbol, B as some$1, Bn as hasProperty, C as flatMap$1, D as getSomes, E as get$1, F as map$1, Fn as optimize, Gn as isNotNull, H as take, Jn as isNullish, K as remove, Kn as isNotNullish, L as of, Ln as string, M as join, Mn as combine, N as last, Nn as hash, O as isArrayEmpty, On as byReferenceUnsafe, Pn as number, R as prepend, U as fromEntries, V as sort, Vn as isBoolean, W as get$2, Y as fromOption, Yn as isNumber, _ as every, _t as orElse, a as startsWith, at as firstSomeOf, b as findFirst, bn as NodeInspectSymbol, c as TaggedError, d as appendAll, er as isString, f as contains$1, ft as isSome, g as ensure, gr as pipeArguments, gt as none, h as empty, ht as match, i as Order, it as filter, k as isArrayNonEmpty, kn as equals, lr as dual, lt as getOrElse, m as dropRight, mt as map, n as make$2, nt as contains, ot as flatMap, p as dedupe, pr as pipe, q as values, r as update, rt as exists, st as fromNullishOr, t as get$3, tt as all, u as append, v as filter$1, vt as some, w as flatten, x as findFirstIndex, y as filterMap, yt as zipWith, zn as symbol$1 } from "./MutableRef-DT4DAKeI.mjs";
|
|
2
|
+
//#region ../../node_modules/.pnpm/effect@4.0.0/node_modules/effect/dist/internal/graph.js
|
|
3
|
+
/** @internal */
|
|
4
|
+
const TypeId = "~effect/Graph";
|
|
5
|
+
/** @internal */
|
|
6
|
+
const toImpl = (graph) => graph;
|
|
7
|
+
/** @internal */
|
|
8
|
+
const isTransforming = (graph) => toImpl(graph).transforming;
|
|
9
|
+
const edgeEquals = (type, self, that) => (type === "directed" ? self.source === that.source && self.target === that.target : self.source === that.source && self.target === that.target || self.source === that.target && self.target === that.source) && equals(self.data, that.data);
|
|
10
|
+
const edgeHash = (type, edge) => type === "directed" ? hash(edge) : optimize(combine(hash(edge.data), endpointsHash(edge.source, edge.target)));
|
|
11
|
+
/**
|
|
12
|
+
* Addition is commutative and does not cancel self-loops.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
const endpointsHash = (source, target) => combine(0, hash(source)) + combine(0, hash(target));
|
|
17
|
+
const graphSeed = /*#__PURE__*/ string("Graph");
|
|
18
|
+
const ProtoGraph = {
|
|
19
|
+
[TypeId]: {
|
|
20
|
+
_N: (_) => _,
|
|
21
|
+
_E: (_) => _
|
|
22
|
+
},
|
|
23
|
+
[Symbol.iterator]() {
|
|
24
|
+
return this.nodes[Symbol.iterator]();
|
|
25
|
+
},
|
|
26
|
+
[NodeInspectSymbol]() {
|
|
27
|
+
return this.toJSON();
|
|
28
|
+
},
|
|
29
|
+
[symbol](that) {
|
|
30
|
+
if (hasProperty(that, "~effect/Graph")) {
|
|
31
|
+
const thatImpl = toImpl(that);
|
|
32
|
+
if (this.nodes.size !== thatImpl.nodes.size || this.edges.size !== thatImpl.edges.size || this.type !== thatImpl.type) return false;
|
|
33
|
+
for (const [nodeIndex, nodeData] of this.nodes) if (!thatImpl.nodes.has(nodeIndex) || !equals(nodeData, thatImpl.nodes.get(nodeIndex))) return false;
|
|
34
|
+
for (const [edgeIndex, edgeData] of this.edges) {
|
|
35
|
+
const otherEdge = thatImpl.edges.get(edgeIndex);
|
|
36
|
+
if (otherEdge === void 0 || !edgeEquals(this.type, edgeData, otherEdge)) return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
},
|
|
42
|
+
[symbol$1]() {
|
|
43
|
+
let hash$2 = graphSeed;
|
|
44
|
+
hash$2 = hash$2 ^ string(this.type);
|
|
45
|
+
hash$2 = hash$2 ^ number(this.nodes.size);
|
|
46
|
+
hash$2 = hash$2 ^ number(this.edges.size);
|
|
47
|
+
for (const [nodeIndex, nodeData] of this.nodes) hash$2 ^= combine(hash(nodeIndex), hash(nodeData));
|
|
48
|
+
for (const [edgeIndex, edgeData] of this.edges) hash$2 ^= combine(hash(edgeIndex), edgeHash(this.type, edgeData));
|
|
49
|
+
return optimize(hash$2);
|
|
50
|
+
},
|
|
51
|
+
toJSON() {
|
|
52
|
+
return {
|
|
53
|
+
_id: "Graph",
|
|
54
|
+
nodeCount: this.nodes.size,
|
|
55
|
+
edgeCount: this.edges.size,
|
|
56
|
+
type: this.type
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
toString() {
|
|
60
|
+
return `Graph(${this.type}, ${this.nodes.size}, ${this.edges.size})`;
|
|
61
|
+
},
|
|
62
|
+
pipe() {
|
|
63
|
+
return pipeArguments(this, arguments);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/** @internal */
|
|
67
|
+
const make$1 = (type, mutable) => {
|
|
68
|
+
const graph = Object.create(ProtoGraph);
|
|
69
|
+
graph.type = type;
|
|
70
|
+
graph.mutable = mutable;
|
|
71
|
+
graph.transforming = false;
|
|
72
|
+
graph.nodes = /* @__PURE__ */ new Map();
|
|
73
|
+
graph.edges = /* @__PURE__ */ new Map();
|
|
74
|
+
graph.adjacency = /* @__PURE__ */ new Map();
|
|
75
|
+
graph.reverseAdjacency = /* @__PURE__ */ new Map();
|
|
76
|
+
graph.nextNodeIndex = 0;
|
|
77
|
+
graph.nextEdgeIndex = 0;
|
|
78
|
+
graph.acyclic = some(true);
|
|
79
|
+
return graph;
|
|
80
|
+
};
|
|
81
|
+
/** @internal */
|
|
82
|
+
const finalize = (source) => {
|
|
83
|
+
const graph = Object.create(ProtoGraph);
|
|
84
|
+
graph.type = source.type;
|
|
85
|
+
graph.mutable = false;
|
|
86
|
+
graph.transforming = false;
|
|
87
|
+
graph.nodes = source.nodes;
|
|
88
|
+
graph.edges = source.edges;
|
|
89
|
+
graph.adjacency = source.adjacency;
|
|
90
|
+
graph.reverseAdjacency = source.reverseAdjacency;
|
|
91
|
+
graph.nextNodeIndex = source.nextNodeIndex;
|
|
92
|
+
graph.nextEdgeIndex = source.nextEdgeIndex;
|
|
93
|
+
graph.acyclic = source.acyclic;
|
|
94
|
+
return graph;
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region ../../node_modules/.pnpm/effect@4.0.0/node_modules/effect/dist/internal/graphCsr.js
|
|
98
|
+
const cache = /*#__PURE__*/ new WeakMap();
|
|
99
|
+
const makeAdjacency = (csr, incoming) => {
|
|
100
|
+
const impl = toImpl(csr.graph);
|
|
101
|
+
const adjacencyMap = incoming ? impl.reverseAdjacency : impl.adjacency;
|
|
102
|
+
const rowOffsets = new Uint32Array(csr.nodeIds.length + 1);
|
|
103
|
+
for (let i = 0; i < csr.nodeIds.length; i++) rowOffsets[i + 1] = rowOffsets[i] + (adjacencyMap.get(csr.nodeIds[i])?.length ?? 0);
|
|
104
|
+
const columnIndices = new Uint32Array(rowOffsets[csr.nodeIds.length]);
|
|
105
|
+
for (let i = 0; i < csr.nodeIds.length; i++) {
|
|
106
|
+
const nodeId = csr.nodeIds[i];
|
|
107
|
+
const adjacency = adjacencyMap.get(nodeId);
|
|
108
|
+
if (adjacency === void 0) continue;
|
|
109
|
+
let offset = rowOffsets[i];
|
|
110
|
+
for (let j = 0; j < adjacency.length; j++) {
|
|
111
|
+
const edge = impl.edges.get(adjacency[j]);
|
|
112
|
+
const neighbor = csr.type === "undirected" ? edge.source === nodeId ? edge.target : edge.source : incoming ? edge.source : edge.target;
|
|
113
|
+
columnIndices[offset++] = csr.indexByNodeId === void 0 ? neighbor : csr.indexByNodeId.get(neighbor);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
rowOffsets,
|
|
118
|
+
columnIndices
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
/** @internal */
|
|
122
|
+
const getOutgoing = (csr) => {
|
|
123
|
+
return csr.outgoingCsr ?? (csr.outgoingCsr = makeAdjacency(csr, false));
|
|
124
|
+
};
|
|
125
|
+
/** @internal */
|
|
126
|
+
const getIncoming = (csr) => {
|
|
127
|
+
return csr.incomingCsr ?? (csr.incomingCsr = makeAdjacency(csr, true));
|
|
128
|
+
};
|
|
129
|
+
/** @internal */
|
|
130
|
+
const invalidate = (graph) => {
|
|
131
|
+
cache.delete(graph);
|
|
132
|
+
};
|
|
133
|
+
/** @internal */
|
|
134
|
+
const get = (graph) => {
|
|
135
|
+
const cacheEnabled = !isTransforming(graph);
|
|
136
|
+
if (cacheEnabled) {
|
|
137
|
+
const cached = cache.get(graph);
|
|
138
|
+
if (cached !== void 0) return cached;
|
|
139
|
+
}
|
|
140
|
+
const impl = toImpl(graph);
|
|
141
|
+
const nodeIds = new Array(impl.nodes.size);
|
|
142
|
+
const nodeData = new Array(impl.nodes.size);
|
|
143
|
+
let compactNodeIds = true;
|
|
144
|
+
let nodePosition = 0;
|
|
145
|
+
for (const [nodeId, data] of impl.nodes) {
|
|
146
|
+
nodeIds[nodePosition] = nodeId;
|
|
147
|
+
nodeData[nodePosition] = data;
|
|
148
|
+
if (nodeId !== nodePosition) compactNodeIds = false;
|
|
149
|
+
nodePosition++;
|
|
150
|
+
}
|
|
151
|
+
const indexByNodeId = compactNodeIds ? void 0 : /* @__PURE__ */ new Map();
|
|
152
|
+
if (indexByNodeId !== void 0) for (let i = 0; i < nodeIds.length; i++) indexByNodeId.set(nodeIds[i], i);
|
|
153
|
+
const result = {
|
|
154
|
+
type: impl.type,
|
|
155
|
+
graph,
|
|
156
|
+
nodeIds,
|
|
157
|
+
nodeData,
|
|
158
|
+
indexByNodeId,
|
|
159
|
+
outgoingCsr: void 0,
|
|
160
|
+
incomingCsr: void 0,
|
|
161
|
+
outgoingEdgeCsr: void 0,
|
|
162
|
+
edgesByIndex: void 0,
|
|
163
|
+
edgeIdsByIndex: void 0,
|
|
164
|
+
compactEdgeEndpoints: void 0,
|
|
165
|
+
indexByEdgeId: void 0
|
|
166
|
+
};
|
|
167
|
+
if (cacheEnabled) cache.set(graph, result);
|
|
168
|
+
return result;
|
|
169
|
+
};
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region ../../node_modules/.pnpm/effect@4.0.0/node_modules/effect/dist/Graph.js
|
|
172
|
+
/**
|
|
173
|
+
* Models relationships between indexed nodes and edges.
|
|
174
|
+
*
|
|
175
|
+
* This module provides immutable and scoped-mutable graph data structures. A
|
|
176
|
+
* graph can be directed or undirected, and it can store user-defined data on
|
|
177
|
+
* both nodes and edges. The module includes traversal, analysis, path-finding,
|
|
178
|
+
* transformation, and diagram export utilities.
|
|
179
|
+
*
|
|
180
|
+
* @since 4.0.0
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* Error thrown by graph operations when the requested graph structure is
|
|
184
|
+
* invalid, such as referencing a missing node or using unsupported edge
|
|
185
|
+
* weights.
|
|
186
|
+
*
|
|
187
|
+
* **When to use**
|
|
188
|
+
*
|
|
189
|
+
* Use when handling failures thrown by graph operations that reject invalid
|
|
190
|
+
* graph structure or unsupported algorithm inputs.
|
|
191
|
+
*
|
|
192
|
+
* @category errors
|
|
193
|
+
* @since 3.18.0
|
|
194
|
+
*/
|
|
195
|
+
var GraphError = class extends (/*#__PURE__*/ TaggedError("GraphError")) {};
|
|
196
|
+
/** @internal */
|
|
197
|
+
const missingNode = (node) => new GraphError({ message: `Node ${node} does not exist` });
|
|
198
|
+
/** @internal */
|
|
199
|
+
function assertMutable(graph) {
|
|
200
|
+
if (!graph.mutable) throw new GraphError({ message: "Graph is not mutable" });
|
|
201
|
+
}
|
|
202
|
+
/** @internal */
|
|
203
|
+
const getMutableImplForMutation = (graph) => {
|
|
204
|
+
assertMutable(graph);
|
|
205
|
+
if (isTransforming(graph)) throw new GraphError({ message: "Cannot mutate graph during a transformation" });
|
|
206
|
+
invalidate(graph);
|
|
207
|
+
return toImpl(graph);
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Creates a graph constructor for the specified graph kind.
|
|
211
|
+
*
|
|
212
|
+
* **When to use**
|
|
213
|
+
*
|
|
214
|
+
* Use when the graph kind is selected dynamically. Prefer `directed` or
|
|
215
|
+
* `undirected` when the kind is known statically.
|
|
216
|
+
*
|
|
217
|
+
* **Example** (Constructing by kind)
|
|
218
|
+
*
|
|
219
|
+
* ```ts import.meta.vitest
|
|
220
|
+
* import { Graph } from "effect"
|
|
221
|
+
*
|
|
222
|
+
* const makeGraph = Graph.make("directed")
|
|
223
|
+
* const graph = makeGraph<string, number>((mutable) => {
|
|
224
|
+
* Graph.addNode(mutable, "A")
|
|
225
|
+
* })
|
|
226
|
+
*
|
|
227
|
+
* graph.type // => "directed"
|
|
228
|
+
* ```
|
|
229
|
+
*
|
|
230
|
+
* @see {@link directed} for constructing a directed graph directly
|
|
231
|
+
* @see {@link undirected} for constructing an undirected graph directly
|
|
232
|
+
* @category constructors
|
|
233
|
+
* @since 4.0.0
|
|
234
|
+
*/
|
|
235
|
+
const make = (type) => (mutate) => {
|
|
236
|
+
if (type !== "directed" && type !== "undirected") throw new GraphError({ message: "Graph type must be directed or undirected" });
|
|
237
|
+
if (mutate === void 0) return make$1(type, false);
|
|
238
|
+
const graph = make$1(type, true);
|
|
239
|
+
const mutable = byReferenceUnsafe(graph);
|
|
240
|
+
return mutateScoped(mutable, mutate);
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Creates a directed graph, optionally with initial mutations.
|
|
244
|
+
*
|
|
245
|
+
* **When to use**
|
|
246
|
+
*
|
|
247
|
+
* Use when relationships have a source-to-target direction, such as
|
|
248
|
+
* dependencies, workflows, or routing links.
|
|
249
|
+
*
|
|
250
|
+
* **Gotchas**
|
|
251
|
+
*
|
|
252
|
+
* The mutable callback handle is finalized when the callback returns and must
|
|
253
|
+
* not be retained for later mutation.
|
|
254
|
+
*
|
|
255
|
+
* **Example** (Creating a directed graph)
|
|
256
|
+
*
|
|
257
|
+
* ```ts import.meta.vitest
|
|
258
|
+
* import { Graph } from "effect"
|
|
259
|
+
*
|
|
260
|
+
* // Directed graph with initial nodes and edges
|
|
261
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
262
|
+
* const a = Graph.addNode(mutable, "A")
|
|
263
|
+
* const b = Graph.addNode(mutable, "B")
|
|
264
|
+
* const c = Graph.addNode(mutable, "C")
|
|
265
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
266
|
+
* Graph.addEdge(mutable, b, c, "B->C")
|
|
267
|
+
* })
|
|
268
|
+
* Array.of(Graph.nodeCount(graph), Graph.edgeCount(graph)) // => [3, 2]
|
|
269
|
+
* ```
|
|
270
|
+
*
|
|
271
|
+
* @category constructors
|
|
272
|
+
* @since 3.18.0
|
|
273
|
+
*/
|
|
274
|
+
const directed = /*#__PURE__*/ make("directed");
|
|
275
|
+
/**
|
|
276
|
+
* Converts a mutable graph back to an immutable graph, ending the mutation scope.
|
|
277
|
+
*
|
|
278
|
+
* **When to use**
|
|
279
|
+
*
|
|
280
|
+
* Use to finish a mutation scope opened with `beginMutation`.
|
|
281
|
+
*
|
|
282
|
+
* **Gotchas**
|
|
283
|
+
*
|
|
284
|
+
* Finalization is terminal. Later public mutation operations on the same
|
|
285
|
+
* mutable handle fail with a `GraphError`.
|
|
286
|
+
*
|
|
287
|
+
* **Example** (Ending a mutation scope)
|
|
288
|
+
*
|
|
289
|
+
* ```ts import.meta.vitest
|
|
290
|
+
* import { Graph } from "effect"
|
|
291
|
+
*
|
|
292
|
+
* const graph = Graph.directed<string, number>()
|
|
293
|
+
* const mutable = Graph.beginMutation(graph)
|
|
294
|
+
* // ... perform mutations on mutable ...
|
|
295
|
+
* Graph.nodeCount(Graph.endMutation(mutable)) // => 0
|
|
296
|
+
* ```
|
|
297
|
+
*
|
|
298
|
+
* @see {@link beginMutation} for opening a manual mutation scope
|
|
299
|
+
* @see {@link mutate} for automatically scoped mutation
|
|
300
|
+
* @category mutations
|
|
301
|
+
* @since 3.18.0
|
|
302
|
+
*/
|
|
303
|
+
const endMutation = (mutable) => {
|
|
304
|
+
assertMutable(mutable);
|
|
305
|
+
if (isTransforming(mutable)) throw new GraphError({ message: "Cannot mutate graph during a transformation" });
|
|
306
|
+
const source = toImpl(mutable);
|
|
307
|
+
invalidate(mutable);
|
|
308
|
+
const graph = finalize(source);
|
|
309
|
+
source.mutable = false;
|
|
310
|
+
return graph;
|
|
311
|
+
};
|
|
312
|
+
/** @internal */
|
|
313
|
+
const mutateScoped = (mutable, f) => {
|
|
314
|
+
try {
|
|
315
|
+
f(mutable);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (mutable.mutable) endMutation(mutable);
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
return endMutation(mutable);
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Adds a new node to a mutable graph and returns its index.
|
|
324
|
+
*
|
|
325
|
+
* **When to use**
|
|
326
|
+
*
|
|
327
|
+
* Use to allocate a new node in a mutable graph before storing edges or
|
|
328
|
+
* querying it by index.
|
|
329
|
+
*
|
|
330
|
+
* **Details**
|
|
331
|
+
*
|
|
332
|
+
* The returned index is allocated from the graph's next node index. The mutable
|
|
333
|
+
* graph stores the node data and initializes empty incoming and outgoing edge
|
|
334
|
+
* indexes for the new node.
|
|
335
|
+
*
|
|
336
|
+
* **Gotchas**
|
|
337
|
+
*
|
|
338
|
+
* `NodeIndex` values are identifiers and are not reused after removals.
|
|
339
|
+
*
|
|
340
|
+
* **Example** (Adding nodes)
|
|
341
|
+
*
|
|
342
|
+
* ```ts import.meta.vitest
|
|
343
|
+
* import { Graph } from "effect"
|
|
344
|
+
*
|
|
345
|
+
* Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
346
|
+
* Graph.addNode(mutable, "Node A") // => 0
|
|
347
|
+
* Graph.addNode(mutable, "Node B") // => 1
|
|
348
|
+
* })
|
|
349
|
+
* ```
|
|
350
|
+
*
|
|
351
|
+
* @see {@link mutate} for obtaining a mutable graph from an immutable graph
|
|
352
|
+
* @see {@link addEdge} for connecting existing nodes
|
|
353
|
+
*
|
|
354
|
+
* @category mutations
|
|
355
|
+
* @since 3.18.0
|
|
356
|
+
*/
|
|
357
|
+
const addNode = (mutable, data) => {
|
|
358
|
+
const impl = getMutableImplForMutation(mutable);
|
|
359
|
+
const nodeIndex = impl.nextNodeIndex;
|
|
360
|
+
if (!Number.isSafeInteger(nodeIndex)) throw new GraphError({ message: "Graph has exhausted safe node indexes" });
|
|
361
|
+
impl.nodes.set(nodeIndex, data);
|
|
362
|
+
impl.adjacency.set(nodeIndex, []);
|
|
363
|
+
impl.reverseAdjacency.set(nodeIndex, []);
|
|
364
|
+
impl.nextNodeIndex = impl.nextNodeIndex + 1;
|
|
365
|
+
return nodeIndex;
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Gets the data associated with a node index safely, if it exists.
|
|
369
|
+
*
|
|
370
|
+
* **Example** (Getting node data)
|
|
371
|
+
*
|
|
372
|
+
* ```ts import.meta.vitest
|
|
373
|
+
* import { Graph, Option } from "effect"
|
|
374
|
+
*
|
|
375
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
376
|
+
* Graph.addNode(mutable, "Node A")
|
|
377
|
+
* })
|
|
378
|
+
*
|
|
379
|
+
* Graph.getNode(graph, 0) // => Option.some("Node A")
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
* @category getters
|
|
383
|
+
* @since 3.18.0
|
|
384
|
+
*/
|
|
385
|
+
const getNode = /*#__PURE__*/ dual(2, (graph, nodeIndex) => {
|
|
386
|
+
const impl = toImpl(graph);
|
|
387
|
+
return impl.nodes.has(nodeIndex) ? some(impl.nodes.get(nodeIndex)) : none();
|
|
388
|
+
});
|
|
389
|
+
/** @internal */
|
|
390
|
+
const invalidateCycleFlagOnAddition = (mutable) => {
|
|
391
|
+
if (isSome(mutable.acyclic) && mutable.acyclic.value === true) mutable.acyclic = none();
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* Adds a new edge to a mutable graph and returns its index.
|
|
395
|
+
*
|
|
396
|
+
* **When to use**
|
|
397
|
+
*
|
|
398
|
+
* Use to connect two existing nodes in a mutable graph while storing edge data
|
|
399
|
+
* and receiving the new edge identifier.
|
|
400
|
+
*
|
|
401
|
+
* **Details**
|
|
402
|
+
*
|
|
403
|
+
* Self-loops and parallel edges are allowed. Undirected graphs retain the
|
|
404
|
+
* supplied source and target orientation in the stored `Edge`, while traversal
|
|
405
|
+
* and neighbor queries treat the connection as bidirectional.
|
|
406
|
+
*
|
|
407
|
+
* **Gotchas**
|
|
408
|
+
*
|
|
409
|
+
* The source and target nodes must already exist in the mutable graph; missing
|
|
410
|
+
* endpoints throw a `GraphError`.
|
|
411
|
+
*
|
|
412
|
+
* **Example** (Adding edges)
|
|
413
|
+
*
|
|
414
|
+
* ```ts import.meta.vitest
|
|
415
|
+
* import { Graph } from "effect"
|
|
416
|
+
*
|
|
417
|
+
* Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
418
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
419
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
420
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 42) // => 0
|
|
421
|
+
* })
|
|
422
|
+
* ```
|
|
423
|
+
*
|
|
424
|
+
* @see {@link mutate} for obtaining a mutable graph from an immutable graph
|
|
425
|
+
* @see {@link addNode} for creating node indexes before connecting them
|
|
426
|
+
*
|
|
427
|
+
* @category mutations
|
|
428
|
+
* @since 3.18.0
|
|
429
|
+
*/
|
|
430
|
+
const addEdge = (mutable, source, target, data) => {
|
|
431
|
+
const impl = getMutableImplForMutation(mutable);
|
|
432
|
+
if (!impl.nodes.has(source)) throw missingNode(source);
|
|
433
|
+
if (!impl.nodes.has(target)) throw missingNode(target);
|
|
434
|
+
const edgeIndex = impl.nextEdgeIndex;
|
|
435
|
+
if (!Number.isSafeInteger(edgeIndex)) throw new GraphError({ message: "Graph has exhausted safe edge indexes" });
|
|
436
|
+
const edgeData = {
|
|
437
|
+
source,
|
|
438
|
+
target,
|
|
439
|
+
data
|
|
440
|
+
};
|
|
441
|
+
impl.edges.set(edgeIndex, edgeData);
|
|
442
|
+
const sourceAdjacency = impl.adjacency.get(source);
|
|
443
|
+
if (sourceAdjacency !== void 0) sourceAdjacency.push(edgeIndex);
|
|
444
|
+
const targetReverseAdjacency = impl.reverseAdjacency.get(target);
|
|
445
|
+
if (targetReverseAdjacency !== void 0) targetReverseAdjacency.push(edgeIndex);
|
|
446
|
+
if (impl.type === "undirected") {
|
|
447
|
+
const targetAdjacency = impl.adjacency.get(target);
|
|
448
|
+
if (targetAdjacency !== void 0) targetAdjacency.push(edgeIndex);
|
|
449
|
+
const sourceReverseAdjacency = impl.reverseAdjacency.get(source);
|
|
450
|
+
if (sourceReverseAdjacency !== void 0) sourceReverseAdjacency.push(edgeIndex);
|
|
451
|
+
}
|
|
452
|
+
impl.nextEdgeIndex = impl.nextEdgeIndex + 1;
|
|
453
|
+
invalidateCycleFlagOnAddition(impl);
|
|
454
|
+
return edgeIndex;
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Returns the strongly connected components of a directed graph.
|
|
458
|
+
*
|
|
459
|
+
* **When to use**
|
|
460
|
+
*
|
|
461
|
+
* Use when grouping nodes so every node in a component can reach every other
|
|
462
|
+
* node in that component.
|
|
463
|
+
*
|
|
464
|
+
* **Details**
|
|
465
|
+
*
|
|
466
|
+
* Each component is represented as an array of node indices and is computed
|
|
467
|
+
* with Kosaraju's algorithm.
|
|
468
|
+
*
|
|
469
|
+
* **Gotchas**
|
|
470
|
+
*
|
|
471
|
+
* Throws a `GraphError` when used with an undirected graph.
|
|
472
|
+
*
|
|
473
|
+
* **Example** (Finding strongly connected components)
|
|
474
|
+
*
|
|
475
|
+
* ```ts import.meta.vitest
|
|
476
|
+
* import { Graph } from "effect"
|
|
477
|
+
*
|
|
478
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
479
|
+
* const a = Graph.addNode(mutable, "A")
|
|
480
|
+
* const b = Graph.addNode(mutable, "B")
|
|
481
|
+
* const c = Graph.addNode(mutable, "C")
|
|
482
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
483
|
+
* Graph.addEdge(mutable, b, c, "B->C")
|
|
484
|
+
* Graph.addEdge(mutable, c, a, "C->A") // Creates SCC: A-B-C
|
|
485
|
+
* })
|
|
486
|
+
*
|
|
487
|
+
* Graph.stronglyConnectedComponents(graph) // => [[0, 2, 1]]
|
|
488
|
+
* ```
|
|
489
|
+
*
|
|
490
|
+
* @see {@link isStronglyConnected} when only a boolean check is needed
|
|
491
|
+
* @see {@link weaklyConnectedComponents} when edge orientation should be ignored
|
|
492
|
+
* @category algorithms
|
|
493
|
+
* @since 3.18.0
|
|
494
|
+
*/
|
|
495
|
+
const stronglyConnectedComponents = (graph) => {
|
|
496
|
+
if (graph.type === "undirected") throw new GraphError({ message: "Cannot find strongly connected components of undirected graph" });
|
|
497
|
+
const cache = get(graph);
|
|
498
|
+
const outgoing = getOutgoing(cache);
|
|
499
|
+
const incoming = getIncoming(cache);
|
|
500
|
+
const visited = new Uint8Array(cache.nodeIds.length);
|
|
501
|
+
const finishOrder = [];
|
|
502
|
+
const stack = [];
|
|
503
|
+
const positions = [];
|
|
504
|
+
for (let start = 0; start < cache.nodeIds.length; start++) {
|
|
505
|
+
if (visited[start] !== 0) continue;
|
|
506
|
+
visited[start] = 1;
|
|
507
|
+
stack.push(start);
|
|
508
|
+
positions.push(outgoing.rowOffsets[start]);
|
|
509
|
+
while (stack.length > 0) {
|
|
510
|
+
const frame = stack.length - 1;
|
|
511
|
+
const node = stack[frame];
|
|
512
|
+
const position = positions[frame];
|
|
513
|
+
if (position < outgoing.rowOffsets[node + 1]) {
|
|
514
|
+
positions[frame] = position + 1;
|
|
515
|
+
const neighbor = outgoing.columnIndices[position];
|
|
516
|
+
if (visited[neighbor] === 0) {
|
|
517
|
+
visited[neighbor] = 1;
|
|
518
|
+
stack.push(neighbor);
|
|
519
|
+
positions.push(outgoing.rowOffsets[neighbor]);
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
finishOrder.push(node);
|
|
523
|
+
stack.pop();
|
|
524
|
+
positions.pop();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
visited.fill(0);
|
|
529
|
+
const components = [];
|
|
530
|
+
for (let i = finishOrder.length - 1; i >= 0; i--) {
|
|
531
|
+
const start = finishOrder[i];
|
|
532
|
+
if (visited[start] !== 0) continue;
|
|
533
|
+
const component = [];
|
|
534
|
+
stack.push(start);
|
|
535
|
+
while (stack.length > 0) {
|
|
536
|
+
const node = stack.pop();
|
|
537
|
+
if (visited[node] !== 0) continue;
|
|
538
|
+
visited[node] = 1;
|
|
539
|
+
component.push(cache.nodeIds[node]);
|
|
540
|
+
for (let j = incoming.rowOffsets[node]; j < incoming.rowOffsets[node + 1]; j++) {
|
|
541
|
+
const predecessor = incoming.columnIndices[j];
|
|
542
|
+
if (visited[predecessor] === 0) stack.push(predecessor);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
components.push(component);
|
|
546
|
+
}
|
|
547
|
+
return components;
|
|
548
|
+
};
|
|
549
|
+
//#endregion
|
|
550
|
+
//#region ../../node_modules/.pnpm/@oxlint+plugins@1.85.0/node_modules/@oxlint/plugins/index.js
|
|
551
|
+
/**
|
|
552
|
+
* Define a plugin.
|
|
553
|
+
*
|
|
554
|
+
* No-op function, just to provide type safety. Input is passed through unchanged.
|
|
555
|
+
*
|
|
556
|
+
* @param plugin - Plugin to define
|
|
557
|
+
* @returns Same plugin as passed in
|
|
558
|
+
*/
|
|
559
|
+
function definePlugin(plugin) {
|
|
560
|
+
return plugin;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Define a rule.
|
|
564
|
+
*
|
|
565
|
+
* No-op function, just to provide type safety. Input is passed through unchanged.
|
|
566
|
+
*
|
|
567
|
+
* @param rule - Rule to define
|
|
568
|
+
* @returns Same rule as passed in
|
|
569
|
+
*/
|
|
570
|
+
function defineRule(rule) {
|
|
571
|
+
return rule;
|
|
572
|
+
}
|
|
573
|
+
Object.freeze({
|
|
574
|
+
get filename() {
|
|
575
|
+
throw Error("Cannot access `context.filename` in `createOnce`");
|
|
576
|
+
},
|
|
577
|
+
getFilename() {
|
|
578
|
+
throw Error("Cannot call `context.getFilename` in `createOnce`");
|
|
579
|
+
},
|
|
580
|
+
get physicalFilename() {
|
|
581
|
+
throw Error("Cannot access `context.physicalFilename` in `createOnce`");
|
|
582
|
+
},
|
|
583
|
+
getPhysicalFilename() {
|
|
584
|
+
throw Error("Cannot call `context.getPhysicalFilename` in `createOnce`");
|
|
585
|
+
},
|
|
586
|
+
get cwd() {
|
|
587
|
+
throw Error("Cannot access `context.cwd` in `createOnce`");
|
|
588
|
+
},
|
|
589
|
+
getCwd() {
|
|
590
|
+
throw Error("Cannot call `context.getCwd` in `createOnce`");
|
|
591
|
+
},
|
|
592
|
+
get sourceCode() {
|
|
593
|
+
throw Error("Cannot access `context.sourceCode` in `createOnce`");
|
|
594
|
+
},
|
|
595
|
+
getSourceCode() {
|
|
596
|
+
throw Error("Cannot call `context.getSourceCode` in `createOnce`");
|
|
597
|
+
},
|
|
598
|
+
get languageOptions() {
|
|
599
|
+
throw Error("Cannot access `context.languageOptions` in `createOnce`");
|
|
600
|
+
},
|
|
601
|
+
get settings() {
|
|
602
|
+
throw Error("Cannot access `context.settings` in `createOnce`");
|
|
603
|
+
},
|
|
604
|
+
extend(extension) {
|
|
605
|
+
return Object.freeze(Object.assign(Object.create(this), extension));
|
|
606
|
+
},
|
|
607
|
+
get parserOptions() {
|
|
608
|
+
throw Error("Cannot access `context.parserOptions` in `createOnce`");
|
|
609
|
+
},
|
|
610
|
+
get parserPath() {
|
|
611
|
+
throw Error("Cannot access `context.parserPath` in `createOnce`");
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
//#endregion
|
|
615
|
+
//#region ../../node_modules/.pnpm/oxlint@1.85.0_oxlint-tsgolint@7.0.2003_vite-plus@1.0.0_@opentelemetry+api@1.9.1_@types+_4709a04ba252a52ff927b8b254462122/node_modules/oxlint/dist/index.js
|
|
616
|
+
/**
|
|
617
|
+
* Define an Oxlint configuration with type inference.
|
|
618
|
+
*
|
|
619
|
+
* @param config - Oxlint configuration
|
|
620
|
+
* @returns Config unchanged
|
|
621
|
+
*/
|
|
622
|
+
function defineConfig(config) {
|
|
623
|
+
return config;
|
|
624
|
+
}
|
|
625
|
+
//#endregion
|
|
626
|
+
//#region src/rules/shared.ts
|
|
627
|
+
function variableFromScope(input) {
|
|
628
|
+
if (input.scope === null) return none();
|
|
629
|
+
const scope = input.scope;
|
|
630
|
+
return pipe(fromNullishOr(scope.set.get(input.name)), orElse(() => variableFromScope({
|
|
631
|
+
name: input.name,
|
|
632
|
+
scope: scope.upper
|
|
633
|
+
})));
|
|
634
|
+
}
|
|
635
|
+
function variableFor(context, node) {
|
|
636
|
+
return variableFromScope({
|
|
637
|
+
name: node.name,
|
|
638
|
+
scope: context.sourceCode.getScope(node)
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function isImportBinding(input) {
|
|
642
|
+
return pipe(variableFor(input.context, input.node), exists((variable) => some$1(variable.defs, (definition) => {
|
|
643
|
+
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration") return false;
|
|
644
|
+
const source = definition.parent.source.value;
|
|
645
|
+
if (isString(input.source) ? source !== input.source : !input.source.test(source)) return false;
|
|
646
|
+
return input.importedName === void 0 || definition.node.type === "ImportSpecifier" && definition.node.imported.type === "Identifier" && definition.node.imported.name === input.importedName;
|
|
647
|
+
})));
|
|
648
|
+
}
|
|
649
|
+
function isNamespaceImport(input) {
|
|
650
|
+
return pipe(variableFor(input.context, input.node), exists((variable) => some$1(variable.defs, (definition) => definition.type === "ImportBinding" && definition.node.type === "ImportNamespaceSpecifier" && definition.parent?.type === "ImportDeclaration" && definition.parent.source.value === input.source)));
|
|
651
|
+
}
|
|
652
|
+
function returnedExpression(node) {
|
|
653
|
+
if (node.body === null) return;
|
|
654
|
+
if (node.body.type !== "BlockStatement") return node.body;
|
|
655
|
+
if (node.body.body.length !== 1 || node.body.body[0]?.type !== "ReturnStatement") return;
|
|
656
|
+
return node.body.body[0].argument;
|
|
657
|
+
}
|
|
658
|
+
function memberName(node) {
|
|
659
|
+
if (!node.computed && node.property.type === "Identifier") return some(node.property.name);
|
|
660
|
+
if (node.computed && node.property.type === "Literal" && isString(node.property.value)) return some(node.property.value);
|
|
661
|
+
return none();
|
|
662
|
+
}
|
|
663
|
+
function importedMember(input) {
|
|
664
|
+
return input.node.type === "MemberExpression" && input.node.object.type === "Identifier" && isImportBinding({
|
|
665
|
+
context: input.context,
|
|
666
|
+
importedName: input.importedName,
|
|
667
|
+
node: input.node.object,
|
|
668
|
+
source: "effect"
|
|
669
|
+
}) && (input.propertyName === void 0 || contains(memberName(input.node), input.propertyName));
|
|
670
|
+
}
|
|
671
|
+
function effectBuilderName(input) {
|
|
672
|
+
const builder = input.node.type === "CallExpression" ? input.node.callee : input.node;
|
|
673
|
+
return builder.type === "MemberExpression" && importedMember({
|
|
674
|
+
context: input.context,
|
|
675
|
+
importedName: "Effect",
|
|
676
|
+
node: builder
|
|
677
|
+
}) ? memberName(builder) : none();
|
|
678
|
+
}
|
|
679
|
+
function isSchemaOperationName(name) {
|
|
680
|
+
return /^(?:(?:decode|encode)(?:Unknown)?(?:Effect|Exit|Option|Promise|Result|Sync)|asserts|is)$/u.test(name);
|
|
681
|
+
}
|
|
682
|
+
function isSchemaOperationCall(input) {
|
|
683
|
+
if (input.node.callee.type !== "MemberExpression" || !importedMember({
|
|
684
|
+
context: input.context,
|
|
685
|
+
importedName: "Schema",
|
|
686
|
+
node: input.node.callee
|
|
687
|
+
})) return false;
|
|
688
|
+
return pipe(memberName(input.node.callee), exists(isSchemaOperationName));
|
|
689
|
+
}
|
|
690
|
+
function isReactUseState(input) {
|
|
691
|
+
if (input.node.callee.type === "Identifier") return isImportBinding({
|
|
692
|
+
context: input.context,
|
|
693
|
+
importedName: "useState",
|
|
694
|
+
node: input.node.callee,
|
|
695
|
+
source: "react"
|
|
696
|
+
});
|
|
697
|
+
return input.node.callee.type === "MemberExpression" && contains(memberName(input.node.callee), "useState") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
698
|
+
context: input.context,
|
|
699
|
+
node: input.node.callee.object,
|
|
700
|
+
source: "react"
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
function statementDeclaration(statement) {
|
|
704
|
+
return statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
705
|
+
}
|
|
706
|
+
function typeAlias(statement) {
|
|
707
|
+
const declaration = statementDeclaration(statement);
|
|
708
|
+
return declaration?.type === "TSTypeAliasDeclaration" ? some(declaration) : none();
|
|
709
|
+
}
|
|
710
|
+
function isInferredType(input) {
|
|
711
|
+
return input.node.type === "TSTypeQuery" && input.node.exprName.type === "TSQualifiedName" && input.node.exprName.left.type === "Identifier" && input.node.exprName.left.name === input.name && input.node.exprName.right.name === "Type";
|
|
712
|
+
}
|
|
713
|
+
function schemaQualifiedType(input) {
|
|
714
|
+
return input.node.type === "TSTypeReference" && input.node.typeName.type === "TSQualifiedName" && input.node.typeName.left.type === "Identifier" && input.node.typeName.left.name === "Schema" && input.node.typeName.right.name === input.propertyName && isImportBinding({
|
|
715
|
+
context: input.context,
|
|
716
|
+
importedName: "Schema",
|
|
717
|
+
node: input.node.typeName.left,
|
|
718
|
+
source: "effect"
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
function schemaSchemaType(input) {
|
|
722
|
+
return schemaQualifiedType({
|
|
723
|
+
context: input.context,
|
|
724
|
+
node: input.node,
|
|
725
|
+
propertyName: "Schema"
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
function isSchemaCodecType(input) {
|
|
729
|
+
return schemaQualifiedType({
|
|
730
|
+
context: input.context,
|
|
731
|
+
node: input.node,
|
|
732
|
+
propertyName: "Codec"
|
|
733
|
+
}) || schemaSchemaType(input);
|
|
734
|
+
}
|
|
735
|
+
function typeArgumentName(node) {
|
|
736
|
+
if (node.type !== "TSTypeReference" || node.typeArguments === null) return none();
|
|
737
|
+
return pipe(get$1(node.typeArguments.params, 0), flatMap((parameter) => parameter.type === "TSTypeReference" && parameter.typeName.type === "Identifier" ? some(parameter.typeName.name) : none()));
|
|
738
|
+
}
|
|
739
|
+
function suspendedTypeName(input) {
|
|
740
|
+
if (input.node.callee.type === "Super" || !importedMember({
|
|
741
|
+
context: input.context,
|
|
742
|
+
importedName: "Schema",
|
|
743
|
+
node: input.node.callee,
|
|
744
|
+
propertyName: "suspend"
|
|
745
|
+
})) return none();
|
|
746
|
+
return pipe(get$1(input.node.arguments, 0), flatMap((argument) => argument.type === "ArrowFunctionExpression" || argument.type === "FunctionExpression" ? fromNullishOr(argument.returnType) : none()), filter((returnType) => isSchemaCodecType({
|
|
747
|
+
context: input.context,
|
|
748
|
+
node: returnType.typeAnnotation
|
|
749
|
+
})), flatMap((returnType) => typeArgumentName(returnType.typeAnnotation)));
|
|
750
|
+
}
|
|
751
|
+
function referencedTypeNames(node) {
|
|
752
|
+
if (node.type === "TSTypeReference") {
|
|
753
|
+
const argumentNames = pipe(fromNullishOr(node.typeArguments), map((typeArguments) => pipe(typeArguments.params, flatMap$1(referencedTypeNames))), getOrElse(() => empty()));
|
|
754
|
+
return node.typeName.type === "Identifier" ? prepend(argumentNames, node.typeName.name) : argumentNames;
|
|
755
|
+
}
|
|
756
|
+
if (node.type === "TSArrayType") return referencedTypeNames(node.elementType);
|
|
757
|
+
if (node.type === "TSUnionType" || node.type === "TSIntersectionType") return pipe(node.types, flatMap$1(referencedTypeNames));
|
|
758
|
+
if (node.type === "TSTemplateLiteralType") return pipe(node.types, flatMap$1(referencedTypeNames));
|
|
759
|
+
if (node.type === "TSTypeOperator" || node.type === "TSParenthesizedType" || node.type === "TSOptionalType" || node.type === "TSRestType") return referencedTypeNames(node.typeAnnotation);
|
|
760
|
+
if (node.type === "TSNamedTupleMember") return referencedTypeNames(node.elementType);
|
|
761
|
+
if (node.type === "TSTupleType") return pipe(node.elementTypes, flatMap$1(referencedTypeNames));
|
|
762
|
+
if (node.type === "TSIndexedAccessType") return appendAll(referencedTypeNames(node.objectType), referencedTypeNames(node.indexType));
|
|
763
|
+
if (node.type === "TSConditionalType") return pipe([
|
|
764
|
+
node.checkType,
|
|
765
|
+
node.extendsType,
|
|
766
|
+
node.trueType,
|
|
767
|
+
node.falseType
|
|
768
|
+
], flatMap$1(referencedTypeNames));
|
|
769
|
+
if (node.type === "TSFunctionType" || node.type === "TSConstructorType") return referencedTypeNames(node.returnType.typeAnnotation);
|
|
770
|
+
if (node.type === "TSMappedType") return pipe([
|
|
771
|
+
node.constraint,
|
|
772
|
+
node.nameType,
|
|
773
|
+
node.typeAnnotation
|
|
774
|
+
], map$1((child) => fromNullishOr(child)), getSomes, flatMap$1(referencedTypeNames));
|
|
775
|
+
if (node.type === "TSTypeLiteral") return pipe(node.members, flatMap$1(memberTypeNames));
|
|
776
|
+
return empty();
|
|
777
|
+
}
|
|
778
|
+
function memberTypeNames(member) {
|
|
779
|
+
if (member.type === "TSIndexSignature") return referencedTypeNames(member.typeAnnotation.typeAnnotation);
|
|
780
|
+
return pipe(fromNullishOr(member.type === "TSPropertySignature" ? member.typeAnnotation : member.returnType), map((annotation) => referencedTypeNames(annotation.typeAnnotation)), getOrElse(() => empty()));
|
|
781
|
+
}
|
|
782
|
+
function isNode(value) {
|
|
783
|
+
return hasProperty(value, "type") && isString(value.type);
|
|
784
|
+
}
|
|
785
|
+
function childNodes(node) {
|
|
786
|
+
if (!isReadonlyObject(node)) return empty();
|
|
787
|
+
return pipe(values(remove(node, "parent")), flatMap$1(ensure), filter$1(isNode));
|
|
788
|
+
}
|
|
789
|
+
function suspendSeeds(input) {
|
|
790
|
+
const seeds = pipe(childNodes(input.node), flatMap$1((child) => suspendSeeds({
|
|
791
|
+
context: input.context,
|
|
792
|
+
node: child
|
|
793
|
+
})));
|
|
794
|
+
if (input.node.type !== "CallExpression") return seeds;
|
|
795
|
+
return pipe(suspendedTypeName({
|
|
796
|
+
context: input.context,
|
|
797
|
+
node: input.node
|
|
798
|
+
}), match({
|
|
799
|
+
onNone: () => seeds,
|
|
800
|
+
onSome: (name) => prepend(seeds, name)
|
|
801
|
+
}));
|
|
802
|
+
}
|
|
803
|
+
function referenceNames(node) {
|
|
804
|
+
const nested = pipe(childNodes(node), filter$1((child) => !(node.type === "Property" && !node.computed && child === node.key) && !(node.type === "MemberExpression" && !node.computed && child === node.property)), flatMap$1(referenceNames));
|
|
805
|
+
return node.type === "Identifier" ? prepend(nested, node.name) : nested;
|
|
806
|
+
}
|
|
807
|
+
function schemaEdges(statement) {
|
|
808
|
+
const declaration = statementDeclaration(statement);
|
|
809
|
+
if (declaration?.type === "TSTypeAliasDeclaration") return isInferredType({
|
|
810
|
+
name: declaration.id.name,
|
|
811
|
+
node: declaration.typeAnnotation
|
|
812
|
+
}) ? empty() : [{
|
|
813
|
+
name: declaration.id.name,
|
|
814
|
+
targets: referencedTypeNames(declaration.typeAnnotation)
|
|
815
|
+
}];
|
|
816
|
+
if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const") return empty();
|
|
817
|
+
return flatMap$1(declaration.declarations, (declarator) => {
|
|
818
|
+
if (declarator.id.type !== "Identifier" || declarator.init === null) return empty();
|
|
819
|
+
return [{
|
|
820
|
+
name: declarator.id.name,
|
|
821
|
+
targets: referenceNames(declarator.init)
|
|
822
|
+
}];
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
function schemaCycleNames(input) {
|
|
826
|
+
const seeds = suspendSeeds({
|
|
827
|
+
context: input.context,
|
|
828
|
+
node: input.program
|
|
829
|
+
});
|
|
830
|
+
if (isArrayEmpty(seeds)) return empty();
|
|
831
|
+
const edges = pipe(input.program.body, flatMap$1(schemaEdges));
|
|
832
|
+
const names = pipe(edges, map$1((edge) => edge.name), dedupe);
|
|
833
|
+
const graph = directed((mutable) => {
|
|
834
|
+
const indices = fromEntries(map$1(names, (name) => [name, addNode(mutable, name)]));
|
|
835
|
+
for (const edge of edges) for (const target of edge.targets) zipWith(get$2(indices, edge.name), get$2(indices, target), (source, sink) => addEdge(mutable, source, sink, "reads"));
|
|
836
|
+
});
|
|
837
|
+
return pipe(stronglyConnectedComponents(graph), map$1((component) => getSomes(map$1(component, (index) => getNode(graph, index)))), filter$1((component) => (component.length > 1 || some$1(edges, (edge) => contains$1(component, edge.name) && contains$1(edge.targets, edge.name))) && some$1(component, (name) => contains$1(seeds, name))), flatten);
|
|
838
|
+
}
|
|
839
|
+
function hasSingleStatementExpression(node) {
|
|
840
|
+
if (node.body === null) return false;
|
|
841
|
+
if (node.body.type !== "BlockStatement") return true;
|
|
842
|
+
if (node.body.body.length !== 1) return false;
|
|
843
|
+
const statement = node.body.body[0];
|
|
844
|
+
return statement?.type === "ExpressionStatement" || statement?.type === "ReturnStatement" && statement.argument !== null;
|
|
845
|
+
}
|
|
846
|
+
function bindingName(node) {
|
|
847
|
+
if (node.type === "FunctionDeclaration") return fromNullishOr(node.id?.name);
|
|
848
|
+
if (node.parent.type === "VariableDeclarator" && node.parent.id.type === "Identifier") return some(node.parent.id.name);
|
|
849
|
+
return none();
|
|
850
|
+
}
|
|
851
|
+
function singleUseThunk(input) {
|
|
852
|
+
if (input.node.params.length !== 0 || input.node.typeParameters !== null || !hasSingleStatementExpression(input.node)) return false;
|
|
853
|
+
return pipe(bindingName(input.node), flatMap((name) => variableFromScope({
|
|
854
|
+
name,
|
|
855
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
856
|
+
})), exists((variable) => filter$1(variable.references, (reference) => reference.isRead()).length === 1));
|
|
857
|
+
}
|
|
858
|
+
//#endregion
|
|
859
|
+
//#region src/rules/no-array-wrap-ternary.ts
|
|
860
|
+
function testedValue(node) {
|
|
861
|
+
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression" || node.callee.object.type !== "Identifier" || node.callee.object.name !== "Array" || !contains(memberName(node.callee), "isArray") || node.arguments.length !== 1) return none();
|
|
862
|
+
const argument = node.arguments[0];
|
|
863
|
+
if (argument?.type !== "Identifier" && argument?.type !== "MemberExpression") return none();
|
|
864
|
+
return some(argument);
|
|
865
|
+
}
|
|
866
|
+
function isSingletonArray(input) {
|
|
867
|
+
if (input.node.type !== "ArrayExpression" || input.node.elements.length !== 1) return false;
|
|
868
|
+
const element = input.node.elements[0];
|
|
869
|
+
if (isNullish(element) || element.type === "SpreadElement") return false;
|
|
870
|
+
return input.context.sourceCode.getText(element) === input.context.sourceCode.getText(input.value);
|
|
871
|
+
}
|
|
872
|
+
function restatesArrayEnsure(input) {
|
|
873
|
+
const test = input.node.test.type === "UnaryExpression" && input.node.test.operator === "!" ? input.node.test.argument : input.node.test;
|
|
874
|
+
const negated = test !== input.node.test;
|
|
875
|
+
return exists(testedValue(test), (value) => input.context.sourceCode.getText(negated ? input.node.alternate : input.node.consequent) === input.context.sourceCode.getText(value) && isSingletonArray({
|
|
876
|
+
context: input.context,
|
|
877
|
+
node: negated ? input.node.consequent : input.node.alternate,
|
|
878
|
+
value
|
|
879
|
+
}));
|
|
880
|
+
}
|
|
881
|
+
const noArrayWrapTernary = defineRule({
|
|
882
|
+
create: (context) => ({ ConditionalExpression: (node) => {
|
|
883
|
+
if (restatesArrayEnsure({
|
|
884
|
+
context,
|
|
885
|
+
node
|
|
886
|
+
})) context.report({
|
|
887
|
+
message: "Use Array.ensure; the ternary restates it.",
|
|
888
|
+
node
|
|
889
|
+
});
|
|
890
|
+
} }),
|
|
891
|
+
meta: { type: "problem" }
|
|
892
|
+
});
|
|
893
|
+
//#endregion
|
|
894
|
+
//#region src/rules/no-constant-function.ts
|
|
895
|
+
function returnsConstant(node) {
|
|
896
|
+
const returned = returnedExpression(node);
|
|
897
|
+
return isNotNullish(returned) && (returned.type === "Literal" || returned.type === "Identifier" || returned.type === "MemberExpression" || returned.type === "TemplateLiteral" && isArrayEmpty(returned.expressions));
|
|
898
|
+
}
|
|
899
|
+
function functionScope(input) {
|
|
900
|
+
if (input.scope.block === input.node) return some(input.scope);
|
|
901
|
+
return findFirst(input.scope.childScopes, (child) => child.block === input.node);
|
|
902
|
+
}
|
|
903
|
+
function scopeReferences(scope) {
|
|
904
|
+
return appendAll(scope.references, flatMap$1(scope.childScopes, scopeReferences));
|
|
905
|
+
}
|
|
906
|
+
function isImportedVariable(variable) {
|
|
907
|
+
return isArrayNonEmpty(variable.defs) && every(variable.defs, (definition) => definition.type === "ImportBinding");
|
|
908
|
+
}
|
|
909
|
+
function resolvesToImport(reference) {
|
|
910
|
+
return pipe(variableFromScope({
|
|
911
|
+
name: reference.identifier.name,
|
|
912
|
+
scope: reference.from
|
|
913
|
+
}), exists(isImportedVariable));
|
|
914
|
+
}
|
|
915
|
+
function isConstantFunction(input) {
|
|
916
|
+
if (input.node.async || input.node.generator || isNotNullish(input.node.typeParameters)) return false;
|
|
917
|
+
if (some$1(input.spans, (span) => span.start >= input.node.start && span.end <= input.node.end)) return false;
|
|
918
|
+
return returnsConstant(input.node) && pipe(functionScope({
|
|
919
|
+
node: input.node,
|
|
920
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
921
|
+
}), exists((scope) => every(scopeReferences(scope), resolvesToImport))) && pipe(bindingName(input.node), flatMap((name) => variableFromScope({
|
|
922
|
+
name,
|
|
923
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
924
|
+
})), exists((variable) => every(filter$1(variable.references, (reference) => reference.isRead()), (reference) => reference.identifier.parent.type === "CallExpression" && reference.identifier.parent.callee === reference.identifier)));
|
|
925
|
+
}
|
|
926
|
+
function isConstBound(node) {
|
|
927
|
+
return node.parent.type === "VariableDeclarator" && node.parent.parent.type === "VariableDeclaration" && node.parent.parent.kind === "const";
|
|
928
|
+
}
|
|
929
|
+
const noConstantFunction = defineRule({
|
|
930
|
+
create: (context) => {
|
|
931
|
+
const spans = make$2(empty());
|
|
932
|
+
function mark(node) {
|
|
933
|
+
update(spans, append(node));
|
|
934
|
+
}
|
|
935
|
+
function report(node) {
|
|
936
|
+
if (isConstantFunction({
|
|
937
|
+
context,
|
|
938
|
+
node,
|
|
939
|
+
spans: get$3(spans)
|
|
940
|
+
}) && !singleUseThunk({
|
|
941
|
+
context,
|
|
942
|
+
node
|
|
943
|
+
})) context.report({
|
|
944
|
+
message: "This function reads nothing but imports, so it is a value: hold its result in a const.",
|
|
945
|
+
node
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
"ArrowFunctionExpression:exit": (node) => {
|
|
950
|
+
if (isConstBound(node)) report(node);
|
|
951
|
+
},
|
|
952
|
+
"FunctionDeclaration:exit": report,
|
|
953
|
+
"FunctionExpression:exit": (node) => {
|
|
954
|
+
if (isConstBound(node)) report(node);
|
|
955
|
+
},
|
|
956
|
+
JSXElement: mark,
|
|
957
|
+
JSXFragment: mark,
|
|
958
|
+
ThisExpression: mark
|
|
959
|
+
};
|
|
960
|
+
},
|
|
961
|
+
meta: { type: "problem" }
|
|
962
|
+
});
|
|
963
|
+
//#endregion
|
|
964
|
+
//#region src/rules/no-deep-pipe.ts
|
|
965
|
+
function isPipeCall(node) {
|
|
966
|
+
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "pipe";
|
|
967
|
+
}
|
|
968
|
+
function enclosingPipes(node) {
|
|
969
|
+
if (node.type === "Program" || contains$1([
|
|
970
|
+
"ArrowFunctionExpression",
|
|
971
|
+
"FunctionDeclaration",
|
|
972
|
+
"FunctionExpression"
|
|
973
|
+
], node.type)) return 0;
|
|
974
|
+
const nested = isPipeCall(node.parent) && some$1(node.parent.arguments, (argument) => argument === node);
|
|
975
|
+
return enclosingPipes(node.parent) + (nested ? 1 : 0);
|
|
976
|
+
}
|
|
977
|
+
const noDeepPipe = defineRule({
|
|
978
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
979
|
+
if (isPipeCall(node) && enclosingPipes(node) >= 3) context.report({
|
|
980
|
+
message: "Flatten this pipeline; pipe calls nest at most three deep.",
|
|
981
|
+
node
|
|
982
|
+
});
|
|
983
|
+
} }),
|
|
984
|
+
meta: { type: "problem" }
|
|
985
|
+
});
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/rules/no-destructured-parameter.ts
|
|
988
|
+
function isPattern(node) {
|
|
989
|
+
const target = node.type === "AssignmentPattern" ? node.left : node;
|
|
990
|
+
if (target.type === "ArrayPattern") return true;
|
|
991
|
+
if (target.type !== "ObjectPattern") return false;
|
|
992
|
+
const named = filter$1(target.properties, (property) => property.type === "Property");
|
|
993
|
+
return !(named.length === 1 && named[0]?.key.type === "Identifier" && named[0].key.name === "ref" && some$1(target.properties, (property) => property.type === "RestElement"));
|
|
994
|
+
}
|
|
995
|
+
const noDestructuredParameter = defineRule({
|
|
996
|
+
create: (context) => {
|
|
997
|
+
function reportParameters(node) {
|
|
998
|
+
for (const parameter of filter$1(node.params, isPattern)) context.report({
|
|
999
|
+
message: "Take the value whole and read its fields instead of destructuring it.",
|
|
1000
|
+
node: parameter
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function reportLoopVariable(node) {
|
|
1004
|
+
if (node.left.type === "VariableDeclaration" && some$1(node.left.declarations, (declarator) => isPattern(declarator.id))) context.report({
|
|
1005
|
+
message: "Take the value whole and read its fields instead of destructuring it.",
|
|
1006
|
+
node: node.left
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
ArrowFunctionExpression: reportParameters,
|
|
1011
|
+
ForInStatement: reportLoopVariable,
|
|
1012
|
+
ForOfStatement: reportLoopVariable,
|
|
1013
|
+
FunctionDeclaration: reportParameters,
|
|
1014
|
+
FunctionExpression: reportParameters
|
|
1015
|
+
};
|
|
1016
|
+
},
|
|
1017
|
+
meta: { type: "problem" }
|
|
1018
|
+
});
|
|
1019
|
+
//#endregion
|
|
1020
|
+
//#region src/rules/no-double-nullish-check.ts
|
|
1021
|
+
function nullishOperand(node) {
|
|
1022
|
+
if (node.type === "Literal" && node.value === null) return some("null");
|
|
1023
|
+
return node.type === "Identifier" && node.name === "undefined" ? some("undefined") : none();
|
|
1024
|
+
}
|
|
1025
|
+
function operands(input) {
|
|
1026
|
+
if (input.node.type !== "LogicalExpression" || input.node.operator !== input.operator) return [input.node];
|
|
1027
|
+
return appendAll(operands({
|
|
1028
|
+
node: input.node.left,
|
|
1029
|
+
operator: input.operator
|
|
1030
|
+
}), operands({
|
|
1031
|
+
node: input.node.right,
|
|
1032
|
+
operator: input.operator
|
|
1033
|
+
}));
|
|
1034
|
+
}
|
|
1035
|
+
function comparedNullish(input) {
|
|
1036
|
+
if (input.node.type !== "BinaryExpression" || input.node.operator !== input.operator) return none();
|
|
1037
|
+
const subject = input.context.sourceCode.getText(input.node.left);
|
|
1038
|
+
return map(nullishOperand(input.node.right), (nullish) => ({
|
|
1039
|
+
nullish,
|
|
1040
|
+
subject
|
|
1041
|
+
}));
|
|
1042
|
+
}
|
|
1043
|
+
const noDoubleNullishCheck = defineRule({
|
|
1044
|
+
create: (context) => ({ LogicalExpression: (node) => {
|
|
1045
|
+
if (node.operator === "??") return;
|
|
1046
|
+
if (node.parent.type === "LogicalExpression" && node.parent.operator === node.operator) return;
|
|
1047
|
+
const comparison = node.operator === "&&" ? "!==" : "===";
|
|
1048
|
+
const checks = pipe(operands({
|
|
1049
|
+
node,
|
|
1050
|
+
operator: node.operator
|
|
1051
|
+
}), filterMap((operand) => fromOption(comparedNullish({
|
|
1052
|
+
context,
|
|
1053
|
+
node: operand,
|
|
1054
|
+
operator: comparison
|
|
1055
|
+
}), () => void 0)));
|
|
1056
|
+
if (some$1(checks, (check) => some$1(checks, (other) => other.subject === check.subject && other.nullish !== check.nullish))) context.report({
|
|
1057
|
+
message: comparison === "!==" ? "Use Predicate.isNotNullish." : "Use Predicate.isNullish.",
|
|
1058
|
+
node
|
|
1059
|
+
});
|
|
1060
|
+
} }),
|
|
1061
|
+
meta: { type: "problem" }
|
|
1062
|
+
});
|
|
1063
|
+
//#endregion
|
|
1064
|
+
//#region src/rules/no-effect-property-arrow.ts
|
|
1065
|
+
const noEffectPropertyArrow = defineRule({
|
|
1066
|
+
create: (context) => ({ Property: (node) => {
|
|
1067
|
+
if (node.value.type !== "ArrowFunctionExpression" || isArrayEmpty(node.value.params) || node.value.body.type !== "CallExpression") return;
|
|
1068
|
+
const callee = node.value.body.callee;
|
|
1069
|
+
if (callee.type === "Identifier" && isImportBinding({
|
|
1070
|
+
context,
|
|
1071
|
+
importedName: "pipe",
|
|
1072
|
+
node: callee,
|
|
1073
|
+
source: "effect"
|
|
1074
|
+
}) && some$1(node.value.body.arguments, (argument) => argument.type !== "SpreadElement" && importedMember({
|
|
1075
|
+
context,
|
|
1076
|
+
importedName: "Effect",
|
|
1077
|
+
node: argument
|
|
1078
|
+
}) || argument.type === "CallExpression" && importedMember({
|
|
1079
|
+
context,
|
|
1080
|
+
importedName: "Effect",
|
|
1081
|
+
node: argument.callee
|
|
1082
|
+
})) || callee.type === "MemberExpression" && importedMember({
|
|
1083
|
+
context,
|
|
1084
|
+
importedName: "Effect",
|
|
1085
|
+
node: callee,
|
|
1086
|
+
propertyName: "tryPromise"
|
|
1087
|
+
})) context.report({
|
|
1088
|
+
message: "Define this property with Effect.fn or Effect.fnUntraced instead of an arrow.",
|
|
1089
|
+
node
|
|
1090
|
+
});
|
|
1091
|
+
} }),
|
|
1092
|
+
meta: { type: "problem" }
|
|
1093
|
+
});
|
|
1094
|
+
//#endregion
|
|
1095
|
+
//#region src/rules/no-error-message-assertion.ts
|
|
1096
|
+
function receiverName(node) {
|
|
1097
|
+
if (node.type === "Identifier") return some(node.name);
|
|
1098
|
+
return node.type === "MemberExpression" ? memberName(node) : none();
|
|
1099
|
+
}
|
|
1100
|
+
function isErrorMessage(node) {
|
|
1101
|
+
return node?.type === "MemberExpression" && contains(memberName(node), "message") && exists(receiverName(node.object), (name) => /^(?:e|err|error|failure|cause|exception)$|Error$/iu.test(name));
|
|
1102
|
+
}
|
|
1103
|
+
function expectsMessage(node) {
|
|
1104
|
+
const subject = node.object.type === "MemberExpression" && contains(memberName(node.object), "not") ? node.object.object : node.object;
|
|
1105
|
+
return subject.type === "CallExpression" && subject.callee.type === "Identifier" && subject.callee.name === "expect" && isErrorMessage(subject.arguments[0]);
|
|
1106
|
+
}
|
|
1107
|
+
function assertsMessage(node) {
|
|
1108
|
+
const [actual, expected] = node.arguments;
|
|
1109
|
+
return node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "assert" && isErrorMessage(actual) && (expected?.type === "Literal" || expected?.type === "TemplateLiteral");
|
|
1110
|
+
}
|
|
1111
|
+
const noErrorMessageAssertion = defineRule({
|
|
1112
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1113
|
+
if (assertsMessage(node)) {
|
|
1114
|
+
context.report({
|
|
1115
|
+
message: "Assert the failure, never its message wording.",
|
|
1116
|
+
node
|
|
1117
|
+
});
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (node.callee.type === "MemberExpression" && (node.arguments[0]?.type === "Literal" || node.arguments[0]?.type === "TemplateLiteral") && (exists(memberName(node.callee), (name) => contains$1(["toThrow", "toThrowError"], name)) || expectsMessage(node.callee))) context.report({
|
|
1121
|
+
message: "Assert the failure, never its message wording.",
|
|
1122
|
+
node
|
|
1123
|
+
});
|
|
1124
|
+
} }),
|
|
1125
|
+
meta: { type: "problem" }
|
|
1126
|
+
});
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region src/rules/no-fail-in-generator.ts
|
|
1129
|
+
function insideEffectGenerator(input) {
|
|
1130
|
+
if (input.node.type === "Program") return false;
|
|
1131
|
+
if ((input.node.type === "ArrowFunctionExpression" || input.node.type === "FunctionDeclaration" || input.node.type === "FunctionExpression") && !input.node.generator) return false;
|
|
1132
|
+
if (input.node.type === "FunctionExpression" && input.node.generator && input.node.parent.type === "CallExpression" && exists(effectBuilderName({
|
|
1133
|
+
context: input.context,
|
|
1134
|
+
node: input.node.parent.callee
|
|
1135
|
+
}), (name) => contains$1([
|
|
1136
|
+
"fn",
|
|
1137
|
+
"fnUntraced",
|
|
1138
|
+
"gen"
|
|
1139
|
+
], name))) return true;
|
|
1140
|
+
return insideEffectGenerator({
|
|
1141
|
+
context: input.context,
|
|
1142
|
+
node: input.node.parent
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
const noFailInGenerator = defineRule({
|
|
1146
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1147
|
+
if (node.callee.type === "MemberExpression" && importedMember({
|
|
1148
|
+
context,
|
|
1149
|
+
importedName: "Effect",
|
|
1150
|
+
node: node.callee,
|
|
1151
|
+
propertyName: "fail"
|
|
1152
|
+
}) && node.parent.type === "YieldExpression" && node.parent.delegate && node.arguments[0]?.type !== "NewExpression" && insideEffectGenerator({
|
|
1153
|
+
context,
|
|
1154
|
+
node
|
|
1155
|
+
})) context.report({
|
|
1156
|
+
message: "Yield the error instead: return yield* E.make(...).",
|
|
1157
|
+
node
|
|
1158
|
+
});
|
|
1159
|
+
} }),
|
|
1160
|
+
meta: { type: "problem" }
|
|
1161
|
+
});
|
|
1162
|
+
//#endregion
|
|
1163
|
+
//#region src/rules/no-fake-ref-state.ts
|
|
1164
|
+
function propertyName(node) {
|
|
1165
|
+
if (!node.computed && node.key.type === "Identifier") return some(node.key.name);
|
|
1166
|
+
if (node.key.type === "Literal" && isString(node.key.value)) return some(node.key.value);
|
|
1167
|
+
return none();
|
|
1168
|
+
}
|
|
1169
|
+
function isFakeRefState(input) {
|
|
1170
|
+
if (!isReactUseState(input) || input.node.arguments[0]?.type !== "ArrowFunctionExpression" || input.node.arguments[0].body.type !== "ObjectExpression") return false;
|
|
1171
|
+
return some$1(input.node.arguments[0].body.properties, (property) => property.type === "Property" && contains(propertyName(property), "current"));
|
|
1172
|
+
}
|
|
1173
|
+
const noFakeRefState = defineRule({
|
|
1174
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1175
|
+
if (isFakeRefState({
|
|
1176
|
+
context,
|
|
1177
|
+
node
|
|
1178
|
+
})) context.report({
|
|
1179
|
+
message: "Use useRef for ref-shaped lifecycle state.",
|
|
1180
|
+
node
|
|
1181
|
+
});
|
|
1182
|
+
} }),
|
|
1183
|
+
meta: { type: "problem" }
|
|
1184
|
+
});
|
|
1185
|
+
//#endregion
|
|
1186
|
+
//#region src/rules/no-hand-written-guard.ts
|
|
1187
|
+
function isSubject(input) {
|
|
1188
|
+
return input.node.type === "Identifier" && input.node.name === input.name;
|
|
1189
|
+
}
|
|
1190
|
+
function comparison(input) {
|
|
1191
|
+
if (input.subject.type === "UnaryExpression" && input.subject.operator === "typeof" && isSubject({
|
|
1192
|
+
name: input.name,
|
|
1193
|
+
node: input.subject.argument
|
|
1194
|
+
}) && input.other.type === "Literal" && isString(input.other.value)) return some(`typeof ${input.operator} ${input.other.value}`);
|
|
1195
|
+
if (!isSubject({
|
|
1196
|
+
name: input.name,
|
|
1197
|
+
node: input.subject
|
|
1198
|
+
})) return none();
|
|
1199
|
+
if (input.other.type === "Literal" && input.other.raw === "null") return some(`${input.operator} null`);
|
|
1200
|
+
if (input.other.type === "Identifier" && input.other.name === "undefined") return some(`${input.operator} undefined`);
|
|
1201
|
+
return none();
|
|
1202
|
+
}
|
|
1203
|
+
function check(input) {
|
|
1204
|
+
if (input.node.type === "LogicalExpression" && input.node.operator === "&&") return pipe(all([check({
|
|
1205
|
+
name: input.name,
|
|
1206
|
+
node: input.node.left
|
|
1207
|
+
}), check({
|
|
1208
|
+
name: input.name,
|
|
1209
|
+
node: input.node.right
|
|
1210
|
+
})]), map(flatten));
|
|
1211
|
+
if (input.node.type === "UnaryExpression" && input.node.operator === "!" && input.node.argument.type === "CallExpression" && input.node.argument.callee.type === "MemberExpression" && isSubject({
|
|
1212
|
+
name: "Array",
|
|
1213
|
+
node: input.node.argument.callee.object
|
|
1214
|
+
}) && contains(memberName(input.node.argument.callee), "isArray") && input.node.argument.arguments.length === 1 && every(input.node.argument.arguments, (argument) => isSubject({
|
|
1215
|
+
name: input.name,
|
|
1216
|
+
node: argument
|
|
1217
|
+
}))) return some(["!Array.isArray"]);
|
|
1218
|
+
if (input.node.type !== "BinaryExpression") return none();
|
|
1219
|
+
if (input.node.operator === "instanceof") return isSubject({
|
|
1220
|
+
name: input.name,
|
|
1221
|
+
node: input.node.left
|
|
1222
|
+
}) && input.node.right.type === "Identifier" ? some([`instanceof ${input.node.right.name}`]) : none();
|
|
1223
|
+
return pipe(firstSomeOf([comparison({
|
|
1224
|
+
name: input.name,
|
|
1225
|
+
operator: input.node.operator,
|
|
1226
|
+
other: input.node.right,
|
|
1227
|
+
subject: input.node.left
|
|
1228
|
+
}), comparison({
|
|
1229
|
+
name: input.name,
|
|
1230
|
+
operator: input.node.operator,
|
|
1231
|
+
other: input.node.left,
|
|
1232
|
+
subject: input.node.right
|
|
1233
|
+
})]), map(of));
|
|
1234
|
+
}
|
|
1235
|
+
function report(input) {
|
|
1236
|
+
const predicate = input.node.returnType?.typeAnnotation;
|
|
1237
|
+
const returned = returnedExpression(input.node);
|
|
1238
|
+
if (predicate?.type !== "TSTypePredicate" || predicate.asserts || predicate.parameterName.type !== "Identifier" || isNullish(returned)) return;
|
|
1239
|
+
const name = pipe(check({
|
|
1240
|
+
name: predicate.parameterName.name,
|
|
1241
|
+
node: returned
|
|
1242
|
+
}), map((checks) => pipe(checks, sort(Order), join(" && "))), flatMap((key) => get$2({
|
|
1243
|
+
"!= null": "isNotNullish",
|
|
1244
|
+
"!== null": "isNotNull",
|
|
1245
|
+
"!== null && !Array.isArray && typeof === object": "isObject",
|
|
1246
|
+
"!== null && typeof === object": "isObjectOrArray",
|
|
1247
|
+
"!== undefined": "isNotUndefined",
|
|
1248
|
+
"== null": "isNullish",
|
|
1249
|
+
"=== null": "isNull",
|
|
1250
|
+
"=== undefined": "isUndefined",
|
|
1251
|
+
"instanceof Date": "isDate",
|
|
1252
|
+
"instanceof Error": "isError",
|
|
1253
|
+
"instanceof Map": "isMap",
|
|
1254
|
+
"instanceof RegExp": "isRegExp",
|
|
1255
|
+
"instanceof Set": "isSet",
|
|
1256
|
+
"instanceof Uint8Array": "isUint8Array",
|
|
1257
|
+
"typeof === bigint": "isBigInt",
|
|
1258
|
+
"typeof === boolean": "isBoolean",
|
|
1259
|
+
"typeof === function": "isFunction",
|
|
1260
|
+
"typeof === number": "isNumber",
|
|
1261
|
+
"typeof === string": "isString",
|
|
1262
|
+
"typeof === symbol": "isSymbol",
|
|
1263
|
+
"typeof === undefined": "isUndefined"
|
|
1264
|
+
}, key)));
|
|
1265
|
+
map(name, (guard) => {
|
|
1266
|
+
input.context.report({
|
|
1267
|
+
message: `Use Predicate.${guard} instead of this hand-written type guard.`,
|
|
1268
|
+
node: input.node
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
const noHandWrittenGuard = defineRule({
|
|
1273
|
+
create: (context) => ({
|
|
1274
|
+
ArrowFunctionExpression: (node) => {
|
|
1275
|
+
report({
|
|
1276
|
+
context,
|
|
1277
|
+
node
|
|
1278
|
+
});
|
|
1279
|
+
},
|
|
1280
|
+
FunctionDeclaration: (node) => {
|
|
1281
|
+
report({
|
|
1282
|
+
context,
|
|
1283
|
+
node
|
|
1284
|
+
});
|
|
1285
|
+
},
|
|
1286
|
+
FunctionExpression: (node) => {
|
|
1287
|
+
report({
|
|
1288
|
+
context,
|
|
1289
|
+
node
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
}),
|
|
1293
|
+
meta: { type: "problem" }
|
|
1294
|
+
});
|
|
1295
|
+
//#endregion
|
|
1296
|
+
//#region src/rules/no-let.ts
|
|
1297
|
+
const noLet = defineRule({
|
|
1298
|
+
create: (context) => ({ VariableDeclaration: (node) => {
|
|
1299
|
+
if (node.kind === "let") context.report({
|
|
1300
|
+
message: "Replace let with a const pipeline or a fold such as Array.reduce.",
|
|
1301
|
+
node
|
|
1302
|
+
});
|
|
1303
|
+
} }),
|
|
1304
|
+
meta: { type: "problem" }
|
|
1305
|
+
});
|
|
1306
|
+
//#endregion
|
|
1307
|
+
//#region src/rules/no-native-emptiness-check.ts
|
|
1308
|
+
function emptinessPredicate(node) {
|
|
1309
|
+
const comparesLength = node.left.type === "MemberExpression" && contains(memberName(node.left), "length") && node.right.type === "Literal" && node.right.value === 0;
|
|
1310
|
+
if (comparesLength && node.operator === "===") return some("Array.isArrayEmpty, Array.isReadonlyArrayEmpty, or String.isEmpty");
|
|
1311
|
+
if (comparesLength && node.operator === ">") return some("Array.isArrayNonEmpty, Array.isReadonlyArrayNonEmpty, or String.isNonEmpty");
|
|
1312
|
+
return none();
|
|
1313
|
+
}
|
|
1314
|
+
const noNativeEmptinessCheck = defineRule({
|
|
1315
|
+
create: (context) => ({ BinaryExpression: (node) => {
|
|
1316
|
+
map(emptinessPredicate(node), (predicate) => {
|
|
1317
|
+
context.report({
|
|
1318
|
+
message: `Use ${predicate}.`,
|
|
1319
|
+
node
|
|
1320
|
+
});
|
|
1321
|
+
});
|
|
1322
|
+
} }),
|
|
1323
|
+
meta: { type: "problem" }
|
|
1324
|
+
});
|
|
1325
|
+
//#endregion
|
|
1326
|
+
//#region src/rules/no-native-method-call.ts
|
|
1327
|
+
const nativeMethods = [
|
|
1328
|
+
"at",
|
|
1329
|
+
"catch",
|
|
1330
|
+
"charAt",
|
|
1331
|
+
"charCodeAt",
|
|
1332
|
+
"codePointAt",
|
|
1333
|
+
"concat",
|
|
1334
|
+
"endsWith",
|
|
1335
|
+
"entries",
|
|
1336
|
+
"every",
|
|
1337
|
+
"fill",
|
|
1338
|
+
"filter",
|
|
1339
|
+
"finally",
|
|
1340
|
+
"find",
|
|
1341
|
+
"findIndex",
|
|
1342
|
+
"findLast",
|
|
1343
|
+
"findLastIndex",
|
|
1344
|
+
"flat",
|
|
1345
|
+
"flatMap",
|
|
1346
|
+
"forEach",
|
|
1347
|
+
"includes",
|
|
1348
|
+
"indexOf",
|
|
1349
|
+
"join",
|
|
1350
|
+
"keys",
|
|
1351
|
+
"lastIndexOf",
|
|
1352
|
+
"localeCompare",
|
|
1353
|
+
"map",
|
|
1354
|
+
"match",
|
|
1355
|
+
"matchAll",
|
|
1356
|
+
"normalize",
|
|
1357
|
+
"padEnd",
|
|
1358
|
+
"padStart",
|
|
1359
|
+
"pop",
|
|
1360
|
+
"push",
|
|
1361
|
+
"reduce",
|
|
1362
|
+
"reduceRight",
|
|
1363
|
+
"repeat",
|
|
1364
|
+
"replace",
|
|
1365
|
+
"replaceAll",
|
|
1366
|
+
"reverse",
|
|
1367
|
+
"shift",
|
|
1368
|
+
"slice",
|
|
1369
|
+
"some",
|
|
1370
|
+
"sort",
|
|
1371
|
+
"splice",
|
|
1372
|
+
"split",
|
|
1373
|
+
"startsWith",
|
|
1374
|
+
"substring",
|
|
1375
|
+
"then",
|
|
1376
|
+
"toLocaleLowerCase",
|
|
1377
|
+
"toLocaleUpperCase",
|
|
1378
|
+
"toLowerCase",
|
|
1379
|
+
"toReversed",
|
|
1380
|
+
"toSorted",
|
|
1381
|
+
"toSpliced",
|
|
1382
|
+
"toUpperCase",
|
|
1383
|
+
"trim",
|
|
1384
|
+
"trimEnd",
|
|
1385
|
+
"trimStart",
|
|
1386
|
+
"unshift",
|
|
1387
|
+
"values",
|
|
1388
|
+
"with"
|
|
1389
|
+
];
|
|
1390
|
+
const noNativeMethodCall = defineRule({
|
|
1391
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1392
|
+
if (node.callee.type !== "MemberExpression" || node.callee.object.type === "Identifier" && (node.callee.object.name === "path" || isImportBinding({
|
|
1393
|
+
context,
|
|
1394
|
+
node: node.callee.object,
|
|
1395
|
+
source: /^(?:effect(?:\/|$)|@effect\/)/u
|
|
1396
|
+
}))) return;
|
|
1397
|
+
const receiver = node.callee.object;
|
|
1398
|
+
pipe(memberName(node.callee), filter((name) => contains$1(nativeMethods, name)), filter((name) => !contains$1([
|
|
1399
|
+
"entries",
|
|
1400
|
+
"keys",
|
|
1401
|
+
"values"
|
|
1402
|
+
], name) || isArrayEmpty(node.arguments) || receiver.type === "Identifier" && receiver.name === "Object"), map((name) => {
|
|
1403
|
+
context.report({
|
|
1404
|
+
message: `Replace .${name}() on a native array, string, record, or Promise with its Effect Array, String, Record, or Effect function; a third-party API's own method stays, with an inline disable and its reason.`,
|
|
1405
|
+
node: node.callee
|
|
1406
|
+
});
|
|
1407
|
+
}));
|
|
1408
|
+
} }),
|
|
1409
|
+
meta: { type: "problem" }
|
|
1410
|
+
});
|
|
1411
|
+
//#endregion
|
|
1412
|
+
//#region src/rules/no-option-value-access.ts
|
|
1413
|
+
function checks(input) {
|
|
1414
|
+
if (input.test.type === "LogicalExpression" && input.test.operator === input.operator) return checks({
|
|
1415
|
+
...input,
|
|
1416
|
+
test: input.test.left
|
|
1417
|
+
}) || checks({
|
|
1418
|
+
...input,
|
|
1419
|
+
test: input.test.right
|
|
1420
|
+
});
|
|
1421
|
+
return input.test.type === "CallExpression" && importedMember({
|
|
1422
|
+
context: input.context,
|
|
1423
|
+
importedName: "Option",
|
|
1424
|
+
node: input.test.callee,
|
|
1425
|
+
propertyName: input.name
|
|
1426
|
+
}) && input.test.arguments[0] !== void 0 && input.context.sourceCode.getText(input.test.arguments[0]) === input.subject;
|
|
1427
|
+
}
|
|
1428
|
+
function exits(node) {
|
|
1429
|
+
return contains$1([
|
|
1430
|
+
"ContinueStatement",
|
|
1431
|
+
"ReturnStatement",
|
|
1432
|
+
"ThrowStatement"
|
|
1433
|
+
], node.type) || node.type === "BlockStatement" && some$1(node.body, exits);
|
|
1434
|
+
}
|
|
1435
|
+
function earlyExit(input) {
|
|
1436
|
+
const parent = input.node.parent;
|
|
1437
|
+
if (parent?.type !== "BlockStatement" && parent?.type !== "Program") return false;
|
|
1438
|
+
const index = findFirstIndex(parent.body, (statement) => statement === input.node);
|
|
1439
|
+
return some$1(take(parent.body, getOrElse(index, () => 0)), (statement) => statement.type === "IfStatement" && exits(statement.consequent) && checks({
|
|
1440
|
+
...input,
|
|
1441
|
+
name: "isNone",
|
|
1442
|
+
operator: "||",
|
|
1443
|
+
test: statement.test
|
|
1444
|
+
}));
|
|
1445
|
+
}
|
|
1446
|
+
function guarded(input) {
|
|
1447
|
+
const parent = input.node.parent;
|
|
1448
|
+
if (parent === null || parent.type === "FunctionDeclaration") return false;
|
|
1449
|
+
function some(test) {
|
|
1450
|
+
return checks({
|
|
1451
|
+
...input,
|
|
1452
|
+
name: "isSome",
|
|
1453
|
+
operator: "&&",
|
|
1454
|
+
test
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
return parent.type === "IfStatement" && parent.consequent === input.node && some(parent.test) || parent.type === "ConditionalExpression" && parent.consequent === input.node && some(parent.test) || parent.type === "LogicalExpression" && parent.right === input.node && parent.operator === "&&" && some(parent.left) || parent.type === "LogicalExpression" && parent.right === input.node && parent.operator === "||" && checks({
|
|
1458
|
+
...input,
|
|
1459
|
+
name: "isNone",
|
|
1460
|
+
operator: "||",
|
|
1461
|
+
test: parent.left
|
|
1462
|
+
}) || earlyExit(input) || guarded({
|
|
1463
|
+
...input,
|
|
1464
|
+
node: parent
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
const noOptionValueAccess = defineRule({
|
|
1468
|
+
create: (context) => ({ MemberExpression: (node) => {
|
|
1469
|
+
if (!contains(memberName(node), "value")) return;
|
|
1470
|
+
if (guarded({
|
|
1471
|
+
context,
|
|
1472
|
+
node,
|
|
1473
|
+
subject: context.sourceCode.getText(node.object)
|
|
1474
|
+
})) context.report({
|
|
1475
|
+
message: "Read the value with Option.match, Option.map, or Option.getOrElse.",
|
|
1476
|
+
node
|
|
1477
|
+
});
|
|
1478
|
+
} }),
|
|
1479
|
+
meta: { type: "problem" }
|
|
1480
|
+
});
|
|
1481
|
+
//#endregion
|
|
1482
|
+
//#region src/rules/no-readonly-type-syntax.ts
|
|
1483
|
+
function isTopLevelStatement(node) {
|
|
1484
|
+
if (node.parent.type === "Program") return true;
|
|
1485
|
+
return node.parent.type === "ExportNamedDeclaration" && node.parent.parent.type === "Program";
|
|
1486
|
+
}
|
|
1487
|
+
function insideCycleType(input) {
|
|
1488
|
+
if (input.node.type === "Program") return false;
|
|
1489
|
+
if (input.node.type === "TSTypeAliasDeclaration" && isTopLevelStatement(input.node)) return contains$1(input.cycleNames, input.node.id.name);
|
|
1490
|
+
return insideCycleType({
|
|
1491
|
+
cycleNames: input.cycleNames,
|
|
1492
|
+
node: input.node.parent
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
function referencedName(node) {
|
|
1496
|
+
if (node.type !== "TSTypeReference") return none();
|
|
1497
|
+
if (node.typeName.type === "Identifier") return some(node.typeName.name);
|
|
1498
|
+
return node.typeName.type === "TSQualifiedName" ? some(node.typeName.right.name) : none();
|
|
1499
|
+
}
|
|
1500
|
+
function shapeLiterals(input) {
|
|
1501
|
+
if (input.type.type === "TSTypeLiteral") return [input.type];
|
|
1502
|
+
return pipe(referencedName(input.type), filter((name) => !contains$1(input.seen, name)), map((name) => pipe(input.aliases, filter$1((alias) => alias.id.name === name), flatMap$1((alias) => shapeLiterals({
|
|
1503
|
+
aliases: input.aliases,
|
|
1504
|
+
seen: append(input.seen, name),
|
|
1505
|
+
type: alias.typeAnnotation
|
|
1506
|
+
})))), getOrElse(() => empty()));
|
|
1507
|
+
}
|
|
1508
|
+
const noReadonlyTypeSyntax = defineRule({
|
|
1509
|
+
create: (context) => {
|
|
1510
|
+
const cycleNames = schemaCycleNames({
|
|
1511
|
+
context,
|
|
1512
|
+
program: context.sourceCode.ast
|
|
1513
|
+
});
|
|
1514
|
+
const shapes = make$2(empty());
|
|
1515
|
+
const aliases = make$2(empty());
|
|
1516
|
+
const properties = make$2(empty());
|
|
1517
|
+
const methods = make$2(empty());
|
|
1518
|
+
function exempt(node) {
|
|
1519
|
+
return insideCycleType({
|
|
1520
|
+
cycleNames,
|
|
1521
|
+
node
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
return {
|
|
1525
|
+
CallExpression: (node) => {
|
|
1526
|
+
if (importedMember({
|
|
1527
|
+
context,
|
|
1528
|
+
importedName: "Context",
|
|
1529
|
+
node: node.callee,
|
|
1530
|
+
propertyName: "Service"
|
|
1531
|
+
})) pipe(fromNullishOr(node.typeArguments?.params[1]), map((shape) => update(shapes, append(shape))));
|
|
1532
|
+
},
|
|
1533
|
+
"Program:exit": () => {
|
|
1534
|
+
const literals = pipe(get$3(shapes), flatMap$1((shape) => shapeLiterals({
|
|
1535
|
+
aliases: get$3(aliases),
|
|
1536
|
+
seen: [],
|
|
1537
|
+
type: shape
|
|
1538
|
+
})));
|
|
1539
|
+
function inShape(node) {
|
|
1540
|
+
return some$1(literals, (literal) => literal === node.parent);
|
|
1541
|
+
}
|
|
1542
|
+
for (const node of get$3(properties)) {
|
|
1543
|
+
if (inShape(node) && !node.readonly) context.report({
|
|
1544
|
+
message: "Mark this service property readonly.",
|
|
1545
|
+
node
|
|
1546
|
+
});
|
|
1547
|
+
if (!inShape(node) && node.readonly && !exempt(node)) context.report({
|
|
1548
|
+
message: "Remove the readonly property modifier.",
|
|
1549
|
+
node
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
for (const node of get$3(methods)) if (inShape(node)) context.report({
|
|
1553
|
+
message: "Write this service method as a readonly property holding a function.",
|
|
1554
|
+
node
|
|
1555
|
+
});
|
|
1556
|
+
},
|
|
1557
|
+
TSIndexSignature: (node) => {
|
|
1558
|
+
if (node.readonly) context.report({
|
|
1559
|
+
message: "Remove the readonly index modifier.",
|
|
1560
|
+
node
|
|
1561
|
+
});
|
|
1562
|
+
},
|
|
1563
|
+
TSMappedType: (node) => {
|
|
1564
|
+
if (node.readonly === true || node.readonly === "+") context.report({
|
|
1565
|
+
message: "Remove the readonly mapped-type modifier.",
|
|
1566
|
+
node
|
|
1567
|
+
});
|
|
1568
|
+
},
|
|
1569
|
+
TSMethodSignature: (node) => {
|
|
1570
|
+
update(methods, append(node));
|
|
1571
|
+
},
|
|
1572
|
+
TSPropertySignature: (node) => {
|
|
1573
|
+
update(properties, append(node));
|
|
1574
|
+
},
|
|
1575
|
+
TSTypeAliasDeclaration: (node) => {
|
|
1576
|
+
update(aliases, append(node));
|
|
1577
|
+
},
|
|
1578
|
+
TSTypeOperator: (node) => {
|
|
1579
|
+
if (node.operator === "readonly" && !exempt(node)) context.report({
|
|
1580
|
+
message: "Use a mutable type shape.",
|
|
1581
|
+
node
|
|
1582
|
+
});
|
|
1583
|
+
},
|
|
1584
|
+
TSTypeReference: (node) => {
|
|
1585
|
+
if (node.typeName.type === "Identifier" && node.typeName.name === "Readonly" && !exempt(node)) context.report({
|
|
1586
|
+
message: "Use a mutable type shape.",
|
|
1587
|
+
node
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
},
|
|
1592
|
+
meta: { type: "problem" }
|
|
1593
|
+
});
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region src/rules/no-redundant-return-type.ts
|
|
1596
|
+
function isExported(node) {
|
|
1597
|
+
if (node.parent.type === "ExportNamedDeclaration" || node.parent.type === "ExportDefaultDeclaration") return true;
|
|
1598
|
+
return node.parent.type === "VariableDeclarator" && node.parent.parent.type === "VariableDeclaration" && node.parent.parent.parent.type === "ExportNamedDeclaration";
|
|
1599
|
+
}
|
|
1600
|
+
function inferredIdentically$1(input) {
|
|
1601
|
+
const text = input.context.sourceCode.getText;
|
|
1602
|
+
const body = input.node.body;
|
|
1603
|
+
if (body === null) return false;
|
|
1604
|
+
const block = body.type === "BlockStatement";
|
|
1605
|
+
const values = body.type === "BlockStatement" ? map$1(input.returns, (statement) => statement.argument) : [body];
|
|
1606
|
+
const expressions = filter$1(values, isNotNull);
|
|
1607
|
+
if (input.type.type === "TSVoidKeyword") return block && isArrayEmpty(expressions);
|
|
1608
|
+
if (isArrayEmpty(expressions) || expressions.length !== values.length) return false;
|
|
1609
|
+
if (input.type.type === "TSStringKeyword") return every(expressions, (value) => value.type === "TemplateLiteral" && isArrayNonEmpty(value.expressions));
|
|
1610
|
+
if (isExported(input.node)) return false;
|
|
1611
|
+
return every(expressions, (value) => value.type === "TSAsExpression" && text(value.typeAnnotation) === text(input.type) || !block && value.type === "NewExpression" && input.type.type === "TSTypeReference" && input.type.typeArguments === null && text(value.callee) === text(input.type.typeName));
|
|
1612
|
+
}
|
|
1613
|
+
const noRedundantReturnType = defineRule({
|
|
1614
|
+
create: (context) => {
|
|
1615
|
+
const frames = make$2(empty());
|
|
1616
|
+
function enter() {
|
|
1617
|
+
update(frames, append({ returns: empty() }));
|
|
1618
|
+
}
|
|
1619
|
+
function exit(node) {
|
|
1620
|
+
const frame = last(get$3(frames));
|
|
1621
|
+
update(frames, dropRight(1));
|
|
1622
|
+
if (node.async || node.generator || isNullish(node.returnType)) return;
|
|
1623
|
+
if (inferredIdentically$1({
|
|
1624
|
+
context,
|
|
1625
|
+
node,
|
|
1626
|
+
returns: match(frame, {
|
|
1627
|
+
onNone: () => empty(),
|
|
1628
|
+
onSome: (top) => top.returns
|
|
1629
|
+
}),
|
|
1630
|
+
type: node.returnType.typeAnnotation
|
|
1631
|
+
})) context.report({
|
|
1632
|
+
message: "Drop this return type; inference gives the same type.",
|
|
1633
|
+
node: node.returnType
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
ArrowFunctionExpression: enter,
|
|
1638
|
+
"ArrowFunctionExpression:exit": exit,
|
|
1639
|
+
FunctionDeclaration: enter,
|
|
1640
|
+
"FunctionDeclaration:exit": exit,
|
|
1641
|
+
FunctionExpression: enter,
|
|
1642
|
+
"FunctionExpression:exit": exit,
|
|
1643
|
+
ReturnStatement: (node) => {
|
|
1644
|
+
update(frames, (stack) => pipe(last(stack), match({
|
|
1645
|
+
onNone: () => stack,
|
|
1646
|
+
onSome: (top) => append(dropRight(stack, 1), { returns: append(top.returns, node) })
|
|
1647
|
+
})));
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
},
|
|
1651
|
+
meta: { type: "problem" }
|
|
1652
|
+
});
|
|
1653
|
+
//#endregion
|
|
1654
|
+
//#region src/rules/no-redundant-state-type.ts
|
|
1655
|
+
const noRedundantStateType = defineRule({
|
|
1656
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1657
|
+
const argument = node.arguments[0];
|
|
1658
|
+
const type = node.typeArguments?.params[0];
|
|
1659
|
+
if (node.arguments.length !== 1 || node.typeArguments?.params.length !== 1 || argument?.type !== "Literal" || node.callee.type === "Super") return;
|
|
1660
|
+
if (!(isReactUseState({
|
|
1661
|
+
context,
|
|
1662
|
+
node
|
|
1663
|
+
}) || some$1([
|
|
1664
|
+
"MutableRef",
|
|
1665
|
+
"Ref",
|
|
1666
|
+
"SubscriptionRef"
|
|
1667
|
+
], (importedName) => importedMember({
|
|
1668
|
+
context,
|
|
1669
|
+
importedName,
|
|
1670
|
+
node: node.callee,
|
|
1671
|
+
propertyName: "make"
|
|
1672
|
+
})))) return;
|
|
1673
|
+
if (isString(argument.value) && type?.type === "TSStringKeyword" || isNumber(argument.value) && type?.type === "TSNumberKeyword" || isBoolean(argument.value) && type?.type === "TSBooleanKeyword") context.report({
|
|
1674
|
+
message: "Drop this state type; the initializer infers the same primitive type.",
|
|
1675
|
+
node: type
|
|
1676
|
+
});
|
|
1677
|
+
} }),
|
|
1678
|
+
meta: { type: "problem" }
|
|
1679
|
+
});
|
|
1680
|
+
//#endregion
|
|
1681
|
+
//#region src/rules/no-redundant-use-ref-null-type.ts
|
|
1682
|
+
function isReactUseRef(input) {
|
|
1683
|
+
if (input.node.callee.type === "Identifier") return isImportBinding({
|
|
1684
|
+
context: input.context,
|
|
1685
|
+
importedName: "useRef",
|
|
1686
|
+
node: input.node.callee,
|
|
1687
|
+
source: "react"
|
|
1688
|
+
});
|
|
1689
|
+
return input.node.callee.type === "MemberExpression" && contains(memberName(input.node.callee), "useRef") && input.node.callee.object.type === "Identifier" && isNamespaceImport({
|
|
1690
|
+
context: input.context,
|
|
1691
|
+
node: input.node.callee.object,
|
|
1692
|
+
source: "react"
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
function redundantUseRefNullType(input) {
|
|
1696
|
+
const type = input.node.typeArguments?.params[0];
|
|
1697
|
+
return isReactUseRef(input) && input.node.arguments[0]?.type === "Literal" && input.node.arguments[0].value === null && type?.type === "TSUnionType" && some$1(type.types, (member) => member.type === "TSNullKeyword");
|
|
1698
|
+
}
|
|
1699
|
+
const noRedundantUseRefNullType = defineRule({
|
|
1700
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1701
|
+
if (redundantUseRefNullType({
|
|
1702
|
+
context,
|
|
1703
|
+
node
|
|
1704
|
+
})) context.report({
|
|
1705
|
+
message: "Remove null from the explicit useRef type; the null initializer already owns it.",
|
|
1706
|
+
node
|
|
1707
|
+
});
|
|
1708
|
+
} }),
|
|
1709
|
+
meta: { type: "problem" }
|
|
1710
|
+
});
|
|
1711
|
+
//#endregion
|
|
1712
|
+
//#region src/rules/no-redundant-variable-annotation.ts
|
|
1713
|
+
function isStringLiteral(node) {
|
|
1714
|
+
return node.type === "Literal" && isString(node.value);
|
|
1715
|
+
}
|
|
1716
|
+
function inferredIdentically(input) {
|
|
1717
|
+
const text = input.context.sourceCode.getText;
|
|
1718
|
+
if (input.init.type === "TSAsExpression") return text(input.init.typeAnnotation) === text(input.type);
|
|
1719
|
+
return input.init.type === "ConditionalExpression" && input.type.type === "TSStringKeyword" && isStringLiteral(input.init.consequent) && isStringLiteral(input.init.alternate);
|
|
1720
|
+
}
|
|
1721
|
+
const noRedundantVariableAnnotation = defineRule({
|
|
1722
|
+
create: (context) => ({ VariableDeclarator: (node) => {
|
|
1723
|
+
const annotation = node.id.typeAnnotation;
|
|
1724
|
+
if (node.init === null || isNullish(annotation)) return;
|
|
1725
|
+
if (node.init.type === "ArrayExpression" && isArrayEmpty(node.init.elements)) {
|
|
1726
|
+
context.report({
|
|
1727
|
+
message: "Write Array.empty<T>() instead of annotating an empty array.",
|
|
1728
|
+
node: annotation
|
|
1729
|
+
});
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (inferredIdentically({
|
|
1733
|
+
context,
|
|
1734
|
+
init: node.init,
|
|
1735
|
+
type: annotation.typeAnnotation
|
|
1736
|
+
})) context.report({
|
|
1737
|
+
message: "Drop this annotation; inference gives the same type.",
|
|
1738
|
+
node: annotation
|
|
1739
|
+
});
|
|
1740
|
+
} }),
|
|
1741
|
+
meta: { type: "problem" }
|
|
1742
|
+
});
|
|
1743
|
+
//#endregion
|
|
1744
|
+
//#region src/rules/no-reinvented-schema.ts
|
|
1745
|
+
const shipped = [
|
|
1746
|
+
{
|
|
1747
|
+
base: "String",
|
|
1748
|
+
check: "isMinLength",
|
|
1749
|
+
only: 1,
|
|
1750
|
+
schema: "NonEmptyString"
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
base: "String",
|
|
1754
|
+
check: "isNonEmpty",
|
|
1755
|
+
only: void 0,
|
|
1756
|
+
schema: "NonEmptyString"
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
base: "String",
|
|
1760
|
+
check: "isTrimmed",
|
|
1761
|
+
only: void 0,
|
|
1762
|
+
schema: "Trimmed"
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
base: "Number",
|
|
1766
|
+
check: "isInt",
|
|
1767
|
+
only: void 0,
|
|
1768
|
+
schema: "Int"
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
base: "Int",
|
|
1772
|
+
check: "isGreaterThanOrEqualTo",
|
|
1773
|
+
only: 0,
|
|
1774
|
+
schema: "Natural"
|
|
1775
|
+
}
|
|
1776
|
+
];
|
|
1777
|
+
function schemaMember(input) {
|
|
1778
|
+
return input.node.type === "MemberExpression" && importedMember({
|
|
1779
|
+
context: input.context,
|
|
1780
|
+
importedName: "Schema",
|
|
1781
|
+
node: input.node
|
|
1782
|
+
}) ? memberName(input.node) : none();
|
|
1783
|
+
}
|
|
1784
|
+
function shippedSchema(input) {
|
|
1785
|
+
if (input.filter.type !== "CallExpression") return none();
|
|
1786
|
+
const base = schemaMember({
|
|
1787
|
+
context: input.context,
|
|
1788
|
+
node: input.base
|
|
1789
|
+
});
|
|
1790
|
+
const check = schemaMember({
|
|
1791
|
+
context: input.context,
|
|
1792
|
+
node: input.filter.callee
|
|
1793
|
+
});
|
|
1794
|
+
const [value, ...rest] = input.filter.arguments;
|
|
1795
|
+
return pipe(shipped, findFirst((entry) => contains(base, entry.base) && contains(check, entry.check) && isArrayEmpty(rest) && (entry.only === void 0 ? value === void 0 : value?.type === "Literal" && value.value === entry.only)), map((entry) => entry.schema));
|
|
1796
|
+
}
|
|
1797
|
+
function pipedBase(node) {
|
|
1798
|
+
const parent = node.parent;
|
|
1799
|
+
if (parent.type !== "CallExpression") return none();
|
|
1800
|
+
if (parent.callee.type === "Identifier" && parent.callee.name === "pipe" && parent.arguments.length === 2) return parent.arguments[1] === node ? fromNullishOr(parent.arguments[0]) : none();
|
|
1801
|
+
return parent.callee.type === "MemberExpression" && contains(memberName(parent.callee), "pipe") && parent.arguments.length === 1 ? some(parent.callee.object) : none();
|
|
1802
|
+
}
|
|
1803
|
+
function reinventedSchema(input) {
|
|
1804
|
+
if (input.node.callee.type !== "MemberExpression" || !contains(memberName(input.node.callee), "check")) return none();
|
|
1805
|
+
const callee = input.node.callee;
|
|
1806
|
+
const [filter] = input.node.arguments;
|
|
1807
|
+
if (isSome(schemaMember({
|
|
1808
|
+
context: input.context,
|
|
1809
|
+
node: callee
|
|
1810
|
+
}))) return pipe(pipedBase(input.node), flatMap((base) => input.node.arguments.length === 1 && filter !== void 0 ? shippedSchema({
|
|
1811
|
+
base,
|
|
1812
|
+
context: input.context,
|
|
1813
|
+
filter
|
|
1814
|
+
}) : none()));
|
|
1815
|
+
return findFirst(input.node.arguments, (argument) => shippedSchema({
|
|
1816
|
+
base: callee.object,
|
|
1817
|
+
context: input.context,
|
|
1818
|
+
filter: argument
|
|
1819
|
+
}));
|
|
1820
|
+
}
|
|
1821
|
+
const noReinventedSchema = defineRule({
|
|
1822
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1823
|
+
map(reinventedSchema({
|
|
1824
|
+
context,
|
|
1825
|
+
node
|
|
1826
|
+
}), (schema) => {
|
|
1827
|
+
context.report({
|
|
1828
|
+
message: `Replace this base and its matching refinement with Schema.${schema}, keeping every other check.`,
|
|
1829
|
+
node
|
|
1830
|
+
});
|
|
1831
|
+
});
|
|
1832
|
+
} }),
|
|
1833
|
+
meta: { type: "problem" }
|
|
1834
|
+
});
|
|
1835
|
+
//#endregion
|
|
1836
|
+
//#region src/rules/no-renamed-import.ts
|
|
1837
|
+
const noRenamedImport = defineRule({
|
|
1838
|
+
create: (context) => ({ ImportSpecifier: (node) => {
|
|
1839
|
+
const imported = node.imported.type === "Literal" ? node.imported.value : node.imported.name;
|
|
1840
|
+
if (imported === node.local.name || imported === "it" && node.local.name === "test") return;
|
|
1841
|
+
context.report({
|
|
1842
|
+
message: `Import ${imported} under its own name.`,
|
|
1843
|
+
node
|
|
1844
|
+
});
|
|
1845
|
+
} }),
|
|
1846
|
+
meta: { type: "problem" }
|
|
1847
|
+
});
|
|
1848
|
+
//#endregion
|
|
1849
|
+
//#region src/rules/no-stored-schema-operation.ts
|
|
1850
|
+
function storedSchemaOperation(node) {
|
|
1851
|
+
return node.parent.type === "VariableDeclarator" && node.parent.init === node || node.parent.type === "Property" && node.parent.value === node || node.parent.type === "PropertyDefinition" && node.parent.value === node || node.parent.type === "ArrayExpression" || node.parent.type === "AssignmentExpression" && node.parent.right === node;
|
|
1852
|
+
}
|
|
1853
|
+
const noStoredSchemaOperation = defineRule({
|
|
1854
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
1855
|
+
if (isSchemaOperationCall({
|
|
1856
|
+
context,
|
|
1857
|
+
node
|
|
1858
|
+
}) && storedSchemaOperation(node)) context.report({
|
|
1859
|
+
message: "Inline this Schema operation at its consumption site; never store compiled operations.",
|
|
1860
|
+
node
|
|
1861
|
+
});
|
|
1862
|
+
} }),
|
|
1863
|
+
meta: { type: "problem" }
|
|
1864
|
+
});
|
|
1865
|
+
//#endregion
|
|
1866
|
+
//#region src/rules/no-switch-statement.ts
|
|
1867
|
+
const noSwitchStatement = defineRule({
|
|
1868
|
+
create: (context) => ({ SwitchStatement: (node) => {
|
|
1869
|
+
context.report({
|
|
1870
|
+
message: "Use Match instead of switch.",
|
|
1871
|
+
node
|
|
1872
|
+
});
|
|
1873
|
+
} }),
|
|
1874
|
+
meta: { type: "problem" }
|
|
1875
|
+
});
|
|
1876
|
+
//#endregion
|
|
1877
|
+
//#region src/rules/no-trivial-indirection.ts
|
|
1878
|
+
function parameterName(parameter) {
|
|
1879
|
+
if (parameter.type === "Identifier") return some(parameter.name);
|
|
1880
|
+
if (parameter.type === "RestElement" && parameter.argument.type === "Identifier") return some(parameter.argument.name);
|
|
1881
|
+
return none();
|
|
1882
|
+
}
|
|
1883
|
+
function forwardedCall(input) {
|
|
1884
|
+
return input.node.arguments.length === input.names.length && every(input.node.arguments, (argument, index) => {
|
|
1885
|
+
const name = input.names[index];
|
|
1886
|
+
if (argument.type === "SpreadElement") return argument.argument.type === "Identifier" && argument.argument.name === name;
|
|
1887
|
+
return argument.type === "Identifier" && argument.name === name;
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
function exactForwardingFunction(node) {
|
|
1891
|
+
const names = pipe(node.params, map$1(parameterName), getSomes);
|
|
1892
|
+
const returned = returnedExpression(node);
|
|
1893
|
+
if (isArrayEmpty(names) || names.length !== node.params.length || isNullish(returned)) return false;
|
|
1894
|
+
if (returned.type === "Identifier") return names.length === 1 && returned.name === names[0];
|
|
1895
|
+
return (returned.type === "CallExpression" || returned.type === "NewExpression") && returned.callee.type === "Identifier" && forwardedCall({
|
|
1896
|
+
names,
|
|
1897
|
+
node: returned
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
function isIdentity(node) {
|
|
1901
|
+
const [parameter] = node.params;
|
|
1902
|
+
const returned = returnedExpression(node);
|
|
1903
|
+
return node.params.length === 1 && parameter?.type === "Identifier" && returned?.type === "Identifier" && returned.name === parameter.name;
|
|
1904
|
+
}
|
|
1905
|
+
function indirectionMessage(node) {
|
|
1906
|
+
return isIdentity(node) ? "Pass identity from effect instead of this function." : "Inline this function at its only use site.";
|
|
1907
|
+
}
|
|
1908
|
+
function isExport(node) {
|
|
1909
|
+
return node?.type === "ExportNamedDeclaration" || node?.type === "ExportDefaultDeclaration";
|
|
1910
|
+
}
|
|
1911
|
+
function unexportedBindingName(node) {
|
|
1912
|
+
if (node.type === "FunctionDeclaration" && node.id !== null && !isExport(node.parent)) return some(node.id.name);
|
|
1913
|
+
if (node.parent.type === "VariableDeclarator" && node.parent.id.type === "Identifier" && node.parent.id.typeAnnotation === null && !isExport(node.parent.parent.parent)) return some(node.parent.id.name);
|
|
1914
|
+
return none();
|
|
1915
|
+
}
|
|
1916
|
+
function singleCallerWrapper(input) {
|
|
1917
|
+
const names = pipe(input.node.params, map$1(parameterName), getSomes);
|
|
1918
|
+
const returned = returnedExpression(input.node);
|
|
1919
|
+
if (input.node.async || input.node.generator || input.node.returnType?.typeAnnotation.type === "TSTypePredicate" || isArrayEmpty(names) || names.length !== input.node.params.length || returned?.type !== "CallExpression") return false;
|
|
1920
|
+
return pipe(unexportedBindingName(input.node), flatMap((name) => variableFromScope({
|
|
1921
|
+
name,
|
|
1922
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
1923
|
+
})), exists((variable) => {
|
|
1924
|
+
const reads = filter$1(variable.references, (reference) => reference.isRead());
|
|
1925
|
+
return reads.length === 1 && every(reads, (reference) => reference.identifier.parent.type === "CallExpression" && reference.identifier.parent.callee === reference.identifier);
|
|
1926
|
+
}));
|
|
1927
|
+
}
|
|
1928
|
+
function redundantConstAlias(input) {
|
|
1929
|
+
if (input.node.parent.type !== "VariableDeclaration" || input.node.parent.kind !== "const" || input.node.id.type !== "Identifier" || input.node.id.typeAnnotation !== null || input.node.init?.type !== "Identifier" || !pipe(variableFor(input.context, input.node.init), exists((variable) => {
|
|
1930
|
+
if (some$1(variable.references, (reference) => reference.isWrite() && !reference.init)) return false;
|
|
1931
|
+
return some$1(variable.defs, (definition) => {
|
|
1932
|
+
if (definition.type === "ImportBinding" || definition.type === "Parameter") return true;
|
|
1933
|
+
return definition.node.type === "VariableDeclarator" && definition.node.parent.type === "VariableDeclaration" && definition.node.parent.kind === "const";
|
|
1934
|
+
});
|
|
1935
|
+
}))) return false;
|
|
1936
|
+
return pipe(variableFromScope({
|
|
1937
|
+
name: input.node.id.name,
|
|
1938
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
1939
|
+
}), exists((alias) => filter$1(alias.references, (reference) => reference.isRead()).length === 1));
|
|
1940
|
+
}
|
|
1941
|
+
function singleUseLiteral(input) {
|
|
1942
|
+
if (input.node.parent.type !== "VariableDeclaration" || input.node.parent.kind !== "const" || isExport(input.node.parent.parent) || input.node.id.type !== "Identifier" || input.node.id.typeAnnotation !== null || !(input.node.init?.type === "Literal" && !hasProperty(input.node.init, "regex") || input.node.init?.type === "TemplateLiteral" && isArrayEmpty(input.node.init.expressions))) return false;
|
|
1943
|
+
return pipe(variableFromScope({
|
|
1944
|
+
name: input.node.id.name,
|
|
1945
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
1946
|
+
}), map((variable) => filter$1(variable.references, (reference) => reference.isRead())), exists((reads) => reads.length === 1 && reads[0]?.identifier.parent.type !== "ExportSpecifier"));
|
|
1947
|
+
}
|
|
1948
|
+
function singleUseLayerPart(input) {
|
|
1949
|
+
const init = input.node.init;
|
|
1950
|
+
if (input.node.parent.type !== "VariableDeclaration" || input.node.parent.kind !== "const" || isExport(input.node.parent.parent) || input.node.id.type !== "Identifier" || input.node.id.typeAnnotation !== null || init === null) return false;
|
|
1951
|
+
return pipe(variableFromScope({
|
|
1952
|
+
name: input.node.id.name,
|
|
1953
|
+
scope: input.context.sourceCode.getScope(input.node)
|
|
1954
|
+
}), map((variable) => filter$1(variable.references, (reference) => reference.isRead())), exists((reads) => {
|
|
1955
|
+
const consumer = reads[0]?.identifier.parent;
|
|
1956
|
+
if (reads.length !== 1 || consumer?.type !== "CallExpression") return false;
|
|
1957
|
+
if (init.type === "MemberExpression") return contains(memberName(init), "layer") && (importedMember({
|
|
1958
|
+
context: input.context,
|
|
1959
|
+
importedName: "Layer",
|
|
1960
|
+
node: consumer.callee
|
|
1961
|
+
}) || importedMember({
|
|
1962
|
+
context: input.context,
|
|
1963
|
+
importedName: "Effect",
|
|
1964
|
+
node: consumer.callee,
|
|
1965
|
+
propertyName: "provide"
|
|
1966
|
+
}));
|
|
1967
|
+
return init.type === "CallExpression" && importedMember({
|
|
1968
|
+
context: input.context,
|
|
1969
|
+
importedName: "Effect",
|
|
1970
|
+
node: init.callee,
|
|
1971
|
+
propertyName: "gen"
|
|
1972
|
+
}) && consumer.arguments[1] === reads[0]?.identifier && importedMember({
|
|
1973
|
+
context: input.context,
|
|
1974
|
+
importedName: "Layer",
|
|
1975
|
+
node: consumer.callee,
|
|
1976
|
+
propertyName: "effect"
|
|
1977
|
+
});
|
|
1978
|
+
}));
|
|
1979
|
+
}
|
|
1980
|
+
const noTrivialIndirection = defineRule({
|
|
1981
|
+
create: (context) => ({
|
|
1982
|
+
ArrowFunctionExpression: (node) => {
|
|
1983
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property") && (exactForwardingFunction(node) || singleUseThunk({
|
|
1984
|
+
context,
|
|
1985
|
+
node
|
|
1986
|
+
}) || singleCallerWrapper({
|
|
1987
|
+
context,
|
|
1988
|
+
node
|
|
1989
|
+
}))) context.report({
|
|
1990
|
+
message: indirectionMessage(node),
|
|
1991
|
+
node
|
|
1992
|
+
});
|
|
1993
|
+
},
|
|
1994
|
+
FunctionDeclaration: (node) => {
|
|
1995
|
+
if (exactForwardingFunction(node) || singleUseThunk({
|
|
1996
|
+
context,
|
|
1997
|
+
node
|
|
1998
|
+
}) || singleCallerWrapper({
|
|
1999
|
+
context,
|
|
2000
|
+
node
|
|
2001
|
+
})) context.report({
|
|
2002
|
+
message: indirectionMessage(node),
|
|
2003
|
+
node
|
|
2004
|
+
});
|
|
2005
|
+
},
|
|
2006
|
+
FunctionExpression: (node) => {
|
|
2007
|
+
if ((node.parent.type === "VariableDeclarator" || node.parent.type === "Property" || node.parent.type === "MethodDefinition" && node.parent.override !== true) && (exactForwardingFunction(node) || singleUseThunk({
|
|
2008
|
+
context,
|
|
2009
|
+
node
|
|
2010
|
+
}) || singleCallerWrapper({
|
|
2011
|
+
context,
|
|
2012
|
+
node
|
|
2013
|
+
}))) context.report({
|
|
2014
|
+
message: indirectionMessage(node),
|
|
2015
|
+
node
|
|
2016
|
+
});
|
|
2017
|
+
},
|
|
2018
|
+
VariableDeclarator: (node) => {
|
|
2019
|
+
if (redundantConstAlias({
|
|
2020
|
+
context,
|
|
2021
|
+
node
|
|
2022
|
+
})) context.report({
|
|
2023
|
+
message: "Use the immutable source binding directly.",
|
|
2024
|
+
node
|
|
2025
|
+
});
|
|
2026
|
+
if (singleUseLiteral({
|
|
2027
|
+
context,
|
|
2028
|
+
node
|
|
2029
|
+
})) context.report({
|
|
2030
|
+
message: "Inline this literal at its only use site.",
|
|
2031
|
+
node
|
|
2032
|
+
});
|
|
2033
|
+
if (singleUseLayerPart({
|
|
2034
|
+
context,
|
|
2035
|
+
node
|
|
2036
|
+
})) context.report({
|
|
2037
|
+
message: "Write this layer part where its only layer uses it.",
|
|
2038
|
+
node
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
}),
|
|
2042
|
+
meta: { type: "problem" }
|
|
2043
|
+
});
|
|
2044
|
+
//#endregion
|
|
2045
|
+
//#region src/rules/no-typeof.ts
|
|
2046
|
+
const noTypeof = defineRule({
|
|
2047
|
+
create: (context) => ({ UnaryExpression: (node) => {
|
|
2048
|
+
if (node.operator !== "typeof") return;
|
|
2049
|
+
if (node.argument.type === "Identifier" && !exists(variableFor(context, node.argument), (variable) => isArrayNonEmpty(variable.defs))) return;
|
|
2050
|
+
context.report({
|
|
2051
|
+
message: "Replace typeof with a Predicate guard such as Predicate.isString.",
|
|
2052
|
+
node
|
|
2053
|
+
});
|
|
2054
|
+
} }),
|
|
2055
|
+
meta: { type: "problem" }
|
|
2056
|
+
});
|
|
2057
|
+
//#endregion
|
|
2058
|
+
//#region src/rules/no-undestructured-use-state.ts
|
|
2059
|
+
const noUndestructuredUseState = defineRule({
|
|
2060
|
+
create: (context) => ({ VariableDeclarator: (node) => {
|
|
2061
|
+
if (node.init?.type === "CallExpression" && isReactUseState({
|
|
2062
|
+
context,
|
|
2063
|
+
node: node.init
|
|
2064
|
+
}) && node.id.type !== "ArrayPattern") context.report({
|
|
2065
|
+
message: "Destructure the state value and setter at the declaration.",
|
|
2066
|
+
node
|
|
2067
|
+
});
|
|
2068
|
+
} }),
|
|
2069
|
+
meta: { type: "problem" }
|
|
2070
|
+
});
|
|
2071
|
+
//#endregion
|
|
2072
|
+
//#region src/rules/no-unexplained-disable.ts
|
|
2073
|
+
const noUnexplainedDisable = defineRule({
|
|
2074
|
+
create: (context) => ({ Program: () => {
|
|
2075
|
+
for (const comment of context.sourceCode.getAllComments()) if (/^\s*(?:eslint|oxlint)-disable\S*\s(?:(?!\s--\s).)*\bsort-keys\b/u.test(comment.value)) context.report({
|
|
2076
|
+
message: "Sort the keys, starting a new sorted group after a blank line; never disable sort-keys.",
|
|
2077
|
+
node: comment
|
|
2078
|
+
});
|
|
2079
|
+
else if (/^\s*(?:eslint-disable|oxlint-disable|@effect-diagnostics)/u.test(comment.value) && !/\s--\s*\S/u.test(comment.value)) context.report({
|
|
2080
|
+
message: "Give this disable a `-- reason`.",
|
|
2081
|
+
node: comment
|
|
2082
|
+
});
|
|
2083
|
+
} }),
|
|
2084
|
+
meta: { type: "problem" }
|
|
2085
|
+
});
|
|
2086
|
+
//#endregion
|
|
2087
|
+
//#region src/rules/no-unvalidated-json-decode.ts
|
|
2088
|
+
function unknownJsonSchema(input) {
|
|
2089
|
+
if (isSchemaOperationCall(input) && input.node.callee.type === "MemberExpression" && pipe(memberName(input.node.callee), exists(startsWith("decode"))) && input.node.arguments[0]?.type === "MemberExpression" && importedMember({
|
|
2090
|
+
context: input.context,
|
|
2091
|
+
importedName: "Schema",
|
|
2092
|
+
node: input.node.arguments[0],
|
|
2093
|
+
propertyName: "UnknownFromJsonString"
|
|
2094
|
+
})) return true;
|
|
2095
|
+
if (input.node.callee.type !== "MemberExpression" || !importedMember({
|
|
2096
|
+
context: input.context,
|
|
2097
|
+
importedName: "Schema",
|
|
2098
|
+
node: input.node.callee,
|
|
2099
|
+
propertyName: "fromJsonString"
|
|
2100
|
+
}) || input.node.arguments[0]?.type !== "MemberExpression" || !importedMember({
|
|
2101
|
+
context: input.context,
|
|
2102
|
+
importedName: "Schema",
|
|
2103
|
+
node: input.node.arguments[0],
|
|
2104
|
+
propertyName: "Unknown"
|
|
2105
|
+
})) return false;
|
|
2106
|
+
const parent = input.node.parent;
|
|
2107
|
+
if (parent.type !== "CallExpression" || parent.arguments[0] !== input.node || parent.callee.type !== "MemberExpression") return false;
|
|
2108
|
+
return importedMember({
|
|
2109
|
+
context: input.context,
|
|
2110
|
+
importedName: "Schema",
|
|
2111
|
+
node: parent.callee
|
|
2112
|
+
}) && pipe(memberName(parent.callee), exists(startsWith("decode")));
|
|
2113
|
+
}
|
|
2114
|
+
const noUnvalidatedJsonDecode = defineRule({
|
|
2115
|
+
create: (context) => ({ CallExpression: (node) => {
|
|
2116
|
+
if (unknownJsonSchema({
|
|
2117
|
+
context,
|
|
2118
|
+
node
|
|
2119
|
+
})) context.report({
|
|
2120
|
+
message: "Replace Schema.Unknown with the protocol Schema at this JSON boundary.",
|
|
2121
|
+
node
|
|
2122
|
+
});
|
|
2123
|
+
} }),
|
|
2124
|
+
meta: { type: "problem" }
|
|
2125
|
+
});
|
|
2126
|
+
//#endregion
|
|
2127
|
+
//#region src/rules/schema-type-pair.ts
|
|
2128
|
+
function expressionRoot(node) {
|
|
2129
|
+
if (node.type === "MemberExpression") return expressionRoot(node.object);
|
|
2130
|
+
if (node.type === "CallExpression" && node.callee.type !== "Super") return expressionRoot(node.callee);
|
|
2131
|
+
if (node.type === "ChainExpression") return expressionRoot(node.expression);
|
|
2132
|
+
if (node.type === "TSInstantiationExpression") return expressionRoot(node.expression);
|
|
2133
|
+
if (node.type === "TSSatisfiesExpression") return expressionRoot(node.expression);
|
|
2134
|
+
return node;
|
|
2135
|
+
}
|
|
2136
|
+
function expressionUsesImport(input) {
|
|
2137
|
+
const root = expressionRoot(input.node);
|
|
2138
|
+
return root.type === "Identifier" && isImportBinding({
|
|
2139
|
+
context: input.context,
|
|
2140
|
+
importedName: input.importedName,
|
|
2141
|
+
node: root,
|
|
2142
|
+
source: "effect"
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
function schemaDefinitionMember(node) {
|
|
2146
|
+
if (node.type === "CallExpression" && node.callee.type !== "Super") return schemaDefinitionMember(node.callee);
|
|
2147
|
+
if (node.type === "TSInstantiationExpression") return schemaDefinitionMember(node.expression);
|
|
2148
|
+
if (node.type === "TSSatisfiesExpression") return schemaDefinitionMember(node.expression);
|
|
2149
|
+
if (node.type === "MemberExpression") {
|
|
2150
|
+
if (node.object.type === "Identifier") return some(node);
|
|
2151
|
+
if (node.object.type !== "Super") return schemaDefinitionMember(node.object);
|
|
2152
|
+
}
|
|
2153
|
+
return none();
|
|
2154
|
+
}
|
|
2155
|
+
function isSchemaDefinitionName(name) {
|
|
2156
|
+
return !isSchemaOperationName(name) && !/^to[A-Z]/u.test(name);
|
|
2157
|
+
}
|
|
2158
|
+
function isSchemaDefinition(input) {
|
|
2159
|
+
if (input.node.type === "CallExpression" && input.node.callee.type === "Identifier" && isImportBinding({
|
|
2160
|
+
context: input.context,
|
|
2161
|
+
importedName: "pipe",
|
|
2162
|
+
node: input.node.callee,
|
|
2163
|
+
source: "effect"
|
|
2164
|
+
}) && input.node.arguments[0] !== void 0 && input.node.arguments[0].type !== "SpreadElement") return isSchemaDefinition({
|
|
2165
|
+
context: input.context,
|
|
2166
|
+
node: input.node.arguments[0]
|
|
2167
|
+
});
|
|
2168
|
+
if (input.node.type === "TSSatisfiesExpression") return isSchemaDefinition({
|
|
2169
|
+
context: input.context,
|
|
2170
|
+
node: input.node.expression
|
|
2171
|
+
});
|
|
2172
|
+
if (input.node.type === "CallExpression" && input.node.callee.type === "MemberExpression" && !importedMember({
|
|
2173
|
+
context: input.context,
|
|
2174
|
+
importedName: "Schema",
|
|
2175
|
+
node: input.node.callee
|
|
2176
|
+
}) && contains(memberName(input.node.callee), "make")) return false;
|
|
2177
|
+
if (!expressionUsesImport({
|
|
2178
|
+
context: input.context,
|
|
2179
|
+
importedName: "Schema",
|
|
2180
|
+
node: input.node
|
|
2181
|
+
})) return false;
|
|
2182
|
+
return pipe(schemaDefinitionMember(input.node), exists((member) => {
|
|
2183
|
+
if (!importedMember({
|
|
2184
|
+
context: input.context,
|
|
2185
|
+
importedName: "Schema",
|
|
2186
|
+
node: member
|
|
2187
|
+
})) return false;
|
|
2188
|
+
return pipe(memberName(member), exists(isSchemaDefinitionName));
|
|
2189
|
+
}));
|
|
2190
|
+
}
|
|
2191
|
+
function namedTypeAlias(input) {
|
|
2192
|
+
return pipe(input.typeStatement, flatMap(typeAlias), filter((declaration) => declaration.id.name === input.name));
|
|
2193
|
+
}
|
|
2194
|
+
function reportSchemaVariable(input) {
|
|
2195
|
+
if (input.variable.id.type !== "Identifier" || !/^[A-Z]/u.test(input.variable.id.name)) return;
|
|
2196
|
+
if (input.variable.init === null || !isSchemaDefinition({
|
|
2197
|
+
context: input.context,
|
|
2198
|
+
node: input.variable.init
|
|
2199
|
+
})) return;
|
|
2200
|
+
const name = input.variable.id.name;
|
|
2201
|
+
const annotation = input.variable.id.typeAnnotation;
|
|
2202
|
+
const typeStatement = pipe(input.program.body, findFirstIndex((statement) => statement === input.statement), filter((index) => index > 0), flatMap((index) => get$1(input.program.body, index - 1)));
|
|
2203
|
+
if (contains$1(input.cycleNames, name)) {
|
|
2204
|
+
if (!pipe(namedTypeAlias({
|
|
2205
|
+
name,
|
|
2206
|
+
typeStatement
|
|
2207
|
+
}), exists((declaration) => !isInferredType({
|
|
2208
|
+
name,
|
|
2209
|
+
node: declaration.typeAnnotation
|
|
2210
|
+
})))) input.context.report({
|
|
2211
|
+
message: `Write \`type ${name} = ...\` by hand immediately before this recursive Schema; typeof ${name}.Type is circular.`,
|
|
2212
|
+
node: input.variable
|
|
2213
|
+
});
|
|
2214
|
+
if (isNotNullish(annotation)) input.context.report({
|
|
2215
|
+
message: "Annotate the Schema.suspend thunk, not the recursive Schema.",
|
|
2216
|
+
node: input.variable.id
|
|
2217
|
+
});
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
if (!pipe(namedTypeAlias({
|
|
2221
|
+
name,
|
|
2222
|
+
typeStatement
|
|
2223
|
+
}), exists((declaration) => isInferredType({
|
|
2224
|
+
name,
|
|
2225
|
+
node: declaration.typeAnnotation
|
|
2226
|
+
})))) input.context.report({
|
|
2227
|
+
message: `Place \`type ${name} = typeof ${name}.Type\` immediately before this Schema.`,
|
|
2228
|
+
node: input.variable
|
|
2229
|
+
});
|
|
2230
|
+
if (input.variable.init.type === "TSSatisfiesExpression" && schemaSchemaType({
|
|
2231
|
+
context: input.context,
|
|
2232
|
+
node: input.variable.init.typeAnnotation
|
|
2233
|
+
})) input.context.report({
|
|
2234
|
+
message: "Infer this schema instead of restating Schema.Schema.",
|
|
2235
|
+
node: input.variable.init
|
|
2236
|
+
});
|
|
2237
|
+
if (isNotNullish(annotation) && schemaSchemaType({
|
|
2238
|
+
context: input.context,
|
|
2239
|
+
node: annotation.typeAnnotation
|
|
2240
|
+
})) input.context.report({
|
|
2241
|
+
message: "Infer this schema instead of annotating Schema.Schema.",
|
|
2242
|
+
node: input.variable.id
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
const schemaTypePair = defineRule({
|
|
2246
|
+
createOnce: (context) => ({ "Program:exit": (program) => {
|
|
2247
|
+
const cycleNames = schemaCycleNames({
|
|
2248
|
+
context,
|
|
2249
|
+
program
|
|
2250
|
+
});
|
|
2251
|
+
for (const statement of program.body) {
|
|
2252
|
+
const declaration = statementDeclaration(statement);
|
|
2253
|
+
if (declaration?.type === "VariableDeclaration") for (const variable of declaration.declarations) reportSchemaVariable({
|
|
2254
|
+
context,
|
|
2255
|
+
cycleNames,
|
|
2256
|
+
program,
|
|
2257
|
+
statement,
|
|
2258
|
+
variable
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
} }),
|
|
2262
|
+
meta: { type: "problem" }
|
|
2263
|
+
});
|
|
2264
|
+
//#endregion
|
|
2265
|
+
//#region src/oxlint.ts
|
|
2266
|
+
const reactLegacyApis = [
|
|
2267
|
+
"Component",
|
|
2268
|
+
"PureComponent",
|
|
2269
|
+
"createRef",
|
|
2270
|
+
"forwardRef",
|
|
2271
|
+
"memo",
|
|
2272
|
+
"useCallback",
|
|
2273
|
+
"useMemo"
|
|
2274
|
+
];
|
|
2275
|
+
const reactLegacyMessage = "Use React 19 function components with refs as props; React Compiler owns memoization.";
|
|
2276
|
+
const mapMessage = "Use HashMap, or MutableHashMap for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
2277
|
+
const runMessage = "Run the entry point with NodeRuntime.runMain, or bridge a Promise an external contract demands once, where it is returned, with Effect.runPromiseWith(Context.empty()).";
|
|
2278
|
+
const setMessage = "Use HashSet, or MutableHashSet for local mutation; for identity keys, wrap objects the code owns in Equal.byReferenceUnsafe, which marks them reference-compared globally.";
|
|
2279
|
+
const oxlint = defineConfig({
|
|
2280
|
+
categories: {
|
|
2281
|
+
correctness: "off",
|
|
2282
|
+
nursery: "off",
|
|
2283
|
+
pedantic: "off",
|
|
2284
|
+
perf: "off",
|
|
2285
|
+
restriction: "off",
|
|
2286
|
+
style: "off",
|
|
2287
|
+
suspicious: "off"
|
|
2288
|
+
},
|
|
2289
|
+
ignorePatterns: [
|
|
2290
|
+
"**/node_modules/**",
|
|
2291
|
+
"**/dist/**",
|
|
2292
|
+
"**/*.gen.ts"
|
|
2293
|
+
],
|
|
2294
|
+
jsPlugins: [
|
|
2295
|
+
{
|
|
2296
|
+
name: "@deslop/coding-standards",
|
|
2297
|
+
specifier: import.meta.url
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
name: "react-doctor",
|
|
2301
|
+
specifier: import.meta.resolve("oxlint-plugin-react-doctor")
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
name: "shadcn",
|
|
2305
|
+
specifier: import.meta.resolve("@shadcn/lint")
|
|
2306
|
+
}
|
|
2307
|
+
],
|
|
2308
|
+
options: {
|
|
2309
|
+
denyWarnings: true,
|
|
2310
|
+
reportUnusedDisableDirectives: "deny",
|
|
2311
|
+
typeAware: true,
|
|
2312
|
+
typeCheck: true
|
|
2313
|
+
},
|
|
2314
|
+
overrides: [
|
|
2315
|
+
{
|
|
2316
|
+
files: ["**/*.config.ts", "**/main.*"],
|
|
2317
|
+
rules: { "import/no-default-export": "off" }
|
|
2318
|
+
},
|
|
2319
|
+
{
|
|
2320
|
+
files: ["**/components/ui/**"],
|
|
2321
|
+
rules: {
|
|
2322
|
+
"shadcn/no-restyle": "off",
|
|
2323
|
+
"shadcn/require-static-classes": "off"
|
|
2324
|
+
}
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
files: ["**/components/ui/**", "**/packages/ui/**"],
|
|
2328
|
+
rules: {
|
|
2329
|
+
"@deslop/coding-standards/no-destructured-parameter": "off",
|
|
2330
|
+
"@deslop/coding-standards/no-renamed-import": "off"
|
|
2331
|
+
}
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
files: ["**/*.ts"],
|
|
2335
|
+
rules: { "react/rules-of-hooks": "off" }
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
files: ["**/*.tsx"],
|
|
2339
|
+
rules: {
|
|
2340
|
+
"effecttsgo/async-function": "off",
|
|
2341
|
+
"typescript/require-await": "error",
|
|
2342
|
+
"typescript/strict-void-return": "off",
|
|
2343
|
+
"unicorn/no-null": "off"
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
],
|
|
2347
|
+
plugins: [
|
|
2348
|
+
"effecttsgo",
|
|
2349
|
+
"eslint",
|
|
2350
|
+
"typescript",
|
|
2351
|
+
"oxc",
|
|
2352
|
+
"import",
|
|
2353
|
+
"react",
|
|
2354
|
+
"unicorn"
|
|
2355
|
+
],
|
|
2356
|
+
rules: {
|
|
2357
|
+
"@deslop/coding-standards/no-array-wrap-ternary": "error",
|
|
2358
|
+
"@deslop/coding-standards/no-constant-function": "error",
|
|
2359
|
+
"@deslop/coding-standards/no-deep-pipe": "error",
|
|
2360
|
+
"@deslop/coding-standards/no-destructured-parameter": "error",
|
|
2361
|
+
"@deslop/coding-standards/no-double-nullish-check": "error",
|
|
2362
|
+
"@deslop/coding-standards/no-effect-property-arrow": "error",
|
|
2363
|
+
"@deslop/coding-standards/no-error-message-assertion": "error",
|
|
2364
|
+
"@deslop/coding-standards/no-fail-in-generator": "error",
|
|
2365
|
+
"@deslop/coding-standards/no-fake-ref-state": "error",
|
|
2366
|
+
"@deslop/coding-standards/no-hand-written-guard": "error",
|
|
2367
|
+
"@deslop/coding-standards/no-let": "error",
|
|
2368
|
+
"@deslop/coding-standards/no-native-emptiness-check": "error",
|
|
2369
|
+
"@deslop/coding-standards/no-native-method-call": "error",
|
|
2370
|
+
"@deslop/coding-standards/no-option-value-access": "error",
|
|
2371
|
+
"@deslop/coding-standards/no-readonly-type-syntax": "error",
|
|
2372
|
+
"@deslop/coding-standards/no-redundant-return-type": "error",
|
|
2373
|
+
"@deslop/coding-standards/no-redundant-state-type": "error",
|
|
2374
|
+
"@deslop/coding-standards/no-redundant-use-ref-null-type": "error",
|
|
2375
|
+
"@deslop/coding-standards/no-redundant-variable-annotation": "error",
|
|
2376
|
+
"@deslop/coding-standards/no-reinvented-schema": "error",
|
|
2377
|
+
"@deslop/coding-standards/no-renamed-import": "error",
|
|
2378
|
+
"@deslop/coding-standards/no-stored-schema-operation": "error",
|
|
2379
|
+
"@deslop/coding-standards/no-switch-statement": "error",
|
|
2380
|
+
"@deslop/coding-standards/no-trivial-indirection": "error",
|
|
2381
|
+
"@deslop/coding-standards/no-typeof": "error",
|
|
2382
|
+
"@deslop/coding-standards/no-undestructured-use-state": "error",
|
|
2383
|
+
"@deslop/coding-standards/no-unexplained-disable": "error",
|
|
2384
|
+
"@deslop/coding-standards/no-unvalidated-json-decode": "error",
|
|
2385
|
+
"@deslop/coding-standards/schema-type-pair": "error",
|
|
2386
|
+
"effecttsgo/abort-controller-in-effect": "error",
|
|
2387
|
+
"effecttsgo/crypto-random-uuid": "error",
|
|
2388
|
+
"effecttsgo/crypto-random-uuid-in-effect": "error",
|
|
2389
|
+
"effecttsgo/global-console": "error",
|
|
2390
|
+
"effecttsgo/global-console-in-effect": "error",
|
|
2391
|
+
"effecttsgo/global-date": "error",
|
|
2392
|
+
"effecttsgo/global-date-in-effect": "error",
|
|
2393
|
+
"effecttsgo/global-fetch": "error",
|
|
2394
|
+
"effecttsgo/global-fetch-in-effect": "error",
|
|
2395
|
+
"effecttsgo/global-random": "error",
|
|
2396
|
+
"effecttsgo/global-random-in-effect": "error",
|
|
2397
|
+
"effecttsgo/global-timers": "error",
|
|
2398
|
+
"effecttsgo/global-timers-in-effect": "error",
|
|
2399
|
+
"effecttsgo/node-builtin-import": "error",
|
|
2400
|
+
"effecttsgo/prefer-schema-over-json": "error",
|
|
2401
|
+
"effecttsgo/process-env": "error",
|
|
2402
|
+
"effecttsgo/process-env-in-effect": "error",
|
|
2403
|
+
"effecttsgo/acquire-release-disposable": "error",
|
|
2404
|
+
"effecttsgo/all-of-map-to-for-each": "error",
|
|
2405
|
+
"effecttsgo/async-function": "error",
|
|
2406
|
+
"effecttsgo/catch-if-tag-to-catch-tag": "error",
|
|
2407
|
+
"effecttsgo/catch-refail-to-tap-error": "error",
|
|
2408
|
+
"effecttsgo/duplicate-package": "error",
|
|
2409
|
+
"effecttsgo/effect-do-notation": "error",
|
|
2410
|
+
"effecttsgo/effect-fn-iife": "error",
|
|
2411
|
+
"effecttsgo/effect-fn-implicit-any": "error",
|
|
2412
|
+
"effecttsgo/effect-fn-opportunity": "error",
|
|
2413
|
+
"effecttsgo/effect-gen-uses-adapter": "error",
|
|
2414
|
+
"effecttsgo/effect-in-failure": "error",
|
|
2415
|
+
"effecttsgo/effect-in-void-success": "error",
|
|
2416
|
+
"effecttsgo/effect-map-flatten": "error",
|
|
2417
|
+
"effecttsgo/effect-map-void": "error",
|
|
2418
|
+
"effecttsgo/effect-succeed-with-void": "error",
|
|
2419
|
+
"effecttsgo/flat-map-ignored-param-to-and-then": "error",
|
|
2420
|
+
"effecttsgo/flat-map-to-map": "error",
|
|
2421
|
+
"effecttsgo/floating-effect": "error",
|
|
2422
|
+
"effecttsgo/floating-effect-in-vitest": "error",
|
|
2423
|
+
"effecttsgo/layer-merge-all-with-dependencies": "error",
|
|
2424
|
+
"effecttsgo/lazy-effect": "error",
|
|
2425
|
+
"effecttsgo/lazy-promise-in-effect-sync": "error",
|
|
2426
|
+
"effecttsgo/leaking-requirements": "error",
|
|
2427
|
+
"effecttsgo/map-some-to-as-some": "error",
|
|
2428
|
+
"effecttsgo/match-effect-to-map-both": "error",
|
|
2429
|
+
"effecttsgo/match-effect-to-match": "error",
|
|
2430
|
+
"effecttsgo/missing-effect-context": "error",
|
|
2431
|
+
"effecttsgo/missing-layer-context": "error",
|
|
2432
|
+
"effecttsgo/missing-return-yield-star": "error",
|
|
2433
|
+
"effecttsgo/missing-star-in-yield-effect-gen": "error",
|
|
2434
|
+
"effecttsgo/multiple-effect-provide": "error",
|
|
2435
|
+
"effecttsgo/nested-effect-gen-yield": "error",
|
|
2436
|
+
"effecttsgo/option-match-to-from-option": "error",
|
|
2437
|
+
"effecttsgo/outdated-api": "error",
|
|
2438
|
+
"effecttsgo/prefer-succeed-some-or-none": "error",
|
|
2439
|
+
"effecttsgo/promise-in-effect-success": "error",
|
|
2440
|
+
"effecttsgo/provide-layer-succeed-to-provide-service": "error",
|
|
2441
|
+
"effecttsgo/race-first-with-sleep-to-timeout": "error",
|
|
2442
|
+
"effecttsgo/return-effect-in-gen": "error",
|
|
2443
|
+
"effecttsgo/run-effect-inside-effect": "error",
|
|
2444
|
+
"effecttsgo/run-of-exit-to-run-exit": "error",
|
|
2445
|
+
"effecttsgo/strict-effect-provide": "error",
|
|
2446
|
+
"effecttsgo/sync-to-succeed": "error",
|
|
2447
|
+
"effecttsgo/try-catch-in-effect-gen": "error",
|
|
2448
|
+
"effecttsgo/unnecessary-effect-gen": "error",
|
|
2449
|
+
"effecttsgo/unnecessary-fail-yieldable-error": "error",
|
|
2450
|
+
"effecttsgo/unnecessary-pipe": "error",
|
|
2451
|
+
"effecttsgo/unnecessary-pipe-chain": "error",
|
|
2452
|
+
"effecttsgo/unsafe-effect-type-assertion": "error",
|
|
2453
|
+
"effecttsgo/any-unknown-in-error-context": "error",
|
|
2454
|
+
"effecttsgo/catch-all-tag-dispatch-to-catch-tag": "error",
|
|
2455
|
+
"effecttsgo/catch-all-to-map-error": "error",
|
|
2456
|
+
"effecttsgo/catch-chain-to-first-success-of": "error",
|
|
2457
|
+
"effecttsgo/catch-conditional-refail-to-catch-if": "error",
|
|
2458
|
+
"effecttsgo/catch-die-to-or-die": "error",
|
|
2459
|
+
"effecttsgo/catch-tag-to-catch-reason": "error",
|
|
2460
|
+
"effecttsgo/catch-to-ignore": "error",
|
|
2461
|
+
"effecttsgo/catch-to-or-else-succeed": "error",
|
|
2462
|
+
"effecttsgo/catch-unfailable-effect": "error",
|
|
2463
|
+
"effecttsgo/flat-map-conditional-to-filter-or-fail": "error",
|
|
2464
|
+
"effecttsgo/global-error-in-effect-catch": "error",
|
|
2465
|
+
"effecttsgo/global-error-in-effect-failure": "error",
|
|
2466
|
+
"effecttsgo/missing-effect-error": "error",
|
|
2467
|
+
"effecttsgo/multiple-catch-tag": "error",
|
|
2468
|
+
"effecttsgo/redundant-map-error": "error",
|
|
2469
|
+
"effecttsgo/redundant-or-die": "error",
|
|
2470
|
+
"effecttsgo/timeout-catch-tag-to-timeout-or-else": "error",
|
|
2471
|
+
"effecttsgo/unknown-in-effect-catch": "error",
|
|
2472
|
+
"effecttsgo/class-self-mismatch": "error",
|
|
2473
|
+
"effecttsgo/deterministic-keys": "error",
|
|
2474
|
+
"effecttsgo/generic-effect-services": "error",
|
|
2475
|
+
"effecttsgo/instance-of-schema": "error",
|
|
2476
|
+
"effecttsgo/new-schema-class": "error",
|
|
2477
|
+
"effecttsgo/obsolete-match-import": "error",
|
|
2478
|
+
"effecttsgo/obsolete-schema-import": "error",
|
|
2479
|
+
"effecttsgo/overridden-schema-constructor": "error",
|
|
2480
|
+
"effecttsgo/prefer-schema-type-property": "error",
|
|
2481
|
+
"effecttsgo/prefer-typed-schema-decoder": "error",
|
|
2482
|
+
"effecttsgo/redundant-schema-tag-identifier": "error",
|
|
2483
|
+
"effecttsgo/schema-literal-non-finite": "error",
|
|
2484
|
+
"effecttsgo/schema-number": "error",
|
|
2485
|
+
"effecttsgo/schema-struct-with-tag": "error",
|
|
2486
|
+
"effecttsgo/schema-sync-in-effect": "error",
|
|
2487
|
+
"effecttsgo/service-not-as-class": "error",
|
|
2488
|
+
"effecttsgo/unnecessary-typeof-type": "error",
|
|
2489
|
+
"typescript/consistent-generic-constructors": "error",
|
|
2490
|
+
"typescript/consistent-indexed-object-style": "error",
|
|
2491
|
+
"typescript/consistent-type-assertions": ["error", {
|
|
2492
|
+
arrayLiteralTypeAssertions: "never",
|
|
2493
|
+
assertionStyle: "never",
|
|
2494
|
+
objectLiteralTypeAssertions: "never"
|
|
2495
|
+
}],
|
|
2496
|
+
"typescript/consistent-type-definitions": ["error", "type"],
|
|
2497
|
+
"typescript/consistent-type-exports": "error",
|
|
2498
|
+
"typescript/consistent-type-imports": ["error", {
|
|
2499
|
+
fixStyle: "separate-type-imports",
|
|
2500
|
+
prefer: "type-imports"
|
|
2501
|
+
}],
|
|
2502
|
+
"typescript/method-signature-style": "error",
|
|
2503
|
+
"typescript/prefer-function-type": "error",
|
|
2504
|
+
"typescript/await-thenable": "error",
|
|
2505
|
+
"typescript/no-array-delete": "error",
|
|
2506
|
+
"typescript/no-base-to-string": "error",
|
|
2507
|
+
"typescript/no-confusing-void-expression": ["error", { ignoreVoidReturningFunctions: true }],
|
|
2508
|
+
"typescript/no-deprecated": "error",
|
|
2509
|
+
"typescript/no-duplicate-type-constituents": "error",
|
|
2510
|
+
"typescript/no-dynamic-delete": "error",
|
|
2511
|
+
"typescript/no-empty-object-type": "error",
|
|
2512
|
+
"typescript/no-explicit-any": "error",
|
|
2513
|
+
"typescript/no-floating-promises": "error",
|
|
2514
|
+
"typescript/no-import-type-side-effects": "error",
|
|
2515
|
+
"typescript/no-inferrable-types": "error",
|
|
2516
|
+
"typescript/no-invalid-void-type": "error",
|
|
2517
|
+
"typescript/no-misused-promises": ["error", { checksVoidReturn: false }],
|
|
2518
|
+
"typescript/no-misused-spread": "error",
|
|
2519
|
+
"typescript/no-namespace": ["error", { allowDeclarations: true }],
|
|
2520
|
+
"typescript/no-non-null-assertion": "error",
|
|
2521
|
+
"typescript/no-redundant-type-constituents": "error",
|
|
2522
|
+
"typescript/no-require-imports": "error",
|
|
2523
|
+
"typescript/no-this-alias": "error",
|
|
2524
|
+
"typescript/no-unnecessary-boolean-literal-compare": "error",
|
|
2525
|
+
"typescript/no-unnecessary-condition": "error",
|
|
2526
|
+
"typescript/no-unnecessary-qualifier": "error",
|
|
2527
|
+
"typescript/no-unnecessary-template-expression": "error",
|
|
2528
|
+
"typescript/no-unnecessary-type-arguments": "error",
|
|
2529
|
+
"typescript/no-unnecessary-type-constraint": "error",
|
|
2530
|
+
"typescript/no-unnecessary-type-conversion": "error",
|
|
2531
|
+
"typescript/no-unnecessary-type-parameters": "error",
|
|
2532
|
+
"typescript/no-unsafe-argument": "error",
|
|
2533
|
+
"typescript/no-unsafe-assignment": "error",
|
|
2534
|
+
"typescript/no-unsafe-call": "error",
|
|
2535
|
+
"typescript/no-unsafe-declaration-merging": "error",
|
|
2536
|
+
"typescript/no-unsafe-function-type": "error",
|
|
2537
|
+
"typescript/no-unsafe-member-access": "error",
|
|
2538
|
+
"typescript/no-unsafe-return": "error",
|
|
2539
|
+
"typescript/no-useless-empty-export": "error",
|
|
2540
|
+
"typescript/no-wrapper-object-types": "error",
|
|
2541
|
+
"typescript/unbound-method": "error",
|
|
2542
|
+
"typescript/unified-signatures": "error",
|
|
2543
|
+
"typescript/prefer-as-const": "error",
|
|
2544
|
+
"typescript/prefer-nullish-coalescing": "error",
|
|
2545
|
+
"typescript/prefer-optional-chain": "error",
|
|
2546
|
+
"typescript/restrict-plus-operands": "error",
|
|
2547
|
+
"typescript/restrict-template-expressions": "error",
|
|
2548
|
+
"typescript/strict-boolean-expressions": ["error", {
|
|
2549
|
+
allowNumber: false,
|
|
2550
|
+
allowString: false
|
|
2551
|
+
}],
|
|
2552
|
+
"typescript/strict-void-return": "error",
|
|
2553
|
+
"arrow-body-style": ["error", "as-needed"],
|
|
2554
|
+
curly: [
|
|
2555
|
+
"error",
|
|
2556
|
+
"multi-line",
|
|
2557
|
+
"consistent"
|
|
2558
|
+
],
|
|
2559
|
+
eqeqeq: "error",
|
|
2560
|
+
"func-names": [
|
|
2561
|
+
"error",
|
|
2562
|
+
"as-needed",
|
|
2563
|
+
{ generators: "never" }
|
|
2564
|
+
],
|
|
2565
|
+
"func-style": ["error", "declaration"],
|
|
2566
|
+
"import/newline-after-import": "error",
|
|
2567
|
+
"import/no-absolute-path": "error",
|
|
2568
|
+
"import/no-commonjs": "error",
|
|
2569
|
+
"import/no-default-export": "error",
|
|
2570
|
+
"import/no-duplicates": "error",
|
|
2571
|
+
"import/no-empty-named-blocks": "error",
|
|
2572
|
+
"import/no-mutable-exports": "error",
|
|
2573
|
+
"import/no-namespace": ["error", { ignore: ["[!#./]*", "[!#./]*/**"] }],
|
|
2574
|
+
"import/no-relative-parent-imports": "error",
|
|
2575
|
+
"no-restricted-imports": ["error", {
|
|
2576
|
+
paths: [
|
|
2577
|
+
{
|
|
2578
|
+
importNames: reactLegacyApis,
|
|
2579
|
+
message: reactLegacyMessage,
|
|
2580
|
+
name: "react"
|
|
2581
|
+
},
|
|
2582
|
+
{
|
|
2583
|
+
importNames: ["vi"],
|
|
2584
|
+
message: "A Layer is the seam; never use vi.",
|
|
2585
|
+
name: "vitest"
|
|
2586
|
+
},
|
|
2587
|
+
{
|
|
2588
|
+
importNames: ["vi"],
|
|
2589
|
+
message: "A Layer is the seam; never use vi.",
|
|
2590
|
+
name: "@effect/vitest"
|
|
2591
|
+
},
|
|
2592
|
+
{
|
|
2593
|
+
importNames: ["expect"],
|
|
2594
|
+
message: "Use assert from @effect/vitest.",
|
|
2595
|
+
name: "vitest"
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
importNames: ["expect"],
|
|
2599
|
+
message: "Use assert from @effect/vitest.",
|
|
2600
|
+
name: "@effect/vitest"
|
|
2601
|
+
}
|
|
2602
|
+
],
|
|
2603
|
+
patterns: [
|
|
2604
|
+
{
|
|
2605
|
+
message: "Use public package exports.",
|
|
2606
|
+
regex: "^@[^/]+/[^/]+/(?:src|lib)(?:/|$)"
|
|
2607
|
+
},
|
|
2608
|
+
{
|
|
2609
|
+
message: "Use glob from the FileSystem service.",
|
|
2610
|
+
regex: "^glob(?:/|$)"
|
|
2611
|
+
},
|
|
2612
|
+
{
|
|
2613
|
+
message: "Use effect/cli.",
|
|
2614
|
+
regex: "^(?:commander|yargs)(?:/|$)"
|
|
2615
|
+
},
|
|
2616
|
+
{
|
|
2617
|
+
message: "Use randomUUIDv4 from the Crypto service.",
|
|
2618
|
+
regex: "^uuid(?:/|$)"
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
message: "Use HttpClient.",
|
|
2622
|
+
regex: "^(?:axios|node-fetch)(?:/|$)"
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
message: "Use Config.",
|
|
2626
|
+
regex: "^dotenv(?:/|$)"
|
|
2627
|
+
},
|
|
2628
|
+
{
|
|
2629
|
+
message: "Use ChildProcess from effect/process.",
|
|
2630
|
+
regex: "^execa(?:/|$)"
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
message: "Use the concurrency option of Effect.all or Effect.forEach.",
|
|
2634
|
+
regex: "^p-limit(?:/|$)"
|
|
2635
|
+
}
|
|
2636
|
+
]
|
|
2637
|
+
}],
|
|
2638
|
+
"max-depth": ["error", 3],
|
|
2639
|
+
"max-nested-callbacks": ["error", 6],
|
|
2640
|
+
"no-cond-assign": "error",
|
|
2641
|
+
"no-continue": "error",
|
|
2642
|
+
"no-control-regex": "error",
|
|
2643
|
+
"no-debugger": "error",
|
|
2644
|
+
"no-else-return": "error",
|
|
2645
|
+
"no-empty-function": ["error", { allow: ["arrowFunctions"] }],
|
|
2646
|
+
"no-eval": "error",
|
|
2647
|
+
"no-extend-native": "error",
|
|
2648
|
+
"no-extra-bind": "error",
|
|
2649
|
+
"no-extra-boolean-cast": "error",
|
|
2650
|
+
"no-implied-eval": "error",
|
|
2651
|
+
"no-invalid-regexp": "error",
|
|
2652
|
+
"no-iterator": "error",
|
|
2653
|
+
"no-lonely-if": "error",
|
|
2654
|
+
"no-misleading-character-class": "error",
|
|
2655
|
+
"no-multi-assign": "error",
|
|
2656
|
+
"no-negated-condition": "error",
|
|
2657
|
+
"no-nested-ternary": "error",
|
|
2658
|
+
"no-new": "error",
|
|
2659
|
+
"no-param-reassign": ["error", { props: true }],
|
|
2660
|
+
"no-plusplus": "error",
|
|
2661
|
+
"no-proto": "error",
|
|
2662
|
+
"no-restricted-exports": ["error", { restrictedNamedExportsPattern: "Live$" }],
|
|
2663
|
+
"no-restricted-globals": [
|
|
2664
|
+
"error",
|
|
2665
|
+
{
|
|
2666
|
+
message: "Interrupt through Effect: a fiber, Effect.timeout, or a Scope.",
|
|
2667
|
+
name: "AbortController"
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
message: "Import Array from 'effect'.",
|
|
2671
|
+
name: "Array"
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
message: "Import Boolean from 'effect'.",
|
|
2675
|
+
name: "Boolean"
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
message: "Define a Schema.TaggedError and fail with it.",
|
|
2679
|
+
name: "Error"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
message: mapMessage,
|
|
2683
|
+
name: "Map"
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
message: "Import Number from 'effect'.",
|
|
2687
|
+
name: "Number"
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
message: "Use Record or Struct from 'effect'.",
|
|
2691
|
+
name: "Object"
|
|
2692
|
+
},
|
|
2693
|
+
{
|
|
2694
|
+
message: "Use Number.parse.",
|
|
2695
|
+
name: "parseFloat"
|
|
2696
|
+
},
|
|
2697
|
+
{
|
|
2698
|
+
message: "Use Number.parse.",
|
|
2699
|
+
name: "parseInt"
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
message: "Use Effect: Effect.tryPromise at the boundary, Effect.all to combine.",
|
|
2703
|
+
name: "Promise"
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
message: "Use Predicate or Record from 'effect'.",
|
|
2707
|
+
name: "Reflect"
|
|
2708
|
+
},
|
|
2709
|
+
{
|
|
2710
|
+
message: setMessage,
|
|
2711
|
+
name: "Set"
|
|
2712
|
+
},
|
|
2713
|
+
{
|
|
2714
|
+
message: "Import String from 'effect'.",
|
|
2715
|
+
name: "String"
|
|
2716
|
+
},
|
|
2717
|
+
{
|
|
2718
|
+
message: "Read configuration with Config and services from the context.",
|
|
2719
|
+
name: "global"
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
message: "Read configuration with Config and services from the context.",
|
|
2723
|
+
name: "globalThis"
|
|
2724
|
+
}
|
|
2725
|
+
],
|
|
2726
|
+
"no-restricted-properties": [
|
|
2727
|
+
"error",
|
|
2728
|
+
{
|
|
2729
|
+
message: "Use standalone pipe.",
|
|
2730
|
+
property: "pipe"
|
|
2731
|
+
},
|
|
2732
|
+
{
|
|
2733
|
+
allowObjects: ["Predicate"],
|
|
2734
|
+
message: "Use Predicate.hasProperty.",
|
|
2735
|
+
property: "hasOwnProperty"
|
|
2736
|
+
},
|
|
2737
|
+
{
|
|
2738
|
+
message: runMessage,
|
|
2739
|
+
object: "Effect",
|
|
2740
|
+
property: "runFork"
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
message: runMessage,
|
|
2744
|
+
object: "Effect",
|
|
2745
|
+
property: "runPromise"
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
message: runMessage,
|
|
2749
|
+
object: "Effect",
|
|
2750
|
+
property: "runPromiseExit"
|
|
2751
|
+
},
|
|
2752
|
+
{
|
|
2753
|
+
message: runMessage,
|
|
2754
|
+
object: "Effect",
|
|
2755
|
+
property: "runSync"
|
|
2756
|
+
},
|
|
2757
|
+
{
|
|
2758
|
+
message: runMessage,
|
|
2759
|
+
object: "Effect",
|
|
2760
|
+
property: "runSyncExit"
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
message: runMessage,
|
|
2764
|
+
object: "ManagedRuntime",
|
|
2765
|
+
property: "make"
|
|
2766
|
+
},
|
|
2767
|
+
{
|
|
2768
|
+
message: "Use Number.max.",
|
|
2769
|
+
object: "Math",
|
|
2770
|
+
property: "max"
|
|
2771
|
+
},
|
|
2772
|
+
{
|
|
2773
|
+
message: "Use Number.min.",
|
|
2774
|
+
object: "Math",
|
|
2775
|
+
property: "min"
|
|
2776
|
+
},
|
|
2777
|
+
{
|
|
2778
|
+
message: "Use Number.round.",
|
|
2779
|
+
object: "Math",
|
|
2780
|
+
property: "round"
|
|
2781
|
+
},
|
|
2782
|
+
...map$1(reactLegacyApis, (property) => ({
|
|
2783
|
+
message: reactLegacyMessage,
|
|
2784
|
+
object: "React",
|
|
2785
|
+
property
|
|
2786
|
+
})),
|
|
2787
|
+
{
|
|
2788
|
+
message: "Use Schema.Struct.",
|
|
2789
|
+
object: "Schema",
|
|
2790
|
+
property: "Class"
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
message: "Use a branded schema.",
|
|
2794
|
+
object: "Schema",
|
|
2795
|
+
property: "Opaque"
|
|
2796
|
+
},
|
|
2797
|
+
{
|
|
2798
|
+
message: "Use Schema.Struct.",
|
|
2799
|
+
object: "Schema",
|
|
2800
|
+
property: "TaggedClass"
|
|
2801
|
+
},
|
|
2802
|
+
{
|
|
2803
|
+
message: "Use Schema.Struct.",
|
|
2804
|
+
object: "Data",
|
|
2805
|
+
property: "Class"
|
|
2806
|
+
},
|
|
2807
|
+
{
|
|
2808
|
+
message: "Use Schema.TaggedError.",
|
|
2809
|
+
object: "Data",
|
|
2810
|
+
property: "Error"
|
|
2811
|
+
},
|
|
2812
|
+
{
|
|
2813
|
+
message: "Use Schema.Struct.",
|
|
2814
|
+
object: "Data",
|
|
2815
|
+
property: "TaggedClass"
|
|
2816
|
+
},
|
|
2817
|
+
{
|
|
2818
|
+
message: "Use Schema.TaggedError.",
|
|
2819
|
+
object: "Data",
|
|
2820
|
+
property: "TaggedError"
|
|
2821
|
+
},
|
|
2822
|
+
...map$1([
|
|
2823
|
+
"addEventListener",
|
|
2824
|
+
"atob",
|
|
2825
|
+
"btoa",
|
|
2826
|
+
"cancelAnimationFrame",
|
|
2827
|
+
"cancelIdleCallback",
|
|
2828
|
+
"dispatchEvent",
|
|
2829
|
+
"localStorage",
|
|
2830
|
+
"navigator",
|
|
2831
|
+
"queueMicrotask",
|
|
2832
|
+
"removeEventListener",
|
|
2833
|
+
"requestAnimationFrame",
|
|
2834
|
+
"requestIdleCallback",
|
|
2835
|
+
"sessionStorage",
|
|
2836
|
+
"structuredClone"
|
|
2837
|
+
], (property) => ({
|
|
2838
|
+
message: `Use the bare ${property} global.`,
|
|
2839
|
+
object: "window",
|
|
2840
|
+
property
|
|
2841
|
+
})),
|
|
2842
|
+
...map$1([
|
|
2843
|
+
"clearInterval",
|
|
2844
|
+
"clearTimeout",
|
|
2845
|
+
"setInterval",
|
|
2846
|
+
"setTimeout"
|
|
2847
|
+
], (property) => ({
|
|
2848
|
+
message: "Use Effect.sleep or Schedule.",
|
|
2849
|
+
object: "window",
|
|
2850
|
+
property
|
|
2851
|
+
})),
|
|
2852
|
+
{
|
|
2853
|
+
message: "Use Console.",
|
|
2854
|
+
object: "window",
|
|
2855
|
+
property: "console"
|
|
2856
|
+
},
|
|
2857
|
+
{
|
|
2858
|
+
message: "Use the Crypto service.",
|
|
2859
|
+
object: "window",
|
|
2860
|
+
property: "crypto"
|
|
2861
|
+
},
|
|
2862
|
+
{
|
|
2863
|
+
message: "Use HttpClient.",
|
|
2864
|
+
object: "window",
|
|
2865
|
+
property: "fetch"
|
|
2866
|
+
},
|
|
2867
|
+
{
|
|
2868
|
+
message: "Use Clock.",
|
|
2869
|
+
object: "window",
|
|
2870
|
+
property: "performance"
|
|
2871
|
+
}
|
|
2872
|
+
],
|
|
2873
|
+
"no-self-assign": "error",
|
|
2874
|
+
"no-shadow": ["error", { allow: [
|
|
2875
|
+
"Array",
|
|
2876
|
+
"Boolean",
|
|
2877
|
+
"Console",
|
|
2878
|
+
"Effect",
|
|
2879
|
+
"HashMap",
|
|
2880
|
+
"Number",
|
|
2881
|
+
"Option",
|
|
2882
|
+
"Schema",
|
|
2883
|
+
"String"
|
|
2884
|
+
] }],
|
|
2885
|
+
"no-sparse-arrays": "error",
|
|
2886
|
+
"no-throw-literal": "error",
|
|
2887
|
+
"no-unmodified-loop-condition": "error",
|
|
2888
|
+
"no-unneeded-ternary": "error",
|
|
2889
|
+
"no-unreachable-loop": "error",
|
|
2890
|
+
"no-unsafe-finally": "error",
|
|
2891
|
+
"no-unused-expressions": "error",
|
|
2892
|
+
"no-useless-assignment": "error",
|
|
2893
|
+
"no-useless-backreference": "error",
|
|
2894
|
+
"no-useless-call": "error",
|
|
2895
|
+
"no-useless-catch": "error",
|
|
2896
|
+
"no-useless-computed-key": "error",
|
|
2897
|
+
"no-useless-concat": "error",
|
|
2898
|
+
"no-useless-constructor": "error",
|
|
2899
|
+
"no-useless-escape": "error",
|
|
2900
|
+
"no-useless-rename": "error",
|
|
2901
|
+
"no-useless-return": "error",
|
|
2902
|
+
"no-void": "error",
|
|
2903
|
+
"object-shorthand": "error",
|
|
2904
|
+
"oxc/branches-sharing-code": "error",
|
|
2905
|
+
"oxc/no-accumulating-spread": "error",
|
|
2906
|
+
"oxc/no-map-spread": "error",
|
|
2907
|
+
"oxc/only-used-in-recursion": "error",
|
|
2908
|
+
"prefer-arrow-callback": "error",
|
|
2909
|
+
"prefer-const": ["error", { destructuring: "all" }],
|
|
2910
|
+
"prefer-template": "error",
|
|
2911
|
+
"require-unicode-regexp": "error",
|
|
2912
|
+
"react-doctor/no-call-component-as-function": "error",
|
|
2913
|
+
"react-doctor/no-create-context-in-render": "error",
|
|
2914
|
+
"react-doctor/no-default-props": "error",
|
|
2915
|
+
"react-doctor/no-inline-exhaustive-style": "error",
|
|
2916
|
+
"react-doctor/no-jsx-element-type": "error",
|
|
2917
|
+
"react-doctor/no-many-boolean-props": "error",
|
|
2918
|
+
"react-doctor/no-prop-types": "error",
|
|
2919
|
+
"react-doctor/no-uncontrolled-input": "error",
|
|
2920
|
+
"react-doctor/shadcn-icon-button-requires-label": "error",
|
|
2921
|
+
"react-doctor/effect-listener-cleanup-mismatch": "error",
|
|
2922
|
+
"react-doctor/no-async-effect-callback": "error",
|
|
2923
|
+
"react-doctor/no-create-object-url-without-revoke": "error",
|
|
2924
|
+
"react-doctor/no-derived-useState": "error",
|
|
2925
|
+
"react-doctor/no-effect-event-in-deps": "error",
|
|
2926
|
+
"react-doctor/no-effect-with-fresh-deps": "error",
|
|
2927
|
+
"react-doctor/no-effect-wrapper-discards-callback-cleanup-return": "error",
|
|
2928
|
+
"react-doctor/no-impure-state-updater": "error",
|
|
2929
|
+
"react-doctor/no-mutable-in-deps": "error",
|
|
2930
|
+
"react-doctor/no-mutating-reducer-state": "error",
|
|
2931
|
+
"react-doctor/no-self-updating-effect": "error",
|
|
2932
|
+
"react-doctor/no-set-state-in-render": "error",
|
|
2933
|
+
"react-doctor/no-stale-timer-ref": "error",
|
|
2934
|
+
"react-doctor/prefer-use-effect-event": "error",
|
|
2935
|
+
"react-doctor/rerender-functional-setstate": "error",
|
|
2936
|
+
"react-doctor/rerender-lazy-ref-init": "error",
|
|
2937
|
+
"react-doctor/rerender-lazy-state-init": "error",
|
|
2938
|
+
"react-doctor/no-enter-submit-without-ime-composition-guard": "error",
|
|
2939
|
+
"react-doctor/no-event-handler": "error",
|
|
2940
|
+
"react-doctor/no-flush-sync": "error",
|
|
2941
|
+
"react-doctor/no-render-in-render": "error",
|
|
2942
|
+
"react-doctor/rendering-conditional-render": "error",
|
|
2943
|
+
"react-doctor/design-no-redundant-padding-axes": "error",
|
|
2944
|
+
"react-doctor/design-no-redundant-size-axes": "error",
|
|
2945
|
+
"react-doctor/no-global-css-variable-animation": "error",
|
|
2946
|
+
"react-doctor/no-gray-on-colored-background": "error",
|
|
2947
|
+
"react-doctor/no-inline-bounce-easing": "error",
|
|
2948
|
+
"react-doctor/no-large-animated-blur": "error",
|
|
2949
|
+
"react-doctor/no-layout-property-animation": "error",
|
|
2950
|
+
"react-doctor/no-layout-transition-inline": "error",
|
|
2951
|
+
"react-doctor/no-long-transition-duration": "error",
|
|
2952
|
+
"react-doctor/no-outline-none": "error",
|
|
2953
|
+
"react-doctor/no-scale-from-zero": "error",
|
|
2954
|
+
"react-doctor/no-transition-all": "error",
|
|
2955
|
+
"react/button-has-type": "error",
|
|
2956
|
+
"react/checked-requires-onchange-or-readonly": "error",
|
|
2957
|
+
"react/exhaustive-deps": "error",
|
|
2958
|
+
"react/iframe-missing-sandbox": "error",
|
|
2959
|
+
"react/immutability": "error",
|
|
2960
|
+
"react/incompatible-library": "error",
|
|
2961
|
+
"react/jsx-boolean-value": ["error", "never"],
|
|
2962
|
+
"react/jsx-curly-brace-presence": ["error", {
|
|
2963
|
+
children: "never",
|
|
2964
|
+
propElementValues: "always",
|
|
2965
|
+
props: "never"
|
|
2966
|
+
}],
|
|
2967
|
+
"react/jsx-fragments": ["error", "syntax"],
|
|
2968
|
+
"react/jsx-key": "error",
|
|
2969
|
+
"react/jsx-no-script-url": "error",
|
|
2970
|
+
"react/jsx-no-target-blank": "error",
|
|
2971
|
+
"react/jsx-no-useless-fragment": "error",
|
|
2972
|
+
"react/memo-dependencies": "error",
|
|
2973
|
+
"react/no-array-index-key": "error",
|
|
2974
|
+
"react/no-children-prop": "error",
|
|
2975
|
+
"react/no-clone-element": "error",
|
|
2976
|
+
"react/no-danger": "error",
|
|
2977
|
+
"react/no-object-type-as-default-prop": "error",
|
|
2978
|
+
"react/no-react-children": "error",
|
|
2979
|
+
"react/no-unknown-property": "error",
|
|
2980
|
+
"react/no-unstable-nested-components": ["error", { allowAsProps: true }],
|
|
2981
|
+
"react/preserve-manual-memoization": "error",
|
|
2982
|
+
"react/purity": "error",
|
|
2983
|
+
"react/refs": "error",
|
|
2984
|
+
"react/rules-of-hooks": "error",
|
|
2985
|
+
"react/self-closing-comp": "error",
|
|
2986
|
+
"react/set-state-in-effect": "error",
|
|
2987
|
+
"react/set-state-in-render": "error",
|
|
2988
|
+
"react/static-components": "error",
|
|
2989
|
+
"react/use-memo": "error",
|
|
2990
|
+
"react/void-dom-elements-no-children": "error",
|
|
2991
|
+
"react/void-use-memo": "error",
|
|
2992
|
+
"shadcn/no-arbitrary-values": "error",
|
|
2993
|
+
"shadcn/no-inline-styles": "error",
|
|
2994
|
+
"shadcn/no-raw-colors": "error",
|
|
2995
|
+
"shadcn/no-restyle": ["error", { allow: ["layout"] }],
|
|
2996
|
+
"shadcn/no-unknown-classes": "error",
|
|
2997
|
+
"shadcn/require-static-classes": "error",
|
|
2998
|
+
"sort-keys": [
|
|
2999
|
+
"error",
|
|
3000
|
+
"asc",
|
|
3001
|
+
{
|
|
3002
|
+
allowLineSeparatedGroups: true,
|
|
3003
|
+
natural: true
|
|
3004
|
+
}
|
|
3005
|
+
],
|
|
3006
|
+
"typescript/ban-ts-comment": ["error", { "ts-nocheck": true }],
|
|
3007
|
+
"typescript/no-restricted-types": ["error", { types: {
|
|
3008
|
+
AbortController: "Use Effect interruption.",
|
|
3009
|
+
Array: "Use T[].",
|
|
3010
|
+
Date: "Use DateTime.",
|
|
3011
|
+
Error: "Use Schema.TaggedError.",
|
|
3012
|
+
Iterable: "Use T[].",
|
|
3013
|
+
Map: mapMessage,
|
|
3014
|
+
Promise: "Use Effect.",
|
|
3015
|
+
ReadonlyArray: "Use T[].",
|
|
3016
|
+
ReadonlyMap: mapMessage,
|
|
3017
|
+
ReadonlySet: setMessage,
|
|
3018
|
+
Set: setMessage,
|
|
3019
|
+
undefined: "Use an optional property or parameter, or let inference supply undefined."
|
|
3020
|
+
} }],
|
|
3021
|
+
"typescript/no-unnecessary-type-assertion": "error",
|
|
3022
|
+
"typescript/no-useless-default-assignment": "error",
|
|
3023
|
+
"unicorn/filename-case": "error",
|
|
3024
|
+
"unicorn/no-immediate-mutation": "error",
|
|
3025
|
+
"unicorn/no-null": "error",
|
|
3026
|
+
"unicorn/no-object-as-default-parameter": "error",
|
|
3027
|
+
"unicorn/no-process-exit": "error",
|
|
3028
|
+
"unicorn/no-static-only-class": "error",
|
|
3029
|
+
"unicorn/no-useless-fallback-in-spread": "error",
|
|
3030
|
+
"unicorn/no-useless-length-check": "error",
|
|
3031
|
+
"unicorn/no-useless-spread": "error",
|
|
3032
|
+
"unicorn/prefer-logical-operator-over-ternary": "error",
|
|
3033
|
+
"unicorn/prefer-optional-catch-binding": "error",
|
|
3034
|
+
"use-isnan": "error"
|
|
3035
|
+
},
|
|
3036
|
+
settings: {
|
|
3037
|
+
react: { version: "19.0" },
|
|
3038
|
+
shadcn: { ui: ["@deslop/components/ui", "@dual/ui/components"] }
|
|
3039
|
+
}
|
|
3040
|
+
});
|
|
3041
|
+
var oxlint_default = definePlugin({
|
|
3042
|
+
meta: { name: "@deslop/coding-standards" },
|
|
3043
|
+
rules: {
|
|
3044
|
+
"no-array-wrap-ternary": noArrayWrapTernary,
|
|
3045
|
+
"no-constant-function": noConstantFunction,
|
|
3046
|
+
"no-deep-pipe": noDeepPipe,
|
|
3047
|
+
"no-destructured-parameter": noDestructuredParameter,
|
|
3048
|
+
"no-double-nullish-check": noDoubleNullishCheck,
|
|
3049
|
+
"no-effect-property-arrow": noEffectPropertyArrow,
|
|
3050
|
+
"no-error-message-assertion": noErrorMessageAssertion,
|
|
3051
|
+
"no-fail-in-generator": noFailInGenerator,
|
|
3052
|
+
"no-fake-ref-state": noFakeRefState,
|
|
3053
|
+
"no-hand-written-guard": noHandWrittenGuard,
|
|
3054
|
+
"no-let": noLet,
|
|
3055
|
+
"no-native-emptiness-check": noNativeEmptinessCheck,
|
|
3056
|
+
"no-native-method-call": noNativeMethodCall,
|
|
3057
|
+
"no-option-value-access": noOptionValueAccess,
|
|
3058
|
+
"no-readonly-type-syntax": noReadonlyTypeSyntax,
|
|
3059
|
+
"no-redundant-return-type": noRedundantReturnType,
|
|
3060
|
+
"no-redundant-state-type": noRedundantStateType,
|
|
3061
|
+
"no-redundant-use-ref-null-type": noRedundantUseRefNullType,
|
|
3062
|
+
"no-redundant-variable-annotation": noRedundantVariableAnnotation,
|
|
3063
|
+
"no-reinvented-schema": noReinventedSchema,
|
|
3064
|
+
"no-renamed-import": noRenamedImport,
|
|
3065
|
+
"no-stored-schema-operation": noStoredSchemaOperation,
|
|
3066
|
+
"no-switch-statement": noSwitchStatement,
|
|
3067
|
+
"no-trivial-indirection": noTrivialIndirection,
|
|
3068
|
+
"no-typeof": noTypeof,
|
|
3069
|
+
"no-undestructured-use-state": noUndestructuredUseState,
|
|
3070
|
+
"no-unexplained-disable": noUnexplainedDisable,
|
|
3071
|
+
"no-unvalidated-json-decode": noUnvalidatedJsonDecode,
|
|
3072
|
+
"schema-type-pair": schemaTypePair
|
|
3073
|
+
}
|
|
3074
|
+
});
|
|
3075
|
+
//#endregion
|
|
3076
|
+
export { oxlint_default as default, oxlint };
|