oxlint-plugin-effect 0.2.0 → 0.2.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/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.js +18 -0
- package/dist/presets/core.d.ts +43 -0
- package/dist/presets/core.js +43 -0
- package/dist/presets/effect-native.d.ts +28 -0
- package/dist/presets/effect-native.js +28 -0
- package/dist/presets/full.d.ts +60 -0
- package/dist/presets/full.js +60 -0
- package/dist/presets/functional.d.ts +21 -0
- package/dist/presets/functional.js +21 -0
- package/dist/presets/index.d.ts +13 -0
- package/dist/presets/index.js +18 -0
- package/dist/presets/strict.d.ts +69 -0
- package/dist/presets/strict.js +69 -0
- package/dist/presets/v3.d.ts +15 -0
- package/dist/presets/v3.js +15 -0
- package/dist/rules/_effect-context.d.ts +19 -0
- package/dist/rules/_effect-context.js +22 -0
- package/dist/rules/index.d.ts +66 -0
- package/dist/rules/index.js +124 -0
- package/dist/rules/no-arrow-ladder.d.ts +6 -0
- package/dist/rules/no-arrow-ladder.js +61 -0
- package/dist/rules/no-async-function.d.ts +6 -0
- package/dist/rules/no-async-function.js +46 -0
- package/dist/rules/no-catch-all-to-map-error.d.ts +6 -0
- package/dist/rules/no-catch-all-to-map-error.js +67 -0
- package/dist/rules/no-effect-as.d.ts +6 -0
- package/dist/rules/no-effect-as.js +16 -0
- package/dist/rules/no-effect-async.d.ts +6 -0
- package/dist/rules/no-effect-async.js +16 -0
- package/dist/rules/no-effect-bind.d.ts +6 -0
- package/dist/rules/no-effect-bind.js +16 -0
- package/dist/rules/no-effect-do.d.ts +6 -0
- package/dist/rules/no-effect-do.js +16 -0
- package/dist/rules/no-effect-fn-generator.d.ts +6 -0
- package/dist/rules/no-effect-fn-generator.js +30 -0
- package/dist/rules/no-effect-gen-adapter.d.ts +6 -0
- package/dist/rules/no-effect-gen-adapter.js +30 -0
- package/dist/rules/no-effect-map-flatten.d.ts +6 -0
- package/dist/rules/no-effect-map-flatten.js +53 -0
- package/dist/rules/no-effect-map-void.d.ts +6 -0
- package/dist/rules/no-effect-map-void.js +40 -0
- package/dist/rules/no-effect-never.d.ts +6 -0
- package/dist/rules/no-effect-never.js +16 -0
- package/dist/rules/no-effect-orElse-ladder.d.ts +6 -0
- package/dist/rules/no-effect-orElse-ladder.js +53 -0
- package/dist/rules/no-effect-succeed-string.d.ts +6 -0
- package/dist/rules/no-effect-succeed-string.js +30 -0
- package/dist/rules/no-effect-succeed-variable.d.ts +6 -0
- package/dist/rules/no-effect-succeed-variable.js +30 -0
- package/dist/rules/no-effect-succeed-void.d.ts +6 -0
- package/dist/rules/no-effect-succeed-void.js +33 -0
- package/dist/rules/no-effect-sync-console.d.ts +6 -0
- package/dist/rules/no-effect-sync-console.js +63 -0
- package/dist/rules/no-effect-sync-wrapper.d.ts +6 -0
- package/dist/rules/no-effect-sync-wrapper.js +37 -0
- package/dist/rules/no-effect-type-alias.d.ts +6 -0
- package/dist/rules/no-effect-type-alias.js +52 -0
- package/dist/rules/no-effect-wrapper-alias.d.ts +6 -0
- package/dist/rules/no-effect-wrapper-alias.js +69 -0
- package/dist/rules/no-extends-native-error.d.ts +6 -0
- package/dist/rules/no-extends-native-error.js +41 -0
- package/dist/rules/no-flatmap-ladder.d.ts +6 -0
- package/dist/rules/no-flatmap-ladder.js +48 -0
- package/dist/rules/no-fromnullable-coalesce.d.ts +6 -0
- package/dist/rules/no-fromnullable-coalesce.js +36 -0
- package/dist/rules/no-global-error-in-catch.d.ts +6 -0
- package/dist/rules/no-global-error-in-catch.js +75 -0
- package/dist/rules/no-global-error-in-failure.d.ts +6 -0
- package/dist/rules/no-global-error-in-failure.js +54 -0
- package/dist/rules/no-globals.d.ts +6 -0
- package/dist/rules/no-globals.js +164 -0
- package/dist/rules/no-if-statement.d.ts +6 -0
- package/dist/rules/no-if-statement.js +18 -0
- package/dist/rules/no-iife-wrapper.d.ts +6 -0
- package/dist/rules/no-iife-wrapper.js +37 -0
- package/dist/rules/no-inline-provide.d.ts +6 -0
- package/dist/rules/no-inline-provide.js +31 -0
- package/dist/rules/no-instanceof-schema.d.ts +6 -0
- package/dist/rules/no-instanceof-schema.js +35 -0
- package/dist/rules/no-manual-effect-channels.d.ts +6 -0
- package/dist/rules/no-manual-effect-channels.js +40 -0
- package/dist/rules/no-match-effect-branch.d.ts +6 -0
- package/dist/rules/no-match-effect-branch.js +64 -0
- package/dist/rules/no-match-void-branch.d.ts +6 -0
- package/dist/rules/no-match-void-branch.js +50 -0
- package/dist/rules/no-multiple-effect-provide.d.ts +6 -0
- package/dist/rules/no-multiple-effect-provide.js +45 -0
- package/dist/rules/no-nested-effect-call.d.ts +6 -0
- package/dist/rules/no-nested-effect-call.js +97 -0
- package/dist/rules/no-nested-effect-gen.d.ts +6 -0
- package/dist/rules/no-nested-effect-gen.js +46 -0
- package/dist/rules/no-nested-pipe.d.ts +6 -0
- package/dist/rules/no-nested-pipe.js +38 -0
- package/dist/rules/no-new-error.d.ts +6 -0
- package/dist/rules/no-new-error.js +39 -0
- package/dist/rules/no-new-promise.d.ts +6 -0
- package/dist/rules/no-new-promise.js +38 -0
- package/dist/rules/no-node-builtin-import.d.ts +6 -0
- package/dist/rules/no-node-builtin-import.js +61 -0
- package/dist/rules/no-option-as.d.ts +6 -0
- package/dist/rules/no-option-as.js +16 -0
- package/dist/rules/no-option-boolean-normalization.d.ts +6 -0
- package/dist/rules/no-option-boolean-normalization.js +50 -0
- package/dist/rules/no-positional-log-error.d.ts +6 -0
- package/dist/rules/no-positional-log-error.js +52 -0
- package/dist/rules/no-redundant-schema-tag-identifier.d.ts +6 -0
- package/dist/rules/no-redundant-schema-tag-identifier.js +46 -0
- package/dist/rules/no-return-in-arrow.d.ts +6 -0
- package/dist/rules/no-return-in-arrow.js +41 -0
- package/dist/rules/no-return-nullish.d.ts +6 -0
- package/dist/rules/no-return-nullish.js +47 -0
- package/dist/rules/no-run-in-effect-gen.d.ts +6 -0
- package/dist/rules/no-run-in-effect-gen.js +48 -0
- package/dist/rules/no-runtime-run-fork.d.ts +6 -0
- package/dist/rules/no-runtime-run-fork.js +19 -0
- package/dist/rules/no-schema-struct-with-tag.d.ts +6 -0
- package/dist/rules/no-schema-struct-with-tag.js +48 -0
- package/dist/rules/no-schema-union-of-literals.d.ts +6 -0
- package/dist/rules/no-schema-union-of-literals.js +35 -0
- package/dist/rules/no-string-sentinel-const.d.ts +6 -0
- package/dist/rules/no-string-sentinel-const.js +38 -0
- package/dist/rules/no-switch-statement.d.ts +6 -0
- package/dist/rules/no-switch-statement.js +16 -0
- package/dist/rules/no-ternary.d.ts +6 -0
- package/dist/rules/no-ternary.js +30 -0
- package/dist/rules/no-throw-statement.d.ts +6 -0
- package/dist/rules/no-throw-statement.js +34 -0
- package/dist/rules/no-try-catch.d.ts +6 -0
- package/dist/rules/no-try-catch.js +34 -0
- package/dist/rules/no-unnecessary-arrow-block.d.ts +6 -0
- package/dist/rules/no-unnecessary-arrow-block.js +34 -0
- package/dist/rules/no-unnecessary-effect-gen.d.ts +6 -0
- package/dist/rules/no-unnecessary-effect-gen.js +35 -0
- package/dist/rules/no-unnecessary-pipe-chain.d.ts +6 -0
- package/dist/rules/no-unnecessary-pipe-chain.js +36 -0
- package/dist/rules/no-unnecessary-pipe.d.ts +6 -0
- package/dist/rules/no-unnecessary-pipe.js +29 -0
- package/dist/vendor/effect-oxlint/AST.d.ts +199 -0
- package/dist/vendor/effect-oxlint/AST.js +214 -0
- package/dist/vendor/effect-oxlint/Comment.d.ts +55 -0
- package/dist/vendor/effect-oxlint/Comment.js +67 -0
- package/dist/vendor/effect-oxlint/Diagnostic.d.ts +86 -0
- package/dist/vendor/effect-oxlint/Diagnostic.js +115 -0
- package/dist/vendor/effect-oxlint/Plugin.d.ts +38 -0
- package/dist/vendor/effect-oxlint/Plugin.js +45 -0
- package/dist/vendor/effect-oxlint/Rule.d.ts +218 -0
- package/dist/vendor/effect-oxlint/Rule.js +309 -0
- package/dist/vendor/effect-oxlint/RuleContext.d.ts +77 -0
- package/dist/vendor/effect-oxlint/RuleContext.js +74 -0
- package/dist/vendor/effect-oxlint/Scope.d.ts +100 -0
- package/dist/vendor/effect-oxlint/Scope.js +110 -0
- package/dist/vendor/effect-oxlint/SourceCode.d.ts +180 -0
- package/dist/vendor/effect-oxlint/SourceCode.js +203 -0
- package/dist/vendor/effect-oxlint/Testing.d.ts +510 -0
- package/dist/vendor/effect-oxlint/Testing.js +963 -0
- package/dist/vendor/effect-oxlint/Token.d.ts +98 -0
- package/dist/vendor/effect-oxlint/Token.js +103 -0
- package/dist/vendor/effect-oxlint/Visitor.d.ts +166 -0
- package/dist/vendor/effect-oxlint/Visitor.js +138 -0
- package/dist/vendor/effect-oxlint/index.d.ts +13 -0
- package/dist/vendor/effect-oxlint/index.js +12 -0
- package/package.json +24 -5
- package/.effect-lsp.json +0 -60
- package/.oxlintrc.json +0 -33
- package/CHANGELOG.md +0 -55
- package/scripts/add-rule.ts +0 -87
- package/scripts/codegen.ts +0 -108
- package/src/index.ts +0 -9
- package/src/plugin.ts +0 -16
- package/src/presets/core.ts +0 -47
- package/src/presets/effect-native.ts +0 -34
- package/src/presets/full.ts +0 -70
- package/src/presets/functional.ts +0 -18
- package/src/presets/index.ts +0 -5
- package/src/presets/strict.ts +0 -77
- package/src/rules/_effect-context.ts +0 -28
- package/src/rules/index.ts +0 -72
- package/src/rules/no-arrow-ladder.ts +0 -70
- package/src/rules/no-async-function.ts +0 -47
- package/src/rules/no-catch-all-to-map-error.ts +0 -90
- package/src/rules/no-effect-as.ts +0 -14
- package/src/rules/no-effect-async.ts +0 -14
- package/src/rules/no-effect-bind.ts +0 -13
- package/src/rules/no-effect-do.ts +0 -14
- package/src/rules/no-effect-fn-generator.ts +0 -58
- package/src/rules/no-effect-gen-adapter.ts +0 -51
- package/src/rules/no-effect-map-flatten.ts +0 -82
- package/src/rules/no-effect-map-void.ts +0 -80
- package/src/rules/no-effect-never.ts +0 -14
- package/src/rules/no-effect-orElse-ladder.ts +0 -79
- package/src/rules/no-effect-succeed-string.ts +0 -52
- package/src/rules/no-effect-succeed-variable.ts +0 -50
- package/src/rules/no-effect-succeed-void.ts +0 -63
- package/src/rules/no-effect-sync-console.ts +0 -91
- package/src/rules/no-effect-sync-wrapper.ts +0 -67
- package/src/rules/no-effect-type-alias.ts +0 -71
- package/src/rules/no-effect-wrapper-alias.ts +0 -95
- package/src/rules/no-extends-native-error.ts +0 -54
- package/src/rules/no-flatmap-ladder.ts +0 -77
- package/src/rules/no-fromnullable-coalesce.ts +0 -62
- package/src/rules/no-global-error-in-catch.ts +0 -104
- package/src/rules/no-global-error-in-failure.ts +0 -83
- package/src/rules/no-globals.ts +0 -178
- package/src/rules/no-if-statement.ts +0 -16
- package/src/rules/no-iife-wrapper.ts +0 -51
- package/src/rules/no-inline-provide.ts +0 -41
- package/src/rules/no-instanceof-schema.ts +0 -42
- package/src/rules/no-manual-effect-channels.ts +0 -58
- package/src/rules/no-match-effect-branch.ts +0 -82
- package/src/rules/no-match-void-branch.ts +0 -72
- package/src/rules/no-multiple-effect-provide.ts +0 -75
- package/src/rules/no-nested-effect-call.ts +0 -57
- package/src/rules/no-nested-effect-gen.ts +0 -43
- package/src/rules/no-nested-pipe.ts +0 -73
- package/src/rules/no-new-error.ts +0 -53
- package/src/rules/no-new-promise.ts +0 -50
- package/src/rules/no-node-builtin-import.ts +0 -68
- package/src/rules/no-option-as.ts +0 -13
- package/src/rules/no-option-boolean-normalization.ts +0 -88
- package/src/rules/no-positional-log-error.ts +0 -76
- package/src/rules/no-redundant-schema-tag-identifier.ts +0 -62
- package/src/rules/no-return-in-arrow.ts +0 -76
- package/src/rules/no-return-nullish.ts +0 -80
- package/src/rules/no-run-in-effect-gen.ts +0 -66
- package/src/rules/no-runtime-run-fork.ts +0 -14
- package/src/rules/no-schema-struct-with-tag.ts +0 -64
- package/src/rules/no-schema-union-of-literals.ts +0 -48
- package/src/rules/no-string-sentinel-const.ts +0 -49
- package/src/rules/no-switch-statement.ts +0 -13
- package/src/rules/no-ternary.ts +0 -30
- package/src/rules/no-throw-statement.ts +0 -39
- package/src/rules/no-try-catch.ts +0 -39
- package/src/rules/no-unnecessary-arrow-block.ts +0 -51
- package/src/rules/no-unnecessary-effect-gen.ts +0 -83
- package/src/rules/no-unnecessary-pipe-chain.ts +0 -47
- package/src/rules/no-unnecessary-pipe.ts +0 -45
- package/src/vendor/effect-oxlint/AST.ts +0 -516
- package/src/vendor/effect-oxlint/Comment.ts +0 -86
- package/src/vendor/effect-oxlint/Diagnostic.ts +0 -199
- package/src/vendor/effect-oxlint/Plugin.ts +0 -71
- package/src/vendor/effect-oxlint/Rule.ts +0 -548
- package/src/vendor/effect-oxlint/RuleContext.ts +0 -144
- package/src/vendor/effect-oxlint/Scope.ts +0 -177
- package/src/vendor/effect-oxlint/SourceCode.ts +0 -377
- package/src/vendor/effect-oxlint/Testing.ts +0 -1227
- package/src/vendor/effect-oxlint/Token.ts +0 -131
- package/src/vendor/effect-oxlint/Visitor.ts +0 -287
- package/src/vendor/effect-oxlint/index.ts +0 -88
- package/tests/ban-rules.test.ts +0 -68
- package/tests/context-rules.test.ts +0 -263
- package/tests/pattern-rules.test.ts +0 -247
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { calleeName, matchImport, matchMember, narrow } from "./AST.js";
|
|
3
|
+
import { make } from "./Diagnostic.js";
|
|
4
|
+
import { RuleContext, fromOxlintContext } from "./RuleContext.js";
|
|
5
|
+
import { merge, toOxlintVisitor } from "./Visitor.js";
|
|
6
|
+
import * as Arr from "effect/Array";
|
|
7
|
+
import { pipe } from "effect/Function";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as P from "effect/Predicate";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Schema from "effect/Schema";
|
|
12
|
+
//#region src/vendor/effect-oxlint/Rule.ts
|
|
13
|
+
var Rule_exports = /* @__PURE__ */ __exportAll({
|
|
14
|
+
banCallOf: () => banCallOf,
|
|
15
|
+
banImport: () => banImport,
|
|
16
|
+
banMember: () => banMember,
|
|
17
|
+
banMultiple: () => banMultiple,
|
|
18
|
+
banNewExpr: () => banNewExpr,
|
|
19
|
+
banStatement: () => banStatement,
|
|
20
|
+
define: () => define,
|
|
21
|
+
meta: () => meta
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Extract the identifier name from a `NewExpression` callee.
|
|
25
|
+
*
|
|
26
|
+
* `AST.calleeName` only accepts `CallExpression`. This mirrors the
|
|
27
|
+
* same logic for `NewExpression.callee`.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
const newExprCalleeName = (callee) => callee.type === "Identifier" && "name" in callee && P.isString(callee.name) ? Option.some(callee.name) : Option.none();
|
|
32
|
+
/**
|
|
33
|
+
* Define an Effect-first oxlint lint rule.
|
|
34
|
+
*
|
|
35
|
+
* The `create` generator runs once per file via `Effect.runSync`.
|
|
36
|
+
* Each visitor handler is also executed via `Effect.runSync` per node.
|
|
37
|
+
* `Ref`-based state created in `create` persists across handler calls
|
|
38
|
+
* via closure.
|
|
39
|
+
*
|
|
40
|
+
* `Effect.runSync` is used here at the runtime boundary — the bridge
|
|
41
|
+
* between oxlint's synchronous plugin API and our Effect world.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
*/
|
|
45
|
+
const define = (config) => ({
|
|
46
|
+
meta: config.meta,
|
|
47
|
+
create(oxlintContext) {
|
|
48
|
+
const ruleCtx = fromOxlintContext(oxlintContext);
|
|
49
|
+
const run = (effect) => Effect.runSync(Effect.provideService(effect, RuleContext, ruleCtx));
|
|
50
|
+
const decodeOptions = () => {
|
|
51
|
+
const schema = config.options;
|
|
52
|
+
if (schema === void 0) return void 0;
|
|
53
|
+
return Schema.decodeUnknownSync(schema)(oxlintContext.options[0]);
|
|
54
|
+
};
|
|
55
|
+
const options = run(Effect.sync(decodeOptions));
|
|
56
|
+
return toOxlintVisitor(run(Effect.gen(() => config.create(options))), run);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Build `RuleMeta` with sensible defaults.
|
|
61
|
+
*
|
|
62
|
+
* @since 0.1.0
|
|
63
|
+
*/
|
|
64
|
+
const meta = (opts) => ({
|
|
65
|
+
type: opts.type,
|
|
66
|
+
...opts.fixable !== void 0 ? { fixable: opts.fixable } : {},
|
|
67
|
+
...opts.hasSuggestions !== void 0 ? { hasSuggestions: opts.hasSuggestions } : {},
|
|
68
|
+
...opts.messages !== void 0 ? { messages: opts.messages } : {},
|
|
69
|
+
docs: {
|
|
70
|
+
description: opts.description,
|
|
71
|
+
effectVersion: opts.effectVersion ?? "both",
|
|
72
|
+
...opts.docs
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* Create a rule that bans `obj.prop` member expression access.
|
|
77
|
+
*
|
|
78
|
+
* Replaces the common `memberExprRule` utility pattern.
|
|
79
|
+
*
|
|
80
|
+
* @since 0.1.0
|
|
81
|
+
*/
|
|
82
|
+
const banMember = (obj, prop, opts) => define({
|
|
83
|
+
name: `ban-${obj}-${P.isString(prop) ? prop : Arr.join(prop, "-")}`,
|
|
84
|
+
meta: meta({
|
|
85
|
+
type: opts.meta?.type ?? "suggestion",
|
|
86
|
+
description: opts.message,
|
|
87
|
+
effectVersion: opts.meta?.effectVersion
|
|
88
|
+
}),
|
|
89
|
+
create: function* () {
|
|
90
|
+
const ctx = yield* RuleContext;
|
|
91
|
+
return { MemberExpression: (node) => pipe(narrow(node, "MemberExpression"), Option.flatMap(matchMember(obj, prop)), Option.match({
|
|
92
|
+
onNone: () => Effect.void,
|
|
93
|
+
onSome: (matched) => ctx.report(make({
|
|
94
|
+
node: matched,
|
|
95
|
+
message: opts.message
|
|
96
|
+
}))
|
|
97
|
+
})) };
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Create a rule that bans imports matching a source string or predicate.
|
|
102
|
+
*
|
|
103
|
+
* Replaces the common `importRule` utility pattern.
|
|
104
|
+
*
|
|
105
|
+
* @since 0.1.0
|
|
106
|
+
*/
|
|
107
|
+
const banImport = (source, opts) => define({
|
|
108
|
+
name: "ban-import",
|
|
109
|
+
meta: meta({
|
|
110
|
+
type: opts.meta?.type ?? "suggestion",
|
|
111
|
+
description: opts.message,
|
|
112
|
+
effectVersion: opts.meta?.effectVersion
|
|
113
|
+
}),
|
|
114
|
+
create: function* () {
|
|
115
|
+
const ctx = yield* RuleContext;
|
|
116
|
+
return { ImportDeclaration: (node) => pipe(narrow(node, "ImportDeclaration"), Option.flatMap(matchImport(source)), Option.match({
|
|
117
|
+
onNone: () => Effect.void,
|
|
118
|
+
onSome: (matched) => ctx.report(make({
|
|
119
|
+
node: matched,
|
|
120
|
+
message: opts.message
|
|
121
|
+
}))
|
|
122
|
+
})) };
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Create a rule that bans bare identifier call expressions.
|
|
127
|
+
*
|
|
128
|
+
* Matches `CallExpression` nodes whose callee is an identifier in
|
|
129
|
+
* the given list (e.g. `fetch()`, `useState()`, `readFileSync()`).
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* // Ban a single call
|
|
134
|
+
* Rule.banCallOf('fetch', { message: 'Use Effect HTTP client' })
|
|
135
|
+
*
|
|
136
|
+
* // Ban multiple calls
|
|
137
|
+
* Rule.banCallOf(['useState', 'useEffect'], { message: 'Use Effect' })
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* @since 0.2.0
|
|
141
|
+
*/
|
|
142
|
+
const banCallOf = (name, opts) => {
|
|
143
|
+
const names = P.isString(name) ? [name] : name;
|
|
144
|
+
return define({
|
|
145
|
+
name: `ban-call-${Arr.join(names, "-")}`,
|
|
146
|
+
meta: meta({
|
|
147
|
+
type: opts.meta?.type ?? "suggestion",
|
|
148
|
+
description: opts.message,
|
|
149
|
+
effectVersion: opts.meta?.effectVersion
|
|
150
|
+
}),
|
|
151
|
+
create: function* () {
|
|
152
|
+
const ctx = yield* RuleContext;
|
|
153
|
+
return { CallExpression: (node) => pipe(narrow(node, "CallExpression"), Option.flatMap(calleeName), Option.filter((n) => Arr.contains(names, n)), Option.match({
|
|
154
|
+
onNone: () => Effect.void,
|
|
155
|
+
onSome: () => ctx.report(make({
|
|
156
|
+
node,
|
|
157
|
+
message: opts.message
|
|
158
|
+
}))
|
|
159
|
+
})) };
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Create a rule that bans `new` expressions with the given callee name.
|
|
165
|
+
*
|
|
166
|
+
* Matches `NewExpression` nodes whose callee is an identifier in
|
|
167
|
+
* the given list (e.g. `new Date()`, `new Error()`).
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* // Ban a single constructor
|
|
172
|
+
* Rule.banNewExpr('Date', { message: 'Use Clock service' })
|
|
173
|
+
*
|
|
174
|
+
* // Ban multiple constructors
|
|
175
|
+
* Rule.banNewExpr(['Error', 'TypeError'], { message: 'Use tagged errors' })
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* @since 0.2.0
|
|
179
|
+
*/
|
|
180
|
+
const banNewExpr = (name, opts) => {
|
|
181
|
+
const names = P.isString(name) ? [name] : name;
|
|
182
|
+
return define({
|
|
183
|
+
name: `ban-new-${Arr.join(names, "-")}`,
|
|
184
|
+
meta: meta({
|
|
185
|
+
type: opts.meta?.type ?? "suggestion",
|
|
186
|
+
description: opts.message,
|
|
187
|
+
effectVersion: opts.meta?.effectVersion
|
|
188
|
+
}),
|
|
189
|
+
create: function* () {
|
|
190
|
+
const ctx = yield* RuleContext;
|
|
191
|
+
return { NewExpression: (node) => pipe(narrow(node, "NewExpression"), Option.flatMap((n) => newExprCalleeName(n.callee)), Option.filter((n) => Arr.contains(names, n)), Option.match({
|
|
192
|
+
onNone: () => Effect.void,
|
|
193
|
+
onSome: () => ctx.report(make({
|
|
194
|
+
node,
|
|
195
|
+
message: opts.message
|
|
196
|
+
}))
|
|
197
|
+
})) };
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Create a rule that bans a specific statement type.
|
|
203
|
+
*
|
|
204
|
+
* @since 0.1.0
|
|
205
|
+
*/
|
|
206
|
+
const banStatement = (nodeType, opts) => define({
|
|
207
|
+
name: `ban-${nodeType}`,
|
|
208
|
+
meta: meta({
|
|
209
|
+
type: opts.meta?.type ?? "suggestion",
|
|
210
|
+
description: opts.message,
|
|
211
|
+
effectVersion: opts.meta?.effectVersion
|
|
212
|
+
}),
|
|
213
|
+
create: function* () {
|
|
214
|
+
const ctx = yield* RuleContext;
|
|
215
|
+
return { [nodeType]: (node) => ctx.report(make({
|
|
216
|
+
node,
|
|
217
|
+
message: opts.message
|
|
218
|
+
})) };
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
/**
|
|
222
|
+
* Create a rule that bans multiple patterns, each with its own message.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* Rule.banMultiple({
|
|
227
|
+
* name: "no-global-date",
|
|
228
|
+
* meta: { type: "suggestion" },
|
|
229
|
+
* specs: [
|
|
230
|
+
* { type: "new", name: "Date", message: "Avoid new Date(). Use Clock service." },
|
|
231
|
+
* { type: "member", object: "Date", property: "now", message: "Avoid Date.now(). Use Clock service." },
|
|
232
|
+
* ],
|
|
233
|
+
* })
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @since 0.3.0
|
|
237
|
+
*/
|
|
238
|
+
const banMultiple = (config) => {
|
|
239
|
+
const description = config.specs.map((s) => s.message).join(" ");
|
|
240
|
+
return define({
|
|
241
|
+
name: config.name,
|
|
242
|
+
meta: meta({
|
|
243
|
+
type: config.meta?.type ?? "suggestion",
|
|
244
|
+
description,
|
|
245
|
+
effectVersion: config.meta?.effectVersion
|
|
246
|
+
}),
|
|
247
|
+
create: function* () {
|
|
248
|
+
const ctx = yield* RuleContext;
|
|
249
|
+
const visitors = [];
|
|
250
|
+
for (const spec of config.specs) switch (spec.type) {
|
|
251
|
+
case "member": {
|
|
252
|
+
const report = (node) => ctx.report(make({
|
|
253
|
+
node,
|
|
254
|
+
message: spec.message
|
|
255
|
+
}));
|
|
256
|
+
visitors.push({ MemberExpression: (node) => pipe(narrow(node, "MemberExpression"), Option.flatMap(matchMember(spec.object, spec.property)), Option.match({
|
|
257
|
+
onNone: () => Effect.void,
|
|
258
|
+
onSome: (matched) => report(matched)
|
|
259
|
+
})) });
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case "call": {
|
|
263
|
+
const names = P.isString(spec.name) ? [spec.name] : spec.name;
|
|
264
|
+
const report = (node) => ctx.report(make({
|
|
265
|
+
node,
|
|
266
|
+
message: spec.message
|
|
267
|
+
}));
|
|
268
|
+
visitors.push({ CallExpression: (node) => pipe(narrow(node, "CallExpression"), Option.flatMap(calleeName), Option.filter((n) => Arr.contains(names, n)), Option.match({
|
|
269
|
+
onNone: () => Effect.void,
|
|
270
|
+
onSome: () => report(node)
|
|
271
|
+
})) });
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
case "new": {
|
|
275
|
+
const names = P.isString(spec.name) ? [spec.name] : spec.name;
|
|
276
|
+
const report = (node) => ctx.report(make({
|
|
277
|
+
node,
|
|
278
|
+
message: spec.message
|
|
279
|
+
}));
|
|
280
|
+
visitors.push({ NewExpression: (node) => pipe(narrow(node, "NewExpression"), Option.flatMap((n) => newExprCalleeName(n.callee)), Option.filter((n) => Arr.contains(names, n)), Option.match({
|
|
281
|
+
onNone: () => Effect.void,
|
|
282
|
+
onSome: () => report(node)
|
|
283
|
+
})) });
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
case "import": {
|
|
287
|
+
const report = (node) => ctx.report(make({
|
|
288
|
+
node,
|
|
289
|
+
message: spec.message
|
|
290
|
+
}));
|
|
291
|
+
visitors.push({ ImportDeclaration: (node) => pipe(narrow(node, "ImportDeclaration"), Option.flatMap(matchImport(spec.source)), Option.match({
|
|
292
|
+
onNone: () => Effect.void,
|
|
293
|
+
onSome: (matched) => report(matched)
|
|
294
|
+
})) });
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case "statement":
|
|
298
|
+
visitors.push({ [spec.nodeType]: (node) => ctx.report(make({
|
|
299
|
+
node,
|
|
300
|
+
message: spec.message
|
|
301
|
+
})) });
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
return merge(...visitors);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
//#endregion
|
|
309
|
+
export { Rule_exports, banCallOf, banImport, banMember, banMultiple, banNewExpr, banStatement, define, meta };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Context$1 from "effect/Context";
|
|
3
|
+
import { Context, Diagnostic, ESTree, LanguageOptions, Options, Settings, SourceCode } from "@oxlint/plugins";
|
|
4
|
+
|
|
5
|
+
//#region src/vendor/effect-oxlint/RuleContext.d.ts
|
|
6
|
+
declare const RuleContext_base: Context$1.ServiceClass<RuleContext, "effect-oxlint/RuleContext", {
|
|
7
|
+
/** Report a lint diagnostic for the current file. */readonly report: (diagnostic: Diagnostic) => Effect.Effect<void>; /** Rule ID in `plugin/rule` form. */
|
|
8
|
+
readonly id: string; /** Absolute path of the file being linted. */
|
|
9
|
+
readonly filename: string; /** Current working directory. */
|
|
10
|
+
readonly cwd: string; /** Raw rule options (JSON values). */
|
|
11
|
+
readonly options: Readonly<Options>; /** Source code access (tokens, comments, text, scope, etc.). */
|
|
12
|
+
readonly sourceCode: SourceCode; /** Language / parser options for this file. */
|
|
13
|
+
readonly languageOptions: Readonly<LanguageOptions>; /** Shared settings from the oxlint config. */
|
|
14
|
+
readonly settings: Readonly<Settings>;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* The lint rule context, provided as an Effect service.
|
|
18
|
+
*
|
|
19
|
+
* Available inside `Rule.define`'s `create` generator and every
|
|
20
|
+
* visitor handler via `yield* RuleContext`.
|
|
21
|
+
*
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
*/
|
|
24
|
+
declare class RuleContext extends RuleContext_base {}
|
|
25
|
+
/**
|
|
26
|
+
* Build a `RuleContext` value from the raw oxlint `Context`.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
declare const fromOxlintContext: (ctx: Context) => RuleContext["Service"];
|
|
31
|
+
/**
|
|
32
|
+
* Effectful access to the rule ID.
|
|
33
|
+
*
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
*/
|
|
36
|
+
declare const id: Effect.Effect<string, never, RuleContext>;
|
|
37
|
+
/**
|
|
38
|
+
* Effectful access to the filename of the file being linted.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
*/
|
|
42
|
+
declare const filename: Effect.Effect<string, never, RuleContext>;
|
|
43
|
+
/**
|
|
44
|
+
* Effectful access to the working directory.
|
|
45
|
+
*
|
|
46
|
+
* @since 0.1.0
|
|
47
|
+
*/
|
|
48
|
+
declare const cwd: Effect.Effect<string, never, RuleContext>;
|
|
49
|
+
/**
|
|
50
|
+
* Effectful access to the SourceCode object.
|
|
51
|
+
*
|
|
52
|
+
* @since 0.1.0
|
|
53
|
+
*/
|
|
54
|
+
declare const sourceCode: Effect.Effect<SourceCode, never, RuleContext>;
|
|
55
|
+
/**
|
|
56
|
+
* Effectful access to the raw source text.
|
|
57
|
+
*
|
|
58
|
+
* @since 0.1.0
|
|
59
|
+
*/
|
|
60
|
+
declare const text: Effect.Effect<string, never, RuleContext>;
|
|
61
|
+
/**
|
|
62
|
+
* Effectful access to the AST root node.
|
|
63
|
+
*
|
|
64
|
+
* @since 0.1.0
|
|
65
|
+
*/
|
|
66
|
+
declare const ast: Effect.Effect<ESTree.Program, never, RuleContext>;
|
|
67
|
+
/**
|
|
68
|
+
* Report a lint diagnostic.
|
|
69
|
+
*
|
|
70
|
+
* Shorthand so handlers can `yield* RuleContext.report(diagnostic)`
|
|
71
|
+
* without first resolving the full service.
|
|
72
|
+
*
|
|
73
|
+
* @since 0.1.0
|
|
74
|
+
*/
|
|
75
|
+
declare const report: (diagnostic: Diagnostic) => Effect.Effect<void, never, RuleContext>;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { RuleContext, ast, cwd, filename, fromOxlintContext, id, report, sourceCode, text };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
//#region src/vendor/effect-oxlint/RuleContext.ts
|
|
4
|
+
/**
|
|
5
|
+
* The lint rule context, provided as an Effect service.
|
|
6
|
+
*
|
|
7
|
+
* Available inside `Rule.define`'s `create` generator and every
|
|
8
|
+
* visitor handler via `yield* RuleContext`.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
var RuleContext = class extends Context.Service()("effect-oxlint/RuleContext") {};
|
|
13
|
+
/**
|
|
14
|
+
* Build a `RuleContext` value from the raw oxlint `Context`.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
const fromOxlintContext = (ctx) => RuleContext.of({
|
|
19
|
+
report: (diagnostic) => Effect.sync(() => ctx.report(diagnostic)),
|
|
20
|
+
id: ctx.id,
|
|
21
|
+
filename: ctx.filename,
|
|
22
|
+
cwd: ctx.cwd,
|
|
23
|
+
options: ctx.options,
|
|
24
|
+
sourceCode: ctx.sourceCode,
|
|
25
|
+
languageOptions: ctx.languageOptions,
|
|
26
|
+
settings: ctx.settings
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Effectful access to the rule ID.
|
|
30
|
+
*
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
*/
|
|
33
|
+
const id = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.id));
|
|
34
|
+
/**
|
|
35
|
+
* Effectful access to the filename of the file being linted.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.1.0
|
|
38
|
+
*/
|
|
39
|
+
const filename = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.filename));
|
|
40
|
+
/**
|
|
41
|
+
* Effectful access to the working directory.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
*/
|
|
45
|
+
const cwd = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.cwd));
|
|
46
|
+
/**
|
|
47
|
+
* Effectful access to the SourceCode object.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
*/
|
|
51
|
+
const sourceCode = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.sourceCode));
|
|
52
|
+
/**
|
|
53
|
+
* Effectful access to the raw source text.
|
|
54
|
+
*
|
|
55
|
+
* @since 0.1.0
|
|
56
|
+
*/
|
|
57
|
+
const text = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.sourceCode.text));
|
|
58
|
+
/**
|
|
59
|
+
* Effectful access to the AST root node.
|
|
60
|
+
*
|
|
61
|
+
* @since 0.1.0
|
|
62
|
+
*/
|
|
63
|
+
const ast = Effect.service(RuleContext).pipe(Effect.map((ctx) => ctx.sourceCode.ast));
|
|
64
|
+
/**
|
|
65
|
+
* Report a lint diagnostic.
|
|
66
|
+
*
|
|
67
|
+
* Shorthand so handlers can `yield* RuleContext.report(diagnostic)`
|
|
68
|
+
* without first resolving the full service.
|
|
69
|
+
*
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
*/
|
|
72
|
+
const report = (diagnostic) => Effect.service(RuleContext).pipe(Effect.flatMap((ctx) => ctx.report(diagnostic)));
|
|
73
|
+
//#endregion
|
|
74
|
+
export { RuleContext, ast, cwd, filename, fromOxlintContext, id, report, sourceCode, text };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
import { Reference, Scope, Variable } from "@oxlint/plugins";
|
|
3
|
+
|
|
4
|
+
//#region src/vendor/effect-oxlint/Scope.d.ts
|
|
5
|
+
declare namespace Scope_d_exports {
|
|
6
|
+
export { childScopes, findVariable, findVariableUp, getReadReferences, getReferences, getWriteReferences, isReadOnly, isStrict, isUsed, isWritten, throughReferences, upper, variables };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Find a variable by name in a scope.
|
|
10
|
+
*
|
|
11
|
+
* Searches the scope's `set` (a `Map<string, Variable>`).
|
|
12
|
+
*
|
|
13
|
+
* @since 0.2.0
|
|
14
|
+
*/
|
|
15
|
+
declare const findVariable: {
|
|
16
|
+
(name: string): (scope: Scope) => Option.Option<Variable>;
|
|
17
|
+
(scope: Scope, name: string): Option.Option<Variable>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Find a variable by name, walking up the scope chain.
|
|
21
|
+
*
|
|
22
|
+
* Starts at the given scope and checks each `upper` scope until found
|
|
23
|
+
* or the global scope is exhausted.
|
|
24
|
+
*
|
|
25
|
+
* @since 0.2.0
|
|
26
|
+
*/
|
|
27
|
+
declare const findVariableUp: {
|
|
28
|
+
(name: string): (scope: Scope) => Option.Option<Variable>;
|
|
29
|
+
(scope: Scope, name: string): Option.Option<Variable>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Check whether a variable has any read references.
|
|
33
|
+
*
|
|
34
|
+
* A variable is considered "used" if at least one reference reads it.
|
|
35
|
+
*
|
|
36
|
+
* @since 0.2.0
|
|
37
|
+
*/
|
|
38
|
+
declare const isUsed: (variable: Variable) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Check whether a variable has any write references.
|
|
41
|
+
*
|
|
42
|
+
* @since 0.2.0
|
|
43
|
+
*/
|
|
44
|
+
declare const isWritten: (variable: Variable) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check whether a variable is only read (no writes).
|
|
47
|
+
*
|
|
48
|
+
* @since 0.2.0
|
|
49
|
+
*/
|
|
50
|
+
declare const isReadOnly: (variable: Variable) => boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Get all references for a variable.
|
|
53
|
+
*
|
|
54
|
+
* @since 0.2.0
|
|
55
|
+
*/
|
|
56
|
+
declare const getReferences: (variable: Variable) => ReadonlyArray<Reference>;
|
|
57
|
+
/**
|
|
58
|
+
* Get the read references for a variable.
|
|
59
|
+
*
|
|
60
|
+
* @since 0.2.0
|
|
61
|
+
*/
|
|
62
|
+
declare const getReadReferences: (variable: Variable) => ReadonlyArray<Reference>;
|
|
63
|
+
/**
|
|
64
|
+
* Get the write references for a variable.
|
|
65
|
+
*
|
|
66
|
+
* @since 0.2.0
|
|
67
|
+
*/
|
|
68
|
+
declare const getWriteReferences: (variable: Variable) => ReadonlyArray<Reference>;
|
|
69
|
+
/**
|
|
70
|
+
* Get the parent (upper) scope.
|
|
71
|
+
*
|
|
72
|
+
* @since 0.2.0
|
|
73
|
+
*/
|
|
74
|
+
declare const upper: (scope: Scope) => Option.Option<Scope>;
|
|
75
|
+
/**
|
|
76
|
+
* Get the child scopes.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.2.0
|
|
79
|
+
*/
|
|
80
|
+
declare const childScopes: (scope: Scope) => ReadonlyArray<Scope>;
|
|
81
|
+
/**
|
|
82
|
+
* Get all variables in a scope.
|
|
83
|
+
*
|
|
84
|
+
* @since 0.2.0
|
|
85
|
+
*/
|
|
86
|
+
declare const variables: (scope: Scope) => ReadonlyArray<Variable>;
|
|
87
|
+
/**
|
|
88
|
+
* Get "through" references (unresolved in this scope).
|
|
89
|
+
*
|
|
90
|
+
* @since 0.2.0
|
|
91
|
+
*/
|
|
92
|
+
declare const throughReferences: (scope: Scope) => ReadonlyArray<Reference>;
|
|
93
|
+
/**
|
|
94
|
+
* Check whether a scope is strict mode.
|
|
95
|
+
*
|
|
96
|
+
* @since 0.2.0
|
|
97
|
+
*/
|
|
98
|
+
declare const isStrict: (scope: Scope) => boolean;
|
|
99
|
+
//#endregion
|
|
100
|
+
export { Scope_d_exports, childScopes, findVariable, findVariableUp, getReadReferences, getReferences, getWriteReferences, isReadOnly, isStrict, isUsed, isWritten, throughReferences, upper, variables };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as Arr from "effect/Array";
|
|
3
|
+
import { dual, pipe } from "effect/Function";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
//#region src/vendor/effect-oxlint/Scope.ts
|
|
6
|
+
var Scope_exports = /* @__PURE__ */ __exportAll({
|
|
7
|
+
childScopes: () => childScopes,
|
|
8
|
+
findVariable: () => findVariable,
|
|
9
|
+
findVariableUp: () => findVariableUp,
|
|
10
|
+
getReadReferences: () => getReadReferences,
|
|
11
|
+
getReferences: () => getReferences,
|
|
12
|
+
getWriteReferences: () => getWriteReferences,
|
|
13
|
+
isReadOnly: () => isReadOnly,
|
|
14
|
+
isStrict: () => isStrict,
|
|
15
|
+
isUsed: () => isUsed,
|
|
16
|
+
isWritten: () => isWritten,
|
|
17
|
+
throughReferences: () => throughReferences,
|
|
18
|
+
upper: () => upper,
|
|
19
|
+
variables: () => variables
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Find a variable by name in a scope.
|
|
23
|
+
*
|
|
24
|
+
* Searches the scope's `set` (a `Map<string, Variable>`).
|
|
25
|
+
*
|
|
26
|
+
* @since 0.2.0
|
|
27
|
+
*/
|
|
28
|
+
const findVariable = dual(2, (scope, name) => Option.fromNullishOr(scope.set.get(name)));
|
|
29
|
+
/**
|
|
30
|
+
* Find a variable by name, walking up the scope chain.
|
|
31
|
+
*
|
|
32
|
+
* Starts at the given scope and checks each `upper` scope until found
|
|
33
|
+
* or the global scope is exhausted.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.2.0
|
|
36
|
+
*/
|
|
37
|
+
const findVariableUp = dual(2, (scope, name) => {
|
|
38
|
+
const walk = (current) => pipe(Option.fromNullishOr(current.set.get(name)), Option.orElse(() => pipe(Option.fromNullishOr(current.upper), Option.flatMap((parent) => walk(parent)))));
|
|
39
|
+
return walk(scope);
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Check whether a variable has any read references.
|
|
43
|
+
*
|
|
44
|
+
* A variable is considered "used" if at least one reference reads it.
|
|
45
|
+
*
|
|
46
|
+
* @since 0.2.0
|
|
47
|
+
*/
|
|
48
|
+
const isUsed = (variable) => Arr.some(variable.references, (ref) => ref.isRead());
|
|
49
|
+
/**
|
|
50
|
+
* Check whether a variable has any write references.
|
|
51
|
+
*
|
|
52
|
+
* @since 0.2.0
|
|
53
|
+
*/
|
|
54
|
+
const isWritten = (variable) => Arr.some(variable.references, (ref) => ref.isWrite());
|
|
55
|
+
/**
|
|
56
|
+
* Check whether a variable is only read (no writes).
|
|
57
|
+
*
|
|
58
|
+
* @since 0.2.0
|
|
59
|
+
*/
|
|
60
|
+
const isReadOnly = (variable) => Arr.some(variable.references, (ref) => ref.isReadOnly());
|
|
61
|
+
/**
|
|
62
|
+
* Get all references for a variable.
|
|
63
|
+
*
|
|
64
|
+
* @since 0.2.0
|
|
65
|
+
*/
|
|
66
|
+
const getReferences = (variable) => variable.references;
|
|
67
|
+
/**
|
|
68
|
+
* Get the read references for a variable.
|
|
69
|
+
*
|
|
70
|
+
* @since 0.2.0
|
|
71
|
+
*/
|
|
72
|
+
const getReadReferences = (variable) => pipe(variable.references, Arr.filter((ref) => ref.isRead()));
|
|
73
|
+
/**
|
|
74
|
+
* Get the write references for a variable.
|
|
75
|
+
*
|
|
76
|
+
* @since 0.2.0
|
|
77
|
+
*/
|
|
78
|
+
const getWriteReferences = (variable) => pipe(variable.references, Arr.filter((ref) => ref.isWrite()));
|
|
79
|
+
/**
|
|
80
|
+
* Get the parent (upper) scope.
|
|
81
|
+
*
|
|
82
|
+
* @since 0.2.0
|
|
83
|
+
*/
|
|
84
|
+
const upper = (scope) => Option.fromNullishOr(scope.upper);
|
|
85
|
+
/**
|
|
86
|
+
* Get the child scopes.
|
|
87
|
+
*
|
|
88
|
+
* @since 0.2.0
|
|
89
|
+
*/
|
|
90
|
+
const childScopes = (scope) => scope.childScopes;
|
|
91
|
+
/**
|
|
92
|
+
* Get all variables in a scope.
|
|
93
|
+
*
|
|
94
|
+
* @since 0.2.0
|
|
95
|
+
*/
|
|
96
|
+
const variables = (scope) => scope.variables;
|
|
97
|
+
/**
|
|
98
|
+
* Get "through" references (unresolved in this scope).
|
|
99
|
+
*
|
|
100
|
+
* @since 0.2.0
|
|
101
|
+
*/
|
|
102
|
+
const throughReferences = (scope) => scope.through;
|
|
103
|
+
/**
|
|
104
|
+
* Check whether a scope is strict mode.
|
|
105
|
+
*
|
|
106
|
+
* @since 0.2.0
|
|
107
|
+
*/
|
|
108
|
+
const isStrict = (scope) => scope.isStrict;
|
|
109
|
+
//#endregion
|
|
110
|
+
export { Scope_exports, childScopes, findVariable, findVariableUp, getReadReferences, getReferences, getWriteReferences, isReadOnly, isStrict, isUsed, isWritten, throughReferences, upper, variables };
|