@aas-dev/stack 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +97 -2
- package/bin/aas.js +2 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +15 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +75 -0
- package/dist/cursor-hook.d.ts +1 -0
- package/dist/cursor-hook.js +28 -0
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +162 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +5 -0
- package/dist/explain.d.ts +4 -0
- package/dist/explain.js +48 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/package-script.d.ts +3 -0
- package/dist/package-script.js +42 -0
- package/dist/plugin/aas.js +379 -0
- package/dist/policy.d.ts +1 -0
- package/dist/policy.js +124 -0
- package/dist/program.d.ts +3 -0
- package/dist/program.js +49 -0
- package/dist/run.d.ts +18 -0
- package/dist/run.js +99 -0
- package/dist/scaffold.d.ts +5 -0
- package/dist/scaffold.js +76 -0
- package/dist/templates.d.ts +6 -0
- package/dist/templates.js +37 -0
- package/dist/verify.d.ts +4 -0
- package/dist/verify.js +190 -0
- package/dist/workspace.d.ts +4 -0
- package/dist/workspace.js +76 -0
- package/package.json +54 -3
- package/plugin/aas.d.mts +8 -0
- package/plugin/aas.mjs +572 -0
- package/templates/AGENTS.md +147 -0
- package/templates/hooks.json +11 -0
- package/templates/pnpm-workspace.yaml +23 -0
- package/templates/pre-commit +3 -0
- package/templates/tsconfig.json +8 -0
- package/templates/verify.yml +20 -0
- package/templates/vite.config.ts +3 -0
- package/tsconfig.base.json +71 -0
package/plugin/aas.mjs
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
const markers = [
|
|
2
|
+
"@ts-ignore",
|
|
3
|
+
"@ts-expect-error",
|
|
4
|
+
"@ts-nocheck",
|
|
5
|
+
"eslint-disable",
|
|
6
|
+
"eslint-enable",
|
|
7
|
+
"oxlint-disable",
|
|
8
|
+
"oxlint-enable",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
const errorNames = new Set([
|
|
12
|
+
"Error",
|
|
13
|
+
"AggregateError",
|
|
14
|
+
"EvalError",
|
|
15
|
+
"RangeError",
|
|
16
|
+
"ReferenceError",
|
|
17
|
+
"SyntaxError",
|
|
18
|
+
"TypeError",
|
|
19
|
+
"URIError",
|
|
20
|
+
])
|
|
21
|
+
|
|
22
|
+
const rawSpecifiers = new Set([
|
|
23
|
+
"child_process",
|
|
24
|
+
"fs",
|
|
25
|
+
"http",
|
|
26
|
+
"https",
|
|
27
|
+
"node:child_process",
|
|
28
|
+
"node:fs",
|
|
29
|
+
"node:fs/promises",
|
|
30
|
+
"node:http",
|
|
31
|
+
"node:https",
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
const socketSpecifiers = new Set(["net", "node:net", "node:tls", "tls"])
|
|
35
|
+
|
|
36
|
+
const consoleMethods = new Set(["debug", "error", "info", "log", "warn"])
|
|
37
|
+
|
|
38
|
+
const effectRunMethods = new Set(["runFork", "runPromise", "runPromiseExit", "runSync"])
|
|
39
|
+
|
|
40
|
+
const bannedTestMembers = new Set(["only", "skip", "skipIf", "todo"])
|
|
41
|
+
|
|
42
|
+
const testAliases = new Set(["fdescribe", "fit", "ftest", "xdescribe", "xit", "xtest"])
|
|
43
|
+
|
|
44
|
+
const report = (context, node, message) => {
|
|
45
|
+
context.report({ node, message })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const literalName = (property, computed) => {
|
|
49
|
+
if (!computed && property.type === "Identifier") {
|
|
50
|
+
return property.name
|
|
51
|
+
}
|
|
52
|
+
if (
|
|
53
|
+
computed &&
|
|
54
|
+
(property.type === "Literal" || property.type === "StringLiteral") &&
|
|
55
|
+
typeof property.value === "string"
|
|
56
|
+
) {
|
|
57
|
+
return property.value
|
|
58
|
+
}
|
|
59
|
+
return ""
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isProcessEnv = (node) => {
|
|
63
|
+
if (node.type !== "MemberExpression") {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
return (
|
|
67
|
+
node.object.type === "Identifier" &&
|
|
68
|
+
node.object.name === "process" &&
|
|
69
|
+
literalName(node.property, node.computed) === "env"
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const hasName = (node, name) => {
|
|
74
|
+
if (node === null || node === undefined || typeof node !== "object") {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
if (node.type === "Identifier" && node.name === name) {
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
return (
|
|
81
|
+
hasName(node.callee, name) ||
|
|
82
|
+
hasName(node.object, name) ||
|
|
83
|
+
hasName(node.property, name) ||
|
|
84
|
+
hasName(node.expression, name)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const blessedSuper = (node) => hasName(node, "TaggedError") || hasName(node, "Service")
|
|
89
|
+
|
|
90
|
+
const specifier = (node) => {
|
|
91
|
+
if (node === null || node === undefined) {
|
|
92
|
+
return ""
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
(node.type === "Literal" || node.type === "StringLiteral") &&
|
|
96
|
+
typeof node.value === "string"
|
|
97
|
+
) {
|
|
98
|
+
return node.value
|
|
99
|
+
}
|
|
100
|
+
return ""
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const chainParts = (callee) => {
|
|
104
|
+
const parts = []
|
|
105
|
+
let current = callee
|
|
106
|
+
while (current.type === "MemberExpression" || current.type === "ChainExpression") {
|
|
107
|
+
if (current.type === "ChainExpression") {
|
|
108
|
+
current = current.expression
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
if (current.computed || current.property.type !== "Identifier") {
|
|
112
|
+
return []
|
|
113
|
+
}
|
|
114
|
+
parts.push(current.property.name)
|
|
115
|
+
current = current.object
|
|
116
|
+
}
|
|
117
|
+
if (current.type !== "Identifier") {
|
|
118
|
+
return []
|
|
119
|
+
}
|
|
120
|
+
parts.push(current.name)
|
|
121
|
+
parts.reverse()
|
|
122
|
+
return parts
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const isItEffect = (parts) => {
|
|
126
|
+
if (parts[0] !== "it" || parts[1] !== "effect") {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
let index = 2
|
|
130
|
+
while (index < parts.length) {
|
|
131
|
+
const part = parts[index]
|
|
132
|
+
if (part !== undefined && bannedTestMembers.has(part)) {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
index += 1
|
|
136
|
+
}
|
|
137
|
+
return true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const flagModuleBinding = (context, statement) => {
|
|
141
|
+
if (statement === null || statement === undefined) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
if (statement.type === "VariableDeclaration" && statement.kind !== "const") {
|
|
145
|
+
report(
|
|
146
|
+
context,
|
|
147
|
+
statement,
|
|
148
|
+
"module-scope let and var are banned; keep state in a Context.Service",
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
const binding = statement.init ?? statement.left
|
|
152
|
+
if (
|
|
153
|
+
(statement.type === "ForStatement" ||
|
|
154
|
+
statement.type === "ForInStatement" ||
|
|
155
|
+
statement.type === "ForOfStatement") &&
|
|
156
|
+
binding !== null &&
|
|
157
|
+
binding !== undefined &&
|
|
158
|
+
binding.type === "VariableDeclaration" &&
|
|
159
|
+
binding.kind !== "const"
|
|
160
|
+
) {
|
|
161
|
+
report(context, binding, "module-scope let and var are banned; keep state in a Context.Service")
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const noThrow = {
|
|
166
|
+
meta: { type: "problem" },
|
|
167
|
+
create(context) {
|
|
168
|
+
return {
|
|
169
|
+
ThrowStatement(node) {
|
|
170
|
+
report(context, node, "throw is banned; yield a Schema.TaggedError")
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const noTry = {
|
|
177
|
+
meta: { type: "problem" },
|
|
178
|
+
create(context) {
|
|
179
|
+
return {
|
|
180
|
+
TryStatement(node) {
|
|
181
|
+
report(context, node, "try is banned; failures stay on the Effect channel")
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const noErrorClass = {
|
|
188
|
+
meta: { type: "problem" },
|
|
189
|
+
create(context) {
|
|
190
|
+
return {
|
|
191
|
+
NewExpression(node) {
|
|
192
|
+
const callee = node.callee
|
|
193
|
+
const name =
|
|
194
|
+
callee.type === "Identifier"
|
|
195
|
+
? callee.name
|
|
196
|
+
: callee.type === "MemberExpression"
|
|
197
|
+
? literalName(callee.property, callee.computed)
|
|
198
|
+
: ""
|
|
199
|
+
if (errorNames.has(name)) {
|
|
200
|
+
report(context, node, "new Error is banned; define a Schema.TaggedError")
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const noProcessEnv = {
|
|
208
|
+
meta: { type: "problem" },
|
|
209
|
+
create(context) {
|
|
210
|
+
return {
|
|
211
|
+
MemberExpression(node) {
|
|
212
|
+
if (isProcessEnv(node)) {
|
|
213
|
+
report(
|
|
214
|
+
context,
|
|
215
|
+
node,
|
|
216
|
+
"process.env is banned; declare the variable in .env.schema and read it with Config",
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const noTypeAssertion = {
|
|
225
|
+
meta: { type: "problem" },
|
|
226
|
+
create(context) {
|
|
227
|
+
return {
|
|
228
|
+
TSAsExpression(node) {
|
|
229
|
+
report(
|
|
230
|
+
context,
|
|
231
|
+
node,
|
|
232
|
+
"type assertions are banned, including as const; parse with Schema or use a const type parameter",
|
|
233
|
+
)
|
|
234
|
+
},
|
|
235
|
+
TSTypeAssertion(node) {
|
|
236
|
+
report(
|
|
237
|
+
context,
|
|
238
|
+
node,
|
|
239
|
+
"angle-bracket assertions are banned; parse with Schema or use a const type parameter",
|
|
240
|
+
)
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const noExplicitAny = {
|
|
247
|
+
meta: { type: "problem" },
|
|
248
|
+
create(context) {
|
|
249
|
+
return {
|
|
250
|
+
TSAnyKeyword(node) {
|
|
251
|
+
report(context, node, "any is banned; use unknown and decode it with Schema")
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const noNonNullAssertion = {
|
|
258
|
+
meta: { type: "problem" },
|
|
259
|
+
create(context) {
|
|
260
|
+
return {
|
|
261
|
+
TSNonNullExpression(node) {
|
|
262
|
+
report(
|
|
263
|
+
context,
|
|
264
|
+
node,
|
|
265
|
+
"non-null assertions are banned; narrow the value or decode it with Schema",
|
|
266
|
+
)
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const textOf = (context) => {
|
|
273
|
+
const source = context.sourceCode
|
|
274
|
+
if (typeof source.text === "string") {
|
|
275
|
+
return source.text
|
|
276
|
+
}
|
|
277
|
+
if (typeof source.getText === "function") {
|
|
278
|
+
return source.getText()
|
|
279
|
+
}
|
|
280
|
+
return ""
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const commentStart = (comment) => {
|
|
284
|
+
const range = comment.range
|
|
285
|
+
if (Array.isArray(range)) {
|
|
286
|
+
const start = range[0]
|
|
287
|
+
return typeof start === "number" ? start : -1
|
|
288
|
+
}
|
|
289
|
+
if (range !== null && range !== undefined && typeof range.start === "number") {
|
|
290
|
+
return range.start
|
|
291
|
+
}
|
|
292
|
+
if (typeof comment.start === "number") {
|
|
293
|
+
return comment.start
|
|
294
|
+
}
|
|
295
|
+
return -1
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const isShebang = (comment, text) => {
|
|
299
|
+
if (comment.type === "Shebang" || comment.type === "Hashbang") {
|
|
300
|
+
return true
|
|
301
|
+
}
|
|
302
|
+
return text.startsWith("#!") && commentStart(comment) === 0
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const noComments = {
|
|
306
|
+
meta: { type: "problem" },
|
|
307
|
+
create(context) {
|
|
308
|
+
const text = textOf(context)
|
|
309
|
+
return {
|
|
310
|
+
Program(node) {
|
|
311
|
+
for (const comment of context.sourceCode.getAllComments()) {
|
|
312
|
+
if (isShebang(comment, text)) {
|
|
313
|
+
continue
|
|
314
|
+
}
|
|
315
|
+
const message = "comments are banned; use clearer names or docs"
|
|
316
|
+
if (comment.loc === undefined || comment.loc === null) {
|
|
317
|
+
report(context, node, message)
|
|
318
|
+
} else {
|
|
319
|
+
context.report({ loc: comment.loc, message })
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const noBypassComment = {
|
|
328
|
+
meta: { type: "problem" },
|
|
329
|
+
create(context) {
|
|
330
|
+
return {
|
|
331
|
+
Program(node) {
|
|
332
|
+
for (const comment of context.sourceCode.getAllComments()) {
|
|
333
|
+
let matched = ""
|
|
334
|
+
for (const marker of markers) {
|
|
335
|
+
if (comment.value.includes(marker)) {
|
|
336
|
+
matched = marker
|
|
337
|
+
break
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (matched === "") {
|
|
341
|
+
continue
|
|
342
|
+
}
|
|
343
|
+
const message = `directive comments are banned (${matched})`
|
|
344
|
+
if (comment.loc === undefined || comment.loc === null) {
|
|
345
|
+
report(context, node, message)
|
|
346
|
+
} else {
|
|
347
|
+
context.report({ loc: comment.loc, message })
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const effectTests = {
|
|
356
|
+
meta: { type: "problem" },
|
|
357
|
+
create(context) {
|
|
358
|
+
const filename = typeof context.filename === "string" ? context.filename : ""
|
|
359
|
+
const isTest = filename.endsWith(".test.ts")
|
|
360
|
+
return {
|
|
361
|
+
ImportDeclaration(node) {
|
|
362
|
+
const source = specifier(node.source)
|
|
363
|
+
if (source === "vitest/config") {
|
|
364
|
+
if (!filename.endsWith("/vitest.config.ts") && !filename.endsWith("\\vitest.config.ts")) {
|
|
365
|
+
report(context, node, "import tests from @effect/vitest")
|
|
366
|
+
}
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
if (source === "vitest" || source.startsWith("vitest/") || source.startsWith("@vitest/")) {
|
|
370
|
+
report(context, node, "import tests from @effect/vitest")
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
CallExpression(node) {
|
|
374
|
+
if (!isTest) {
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
const parts = chainParts(node.callee)
|
|
378
|
+
const root = parts[0]
|
|
379
|
+
if (root === undefined) {
|
|
380
|
+
if (
|
|
381
|
+
node.callee.type === "Identifier" &&
|
|
382
|
+
(node.callee.name === "it" ||
|
|
383
|
+
node.callee.name === "test" ||
|
|
384
|
+
testAliases.has(node.callee.name))
|
|
385
|
+
) {
|
|
386
|
+
report(context, node, "tests use it.effect from @effect/vitest")
|
|
387
|
+
}
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
if (isItEffect(parts)) {
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
if (root === "it" || root === "test" || testAliases.has(root)) {
|
|
394
|
+
report(context, node, "tests use it.effect from @effect/vitest")
|
|
395
|
+
return
|
|
396
|
+
}
|
|
397
|
+
if (root === "describe") {
|
|
398
|
+
let index = 1
|
|
399
|
+
while (index < parts.length) {
|
|
400
|
+
const part = parts[index]
|
|
401
|
+
if (part !== undefined && bannedTestMembers.has(part)) {
|
|
402
|
+
report(context, node, "skipped, focused, and todo tests are banned")
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
index += 1
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const isAdapterFile = (context) => {
|
|
414
|
+
const filename = typeof context.filename === "string" ? context.filename : ""
|
|
415
|
+
return filename.endsWith(".adapter.ts")
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const rawMessage = (source) => {
|
|
419
|
+
if (socketSpecifiers.has(source)) {
|
|
420
|
+
return "node:net and node:tls belong in a .adapter.ts file; other raw IO stays banned"
|
|
421
|
+
}
|
|
422
|
+
return "raw IO imports are banned; use Effect FileSystem, HttpClient, or ChildProcess"
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const bannedRaw = (context, node, source) => {
|
|
426
|
+
if (socketSpecifiers.has(source)) {
|
|
427
|
+
if (isAdapterFile(context)) {
|
|
428
|
+
return
|
|
429
|
+
}
|
|
430
|
+
report(context, node, rawMessage(source))
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
if (rawSpecifiers.has(source)) {
|
|
434
|
+
report(context, node, rawMessage(source))
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const noRawIo = {
|
|
439
|
+
meta: { type: "problem" },
|
|
440
|
+
create(context) {
|
|
441
|
+
return {
|
|
442
|
+
ImportDeclaration(node) {
|
|
443
|
+
bannedRaw(context, node, specifier(node.source))
|
|
444
|
+
},
|
|
445
|
+
ImportExpression(node) {
|
|
446
|
+
bannedRaw(context, node, specifier(node.source))
|
|
447
|
+
},
|
|
448
|
+
CallExpression(node) {
|
|
449
|
+
const callee = node.callee
|
|
450
|
+
if (callee.type === "Identifier" && callee.name === "fetch") {
|
|
451
|
+
report(context, node, "global fetch is banned; use Effect HttpClient")
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
if (callee.type === "Identifier" && callee.name === "require") {
|
|
455
|
+
bannedRaw(context, node, specifier(node.arguments[0]))
|
|
456
|
+
return
|
|
457
|
+
}
|
|
458
|
+
if (callee.type !== "MemberExpression") {
|
|
459
|
+
return
|
|
460
|
+
}
|
|
461
|
+
const name = literalName(callee.property, callee.computed)
|
|
462
|
+
if (
|
|
463
|
+
callee.object.type === "Identifier" &&
|
|
464
|
+
callee.object.name === "console" &&
|
|
465
|
+
consoleMethods.has(name)
|
|
466
|
+
) {
|
|
467
|
+
report(context, node, "console is banned; use Effect Console")
|
|
468
|
+
return
|
|
469
|
+
}
|
|
470
|
+
if (
|
|
471
|
+
callee.object.type === "Identifier" &&
|
|
472
|
+
(callee.object.name === "globalThis" ||
|
|
473
|
+
callee.object.name === "global" ||
|
|
474
|
+
callee.object.name === "window") &&
|
|
475
|
+
name === "fetch"
|
|
476
|
+
) {
|
|
477
|
+
report(context, node, "global fetch is banned; use Effect HttpClient")
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const noEffectRun = {
|
|
485
|
+
meta: { type: "problem" },
|
|
486
|
+
create(context) {
|
|
487
|
+
return {
|
|
488
|
+
CallExpression(node) {
|
|
489
|
+
const callee = node.callee
|
|
490
|
+
if (callee.type !== "MemberExpression") {
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
if (
|
|
494
|
+
callee.object.type === "Identifier" &&
|
|
495
|
+
callee.object.name === "Effect" &&
|
|
496
|
+
effectRunMethods.has(literalName(callee.property, callee.computed))
|
|
497
|
+
) {
|
|
498
|
+
report(
|
|
499
|
+
context,
|
|
500
|
+
node,
|
|
501
|
+
"Effect.run* is banned; stay inside Effect and use it.effect in tests",
|
|
502
|
+
)
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const noModuleLet = {
|
|
510
|
+
meta: { type: "problem" },
|
|
511
|
+
create(context) {
|
|
512
|
+
return {
|
|
513
|
+
Program(node) {
|
|
514
|
+
for (const statement of node.body) {
|
|
515
|
+
const declaration =
|
|
516
|
+
statement.type === "ExportNamedDeclaration" ? statement.declaration : statement
|
|
517
|
+
flagModuleBinding(context, declaration)
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const noPlainClass = {
|
|
525
|
+
meta: { type: "problem" },
|
|
526
|
+
create(context) {
|
|
527
|
+
return {
|
|
528
|
+
ClassDeclaration(node) {
|
|
529
|
+
if (!blessedSuper(node.superClass)) {
|
|
530
|
+
report(
|
|
531
|
+
context,
|
|
532
|
+
node,
|
|
533
|
+
"plain classes are banned; extend Schema.TaggedError or Context.Service",
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
ClassExpression(node) {
|
|
538
|
+
if (!blessedSuper(node.superClass)) {
|
|
539
|
+
report(
|
|
540
|
+
context,
|
|
541
|
+
node,
|
|
542
|
+
"plain classes are banned; extend Schema.TaggedError or Context.Service",
|
|
543
|
+
)
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const rules = {
|
|
551
|
+
"effect-tests": effectTests,
|
|
552
|
+
"no-bypass-comment": noBypassComment,
|
|
553
|
+
"no-comments": noComments,
|
|
554
|
+
"no-effect-run": noEffectRun,
|
|
555
|
+
"no-error-class": noErrorClass,
|
|
556
|
+
"no-explicit-any": noExplicitAny,
|
|
557
|
+
"no-module-let": noModuleLet,
|
|
558
|
+
"no-non-null-assertion": noNonNullAssertion,
|
|
559
|
+
"no-plain-class": noPlainClass,
|
|
560
|
+
"no-process-env": noProcessEnv,
|
|
561
|
+
"no-raw-io": noRawIo,
|
|
562
|
+
"no-throw": noThrow,
|
|
563
|
+
"no-try": noTry,
|
|
564
|
+
"no-type-assertion": noTypeAssertion,
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export const ruleNames = Object.keys(rules)
|
|
568
|
+
|
|
569
|
+
export default {
|
|
570
|
+
meta: { name: "aas" },
|
|
571
|
+
rules,
|
|
572
|
+
}
|