@effect/tsgo 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -0
- package/bin/effect-tsgo.js +1133 -326
- package/package.json +11 -10
package/bin/effect-tsgo.js
CHANGED
|
@@ -34157,14 +34157,14 @@ const defaultFormatter = (options) => {
|
|
|
34157
34157
|
* Strips ANSI escape codes from a string to calculate visual width.
|
|
34158
34158
|
* @internal
|
|
34159
34159
|
*/
|
|
34160
|
-
const stripAnsi = (text) => {
|
|
34160
|
+
const stripAnsi$1 = (text) => {
|
|
34161
34161
|
return text.replace(/\u001B\[[0-9;]*m/g, "");
|
|
34162
34162
|
};
|
|
34163
34163
|
/**
|
|
34164
34164
|
* Gets the visual length of a string (excluding ANSI codes).
|
|
34165
34165
|
* @internal
|
|
34166
34166
|
*/
|
|
34167
|
-
const visualLength = (text) => stripAnsi(text).length;
|
|
34167
|
+
const visualLength = (text) => stripAnsi$1(text).length;
|
|
34168
34168
|
/**
|
|
34169
34169
|
* Helper function to pad strings to a specified width.
|
|
34170
34170
|
* @internal
|
|
@@ -45596,7 +45596,7 @@ const cursorMove = (column, row = 0) => {
|
|
|
45596
45596
|
return command;
|
|
45597
45597
|
};
|
|
45598
45598
|
/** @internal */
|
|
45599
|
-
const eraseLines
|
|
45599
|
+
const eraseLines = (rows) => {
|
|
45600
45600
|
let command = "";
|
|
45601
45601
|
for (let i = 0; i < rows; i++) command += `${ESC}2K` + (i < rows - 1 ? `${ESC}1A` : "");
|
|
45602
45602
|
if (rows > 0) command += `${ESC}G`;
|
|
@@ -45812,20 +45812,20 @@ const Action$1 = /* @__PURE__ */ taggedEnum();
|
|
|
45812
45812
|
/**
|
|
45813
45813
|
* Clears all lines taken up by the specified `text`.
|
|
45814
45814
|
*/
|
|
45815
|
-
const eraseText
|
|
45815
|
+
const eraseText = (text, columns) => {
|
|
45816
45816
|
if (columns === 0) return eraseLine + cursorTo(0);
|
|
45817
45817
|
let rows = 0;
|
|
45818
45818
|
const lines = text.split(NEWLINE_REGEXP);
|
|
45819
45819
|
for (const line of lines) rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns);
|
|
45820
|
-
return eraseLines
|
|
45820
|
+
return eraseLines(rows);
|
|
45821
45821
|
};
|
|
45822
45822
|
const lines = (prompt, columns) => {
|
|
45823
45823
|
const lines = prompt.split(NEWLINE_REGEXP);
|
|
45824
45824
|
return columns === 0 ? lines.length : pipe(map$7(lines, (line) => Math.ceil(line.length / columns)), reduce(0, (left, right) => left + right));
|
|
45825
45825
|
};
|
|
45826
45826
|
const clearOutputWithError = (outputText, columns, errorText) => {
|
|
45827
|
-
if (errorText !== void 0 && errorText.length > 0) return cursorDown(lines(errorText, columns)) + eraseText
|
|
45828
|
-
return eraseText
|
|
45827
|
+
if (errorText !== void 0 && errorText.length > 0) return cursorDown(lines(errorText, columns)) + eraseText(`\n${errorText}`, columns) + eraseText(outputText, columns);
|
|
45828
|
+
return eraseText(outputText, columns);
|
|
45829
45829
|
};
|
|
45830
45830
|
const renderBeep = beep;
|
|
45831
45831
|
const NEWLINE_REGEXP = /\r?\n/;
|
|
@@ -45833,7 +45833,7 @@ const handleConfirmClear = (options) => {
|
|
|
45833
45833
|
return fnUntraced(function* (state, _) {
|
|
45834
45834
|
const columns = yield* (yield* Terminal).columns;
|
|
45835
45835
|
const figures = yield* platformFigures;
|
|
45836
|
-
return eraseText
|
|
45836
|
+
return eraseText(renderConfirmOutput(state.value ? options.placeholder.defaultConfirm : options.placeholder.defaultDeny, "?", figures.pointerSmall, options, { plain: true }), columns) + (eraseLine + cursorLeft);
|
|
45837
45837
|
});
|
|
45838
45838
|
};
|
|
45839
45839
|
const renderConfirmOutput = (confirm, leadingSymbol, trailingSymbol, options, renderOptions) => renderPrompt(confirm, options.message, leadingSymbol, trailingSymbol, renderOptions);
|
|
@@ -45903,7 +45903,7 @@ const renderMultiSelectChoices = (state, options, figures, renderOptions) => {
|
|
|
45903
45903
|
const inverseSelectionText = options?.inverseSelection ?? "Inverse Selection";
|
|
45904
45904
|
const metaOptions = [{ title: selectAllText }, { title: inverseSelectionText }];
|
|
45905
45905
|
const allChoices = [...metaOptions, ...choices];
|
|
45906
|
-
const toDisplay = entriesToDisplay
|
|
45906
|
+
const toDisplay = entriesToDisplay(state.index, allChoices.length, options.maxPerPage);
|
|
45907
45907
|
const documents = [];
|
|
45908
45908
|
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
45909
45909
|
const choice = allChoices[index];
|
|
@@ -46039,7 +46039,7 @@ const renderChoiceTitle = (choice, isSelected, renderOptions) => {
|
|
|
46039
46039
|
};
|
|
46040
46040
|
const renderSelectChoices = (state, options, figures, renderOptions) => {
|
|
46041
46041
|
const choices = options.choices;
|
|
46042
|
-
const toDisplay = entriesToDisplay
|
|
46042
|
+
const toDisplay = entriesToDisplay(state, choices.length, options.maxPerPage);
|
|
46043
46043
|
const documents = [];
|
|
46044
46044
|
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
46045
46045
|
const choice = choices[index];
|
|
@@ -46086,7 +46086,7 @@ const handleSelectClear = (options) => fnUntraced(function* (state, _) {
|
|
|
46086
46086
|
const columns = yield* (yield* Terminal).columns;
|
|
46087
46087
|
const figures = yield* platformFigures;
|
|
46088
46088
|
const clearPrompt = eraseLine + cursorLeft;
|
|
46089
|
-
return eraseText
|
|
46089
|
+
return eraseText(`${renderSelectOutput("?", figures.pointerSmall, options, { plain: true })}\n${renderSelectChoices(state, options, figures, { plain: true })}`, columns) + clearPrompt;
|
|
46090
46090
|
});
|
|
46091
46091
|
const handleSelectProcess = (options) => {
|
|
46092
46092
|
return (input, state) => {
|
|
@@ -46262,7 +46262,7 @@ const basePrompt = (options, type) => {
|
|
|
46262
46262
|
clear: handleTextClear(opts)
|
|
46263
46263
|
});
|
|
46264
46264
|
};
|
|
46265
|
-
const entriesToDisplay
|
|
46265
|
+
const entriesToDisplay = (cursor, total, maxVisible) => {
|
|
46266
46266
|
const max = maxVisible === void 0 ? total : maxVisible;
|
|
46267
46267
|
let startIndex = Math.min(total - max, cursor - Math.floor(max / 2));
|
|
46268
46268
|
if (startIndex < 0) startIndex = 0;
|
|
@@ -195732,7 +195732,7 @@ var FileReadError = class extends TaggedError("FileReadError") {
|
|
|
195732
195732
|
//#endregion
|
|
195733
195733
|
//#region package.json
|
|
195734
195734
|
var name = "@effect/tsgo";
|
|
195735
|
-
var version = "0.0.
|
|
195735
|
+
var version = "0.0.17";
|
|
195736
195736
|
|
|
195737
195737
|
//#endregion
|
|
195738
195738
|
//#region src/setup/consts.ts
|
|
@@ -196077,7 +196077,43 @@ const computeTsConfigChanges = (current, target, lspVersion) => {
|
|
|
196077
196077
|
const rootObj = getRootObject(current.sourceFile);
|
|
196078
196078
|
if (!rootObj) return emptyFileChangesResult();
|
|
196079
196079
|
const compilerOptionsProperty = findPropertyInObject(rootObj, "compilerOptions");
|
|
196080
|
-
if (!compilerOptionsProperty || !import_typescript.isObjectLiteralExpression(compilerOptionsProperty.initializer))
|
|
196080
|
+
if (!compilerOptionsProperty || !import_typescript.isObjectLiteralExpression(compilerOptionsProperty.initializer)) {
|
|
196081
|
+
if (isNone(lspVersion)) return emptyFileChangesResult();
|
|
196082
|
+
const ctx = createTrackerContext();
|
|
196083
|
+
const fileChange = tsInternal.textChanges.ChangeTracker.with(ctx, (tracker) => {
|
|
196084
|
+
const schemaProperty = findPropertyInObject(rootObj, "$schema");
|
|
196085
|
+
const shouldAddSchema = !schemaProperty;
|
|
196086
|
+
const shouldUpdateSchema = !!schemaProperty && (!import_typescript.isStringLiteral(schemaProperty.initializer) || schemaProperty.initializer.text !== TSCONFIG_SCHEMA_URL);
|
|
196087
|
+
if (shouldAddSchema) descriptions.push("Add $schema to tsconfig");
|
|
196088
|
+
else if (shouldUpdateSchema) descriptions.push("Update $schema in tsconfig");
|
|
196089
|
+
descriptions.push(`Add compilerOptions with ${LSP_PLUGIN_NAME} plugin`);
|
|
196090
|
+
const schemaPropertyAssignment = import_typescript.factory.createPropertyAssignment(import_typescript.factory.createStringLiteral("$schema"), import_typescript.factory.createStringLiteral(TSCONFIG_SCHEMA_URL));
|
|
196091
|
+
const compilerOptionsAssignment = import_typescript.factory.createPropertyAssignment(import_typescript.factory.createStringLiteral("compilerOptions"), import_typescript.factory.createObjectLiteralExpression([import_typescript.factory.createPropertyAssignment(import_typescript.factory.createStringLiteral("plugins"), import_typescript.factory.createArrayLiteralExpression([createLspPluginObject(target)], true))], true));
|
|
196092
|
+
const nextProperties = rootObj.properties.map((property) => {
|
|
196093
|
+
if (schemaProperty && property === schemaProperty) return schemaPropertyAssignment;
|
|
196094
|
+
return property;
|
|
196095
|
+
});
|
|
196096
|
+
if (shouldAddSchema) nextProperties.push(schemaPropertyAssignment);
|
|
196097
|
+
nextProperties.push(compilerOptionsAssignment);
|
|
196098
|
+
tracker.replaceNode(current.sourceFile, rootObj, import_typescript.factory.createObjectLiteralExpression(nextProperties, true));
|
|
196099
|
+
}).find((fc) => fc.fileName === current.sourceFile.fileName);
|
|
196100
|
+
const changes = fileChange ? fileChange.textChanges : [];
|
|
196101
|
+
if (changes.length === 0) return {
|
|
196102
|
+
codeActions: [],
|
|
196103
|
+
messages
|
|
196104
|
+
};
|
|
196105
|
+
return {
|
|
196106
|
+
codeActions: [{
|
|
196107
|
+
description: descriptions.join("; "),
|
|
196108
|
+
changes: [{
|
|
196109
|
+
fileName: current.sourceFile.fileName,
|
|
196110
|
+
textChanges: changes,
|
|
196111
|
+
isNewFile: false
|
|
196112
|
+
}]
|
|
196113
|
+
}],
|
|
196114
|
+
messages
|
|
196115
|
+
};
|
|
196116
|
+
}
|
|
196081
196117
|
const compilerOptions = compilerOptionsProperty.initializer;
|
|
196082
196118
|
const ctx = createTrackerContext();
|
|
196083
196119
|
const fileChange = tsInternal.textChanges.ChangeTracker.with(ctx, (tracker) => {
|
|
@@ -196420,314 +196456,922 @@ const renderCodeActions = (result, assessmentState) => gen(function* () {
|
|
|
196420
196456
|
});
|
|
196421
196457
|
|
|
196422
196458
|
//#endregion
|
|
196423
|
-
//#region src/
|
|
196424
|
-
var
|
|
196459
|
+
//#region src/metadata.json
|
|
196460
|
+
var groups = [
|
|
196425
196461
|
{
|
|
196426
|
-
"
|
|
196427
|
-
"
|
|
196428
|
-
"
|
|
196429
|
-
"codes": [377030]
|
|
196462
|
+
"id": "correctness",
|
|
196463
|
+
"name": "Correctness",
|
|
196464
|
+
"description": "Wrong, unsafe, or structurally invalid code patterns."
|
|
196430
196465
|
},
|
|
196431
196466
|
{
|
|
196432
|
-
"
|
|
196433
|
-
"
|
|
196434
|
-
"
|
|
196435
|
-
"codes": [377010]
|
|
196467
|
+
"id": "antipattern",
|
|
196468
|
+
"name": "Anti-pattern",
|
|
196469
|
+
"description": "Discouraged patterns that often lead to bugs or confusing behavior."
|
|
196436
196470
|
},
|
|
196437
196471
|
{
|
|
196438
|
-
"
|
|
196439
|
-
"
|
|
196440
|
-
"
|
|
196441
|
-
"codes": [377009]
|
|
196472
|
+
"id": "effectNative",
|
|
196473
|
+
"name": "Effect-native",
|
|
196474
|
+
"description": "Prefer Effect-native APIs and abstractions when available."
|
|
196442
196475
|
},
|
|
196443
196476
|
{
|
|
196444
|
-
"
|
|
196445
|
-
"
|
|
196446
|
-
"
|
|
196447
|
-
|
|
196448
|
-
|
|
196477
|
+
"id": "style",
|
|
196478
|
+
"name": "Style",
|
|
196479
|
+
"description": "Cleanup, consistency, and idiomatic Effect code."
|
|
196480
|
+
}
|
|
196481
|
+
];
|
|
196482
|
+
var rules = [
|
|
196449
196483
|
{
|
|
196450
|
-
"name": "
|
|
196451
|
-
"
|
|
196484
|
+
"name": "anyUnknownInErrorContext",
|
|
196485
|
+
"group": "correctness",
|
|
196486
|
+
"description": "Detects 'any' or 'unknown' types in Effect error or requirements channels",
|
|
196452
196487
|
"defaultSeverity": "off",
|
|
196453
|
-
"
|
|
196488
|
+
"fixable": false,
|
|
196489
|
+
"supportedEffect": ["v3", "v4"],
|
|
196490
|
+
"codes": [377030],
|
|
196491
|
+
"preview": {
|
|
196492
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.gen(function*() {\n yield* Effect.services<unknown>()\n return yield* Effect.fail<any>(\"boom\")\n})\n",
|
|
196493
|
+
"diagnostics": [
|
|
196494
|
+
{
|
|
196495
|
+
"start": 46,
|
|
196496
|
+
"end": 53,
|
|
196497
|
+
"text": "This has unknown in the requirements channel and any in the error channel which is not recommended.\nOnly service identifiers should appear in the requirements channel.\nHaving an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example. effect(anyUnknownInErrorContext)"
|
|
196498
|
+
},
|
|
196499
|
+
{
|
|
196500
|
+
"start": 90,
|
|
196501
|
+
"end": 116,
|
|
196502
|
+
"text": "This has unknown in the requirements channel which is not recommended.\nOnly service identifiers should appear in the requirements channel. effect(anyUnknownInErrorContext)"
|
|
196503
|
+
},
|
|
196504
|
+
{
|
|
196505
|
+
"start": 133,
|
|
196506
|
+
"end": 157,
|
|
196507
|
+
"text": "This has any in the error channel which is not recommended.\nHaving an unknown or any error type is not useful. Consider instead using specific error types baked by Data.TaggedError for example. effect(anyUnknownInErrorContext)"
|
|
196508
|
+
}
|
|
196509
|
+
]
|
|
196510
|
+
}
|
|
196511
|
+
},
|
|
196512
|
+
{
|
|
196513
|
+
"name": "classSelfMismatch",
|
|
196514
|
+
"group": "correctness",
|
|
196515
|
+
"description": "Ensures Self type parameter matches the class name in ServiceMap/Service/Tag/Schema classes",
|
|
196516
|
+
"defaultSeverity": "error",
|
|
196517
|
+
"fixable": true,
|
|
196518
|
+
"supportedEffect": ["v3", "v4"],
|
|
196519
|
+
"codes": [377046],
|
|
196520
|
+
"preview": {
|
|
196521
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\ninterface ServiceShape { value: number }\n\nexport class InvalidContextTag\n extends Effect.Tag(\"ValidContextTag\")<ValidContextTag, ServiceShape>() {}\n\ndeclare class ValidContextTag {}\n",
|
|
196522
|
+
"diagnostics": [{
|
|
196523
|
+
"start": 154,
|
|
196524
|
+
"end": 169,
|
|
196525
|
+
"text": "Self type parameter should be 'InvalidContextTag'. effect(classSelfMismatch)"
|
|
196526
|
+
}]
|
|
196527
|
+
}
|
|
196454
196528
|
},
|
|
196455
196529
|
{
|
|
196456
196530
|
"name": "duplicatePackage",
|
|
196531
|
+
"group": "correctness",
|
|
196457
196532
|
"description": "Warns when multiple versions of an Effect-related package are detected in the program",
|
|
196458
196533
|
"defaultSeverity": "warning",
|
|
196459
|
-
"
|
|
196534
|
+
"fixable": false,
|
|
196535
|
+
"supportedEffect": ["v3", "v4"],
|
|
196536
|
+
"codes": [377051],
|
|
196537
|
+
"preview": {
|
|
196538
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\n// This preview only reports when the workspace resolves duplicated\n// Effect package versions.\nexport const preview = Effect.succeed(true)\n",
|
|
196539
|
+
"diagnostics": []
|
|
196540
|
+
}
|
|
196460
196541
|
},
|
|
196461
196542
|
{
|
|
196462
|
-
"name": "
|
|
196463
|
-
"
|
|
196543
|
+
"name": "floatingEffect",
|
|
196544
|
+
"group": "correctness",
|
|
196545
|
+
"description": "Detects Effect values that are neither yielded nor assigned",
|
|
196546
|
+
"defaultSeverity": "error",
|
|
196547
|
+
"fixable": false,
|
|
196548
|
+
"supportedEffect": ["v3", "v4"],
|
|
196549
|
+
"codes": [377001, 377058],
|
|
196550
|
+
"preview": {
|
|
196551
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nEffect.log(\"forgotten\")\n",
|
|
196552
|
+
"diagnostics": [{
|
|
196553
|
+
"start": 41,
|
|
196554
|
+
"end": 64,
|
|
196555
|
+
"text": "Effect must be yielded or assigned to a variable. effect(floatingEffect)"
|
|
196556
|
+
}]
|
|
196557
|
+
}
|
|
196558
|
+
},
|
|
196559
|
+
{
|
|
196560
|
+
"name": "genericEffectServices",
|
|
196561
|
+
"group": "correctness",
|
|
196562
|
+
"description": "Prevents services with type parameters that cannot be discriminated at runtime",
|
|
196464
196563
|
"defaultSeverity": "warning",
|
|
196465
|
-
"
|
|
196564
|
+
"fixable": false,
|
|
196565
|
+
"supportedEffect": ["v3", "v4"],
|
|
196566
|
+
"codes": [377043],
|
|
196567
|
+
"preview": {
|
|
196568
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport class Preview<_A> extends Effect.Service<Preview<any>>()(\"Preview\", {\n succeed: {}\n}) {}\n",
|
|
196569
|
+
"diagnostics": [{
|
|
196570
|
+
"start": 46,
|
|
196571
|
+
"end": 53,
|
|
196572
|
+
"text": "Effect Services with type parameters are not supported because they cannot be properly discriminated at runtime, which may cause unexpected behavior. effect(genericEffectServices)"
|
|
196573
|
+
}]
|
|
196574
|
+
}
|
|
196466
196575
|
},
|
|
196467
196576
|
{
|
|
196468
|
-
"name": "
|
|
196469
|
-
"
|
|
196470
|
-
"
|
|
196471
|
-
"
|
|
196577
|
+
"name": "missingEffectContext",
|
|
196578
|
+
"group": "correctness",
|
|
196579
|
+
"description": "Detects Effect values with unhandled context requirements",
|
|
196580
|
+
"defaultSeverity": "error",
|
|
196581
|
+
"fixable": false,
|
|
196582
|
+
"supportedEffect": ["v3", "v4"],
|
|
196583
|
+
"codes": [377004],
|
|
196584
|
+
"preview": {
|
|
196585
|
+
"sourceText": "import { Effect, ServiceMap } from \"effect\"\n\nclass Db extends ServiceMap.Service<Db>()(\"Db\", { make: Effect.succeed({}) }) {}\n\n// @ts-expect-error\nexport const preview: Effect.Effect<void> = Db.asEffect().pipe(Effect.asVoid)\n",
|
|
196586
|
+
"diagnostics": [{
|
|
196587
|
+
"start": 160,
|
|
196588
|
+
"end": 167,
|
|
196589
|
+
"text": "Missing context Db in the expected Effect type. effect(missingEffectContext)"
|
|
196590
|
+
}]
|
|
196591
|
+
}
|
|
196472
196592
|
},
|
|
196473
196593
|
{
|
|
196474
|
-
"name": "
|
|
196475
|
-
"
|
|
196476
|
-
"
|
|
196477
|
-
"
|
|
196594
|
+
"name": "missingEffectError",
|
|
196595
|
+
"group": "correctness",
|
|
196596
|
+
"description": "Detects Effect values with unhandled error types",
|
|
196597
|
+
"defaultSeverity": "error",
|
|
196598
|
+
"fixable": true,
|
|
196599
|
+
"supportedEffect": ["v3", "v4"],
|
|
196600
|
+
"codes": [377003],
|
|
196601
|
+
"preview": {
|
|
196602
|
+
"sourceText": "import type * as Effect from \"effect/Effect\"\nimport { Data } from \"effect\"\n\nclass Boom extends Data.TaggedError(\"Boom\")<{}> {}\ndeclare const effect: Effect.Effect<number, Boom>\n\n// @ts-expect-error\nexport const preview: () => Effect.Effect<number> = () => effect\n",
|
|
196603
|
+
"diagnostics": [{
|
|
196604
|
+
"start": 256,
|
|
196605
|
+
"end": 262,
|
|
196606
|
+
"text": "Missing errors Boom in the expected Effect type. effect(missingEffectError)"
|
|
196607
|
+
}]
|
|
196608
|
+
}
|
|
196478
196609
|
},
|
|
196479
196610
|
{
|
|
196480
|
-
"name": "
|
|
196481
|
-
"
|
|
196482
|
-
"
|
|
196483
|
-
"
|
|
196611
|
+
"name": "missingLayerContext",
|
|
196612
|
+
"group": "correctness",
|
|
196613
|
+
"description": "Detects Layer values with unhandled context requirements",
|
|
196614
|
+
"defaultSeverity": "error",
|
|
196615
|
+
"fixable": false,
|
|
196616
|
+
"supportedEffect": ["v3", "v4"],
|
|
196617
|
+
"codes": [377034],
|
|
196618
|
+
"preview": {
|
|
196619
|
+
"sourceText": "import { Effect, Layer, ServiceMap } from \"effect\"\n\nclass A extends ServiceMap.Service<A>()(\"A\", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\ndeclare const layer: Layer.Layer<A, never, A>\n// @ts-expect-error\nexport const preview: Layer.Layer<A> = layer\n",
|
|
196620
|
+
"diagnostics": [{
|
|
196621
|
+
"start": 259,
|
|
196622
|
+
"end": 266,
|
|
196623
|
+
"text": "Missing 'A' in the expected Layer context. effect(missingLayerContext)"
|
|
196624
|
+
}]
|
|
196625
|
+
}
|
|
196484
196626
|
},
|
|
196485
196627
|
{
|
|
196486
|
-
"name": "
|
|
196487
|
-
"
|
|
196628
|
+
"name": "missingReturnYieldStar",
|
|
196629
|
+
"group": "correctness",
|
|
196630
|
+
"description": "Suggests using return yield* for Effects that never succeed",
|
|
196631
|
+
"defaultSeverity": "error",
|
|
196632
|
+
"fixable": true,
|
|
196633
|
+
"supportedEffect": ["v3", "v4"],
|
|
196634
|
+
"codes": [377006],
|
|
196635
|
+
"preview": {
|
|
196636
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n yield* Effect.log(\"before\")\n yield* Effect.fail(\"boom\")\n})\n",
|
|
196637
|
+
"diagnostics": [{
|
|
196638
|
+
"start": 121,
|
|
196639
|
+
"end": 126,
|
|
196640
|
+
"text": "It is recommended to use return yield* for Effects that never succeed to signal a definitive exit point for type narrowing and tooling support. effect(missingReturnYieldStar)"
|
|
196641
|
+
}]
|
|
196642
|
+
}
|
|
196643
|
+
},
|
|
196644
|
+
{
|
|
196645
|
+
"name": "missingStarInYieldEffectGen",
|
|
196646
|
+
"group": "correctness",
|
|
196647
|
+
"description": "Detects bare yield (without *) inside Effect generator scopes",
|
|
196648
|
+
"defaultSeverity": "error",
|
|
196649
|
+
"fixable": true,
|
|
196650
|
+
"supportedEffect": ["v3", "v4"],
|
|
196651
|
+
"codes": [377007, 377008],
|
|
196652
|
+
"preview": {
|
|
196653
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n const value = yield Effect.succeed(1)\n return value\n})\n",
|
|
196654
|
+
"diagnostics": [{
|
|
196655
|
+
"start": 105,
|
|
196656
|
+
"end": 110,
|
|
196657
|
+
"text": "When yielding Effects inside Effect.gen, you should use yield* instead of yield. effect(missingStarInYieldEffectGen)"
|
|
196658
|
+
}]
|
|
196659
|
+
}
|
|
196660
|
+
},
|
|
196661
|
+
{
|
|
196662
|
+
"name": "nonObjectEffectServiceType",
|
|
196663
|
+
"group": "correctness",
|
|
196664
|
+
"description": "Ensures Effect.Service types are objects, not primitives",
|
|
196665
|
+
"defaultSeverity": "error",
|
|
196666
|
+
"fixable": false,
|
|
196667
|
+
"supportedEffect": ["v3"],
|
|
196668
|
+
"codes": [377048],
|
|
196669
|
+
"preview": {
|
|
196670
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport class BadService extends Effect.Service<BadService>()(\"BadService\", {\n // @ts-expect-error\n succeed: \"hello\" as const\n}) {}\n",
|
|
196671
|
+
"diagnostics": [{
|
|
196672
|
+
"start": 142,
|
|
196673
|
+
"end": 149,
|
|
196674
|
+
"text": "Effect.Service requires the service type to be an object {} and not a primitive type. Consider wrapping the value in an object, or manually using Context.Tag or Effect.Tag if you want to use a primitive instead. effect(nonObjectEffectServiceType)"
|
|
196675
|
+
}]
|
|
196676
|
+
}
|
|
196677
|
+
},
|
|
196678
|
+
{
|
|
196679
|
+
"name": "outdatedApi",
|
|
196680
|
+
"group": "correctness",
|
|
196681
|
+
"description": "Detects usage of APIs that have been removed or renamed in Effect v4",
|
|
196488
196682
|
"defaultSeverity": "warning",
|
|
196489
|
-
"
|
|
196683
|
+
"fixable": false,
|
|
196684
|
+
"supportedEffect": ["v4"],
|
|
196685
|
+
"codes": [377052, 377053],
|
|
196686
|
+
"preview": {
|
|
196687
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.gen(function*() {\n // @ts-expect-error\n return yield* Effect.runtime()\n})\n",
|
|
196688
|
+
"diagnostics": [{
|
|
196689
|
+
"start": 0,
|
|
196690
|
+
"end": 0,
|
|
196691
|
+
"text": "This project targets Effect v4, but is using Effect v3 APIs. To find the correct API to use, consult the Effect v4 documentation for the corresponding v4 replacement. effect(outdatedApi)"
|
|
196692
|
+
}, {
|
|
196693
|
+
"start": 126,
|
|
196694
|
+
"end": 133,
|
|
196695
|
+
"text": "runtime is an Effect v3 API, but the project is using Effect v4. Runtime has been removed in Effect v4. Use Effect.services to grab services and then run using Effect.runPromiseWith. effect(outdatedApi)"
|
|
196696
|
+
}]
|
|
196697
|
+
}
|
|
196490
196698
|
},
|
|
196491
196699
|
{
|
|
196492
|
-
"name": "
|
|
196493
|
-
"
|
|
196494
|
-
"
|
|
196495
|
-
"
|
|
196700
|
+
"name": "overriddenSchemaConstructor",
|
|
196701
|
+
"group": "correctness",
|
|
196702
|
+
"description": "Prevents overriding constructors in Schema classes which breaks decoding behavior",
|
|
196703
|
+
"defaultSeverity": "error",
|
|
196704
|
+
"fixable": true,
|
|
196705
|
+
"supportedEffect": ["v3", "v4"],
|
|
196706
|
+
"codes": [377044],
|
|
196707
|
+
"preview": {
|
|
196708
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport class User extends Schema.Class<User>(\"User\")({ name: Schema.String }) {\n constructor(readonly input: { name: string }) { super(input) }\n}\n",
|
|
196709
|
+
"diagnostics": [{
|
|
196710
|
+
"start": 123,
|
|
196711
|
+
"end": 134,
|
|
196712
|
+
"text": "Classes extending Schema must not override the constructor; this is because it silently breaks the schema decoding behaviour. If that's needed, we recommend instead to use a static 'new' method that constructs the instance. effect(overriddenSchemaConstructor)"
|
|
196713
|
+
}]
|
|
196714
|
+
}
|
|
196496
196715
|
},
|
|
196497
196716
|
{
|
|
196498
|
-
"name": "
|
|
196499
|
-
"
|
|
196717
|
+
"name": "catchUnfailableEffect",
|
|
196718
|
+
"group": "antipattern",
|
|
196719
|
+
"description": "Warns when using error handling on Effects that never fail",
|
|
196500
196720
|
"defaultSeverity": "suggestion",
|
|
196501
|
-
"
|
|
196721
|
+
"fixable": false,
|
|
196722
|
+
"supportedEffect": ["v3", "v4"],
|
|
196723
|
+
"codes": [377009],
|
|
196724
|
+
"preview": {
|
|
196725
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.succeed(1).pipe(\n Effect.catch(() => Effect.succeed(0))\n)\n",
|
|
196726
|
+
"diagnostics": [{
|
|
196727
|
+
"start": 82,
|
|
196728
|
+
"end": 94,
|
|
196729
|
+
"text": "Looks like the previous effect never fails, so probably this error handling will never be triggered. effect(catchUnfailableEffect)"
|
|
196730
|
+
}]
|
|
196731
|
+
}
|
|
196502
196732
|
},
|
|
196503
196733
|
{
|
|
196504
|
-
"name": "
|
|
196505
|
-
"
|
|
196506
|
-
"
|
|
196507
|
-
"
|
|
196734
|
+
"name": "effectFnIife",
|
|
196735
|
+
"group": "antipattern",
|
|
196736
|
+
"description": "Effect.fn or Effect.fnUntraced is called as an IIFE; use Effect.gen instead",
|
|
196737
|
+
"defaultSeverity": "warning",
|
|
196738
|
+
"fixable": true,
|
|
196739
|
+
"supportedEffect": ["v3", "v4"],
|
|
196740
|
+
"codes": [377011],
|
|
196741
|
+
"preview": {
|
|
196742
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.fn(\"preview\")(function*() {\n return yield* Effect.succeed(1)\n})()\n",
|
|
196743
|
+
"diagnostics": [{
|
|
196744
|
+
"start": 64,
|
|
196745
|
+
"end": 137,
|
|
196746
|
+
"text": "Effect.fn returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead with Effect.withSpan(\"preview\") piped in the end to maintain tracing spans. effect(effectFnIife)"
|
|
196747
|
+
}]
|
|
196748
|
+
}
|
|
196508
196749
|
},
|
|
196509
196750
|
{
|
|
196510
|
-
"name": "
|
|
196511
|
-
"
|
|
196512
|
-
"
|
|
196513
|
-
"
|
|
196751
|
+
"name": "effectGenUsesAdapter",
|
|
196752
|
+
"group": "antipattern",
|
|
196753
|
+
"description": "Warns when using the deprecated adapter parameter in Effect.gen",
|
|
196754
|
+
"defaultSeverity": "warning",
|
|
196755
|
+
"fixable": false,
|
|
196756
|
+
"supportedEffect": ["v3", "v4"],
|
|
196757
|
+
"codes": [377027],
|
|
196758
|
+
"preview": {
|
|
196759
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*(_) {\n return yield* Effect.succeed(1)\n})\n",
|
|
196760
|
+
"diagnostics": [{
|
|
196761
|
+
"start": 85,
|
|
196762
|
+
"end": 86,
|
|
196763
|
+
"text": "The adapter of Effect.gen is not required anymore, it is now just an alias of pipe. effect(effectGenUsesAdapter)"
|
|
196764
|
+
}]
|
|
196765
|
+
}
|
|
196514
196766
|
},
|
|
196515
196767
|
{
|
|
196516
|
-
"name": "
|
|
196517
|
-
"
|
|
196768
|
+
"name": "effectInFailure",
|
|
196769
|
+
"group": "antipattern",
|
|
196770
|
+
"description": "Warns when an Effect is used inside an Effect failure channel",
|
|
196518
196771
|
"defaultSeverity": "warning",
|
|
196519
|
-
"
|
|
196772
|
+
"fixable": false,
|
|
196773
|
+
"supportedEffect": ["v3", "v4"],
|
|
196774
|
+
"codes": [377054],
|
|
196775
|
+
"preview": {
|
|
196776
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.try({\n try: () => JSON.parse(\"{\"),\n catch: (error) => Effect.logError(error)\n})\n",
|
|
196777
|
+
"diagnostics": [{
|
|
196778
|
+
"start": 46,
|
|
196779
|
+
"end": 53,
|
|
196780
|
+
"text": "The error channel contains an Effect (Effect<void, never, never>). Putting Effect computations in the failure channel is not intended; keep only failure types there. effect(effectInFailure)"
|
|
196781
|
+
}, {
|
|
196782
|
+
"start": 56,
|
|
196783
|
+
"end": 144,
|
|
196784
|
+
"text": "The error channel contains an Effect (Effect<void, never, never>). Putting Effect computations in the failure channel is not intended; keep only failure types there. effect(effectInFailure)"
|
|
196785
|
+
}]
|
|
196786
|
+
}
|
|
196787
|
+
},
|
|
196788
|
+
{
|
|
196789
|
+
"name": "effectInVoidSuccess",
|
|
196790
|
+
"group": "antipattern",
|
|
196791
|
+
"description": "Detects nested Effects in void success channels that may cause unexecuted effects",
|
|
196792
|
+
"defaultSeverity": "warning",
|
|
196793
|
+
"fixable": false,
|
|
196794
|
+
"supportedEffect": ["v3", "v4"],
|
|
196795
|
+
"codes": [377020],
|
|
196796
|
+
"preview": {
|
|
196797
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview: Effect.Effect<void> = Effect.succeed(\n Effect.log(\"nested\")\n)\n",
|
|
196798
|
+
"diagnostics": [{
|
|
196799
|
+
"start": 54,
|
|
196800
|
+
"end": 61,
|
|
196801
|
+
"text": "There is a nested 'Effect<void, never, never>' in the 'void' success channel, beware that this could lead to nested Effect<Effect<...>> that won't be executed. effect(effectInVoidSuccess)"
|
|
196802
|
+
}]
|
|
196803
|
+
}
|
|
196520
196804
|
},
|
|
196521
196805
|
{
|
|
196522
196806
|
"name": "globalErrorInEffectCatch",
|
|
196807
|
+
"group": "antipattern",
|
|
196523
196808
|
"description": "Warns when catch callbacks return global Error type instead of typed errors",
|
|
196524
196809
|
"defaultSeverity": "warning",
|
|
196525
|
-
"
|
|
196810
|
+
"fixable": false,
|
|
196811
|
+
"supportedEffect": ["v3", "v4"],
|
|
196812
|
+
"codes": [377022],
|
|
196813
|
+
"preview": {
|
|
196814
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.tryPromise({\n try: async () => 1,\n catch: (error) => new Error(String(error))\n})\n",
|
|
196815
|
+
"diagnostics": [{
|
|
196816
|
+
"start": 56,
|
|
196817
|
+
"end": 73,
|
|
196818
|
+
"text": "The 'catch' callback in Effect.tryPromise returns global 'Error', which loses type safety as untagged errors merge together. Consider using a tagged error and optionally wrapping the original in a 'cause' property. effect(globalErrorInEffectCatch)"
|
|
196819
|
+
}]
|
|
196820
|
+
}
|
|
196526
196821
|
},
|
|
196527
196822
|
{
|
|
196528
196823
|
"name": "globalErrorInEffectFailure",
|
|
196824
|
+
"group": "antipattern",
|
|
196529
196825
|
"description": "Warns when the global Error type is used in an Effect failure channel",
|
|
196530
196826
|
"defaultSeverity": "warning",
|
|
196531
|
-
"
|
|
196532
|
-
|
|
196533
|
-
|
|
196534
|
-
"
|
|
196535
|
-
|
|
196536
|
-
|
|
196537
|
-
|
|
196827
|
+
"fixable": false,
|
|
196828
|
+
"supportedEffect": ["v3", "v4"],
|
|
196829
|
+
"codes": [377023],
|
|
196830
|
+
"preview": {
|
|
196831
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.fail(new Error(\"boom\"))\n",
|
|
196832
|
+
"diagnostics": [{
|
|
196833
|
+
"start": 68,
|
|
196834
|
+
"end": 85,
|
|
196835
|
+
"text": "Global 'Error' loses type safety as untagged errors merge together in the Effect failure channel. Consider using a tagged error and optionally wrapping the original in a 'cause' property. effect(globalErrorInEffectFailure)"
|
|
196836
|
+
}]
|
|
196837
|
+
}
|
|
196538
196838
|
},
|
|
196539
196839
|
{
|
|
196540
196840
|
"name": "layerMergeAllWithDependencies",
|
|
196841
|
+
"group": "antipattern",
|
|
196541
196842
|
"description": "Detects interdependencies in Layer.mergeAll calls where one layer provides a service that another layer requires",
|
|
196542
196843
|
"defaultSeverity": "warning",
|
|
196543
|
-
"
|
|
196844
|
+
"fixable": true,
|
|
196845
|
+
"supportedEffect": ["v3", "v4"],
|
|
196846
|
+
"codes": [377035],
|
|
196847
|
+
"preview": {
|
|
196848
|
+
"sourceText": "import { Effect, Layer, ServiceMap } from \"effect\"\n\nclass A extends ServiceMap.Service<A>()(\"A\", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nclass B extends ServiceMap.Service<B>()(\"B\", { make: Effect.as(A.asEffect(), {}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Layer.mergeAll(A.Default, B.Default)\n",
|
|
196849
|
+
"diagnostics": [{
|
|
196850
|
+
"start": 355,
|
|
196851
|
+
"end": 364,
|
|
196852
|
+
"text": "This layer provides A which is required by another layer in the same Layer.mergeAll call. Layer.mergeAll creates layers in parallel, so dependencies between layers will not be satisfied. Consider moving this layer into a Layer.provideMerge after the Layer.mergeAll. effect(layerMergeAllWithDependencies)"
|
|
196853
|
+
}]
|
|
196854
|
+
}
|
|
196544
196855
|
},
|
|
196545
196856
|
{
|
|
196546
196857
|
"name": "leakingRequirements",
|
|
196858
|
+
"group": "antipattern",
|
|
196547
196859
|
"description": "Detects implementation services leaked in service methods",
|
|
196548
196860
|
"defaultSeverity": "suggestion",
|
|
196549
|
-
"
|
|
196861
|
+
"fixable": false,
|
|
196862
|
+
"supportedEffect": ["v3", "v4"],
|
|
196863
|
+
"codes": [377041],
|
|
196864
|
+
"preview": {
|
|
196865
|
+
"sourceText": "import type { Effect } from \"effect\"\nimport { ServiceMap } from \"effect\"\n\nclass FileSystem extends ServiceMap.Service<FileSystem, {\n write: (s: string) => Effect.Effect<void>\n}>()(\"FileSystem\") {}\n\nexport class Cache extends ServiceMap.Service<Cache, {\n read: Effect.Effect<string, never, FileSystem>\n save: () => Effect.Effect<void, never, FileSystem>\n}>()(\"Cache\") {}\n",
|
|
196866
|
+
"diagnostics": [{
|
|
196867
|
+
"start": 212,
|
|
196868
|
+
"end": 217,
|
|
196869
|
+
"text": "Methods of this Service require 'FileSystem' from every caller. This leaks implementation details — resolve these dependencies at Layer creation time. effect(leakingRequirements)"
|
|
196870
|
+
}]
|
|
196871
|
+
}
|
|
196550
196872
|
},
|
|
196551
196873
|
{
|
|
196552
|
-
"name": "
|
|
196553
|
-
"
|
|
196554
|
-
"
|
|
196555
|
-
"
|
|
196874
|
+
"name": "multipleEffectProvide",
|
|
196875
|
+
"group": "antipattern",
|
|
196876
|
+
"description": "Warns against chaining Effect.provide calls which can cause service lifecycle issues",
|
|
196877
|
+
"defaultSeverity": "warning",
|
|
196878
|
+
"fixable": true,
|
|
196879
|
+
"supportedEffect": ["v3", "v4"],
|
|
196880
|
+
"codes": [377033],
|
|
196881
|
+
"preview": {
|
|
196882
|
+
"sourceText": "import { Effect, Layer, ServiceMap } from \"effect\"\n\nclass A extends ServiceMap.Service<A>()(\"A\", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nclass B extends ServiceMap.Service<B>()(\"B\", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Effect.void.pipe(Effect.provide(A.Default), Effect.provide(B.Default))\n",
|
|
196883
|
+
"diagnostics": [{
|
|
196884
|
+
"start": 348,
|
|
196885
|
+
"end": 373,
|
|
196886
|
+
"text": "Avoid chaining Effect.provide calls, as this can lead to service lifecycle issues. Instead, merge layers and provide them in a single call. effect(multipleEffectProvide)"
|
|
196887
|
+
}]
|
|
196888
|
+
}
|
|
196556
196889
|
},
|
|
196557
196890
|
{
|
|
196558
|
-
"name": "
|
|
196559
|
-
"
|
|
196560
|
-
"
|
|
196561
|
-
"
|
|
196891
|
+
"name": "returnEffectInGen",
|
|
196892
|
+
"group": "antipattern",
|
|
196893
|
+
"description": "Warns when returning an Effect in a generator causes nested Effect<Effect<...>>",
|
|
196894
|
+
"defaultSeverity": "suggestion",
|
|
196895
|
+
"fixable": true,
|
|
196896
|
+
"supportedEffect": ["v3", "v4"],
|
|
196897
|
+
"codes": [377014],
|
|
196898
|
+
"preview": {
|
|
196899
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n return Effect.succeed(1)\n})\n",
|
|
196900
|
+
"diagnostics": [{
|
|
196901
|
+
"start": 91,
|
|
196902
|
+
"end": 97,
|
|
196903
|
+
"text": "You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>.\nMaybe you wanted to return yield* instead?\nNested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes. effect(returnEffectInGen)"
|
|
196904
|
+
}]
|
|
196905
|
+
}
|
|
196562
196906
|
},
|
|
196563
196907
|
{
|
|
196564
|
-
"name": "
|
|
196565
|
-
"
|
|
196566
|
-
"
|
|
196567
|
-
"
|
|
196908
|
+
"name": "runEffectInsideEffect",
|
|
196909
|
+
"group": "antipattern",
|
|
196910
|
+
"description": "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
196911
|
+
"defaultSeverity": "suggestion",
|
|
196912
|
+
"fixable": true,
|
|
196913
|
+
"supportedEffect": ["v3"],
|
|
196914
|
+
"codes": [377024, 377025],
|
|
196915
|
+
"preview": {
|
|
196916
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.gen(function*() {\n const run = () => Effect.runSync(Effect.succeed(1))\n return run()\n})\n",
|
|
196917
|
+
"diagnostics": [{
|
|
196918
|
+
"start": 101,
|
|
196919
|
+
"end": 115,
|
|
196920
|
+
"text": "Using Effect.runSync inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.\nConsider extracting the Runtime by using for example Effect.runtime and then use Runtime.runSync with the extracted runtime instead. effect(runEffectInsideEffect)"
|
|
196921
|
+
}]
|
|
196922
|
+
}
|
|
196568
196923
|
},
|
|
196569
196924
|
{
|
|
196570
|
-
"name": "
|
|
196571
|
-
"
|
|
196572
|
-
"
|
|
196573
|
-
"
|
|
196925
|
+
"name": "schemaSyncInEffect",
|
|
196926
|
+
"group": "antipattern",
|
|
196927
|
+
"description": "Suggests using Effect-based Schema methods instead of sync methods inside Effect generators",
|
|
196928
|
+
"defaultSeverity": "suggestion",
|
|
196929
|
+
"fixable": false,
|
|
196930
|
+
"supportedEffect": ["v3"],
|
|
196931
|
+
"codes": [377037],
|
|
196932
|
+
"preview": {
|
|
196933
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\n\nconst Person = Schema.Struct({ name: Schema.String, age: Schema.Number })\n\nexport const preview = Effect.gen(function*() {\n const input = yield* Effect.succeed({ name: \"Ada\", age: 1 })\n return Schema.decodeSync(Person)(input)\n})\n",
|
|
196934
|
+
"diagnostics": [{
|
|
196935
|
+
"start": 276,
|
|
196936
|
+
"end": 293,
|
|
196937
|
+
"text": "Using Schema.decodeSync inside an Effect generator is not recommended. Use Schema.decode instead to get properly typed error channel. effect(schemaSyncInEffect)"
|
|
196938
|
+
}]
|
|
196939
|
+
}
|
|
196574
196940
|
},
|
|
196575
196941
|
{
|
|
196576
|
-
"name": "
|
|
196577
|
-
"
|
|
196578
|
-
"
|
|
196579
|
-
"
|
|
196942
|
+
"name": "scopeInLayerEffect",
|
|
196943
|
+
"group": "antipattern",
|
|
196944
|
+
"description": "Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements",
|
|
196945
|
+
"defaultSeverity": "warning",
|
|
196946
|
+
"fixable": true,
|
|
196947
|
+
"supportedEffect": ["v3"],
|
|
196948
|
+
"codes": [377031],
|
|
196949
|
+
"preview": {
|
|
196950
|
+
"sourceText": "import * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\nclass Cache extends Context.Tag(\"Cache\")<Cache, { ok: true }>() {}\nexport const preview = Layer.effect(Cache, Effect.gen(function*() {\n yield* Effect.addFinalizer(() => Effect.void)\n return { ok: true as const }\n}))\n",
|
|
196951
|
+
"diagnostics": [{
|
|
196952
|
+
"start": 211,
|
|
196953
|
+
"end": 338,
|
|
196954
|
+
"text": "Seems like you are constructing a layer with a scope in the requirements.\nConsider using \"scoped\" instead to get rid of the scope in the requirements. effect(scopeInLayerEffect)"
|
|
196955
|
+
}]
|
|
196956
|
+
}
|
|
196580
196957
|
},
|
|
196581
196958
|
{
|
|
196582
|
-
"name": "
|
|
196583
|
-
"
|
|
196584
|
-
"
|
|
196585
|
-
"
|
|
196959
|
+
"name": "strictEffectProvide",
|
|
196960
|
+
"group": "antipattern",
|
|
196961
|
+
"description": "Warns when using Effect.provide with layers outside of application entry points",
|
|
196962
|
+
"defaultSeverity": "off",
|
|
196963
|
+
"fixable": false,
|
|
196964
|
+
"supportedEffect": ["v3", "v4"],
|
|
196965
|
+
"codes": [377032],
|
|
196966
|
+
"preview": {
|
|
196967
|
+
"sourceText": "import { Effect, Layer, ServiceMap } from \"effect\"\n\nclass Config extends ServiceMap.Service<Config>()(\"Config\", { make: Effect.succeed({}) }) {\n static Default = Layer.effect(this, this.make)\n}\nexport const preview = Effect.void.pipe(Effect.provide(Config.Default))\n",
|
|
196968
|
+
"diagnostics": [{
|
|
196969
|
+
"start": 235,
|
|
196970
|
+
"end": 265,
|
|
196971
|
+
"text": "Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide)"
|
|
196972
|
+
}]
|
|
196973
|
+
}
|
|
196586
196974
|
},
|
|
196587
196975
|
{
|
|
196588
|
-
"name": "
|
|
196589
|
-
"
|
|
196590
|
-
"
|
|
196591
|
-
"
|
|
196976
|
+
"name": "tryCatchInEffectGen",
|
|
196977
|
+
"group": "antipattern",
|
|
196978
|
+
"description": "Discourages try/catch in Effect generators in favor of Effect error handling",
|
|
196979
|
+
"defaultSeverity": "suggestion",
|
|
196980
|
+
"fixable": false,
|
|
196981
|
+
"supportedEffect": ["v3", "v4"],
|
|
196982
|
+
"codes": [377012],
|
|
196983
|
+
"preview": {
|
|
196984
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n try { return yield* Effect.succeed(1) }\n catch { return 0 }\n})\n",
|
|
196985
|
+
"diagnostics": [{
|
|
196986
|
+
"start": 91,
|
|
196987
|
+
"end": 151,
|
|
196988
|
+
"text": "Avoid using try/catch inside Effect generators. Use Effect's error handling mechanisms instead (e.g. Effect.try, Effect.tryPromise, Effect.catch, Effect.catchTag). effect(tryCatchInEffectGen)"
|
|
196989
|
+
}]
|
|
196990
|
+
}
|
|
196592
196991
|
},
|
|
196593
196992
|
{
|
|
196594
|
-
"name": "
|
|
196595
|
-
"
|
|
196993
|
+
"name": "unknownInEffectCatch",
|
|
196994
|
+
"group": "antipattern",
|
|
196995
|
+
"description": "Warns when catch callbacks return unknown instead of typed errors",
|
|
196596
196996
|
"defaultSeverity": "warning",
|
|
196597
|
-
"
|
|
196997
|
+
"fixable": false,
|
|
196998
|
+
"supportedEffect": ["v3", "v4"],
|
|
196999
|
+
"codes": [377021],
|
|
197000
|
+
"preview": {
|
|
197001
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.tryPromise({\n try: async () => 1,\n catch: (error) => error\n})\n",
|
|
197002
|
+
"diagnostics": [{
|
|
197003
|
+
"start": 56,
|
|
197004
|
+
"end": 73,
|
|
197005
|
+
"text": "The 'catch' callback in Effect.tryPromise returns 'unknown'. The catch callback should be used to provide typed errors.\nConsider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised. effect(unknownInEffectCatch)"
|
|
197006
|
+
}]
|
|
197007
|
+
}
|
|
196598
197008
|
},
|
|
196599
197009
|
{
|
|
196600
|
-
"name": "
|
|
196601
|
-
"
|
|
197010
|
+
"name": "extendsNativeError",
|
|
197011
|
+
"group": "effectNative",
|
|
197012
|
+
"description": "Warns when a class directly extends the native Error class",
|
|
196602
197013
|
"defaultSeverity": "off",
|
|
196603
|
-
"
|
|
197014
|
+
"fixable": false,
|
|
197015
|
+
"supportedEffect": ["v3", "v4"],
|
|
197016
|
+
"codes": [377055],
|
|
197017
|
+
"preview": {
|
|
197018
|
+
"sourceText": "\nexport class PreviewError extends Error {}\n",
|
|
197019
|
+
"diagnostics": [{
|
|
197020
|
+
"start": 14,
|
|
197021
|
+
"end": 26,
|
|
197022
|
+
"text": "Avoid extending the native 'Error' class directly. Consider using a tagged error (e.g. Data.TaggedError) to maintain type safety in the Effect failure channel. effect(extendsNativeError)"
|
|
197023
|
+
}]
|
|
197024
|
+
}
|
|
196604
197025
|
},
|
|
196605
197026
|
{
|
|
196606
|
-
"name": "
|
|
196607
|
-
"
|
|
196608
|
-
"
|
|
196609
|
-
"
|
|
197027
|
+
"name": "globalFetch",
|
|
197028
|
+
"group": "effectNative",
|
|
197029
|
+
"description": "Warns when using the global fetch function instead of the Effect HTTP client",
|
|
197030
|
+
"defaultSeverity": "off",
|
|
197031
|
+
"fixable": false,
|
|
197032
|
+
"supportedEffect": ["v3", "v4"],
|
|
197033
|
+
"codes": [377061],
|
|
197034
|
+
"preview": {
|
|
197035
|
+
"sourceText": "\nimport { Effect } from \"effect\"\n\nvoid Effect\n\nexport const preview = fetch(\"https://example.com\")\n",
|
|
197036
|
+
"diagnostics": [{
|
|
197037
|
+
"start": 70,
|
|
197038
|
+
"end": 75,
|
|
197039
|
+
"text": "Prefer using HttpClient from effect/unstable/http instead of the global 'fetch' function. effect(globalFetch)"
|
|
197040
|
+
}]
|
|
197041
|
+
}
|
|
196610
197042
|
},
|
|
196611
197043
|
{
|
|
196612
|
-
"name": "
|
|
196613
|
-
"
|
|
196614
|
-
"
|
|
196615
|
-
"
|
|
197044
|
+
"name": "instanceOfSchema",
|
|
197045
|
+
"group": "effectNative",
|
|
197046
|
+
"description": "Suggests using Schema.is instead of instanceof for Effect Schema types",
|
|
197047
|
+
"defaultSeverity": "off",
|
|
197048
|
+
"fixable": true,
|
|
197049
|
+
"supportedEffect": ["v3", "v4"],
|
|
197050
|
+
"codes": [377042],
|
|
197051
|
+
"preview": {
|
|
197052
|
+
"sourceText": "import { Schema } from \"effect\"\n\nclass User extends Schema.Class<User>(\"User\")({ name: Schema.String }) {}\ndeclare const value: unknown\nexport const preview = value instanceof User\n",
|
|
197053
|
+
"diagnostics": [{
|
|
197054
|
+
"start": 159,
|
|
197055
|
+
"end": 180,
|
|
197056
|
+
"text": "Consider using Schema.is instead of instanceof for Effect Schema types. effect(instanceOfSchema)"
|
|
197057
|
+
}]
|
|
197058
|
+
}
|
|
196616
197059
|
},
|
|
196617
197060
|
{
|
|
196618
|
-
"name": "
|
|
196619
|
-
"
|
|
196620
|
-
"
|
|
196621
|
-
"
|
|
197061
|
+
"name": "nodeBuiltinImport",
|
|
197062
|
+
"group": "effectNative",
|
|
197063
|
+
"description": "Warns when importing Node.js built-in modules that have Effect-native counterparts",
|
|
197064
|
+
"defaultSeverity": "off",
|
|
197065
|
+
"fixable": false,
|
|
197066
|
+
"supportedEffect": ["v3", "v4"],
|
|
197067
|
+
"codes": [377057],
|
|
197068
|
+
"preview": {
|
|
197069
|
+
"sourceText": "import fs from \"node:fs\"\n\nexport const preview = fs.readFileSync\n",
|
|
197070
|
+
"diagnostics": [{
|
|
197071
|
+
"start": 15,
|
|
197072
|
+
"end": 24,
|
|
197073
|
+
"text": "Prefer using FileSystem from @effect/platform instead of the Node.js 'fs' module. effect(nodeBuiltinImport)"
|
|
197074
|
+
}]
|
|
197075
|
+
}
|
|
196622
197076
|
},
|
|
196623
197077
|
{
|
|
196624
197078
|
"name": "preferSchemaOverJson",
|
|
197079
|
+
"group": "effectNative",
|
|
196625
197080
|
"description": "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify",
|
|
196626
197081
|
"defaultSeverity": "suggestion",
|
|
196627
|
-
"
|
|
197082
|
+
"fixable": false,
|
|
197083
|
+
"supportedEffect": ["v3", "v4"],
|
|
197084
|
+
"codes": [377026],
|
|
197085
|
+
"preview": {
|
|
197086
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.gen(function*() {\n const text = yield* Effect.succeed('{\"ok\":true}')\n return JSON.parse(text)\n})\n",
|
|
197087
|
+
"diagnostics": [{
|
|
197088
|
+
"start": 142,
|
|
197089
|
+
"end": 158,
|
|
197090
|
+
"text": "Consider using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify. effect(preferSchemaOverJson)"
|
|
197091
|
+
}]
|
|
197092
|
+
}
|
|
196628
197093
|
},
|
|
196629
197094
|
{
|
|
196630
|
-
"name": "
|
|
196631
|
-
"
|
|
197095
|
+
"name": "catchAllToMapError",
|
|
197096
|
+
"group": "style",
|
|
197097
|
+
"description": "Suggests using Effect.mapError instead of Effect.catch + Effect.fail",
|
|
196632
197098
|
"defaultSeverity": "suggestion",
|
|
196633
|
-
"
|
|
197099
|
+
"fixable": true,
|
|
197100
|
+
"supportedEffect": ["v3", "v4"],
|
|
197101
|
+
"codes": [377010],
|
|
197102
|
+
"preview": {
|
|
197103
|
+
"sourceText": "import { Effect } from \"effect\"\n\nclass WrappedError {\n constructor(readonly cause: unknown) {}\n}\n\nexport const preview = Effect.fail(\"boom\").pipe(\n Effect.catch((cause) => Effect.fail(new WrappedError(cause)))\n)\n",
|
|
197104
|
+
"diagnostics": [{
|
|
197105
|
+
"start": 150,
|
|
197106
|
+
"end": 162,
|
|
197107
|
+
"text": "You can use Effect.mapError instead of Effect.catch + Effect.fail to transform the error type. effect(catchAllToMapError)"
|
|
197108
|
+
}]
|
|
197109
|
+
}
|
|
196634
197110
|
},
|
|
196635
197111
|
{
|
|
196636
|
-
"name": "
|
|
196637
|
-
"
|
|
196638
|
-
"
|
|
196639
|
-
"
|
|
197112
|
+
"name": "deterministicKeys",
|
|
197113
|
+
"group": "style",
|
|
197114
|
+
"description": "Enforces deterministic naming for service/tag/error identifiers based on class names",
|
|
197115
|
+
"defaultSeverity": "off",
|
|
197116
|
+
"fixable": true,
|
|
197117
|
+
"supportedEffect": ["v3", "v4"],
|
|
197118
|
+
"codes": [377049],
|
|
197119
|
+
"preview": {
|
|
197120
|
+
"sourceText": "import { ServiceMap } from \"effect\"\n\nexport class RenamedService\n extends ServiceMap.Service<RenamedService, {}>()(\"CustomIdentifier\") {}\n",
|
|
197121
|
+
"diagnostics": []
|
|
197122
|
+
}
|
|
196640
197123
|
},
|
|
196641
197124
|
{
|
|
196642
|
-
"name": "
|
|
196643
|
-
"
|
|
197125
|
+
"name": "effectFnOpportunity",
|
|
197126
|
+
"group": "style",
|
|
197127
|
+
"description": "Suggests using Effect.fn for functions that return an Effect",
|
|
196644
197128
|
"defaultSeverity": "suggestion",
|
|
196645
|
-
"
|
|
197129
|
+
"fixable": true,
|
|
197130
|
+
"supportedEffect": ["v3", "v4"],
|
|
197131
|
+
"codes": [377047],
|
|
197132
|
+
"preview": {
|
|
197133
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = () => Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n",
|
|
197134
|
+
"diagnostics": [{
|
|
197135
|
+
"start": 54,
|
|
197136
|
+
"end": 61,
|
|
197137
|
+
"text": "Can be rewritten as a reusable function: Effect.fn(function*() { ... }). effect(effectFnOpportunity)"
|
|
197138
|
+
}]
|
|
197139
|
+
}
|
|
196646
197140
|
},
|
|
196647
197141
|
{
|
|
196648
|
-
"name": "
|
|
196649
|
-
"
|
|
197142
|
+
"name": "effectMapVoid",
|
|
197143
|
+
"group": "style",
|
|
197144
|
+
"description": "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
196650
197145
|
"defaultSeverity": "suggestion",
|
|
196651
|
-
"
|
|
197146
|
+
"fixable": true,
|
|
197147
|
+
"supportedEffect": ["v3", "v4"],
|
|
197148
|
+
"codes": [377018],
|
|
197149
|
+
"preview": {
|
|
197150
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.succeed(1).pipe(\n Effect.map(() => undefined)\n)\n",
|
|
197151
|
+
"diagnostics": [{
|
|
197152
|
+
"start": 82,
|
|
197153
|
+
"end": 92,
|
|
197154
|
+
"text": "Effect.asVoid can be used instead to discard the success value. effect(effectMapVoid)"
|
|
197155
|
+
}]
|
|
197156
|
+
}
|
|
196652
197157
|
},
|
|
196653
197158
|
{
|
|
196654
|
-
"name": "
|
|
196655
|
-
"
|
|
197159
|
+
"name": "effectSucceedWithVoid",
|
|
197160
|
+
"group": "style",
|
|
197161
|
+
"description": "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
196656
197162
|
"defaultSeverity": "suggestion",
|
|
196657
|
-
"
|
|
197163
|
+
"fixable": true,
|
|
197164
|
+
"supportedEffect": ["v3", "v4"],
|
|
197165
|
+
"codes": [377016],
|
|
197166
|
+
"preview": {
|
|
197167
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.succeed(undefined)\n",
|
|
197168
|
+
"diagnostics": [{
|
|
197169
|
+
"start": 56,
|
|
197170
|
+
"end": 81,
|
|
197171
|
+
"text": "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0). effect(effectSucceedWithVoid)"
|
|
197172
|
+
}]
|
|
197173
|
+
}
|
|
196658
197174
|
},
|
|
196659
197175
|
{
|
|
196660
|
-
"name": "
|
|
196661
|
-
"
|
|
197176
|
+
"name": "missedPipeableOpportunity",
|
|
197177
|
+
"group": "style",
|
|
197178
|
+
"description": "Suggests using .pipe() for nested function calls",
|
|
196662
197179
|
"defaultSeverity": "off",
|
|
196663
|
-
"
|
|
197180
|
+
"fixable": true,
|
|
197181
|
+
"supportedEffect": ["v3", "v4"],
|
|
197182
|
+
"codes": [377050],
|
|
197183
|
+
"preview": {
|
|
197184
|
+
"sourceText": "import { identity, Schema } from \"effect\"\n\nconst User = Schema.Struct({ id: Schema.Number })\nexport const preview = identity(identity(Schema.decodeEffect(User)({ id: 1 })))\n",
|
|
197185
|
+
"diagnostics": [{
|
|
197186
|
+
"start": 116,
|
|
197187
|
+
"end": 172,
|
|
197188
|
+
"text": "Nested function calls can be converted to pipeable style for better readability; consider using Schema.decodeEffect(User)({ id: 1 }).pipe(...) instead. effect(missedPipeableOpportunity)"
|
|
197189
|
+
}]
|
|
197190
|
+
}
|
|
196664
197191
|
},
|
|
196665
197192
|
{
|
|
196666
|
-
"name": "
|
|
196667
|
-
"
|
|
196668
|
-
"
|
|
196669
|
-
"
|
|
197193
|
+
"name": "missingEffectServiceDependency",
|
|
197194
|
+
"group": "style",
|
|
197195
|
+
"description": "Checks that Effect.Service dependencies satisfy all required layer inputs",
|
|
197196
|
+
"defaultSeverity": "off",
|
|
197197
|
+
"fixable": false,
|
|
197198
|
+
"supportedEffect": ["v3"],
|
|
197199
|
+
"codes": [377039, 377040],
|
|
197200
|
+
"preview": {
|
|
197201
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nclass Db extends Effect.Service<Db>()(\"Db\", { succeed: { ok: true } }) {}\nexport class Repo extends Effect.Service<Repo>()(\"Repo\", {\n effect: Effect.gen(function*() {\n yield* Db\n return { all: Effect.succeed([] as Array<number>) }\n })\n}) {}\n",
|
|
197202
|
+
"diagnostics": [{
|
|
197203
|
+
"start": 128,
|
|
197204
|
+
"end": 132,
|
|
197205
|
+
"text": "Service 'Db' is required but not provided by dependencies. effect(missingEffectServiceDependency)"
|
|
197206
|
+
}]
|
|
197207
|
+
}
|
|
196670
197208
|
},
|
|
196671
197209
|
{
|
|
196672
|
-
"name": "
|
|
196673
|
-
"
|
|
196674
|
-
"
|
|
196675
|
-
"
|
|
197210
|
+
"name": "redundantSchemaTagIdentifier",
|
|
197211
|
+
"group": "style",
|
|
197212
|
+
"description": "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
197213
|
+
"defaultSeverity": "suggestion",
|
|
197214
|
+
"fixable": true,
|
|
197215
|
+
"supportedEffect": ["v3", "v4"],
|
|
197216
|
+
"codes": [377045],
|
|
197217
|
+
"preview": {
|
|
197218
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport class Preview\n extends Schema.TaggedClass<Preview>(\"Preview\")(\"Preview\", {\n value: Schema.String\n }) {}\n",
|
|
197219
|
+
"diagnostics": [{
|
|
197220
|
+
"start": 100,
|
|
197221
|
+
"end": 109,
|
|
197222
|
+
"text": "Identifier 'Preview' is redundant since it equals the _tag value. effect(redundantSchemaTagIdentifier)"
|
|
197223
|
+
}]
|
|
197224
|
+
}
|
|
196676
197225
|
},
|
|
196677
197226
|
{
|
|
196678
|
-
"name": "
|
|
196679
|
-
"
|
|
196680
|
-
"
|
|
196681
|
-
"
|
|
197227
|
+
"name": "schemaStructWithTag",
|
|
197228
|
+
"group": "style",
|
|
197229
|
+
"description": "Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field",
|
|
197230
|
+
"defaultSeverity": "suggestion",
|
|
197231
|
+
"fixable": true,
|
|
197232
|
+
"supportedEffect": ["v3", "v4"],
|
|
197233
|
+
"codes": [377036],
|
|
197234
|
+
"preview": {
|
|
197235
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport const preview = Schema.Struct({\n _tag: Schema.Literal(\"User\"),\n name: Schema.String\n})\n",
|
|
197236
|
+
"diagnostics": [{
|
|
197237
|
+
"start": 64,
|
|
197238
|
+
"end": 136,
|
|
197239
|
+
"text": "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor. effect(schemaStructWithTag)"
|
|
197240
|
+
}]
|
|
197241
|
+
}
|
|
196682
197242
|
},
|
|
196683
197243
|
{
|
|
196684
|
-
"name": "
|
|
196685
|
-
"
|
|
197244
|
+
"name": "schemaUnionOfLiterals",
|
|
197245
|
+
"group": "style",
|
|
197246
|
+
"description": "Suggests combining multiple Schema.Literal calls in Schema.Union into a single Schema.Literal",
|
|
196686
197247
|
"defaultSeverity": "off",
|
|
196687
|
-
"
|
|
197248
|
+
"fixable": true,
|
|
197249
|
+
"supportedEffect": ["v3"],
|
|
197250
|
+
"codes": [377038],
|
|
197251
|
+
"preview": {
|
|
197252
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport const preview = Schema.Union(Schema.Literal(\"a\"), Schema.Literal(\"b\"))\n",
|
|
197253
|
+
"diagnostics": [{
|
|
197254
|
+
"start": 64,
|
|
197255
|
+
"end": 118,
|
|
197256
|
+
"text": "A Schema.Union of multiple Schema.Literal calls can be simplified to a single Schema.Literal call. effect(schemaUnionOfLiterals)"
|
|
197257
|
+
}]
|
|
197258
|
+
}
|
|
196688
197259
|
},
|
|
196689
197260
|
{
|
|
196690
|
-
"name": "
|
|
196691
|
-
"
|
|
196692
|
-
"
|
|
196693
|
-
"
|
|
197261
|
+
"name": "serviceNotAsClass",
|
|
197262
|
+
"group": "style",
|
|
197263
|
+
"description": "Warns when ServiceMap.Service is used as a variable instead of a class declaration",
|
|
197264
|
+
"defaultSeverity": "off",
|
|
197265
|
+
"fixable": true,
|
|
197266
|
+
"supportedEffect": ["v4"],
|
|
197267
|
+
"codes": [377056],
|
|
197268
|
+
"preview": {
|
|
197269
|
+
"sourceText": "import { ServiceMap } from \"effect\"\n\nexport const Preview = ServiceMap.Service<{ port: number }>(\"Preview\")\n",
|
|
197270
|
+
"diagnostics": [{
|
|
197271
|
+
"start": 60,
|
|
197272
|
+
"end": 107,
|
|
197273
|
+
"text": "ServiceMap.Service should be used in a class declaration instead of as a variable. Use: class Preview extends ServiceMap.Service<Preview, { port: number }>()(\"Preview\") {} effect(serviceNotAsClass)"
|
|
197274
|
+
}]
|
|
197275
|
+
}
|
|
196694
197276
|
},
|
|
196695
197277
|
{
|
|
196696
|
-
"name": "
|
|
196697
|
-
"
|
|
196698
|
-
"
|
|
196699
|
-
"
|
|
197278
|
+
"name": "strictBooleanExpressions",
|
|
197279
|
+
"group": "style",
|
|
197280
|
+
"description": "Enforces boolean types in conditional expressions for type safety",
|
|
197281
|
+
"defaultSeverity": "off",
|
|
197282
|
+
"fixable": false,
|
|
197283
|
+
"supportedEffect": ["v3", "v4"],
|
|
197284
|
+
"codes": [377029],
|
|
197285
|
+
"preview": {
|
|
197286
|
+
"sourceText": "\ndeclare const value: string | undefined\nexport const preview = value ? 1 : 0\n",
|
|
197287
|
+
"diagnostics": [{
|
|
197288
|
+
"start": 64,
|
|
197289
|
+
"end": 69,
|
|
197290
|
+
"text": "Unexpected `string` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
197291
|
+
}, {
|
|
197292
|
+
"start": 64,
|
|
197293
|
+
"end": 69,
|
|
197294
|
+
"text": "Unexpected `undefined` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
197295
|
+
}]
|
|
197296
|
+
}
|
|
196700
197297
|
},
|
|
196701
197298
|
{
|
|
196702
197299
|
"name": "unnecessaryEffectGen",
|
|
197300
|
+
"group": "style",
|
|
196703
197301
|
"description": "Suggests removing Effect.gen when it contains only a single return statement",
|
|
196704
197302
|
"defaultSeverity": "suggestion",
|
|
196705
|
-
"
|
|
197303
|
+
"fixable": true,
|
|
197304
|
+
"supportedEffect": ["v3", "v4"],
|
|
197305
|
+
"codes": [377017],
|
|
197306
|
+
"preview": {
|
|
197307
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n",
|
|
197308
|
+
"diagnostics": [{
|
|
197309
|
+
"start": 64,
|
|
197310
|
+
"end": 125,
|
|
197311
|
+
"text": "This Effect.gen contains a single return statement. effect(unnecessaryEffectGen)"
|
|
197312
|
+
}]
|
|
197313
|
+
}
|
|
196706
197314
|
},
|
|
196707
197315
|
{
|
|
196708
197316
|
"name": "unnecessaryFailYieldableError",
|
|
197317
|
+
"group": "style",
|
|
196709
197318
|
"description": "Suggests yielding yieldable errors directly instead of wrapping with Effect.fail",
|
|
196710
197319
|
"defaultSeverity": "suggestion",
|
|
196711
|
-
"
|
|
197320
|
+
"fixable": true,
|
|
197321
|
+
"supportedEffect": ["v3", "v4"],
|
|
197322
|
+
"codes": [377019],
|
|
197323
|
+
"preview": {
|
|
197324
|
+
"sourceText": "import * as Data from \"effect/Data\"\nimport * as Effect from \"effect/Effect\"\n\nclass Boom extends Data.TaggedError(\"Boom\")<{}> {}\nexport const preview = Effect.gen(function*() {\n yield* Effect.fail(new Boom())\n})\n",
|
|
197325
|
+
"diagnostics": [{
|
|
197326
|
+
"start": 178,
|
|
197327
|
+
"end": 183,
|
|
197328
|
+
"text": "This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead. effect(unnecessaryFailYieldableError)"
|
|
197329
|
+
}]
|
|
197330
|
+
}
|
|
196712
197331
|
},
|
|
196713
197332
|
{
|
|
196714
197333
|
"name": "unnecessaryPipe",
|
|
197334
|
+
"group": "style",
|
|
196715
197335
|
"description": "Removes pipe calls with no arguments",
|
|
196716
197336
|
"defaultSeverity": "suggestion",
|
|
196717
|
-
"
|
|
197337
|
+
"fixable": true,
|
|
197338
|
+
"supportedEffect": ["v3", "v4"],
|
|
197339
|
+
"codes": [377013],
|
|
197340
|
+
"preview": {
|
|
197341
|
+
"sourceText": "import { pipe } from \"effect/Function\"\n\nexport const preview = pipe(1)\n",
|
|
197342
|
+
"diagnostics": [{
|
|
197343
|
+
"start": 63,
|
|
197344
|
+
"end": 70,
|
|
197345
|
+
"text": "This pipe call contains no arguments. effect(unnecessaryPipe)"
|
|
197346
|
+
}]
|
|
197347
|
+
}
|
|
196718
197348
|
},
|
|
196719
197349
|
{
|
|
196720
197350
|
"name": "unnecessaryPipeChain",
|
|
197351
|
+
"group": "style",
|
|
196721
197352
|
"description": "Simplifies chained pipe calls into a single pipe call",
|
|
196722
197353
|
"defaultSeverity": "suggestion",
|
|
196723
|
-
"
|
|
197354
|
+
"fixable": true,
|
|
197355
|
+
"supportedEffect": ["v3", "v4"],
|
|
197356
|
+
"codes": [377015],
|
|
197357
|
+
"preview": {
|
|
197358
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.succeed(1).pipe(Effect.asVoid).pipe(Effect.as(\"done\"))\n",
|
|
197359
|
+
"diagnostics": [{
|
|
197360
|
+
"start": 64,
|
|
197361
|
+
"end": 125,
|
|
197362
|
+
"text": "Chained pipe calls can be simplified to a single pipe call. effect(unnecessaryPipeChain)"
|
|
197363
|
+
}]
|
|
197364
|
+
}
|
|
196724
197365
|
}
|
|
196725
197366
|
];
|
|
196726
197367
|
|
|
196727
197368
|
//#endregion
|
|
196728
197369
|
//#region src/setup/rule-info.ts
|
|
196729
197370
|
function getAllRules() {
|
|
196730
|
-
return
|
|
197371
|
+
return rules;
|
|
197372
|
+
}
|
|
197373
|
+
function getAllGroups() {
|
|
197374
|
+
return groups;
|
|
196731
197375
|
}
|
|
196732
197376
|
function cycleSeverity(current, direction) {
|
|
196733
197377
|
const order = [
|
|
@@ -196758,187 +197402,349 @@ function getSeverityShortName(severity) {
|
|
|
196758
197402
|
const RESET = "\x1B[0m";
|
|
196759
197403
|
const BOLD = "\x1B[0;1m";
|
|
196760
197404
|
const DIM = "\x1B[0;90m";
|
|
196761
|
-
const
|
|
196762
|
-
const
|
|
196763
|
-
const
|
|
196764
|
-
const
|
|
196765
|
-
const
|
|
196766
|
-
const BG_YELLOW = "\x1B[43m";
|
|
196767
|
-
const BG_BLUE = "\x1B[0;44m";
|
|
196768
|
-
const BG_CYAN = "\x1B[0;46m";
|
|
197405
|
+
const RED = "\x1B[0;31m";
|
|
197406
|
+
const YELLOW = "\x1B[0;33m";
|
|
197407
|
+
const BLUE = "\x1B[0;34m";
|
|
197408
|
+
const ITALIC = "\x1B[0;3m";
|
|
197409
|
+
const UNDERLINE = "\x1B[0;4m";
|
|
196769
197410
|
const ansi = (text, code) => `${code}${text}${RESET}`;
|
|
196770
197411
|
const ERASE_LINE = "\x1B[2K";
|
|
196771
197412
|
const CURSOR_LEFT = "\r";
|
|
196772
197413
|
const CURSOR_HIDE = "\x1B[?25l";
|
|
196773
197414
|
const CURSOR_TO_0 = "\x1B[G";
|
|
196774
197415
|
const BEEP = "\x07";
|
|
197416
|
+
const ANSI_ESCAPE_REGEX = new RegExp(String.raw`\u001b\[[0-?]*[ -/]*[@-~]`, "g");
|
|
197417
|
+
const stripAnsi = (text) => text.replace(ANSI_ESCAPE_REGEX, "");
|
|
197418
|
+
const visibleLength = (text) => stripAnsi(text).length;
|
|
196775
197419
|
|
|
196776
197420
|
//#endregion
|
|
196777
197421
|
//#region src/setup/rule-prompt.ts
|
|
196778
|
-
|
|
196779
|
-
let result = "";
|
|
196780
|
-
for (let i = 0; i < count; i++) {
|
|
196781
|
-
if (i > 0) result += "\x1B[1A";
|
|
196782
|
-
result += ERASE_LINE;
|
|
196783
|
-
}
|
|
196784
|
-
if (count > 0) result += CURSOR_LEFT;
|
|
196785
|
-
return result;
|
|
196786
|
-
}
|
|
197422
|
+
const diagnosticGroups = getAllGroups();
|
|
196787
197423
|
const Action = taggedEnum();
|
|
196788
|
-
const
|
|
196789
|
-
|
|
196790
|
-
|
|
196791
|
-
|
|
196792
|
-
const lines = text.split(/\r?\n/);
|
|
196793
|
-
for (const line of lines) rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns);
|
|
196794
|
-
return eraseLines(rows);
|
|
197424
|
+
const SEARCH_ICON = "/";
|
|
197425
|
+
const MIN_PREVIEW_AND_MESSAGES_LINES = 18;
|
|
197426
|
+
function getControlsLegend(searchText) {
|
|
197427
|
+
return `\u2190/\u2192 change rule \u2191/\u2193 change severity ${searchText.length === 0 ? `${SEARCH_ICON} type to search` : `${SEARCH_ICON} searching: ${searchText}`}`;
|
|
196795
197428
|
}
|
|
196796
|
-
function
|
|
196797
|
-
const max = maxVisible === void 0 ? total : maxVisible;
|
|
196798
|
-
let startIndex = Math.min(total - max, cursor - Math.floor(max / 2));
|
|
196799
|
-
if (startIndex < 0) startIndex = 0;
|
|
196800
|
-
const endIndex = Math.min(startIndex + max, total);
|
|
197429
|
+
function getSeveritySymbol(severity) {
|
|
196801
197430
|
return {
|
|
196802
|
-
|
|
196803
|
-
|
|
196804
|
-
|
|
197431
|
+
off: ".",
|
|
197432
|
+
suggestion: "?",
|
|
197433
|
+
message: "i",
|
|
197434
|
+
warning: "!",
|
|
197435
|
+
error: "x"
|
|
197436
|
+
}[severity];
|
|
196805
197437
|
}
|
|
196806
|
-
const figuresValue = {
|
|
196807
|
-
arrowUp: "↑",
|
|
196808
|
-
arrowDown: "↓",
|
|
196809
|
-
tick: "✔",
|
|
196810
|
-
pointerSmall: "›"
|
|
196811
|
-
};
|
|
196812
197438
|
function getSeverityStyle(severity) {
|
|
196813
197439
|
return {
|
|
196814
|
-
off:
|
|
196815
|
-
suggestion:
|
|
196816
|
-
message:
|
|
196817
|
-
warning:
|
|
196818
|
-
error:
|
|
197440
|
+
off: DIM,
|
|
197441
|
+
suggestion: DIM,
|
|
197442
|
+
message: BLUE,
|
|
197443
|
+
warning: YELLOW,
|
|
197444
|
+
error: RED
|
|
196819
197445
|
}[severity];
|
|
196820
197446
|
}
|
|
196821
|
-
function
|
|
196822
|
-
|
|
196823
|
-
|
|
196824
|
-
|
|
196825
|
-
|
|
196826
|
-
|
|
196827
|
-
|
|
197447
|
+
function renderEntry(entry, severity, isSelected) {
|
|
197448
|
+
return `${ansi(getSeveritySymbol(severity), getSeverityStyle(severity))} ${isSelected ? ansi(entry.name, UNDERLINE) : entry.name}`;
|
|
197449
|
+
}
|
|
197450
|
+
function rowsForLength(length, columns) {
|
|
197451
|
+
if (columns <= 0) return 1;
|
|
197452
|
+
return Math.max(1, 1 + Math.floor(Math.max(length - 1, 0) / columns));
|
|
197453
|
+
}
|
|
197454
|
+
function eraseRenderedLines(lines, columns) {
|
|
197455
|
+
let result = "";
|
|
197456
|
+
for (let lineIndex = lines.length - 1; lineIndex >= 0; lineIndex--) {
|
|
197457
|
+
const rows = rowsForLength(visibleLength(lines[lineIndex] ?? ""), columns);
|
|
197458
|
+
for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
|
|
197459
|
+
result += ERASE_LINE;
|
|
197460
|
+
if (!(lineIndex === 0 && rowIndex === rows - 1)) result += "\x1B[1A";
|
|
196828
197461
|
}
|
|
196829
|
-
}
|
|
197462
|
+
}
|
|
197463
|
+
if (lines.length > 0) result += CURSOR_LEFT;
|
|
197464
|
+
return result;
|
|
196830
197465
|
}
|
|
196831
|
-
function
|
|
196832
|
-
|
|
196833
|
-
const
|
|
196834
|
-
|
|
196835
|
-
|
|
196836
|
-
|
|
196837
|
-
const
|
|
196838
|
-
|
|
196839
|
-
|
|
196840
|
-
if (index === toDisplay.startIndex && toDisplay.startIndex > 0) prefix = figs.arrowUp;
|
|
196841
|
-
else if (index === toDisplay.endIndex - 1 && toDisplay.endIndex < options.rules.length) prefix = figs.arrowDown;
|
|
196842
|
-
const severityStr = ansi(` ${getSeverityShortName(currentSeverity).padEnd(MAX_SEVERITY_LENGTH, " ")} `, getSeverityStyle(currentSeverity));
|
|
196843
|
-
const nameText = hasChanged ? `${rule.name}*` : rule.name;
|
|
196844
|
-
const nameStr = isHighlighted ? ansi(nameText, CYAN_BRIGHT) : nameText;
|
|
196845
|
-
const mainLine = `${prefix} ${severityStr} ${nameStr}`;
|
|
196846
|
-
if (isHighlighted && rule.description) {
|
|
196847
|
-
const indentWidth = 2 + (MAX_SEVERITY_LENGTH + 2) + 1;
|
|
196848
|
-
const indent = " ".repeat(indentWidth);
|
|
196849
|
-
const availableWidth = columns - indentWidth;
|
|
196850
|
-
const truncatedDescription = availableWidth > 0 && rule.description.length > availableWidth ? rule.description.substring(0, availableWidth - 1) + "…" : rule.description;
|
|
196851
|
-
documents.push(mainLine + "\n" + ansi(indent + truncatedDescription, DIM));
|
|
196852
|
-
} else documents.push(mainLine);
|
|
197466
|
+
function wrapPaddedText(text, initialPadding, endPadding, columns) {
|
|
197467
|
+
if (text.length === 0) return [initialPadding + endPadding];
|
|
197468
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 1);
|
|
197469
|
+
const lines = [];
|
|
197470
|
+
let remaining = text;
|
|
197471
|
+
while (remaining.length > 0) {
|
|
197472
|
+
const chunk = remaining.slice(0, available);
|
|
197473
|
+
lines.push(initialPadding + chunk + endPadding);
|
|
197474
|
+
remaining = remaining.slice(chunk.length);
|
|
196853
197475
|
}
|
|
196854
|
-
return
|
|
197476
|
+
return lines;
|
|
196855
197477
|
}
|
|
196856
|
-
function
|
|
196857
|
-
|
|
196858
|
-
|
|
196859
|
-
|
|
196860
|
-
|
|
196861
|
-
|
|
197478
|
+
function wrapListItemText(text, firstLinePadding, continuationPadding, endPadding, columns) {
|
|
197479
|
+
if (text.length === 0) return [firstLinePadding + endPadding];
|
|
197480
|
+
const firstAvailable = Math.max(columns - visibleLength(firstLinePadding) - visibleLength(endPadding), 1);
|
|
197481
|
+
const continuationAvailable = Math.max(columns - visibleLength(continuationPadding) - visibleLength(endPadding), 1);
|
|
197482
|
+
const lines = [];
|
|
197483
|
+
let remaining = text;
|
|
197484
|
+
let isFirstLine = true;
|
|
197485
|
+
while (remaining.length > 0) {
|
|
197486
|
+
const padding = isFirstLine ? firstLinePadding : continuationPadding;
|
|
197487
|
+
const available = isFirstLine ? firstAvailable : continuationAvailable;
|
|
197488
|
+
const chunk = remaining.slice(0, available);
|
|
197489
|
+
lines.push(padding + chunk + endPadding);
|
|
197490
|
+
remaining = remaining.slice(chunk.length);
|
|
197491
|
+
isFirstLine = false;
|
|
197492
|
+
}
|
|
197493
|
+
return lines;
|
|
197494
|
+
}
|
|
197495
|
+
function renderPaddedLine(text, initialPadding, endPadding, columns) {
|
|
197496
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 0);
|
|
197497
|
+
const truncated = visibleLength(text) <= available ? text : text.slice(0, available);
|
|
197498
|
+
const padding = Math.max(available - visibleLength(truncated), 0);
|
|
197499
|
+
return initialPadding + truncated + " ".repeat(padding) + endPadding;
|
|
197500
|
+
}
|
|
197501
|
+
function mergeHighlightRanges(ranges) {
|
|
197502
|
+
const sorted = ranges.filter((range) => range.end > range.start).slice().sort((a, b) => a.start - b.start);
|
|
197503
|
+
const merged = [];
|
|
197504
|
+
for (const range of sorted) {
|
|
197505
|
+
const previous = merged[merged.length - 1];
|
|
197506
|
+
if (!previous || range.start > previous.end) {
|
|
197507
|
+
merged.push(range);
|
|
197508
|
+
continue;
|
|
197509
|
+
}
|
|
197510
|
+
merged[merged.length - 1] = {
|
|
197511
|
+
start: previous.start,
|
|
197512
|
+
end: Math.max(previous.end, range.end)
|
|
197513
|
+
};
|
|
197514
|
+
}
|
|
197515
|
+
return merged;
|
|
197516
|
+
}
|
|
197517
|
+
function stylePreviewLine(line, lineStart, ranges) {
|
|
197518
|
+
if (line.length === 0) return "";
|
|
197519
|
+
const lineEnd = lineStart + line.length;
|
|
197520
|
+
let cursor = 0;
|
|
197521
|
+
let rendered = "";
|
|
197522
|
+
for (const range of ranges) {
|
|
197523
|
+
const start = Math.max(range.start, lineStart);
|
|
197524
|
+
const end = Math.min(range.end, lineEnd);
|
|
197525
|
+
if (end <= start) continue;
|
|
197526
|
+
const startIndex = start - lineStart;
|
|
197527
|
+
const endIndex = end - lineStart;
|
|
197528
|
+
if (cursor < startIndex) rendered += ansi(line.slice(cursor, startIndex), DIM);
|
|
197529
|
+
rendered += ansi(line.slice(startIndex, endIndex), UNDERLINE);
|
|
197530
|
+
cursor = endIndex;
|
|
197531
|
+
}
|
|
197532
|
+
if (cursor < line.length) rendered += ansi(line.slice(cursor), DIM);
|
|
197533
|
+
return rendered;
|
|
197534
|
+
}
|
|
197535
|
+
function wrapSourceLine(line, lineStart, availableWidth) {
|
|
197536
|
+
if (line.length === 0) return [{
|
|
197537
|
+
text: "",
|
|
197538
|
+
start: lineStart,
|
|
197539
|
+
end: lineStart
|
|
197540
|
+
}];
|
|
197541
|
+
const width = Math.max(availableWidth, 1);
|
|
197542
|
+
const wrapped = [];
|
|
197543
|
+
let offset = 0;
|
|
197544
|
+
while (offset < line.length) {
|
|
197545
|
+
const text = line.slice(offset, offset + width);
|
|
197546
|
+
wrapped.push({
|
|
197547
|
+
text,
|
|
197548
|
+
start: lineStart + offset,
|
|
197549
|
+
end: lineStart + offset + text.length
|
|
197550
|
+
});
|
|
197551
|
+
offset += text.length;
|
|
197552
|
+
}
|
|
197553
|
+
return wrapped;
|
|
197554
|
+
}
|
|
197555
|
+
function wrapPreviewSourceText(sourceText, columns) {
|
|
197556
|
+
const availableWidth = Math.max(columns - 2, 1);
|
|
197557
|
+
const logicalLines = sourceText.split("\n");
|
|
197558
|
+
const wrapped = [];
|
|
197559
|
+
let offset = 0;
|
|
197560
|
+
for (const line of logicalLines) {
|
|
197561
|
+
for (const wrappedLine of wrapSourceLine(line, offset, availableWidth)) wrapped.push(wrappedLine);
|
|
197562
|
+
offset += line.length + 1;
|
|
197563
|
+
}
|
|
197564
|
+
return wrapped;
|
|
197565
|
+
}
|
|
197566
|
+
function renderPreviewSourceText(sourceText, diagnostics, columns) {
|
|
197567
|
+
const ranges = mergeHighlightRanges(diagnostics.map((diagnostic) => ({
|
|
197568
|
+
start: diagnostic.start,
|
|
197569
|
+
end: diagnostic.end
|
|
197570
|
+
})));
|
|
197571
|
+
return wrapPreviewSourceText(sourceText, columns).map((line) => {
|
|
197572
|
+
return CURSOR_TO_0 + renderPaddedLine(stylePreviewLine(line.text, line.start, ranges), " ", "", columns);
|
|
196862
197573
|
});
|
|
196863
197574
|
}
|
|
196864
|
-
function
|
|
196865
|
-
return
|
|
196866
|
-
|
|
196867
|
-
|
|
196868
|
-
|
|
196869
|
-
|
|
196870
|
-
|
|
196871
|
-
|
|
197575
|
+
function renderPreviewMessages(diagnostics, columns) {
|
|
197576
|
+
return Array.from(new Set(diagnostics.map((diagnostic) => diagnostic.text))).flatMap((message) => {
|
|
197577
|
+
let isFirstBlock = true;
|
|
197578
|
+
return message.split("\n").flatMap((line) => {
|
|
197579
|
+
const wrappedLines = wrapListItemText(line, isFirstBlock ? "- " : " ", " ", "", columns);
|
|
197580
|
+
isFirstBlock = false;
|
|
197581
|
+
return wrappedLines.map((wrappedLine) => CURSOR_TO_0 + ansi(wrappedLine, DIM + ITALIC));
|
|
197582
|
+
});
|
|
196872
197583
|
});
|
|
196873
197584
|
}
|
|
196874
|
-
function
|
|
196875
|
-
|
|
196876
|
-
|
|
196877
|
-
|
|
196878
|
-
|
|
196879
|
-
|
|
197585
|
+
function renderDivider(columns, legendText) {
|
|
197586
|
+
if (legendText === void 0) return ansi("─".repeat(Math.max(columns, 0)), DIM);
|
|
197587
|
+
const legend = ` ${legendText} `;
|
|
197588
|
+
const legendLength = visibleLength(legend);
|
|
197589
|
+
if (columns <= legendLength) return ansi(legend.slice(0, Math.max(columns, 0)), DIM);
|
|
197590
|
+
const remaining = columns - legendLength;
|
|
197591
|
+
const left = "─".repeat(Math.floor(remaining / 2));
|
|
197592
|
+
const right = "─".repeat(remaining - left.length);
|
|
197593
|
+
return ansi(left + legend + right, DIM);
|
|
196880
197594
|
}
|
|
196881
|
-
function
|
|
196882
|
-
|
|
196883
|
-
|
|
196884
|
-
|
|
196885
|
-
index: newIndex
|
|
196886
|
-
} }));
|
|
197595
|
+
function renderSelectedRuleDivider(selected, severities, columns) {
|
|
197596
|
+
if (!selected) return renderDivider(columns);
|
|
197597
|
+
const currentSeverity = severities[selected.name] ?? selected.defaultSeverity;
|
|
197598
|
+
return renderDivider(columns, `${selected.name} (currently set as ${getSeverityShortName(currentSeverity)})`);
|
|
196887
197599
|
}
|
|
196888
|
-
function
|
|
196889
|
-
|
|
196890
|
-
const
|
|
196891
|
-
return
|
|
196892
|
-
|
|
196893
|
-
|
|
196894
|
-
|
|
196895
|
-
|
|
196896
|
-
|
|
196897
|
-
|
|
197600
|
+
function matchesSearch(entry, searchText) {
|
|
197601
|
+
if (searchText.length === 0) return true;
|
|
197602
|
+
const normalized = searchText.toLowerCase();
|
|
197603
|
+
return entry.name.toLowerCase().includes(normalized) || entry.description.toLowerCase().includes(normalized) || entry.previewSourceText.toLowerCase().includes(normalized);
|
|
197604
|
+
}
|
|
197605
|
+
function getFilteredEntries(entries, searchText) {
|
|
197606
|
+
return entries.filter((entry) => matchesSearch(entry, searchText));
|
|
197607
|
+
}
|
|
197608
|
+
function normalizeStartIndex(length, startIndex) {
|
|
197609
|
+
if (length <= 0) return 0;
|
|
197610
|
+
return (startIndex % length + length) % length;
|
|
197611
|
+
}
|
|
197612
|
+
function isPrintableInput(input) {
|
|
197613
|
+
const printablePattern = new RegExp(String.raw`^[^\u0000-\u001F\u007F]+$`, "u");
|
|
197614
|
+
return !input.key.ctrl && !input.key.meta && input.input !== void 0 && input.input.length > 0 && printablePattern.test(input.input);
|
|
197615
|
+
}
|
|
197616
|
+
function buildVisibleEntries(entries, severities, startIndex, columns) {
|
|
197617
|
+
if (entries.length === 0) return {
|
|
197618
|
+
fullLine: renderPaddedLine(ansi("No matching rules", DIM), " ", " ", columns),
|
|
197619
|
+
visibleRules: []
|
|
197620
|
+
};
|
|
197621
|
+
const itemColumns = Math.max(columns - 4, 0);
|
|
197622
|
+
const separator = " ";
|
|
197623
|
+
const visibleEntries = [];
|
|
197624
|
+
const visibleRules = [];
|
|
197625
|
+
let currentLength = 0;
|
|
197626
|
+
let seenCount = 0;
|
|
197627
|
+
while (seenCount < entries.length) {
|
|
197628
|
+
const rule = entries[(startIndex + seenCount) % entries.length];
|
|
197629
|
+
const entry = renderEntry(rule, severities[rule.name] ?? rule.defaultSeverity, seenCount === 0);
|
|
197630
|
+
const nextLength = visibleEntries.length === 0 ? visibleLength(entry) : currentLength + 2 + visibleLength(entry);
|
|
197631
|
+
if (nextLength > itemColumns) break;
|
|
197632
|
+
visibleEntries.push(entry);
|
|
197633
|
+
visibleRules.push(rule);
|
|
197634
|
+
currentLength = nextLength;
|
|
197635
|
+
seenCount++;
|
|
197636
|
+
}
|
|
197637
|
+
const leftMarker = entries.length > 1 ? ansi("←", DIM) : " ";
|
|
197638
|
+
const rightMarker = entries.length > 1 ? ansi("→", DIM) : " ";
|
|
197639
|
+
return {
|
|
197640
|
+
fullLine: renderPaddedLine(visibleEntries.join(separator), `${leftMarker} `, ` ${rightMarker}`, columns),
|
|
197641
|
+
visibleRules
|
|
197642
|
+
};
|
|
196898
197643
|
}
|
|
196899
|
-
function
|
|
197644
|
+
function buildGroupLine(selected, groups, columns) {
|
|
197645
|
+
if (!selected) return renderPaddedLine("", " ", " ", columns);
|
|
197646
|
+
const selectedIndex = groups.findIndex((group) => group.id === selected.group);
|
|
197647
|
+
return renderPaddedLine(groups.map((_, index) => groups[(selectedIndex + index) % groups.length]).map((group, index) => {
|
|
197648
|
+
const label = group.name.toUpperCase();
|
|
197649
|
+
return index === 0 ? ansi(label, BOLD) : ansi(label, DIM);
|
|
197650
|
+
}).join(" "), " ", " ", columns);
|
|
197651
|
+
}
|
|
197652
|
+
function buildFrame(entries, groups, searchText, severities, startIndex, columns) {
|
|
197653
|
+
const visible = buildVisibleEntries(entries, severities, startIndex, columns);
|
|
197654
|
+
const selected = visible.visibleRules[0];
|
|
197655
|
+
const wrappedDescription = wrapPaddedText(selected?.description ?? "", " ", "", columns);
|
|
197656
|
+
const previewLines = renderPreviewSourceText(selected?.previewSourceText ?? "", selected?.previewDiagnostics ?? [], columns);
|
|
197657
|
+
const previewMessages = renderPreviewMessages(selected?.previewDiagnostics ?? [], columns);
|
|
197658
|
+
const previewSectionLines = [...previewLines, ...previewMessages];
|
|
197659
|
+
const paddingLines = Array.from({ length: Math.max(MIN_PREVIEW_AND_MESSAGES_LINES - previewSectionLines.length, 0) }, () => "");
|
|
197660
|
+
return [
|
|
197661
|
+
CURSOR_HIDE + renderSelectedRuleDivider(selected, severities, columns),
|
|
197662
|
+
...previewSectionLines,
|
|
197663
|
+
...paddingLines,
|
|
197664
|
+
CURSOR_TO_0 + renderDivider(columns),
|
|
197665
|
+
CURSOR_TO_0 + buildGroupLine(selected, groups, columns),
|
|
197666
|
+
CURSOR_TO_0 + visible.fullLine,
|
|
197667
|
+
...wrappedDescription.map((line) => CURSOR_TO_0 + ansi(line, DIM)),
|
|
197668
|
+
CURSOR_TO_0 + renderDivider(columns, getControlsLegend(searchText)),
|
|
197669
|
+
""
|
|
197670
|
+
];
|
|
197671
|
+
}
|
|
197672
|
+
function buildState(entries, groups, startIndex, searchText, severities) {
|
|
197673
|
+
return gen(function* () {
|
|
197674
|
+
const terminal = yield* Terminal;
|
|
197675
|
+
const columns = Math.max(yield* terminal.columns, 1);
|
|
197676
|
+
const filteredEntries = getFilteredEntries(entries, searchText);
|
|
197677
|
+
const normalizedStartIndex = normalizeStartIndex(filteredEntries.length, startIndex);
|
|
197678
|
+
return {
|
|
197679
|
+
startIndex: normalizedStartIndex,
|
|
197680
|
+
searchText,
|
|
197681
|
+
severities,
|
|
197682
|
+
renderedColumns: columns,
|
|
197683
|
+
renderedLines: buildFrame(filteredEntries, groups, searchText, severities, normalizedStartIndex, columns)
|
|
197684
|
+
};
|
|
197685
|
+
});
|
|
197686
|
+
}
|
|
197687
|
+
function renderSubmission(state, entries) {
|
|
197688
|
+
return succeed(CURSOR_TO_0 + JSON.stringify(Object.fromEntries(entries.flatMap((entry) => {
|
|
197689
|
+
const severity = state.severities[entry.name];
|
|
197690
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
197691
|
+
}))) + "\n");
|
|
197692
|
+
}
|
|
197693
|
+
function handleProcess(entries, groups) {
|
|
196900
197694
|
return (input, state) => {
|
|
196901
|
-
const
|
|
197695
|
+
const filteredEntries = getFilteredEntries(entries, state.searchText);
|
|
196902
197696
|
switch (input.key.name) {
|
|
196903
|
-
case "
|
|
196904
|
-
case "
|
|
196905
|
-
|
|
196906
|
-
|
|
196907
|
-
case "
|
|
196908
|
-
|
|
197697
|
+
case "backspace": return buildState(entries, groups, 0, state.searchText.slice(0, -1), state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197698
|
+
case "left":
|
|
197699
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197700
|
+
return buildState(entries, groups, state.startIndex - 1, state.searchText, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197701
|
+
case "right":
|
|
197702
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197703
|
+
return buildState(entries, groups, state.startIndex + 1, state.searchText, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197704
|
+
case "up":
|
|
197705
|
+
case "down":
|
|
197706
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197707
|
+
return buildState(entries, groups, state.startIndex, state.searchText, {
|
|
197708
|
+
...state.severities,
|
|
197709
|
+
[filteredEntries[state.startIndex].name]: cycleSeverity(state.severities[filteredEntries[state.startIndex].name] ?? filteredEntries[state.startIndex].defaultSeverity, input.key.name === "up" ? "left" : "right")
|
|
197710
|
+
}).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
196909
197711
|
case "enter":
|
|
196910
|
-
case "return": return succeed(Action.Submit({ value:
|
|
196911
|
-
|
|
197712
|
+
case "return": return succeed(Action.Submit({ value: Object.fromEntries(entries.flatMap((entry) => {
|
|
197713
|
+
const severity = state.severities[entry.name];
|
|
197714
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
197715
|
+
})) }));
|
|
197716
|
+
default:
|
|
197717
|
+
if (!isPrintableInput(input)) return succeed(Action.Beep());
|
|
197718
|
+
return buildState(entries, groups, 0, state.searchText + input.input, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
196912
197719
|
}
|
|
196913
197720
|
};
|
|
196914
197721
|
}
|
|
196915
|
-
function
|
|
196916
|
-
return
|
|
196917
|
-
|
|
196918
|
-
|
|
196919
|
-
|
|
196920
|
-
|
|
196921
|
-
|
|
196922
|
-
|
|
196923
|
-
|
|
196924
|
-
Beep: () => succeed(BEEP),
|
|
196925
|
-
NextFrame: ({ state }) => renderNextFrame(state, options),
|
|
196926
|
-
Submit: () => renderSubmission(state, options)
|
|
196927
|
-
});
|
|
197722
|
+
function getPromptEntries(rules) {
|
|
197723
|
+
return rules.map((rule) => ({
|
|
197724
|
+
name: rule.name,
|
|
197725
|
+
group: rule.group,
|
|
197726
|
+
description: rule.description,
|
|
197727
|
+
previewSourceText: rule.preview.sourceText,
|
|
197728
|
+
previewDiagnostics: rule.preview.diagnostics,
|
|
197729
|
+
defaultSeverity: rule.defaultSeverity
|
|
197730
|
+
}));
|
|
196928
197731
|
}
|
|
196929
197732
|
function createRulePrompt(rules, initialSeverities) {
|
|
196930
|
-
const
|
|
196931
|
-
|
|
196932
|
-
|
|
196933
|
-
|
|
196934
|
-
|
|
196935
|
-
|
|
196936
|
-
|
|
196937
|
-
|
|
196938
|
-
|
|
196939
|
-
|
|
196940
|
-
process: handleProcess(
|
|
196941
|
-
clear: () =>
|
|
197733
|
+
const entries = getPromptEntries(rules);
|
|
197734
|
+
const groups = diagnosticGroups;
|
|
197735
|
+
return custom(buildState(entries, groups, 0, "", initialSeverities), {
|
|
197736
|
+
render: (state, action) => {
|
|
197737
|
+
switch (action._tag) {
|
|
197738
|
+
case "Beep": return succeed(BEEP);
|
|
197739
|
+
case "NextFrame": return succeed(action.state.renderedLines.join("\n"));
|
|
197740
|
+
case "Submit": return renderSubmission(state, entries);
|
|
197741
|
+
}
|
|
197742
|
+
},
|
|
197743
|
+
process: handleProcess(entries, groups),
|
|
197744
|
+
clear: (state) => gen(function* () {
|
|
197745
|
+
const columns = yield* (yield* Terminal).columns;
|
|
197746
|
+
return eraseRenderedLines(state.renderedLines, columns);
|
|
197747
|
+
})
|
|
196942
197748
|
});
|
|
196943
197749
|
}
|
|
196944
197750
|
|
|
@@ -196987,16 +197793,17 @@ const gatherTargetState = (assessment, context) => gen(function* () {
|
|
|
196987
197793
|
editors: []
|
|
196988
197794
|
};
|
|
196989
197795
|
const diagnosticSeverities = (yield* select({
|
|
196990
|
-
message: "Would you like to customize the
|
|
197796
|
+
message: "Would you like to customize the diagnostics that the language service will provide?",
|
|
196991
197797
|
choices: [{
|
|
196992
197798
|
title: "Yes",
|
|
196993
|
-
description: "
|
|
197799
|
+
description: "Manually review and select which diagnostics to enable",
|
|
196994
197800
|
value: true,
|
|
196995
197801
|
selected: true
|
|
196996
197802
|
}, {
|
|
196997
197803
|
title: "No",
|
|
196998
|
-
description: "Keep the
|
|
196999
|
-
value: false
|
|
197804
|
+
description: "Keep the defaults provided by the language service",
|
|
197805
|
+
value: false,
|
|
197806
|
+
selected: false
|
|
197000
197807
|
}]
|
|
197001
197808
|
})) ? some(yield* createRulePrompt(getAllRules(), getOrElse$1(assessment.tsconfig.currentDiagnosticSeverities, () => ({})))) : none$3();
|
|
197002
197809
|
const hasVscodeSettings = isSome(assessment.vscodeSettings);
|