@effect/tsgo 0.0.15 → 0.0.16
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/bin/effect-tsgo.js +1116 -326
- package/package.json +8 -8
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.16";
|
|
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,905 @@ 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
|
-
|
|
196472
|
+
"id": "effectNative",
|
|
196473
|
+
"name": "Effect-native",
|
|
196474
|
+
"description": "Prefer Effect-native APIs and abstractions when available."
|
|
196475
|
+
},
|
|
196476
|
+
{
|
|
196477
|
+
"id": "style",
|
|
196478
|
+
"name": "Style",
|
|
196479
|
+
"description": "Cleanup, consistency, and idiomatic Effect code."
|
|
196480
|
+
}
|
|
196481
|
+
];
|
|
196482
|
+
var rules = [
|
|
196483
|
+
{
|
|
196484
|
+
"name": "anyUnknownInErrorContext",
|
|
196485
|
+
"group": "correctness",
|
|
196486
|
+
"description": "Detects 'any' or 'unknown' types in Effect error or requirements channels",
|
|
196487
|
+
"defaultSeverity": "off",
|
|
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
|
+
}
|
|
196442
196511
|
},
|
|
196443
196512
|
{
|
|
196444
196513
|
"name": "classSelfMismatch",
|
|
196514
|
+
"group": "correctness",
|
|
196445
196515
|
"description": "Ensures Self type parameter matches the class name in Service/Tag/Schema classes",
|
|
196446
196516
|
"defaultSeverity": "error",
|
|
196447
|
-
"
|
|
196448
|
-
|
|
196449
|
-
|
|
196450
|
-
"
|
|
196451
|
-
|
|
196452
|
-
|
|
196453
|
-
|
|
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
|
+
}
|
|
197008
|
+
},
|
|
197009
|
+
{
|
|
197010
|
+
"name": "extendsNativeError",
|
|
197011
|
+
"group": "effectNative",
|
|
197012
|
+
"description": "Warns when a class directly extends the native Error class",
|
|
197013
|
+
"defaultSeverity": "off",
|
|
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
|
+
}
|
|
197025
|
+
},
|
|
197026
|
+
{
|
|
197027
|
+
"name": "instanceOfSchema",
|
|
197028
|
+
"group": "effectNative",
|
|
197029
|
+
"description": "Suggests using Schema.is instead of instanceof for Effect Schema types",
|
|
197030
|
+
"defaultSeverity": "off",
|
|
197031
|
+
"fixable": true,
|
|
197032
|
+
"supportedEffect": ["v3", "v4"],
|
|
197033
|
+
"codes": [377042],
|
|
197034
|
+
"preview": {
|
|
197035
|
+
"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",
|
|
197036
|
+
"diagnostics": [{
|
|
197037
|
+
"start": 159,
|
|
197038
|
+
"end": 180,
|
|
197039
|
+
"text": "Consider using Schema.is instead of instanceof for Effect Schema types. effect(instanceOfSchema)"
|
|
197040
|
+
}]
|
|
197041
|
+
}
|
|
196598
197042
|
},
|
|
196599
197043
|
{
|
|
196600
197044
|
"name": "nodeBuiltinImport",
|
|
197045
|
+
"group": "effectNative",
|
|
196601
197046
|
"description": "Warns when importing Node.js built-in modules that have Effect-native counterparts",
|
|
196602
197047
|
"defaultSeverity": "off",
|
|
196603
|
-
"
|
|
197048
|
+
"fixable": false,
|
|
197049
|
+
"supportedEffect": ["v3", "v4"],
|
|
197050
|
+
"codes": [377057],
|
|
197051
|
+
"preview": {
|
|
197052
|
+
"sourceText": "import fs from \"node:fs\"\n\nexport const preview = fs.readFileSync\n",
|
|
197053
|
+
"diagnostics": [{
|
|
197054
|
+
"start": 15,
|
|
197055
|
+
"end": 24,
|
|
197056
|
+
"text": "Prefer using FileSystem from @effect/platform instead of the Node.js 'fs' module. effect(nodeBuiltinImport)"
|
|
197057
|
+
}]
|
|
197058
|
+
}
|
|
196604
197059
|
},
|
|
196605
197060
|
{
|
|
196606
|
-
"name": "
|
|
196607
|
-
"
|
|
196608
|
-
"
|
|
196609
|
-
"
|
|
197061
|
+
"name": "preferSchemaOverJson",
|
|
197062
|
+
"group": "effectNative",
|
|
197063
|
+
"description": "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify",
|
|
197064
|
+
"defaultSeverity": "suggestion",
|
|
197065
|
+
"fixable": false,
|
|
197066
|
+
"supportedEffect": ["v3", "v4"],
|
|
197067
|
+
"codes": [377026],
|
|
197068
|
+
"preview": {
|
|
197069
|
+
"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",
|
|
197070
|
+
"diagnostics": [{
|
|
197071
|
+
"start": 142,
|
|
197072
|
+
"end": 158,
|
|
197073
|
+
"text": "Consider using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify. effect(preferSchemaOverJson)"
|
|
197074
|
+
}]
|
|
197075
|
+
}
|
|
196610
197076
|
},
|
|
196611
197077
|
{
|
|
196612
|
-
"name": "
|
|
196613
|
-
"
|
|
196614
|
-
"
|
|
196615
|
-
"
|
|
197078
|
+
"name": "catchAllToMapError",
|
|
197079
|
+
"group": "style",
|
|
197080
|
+
"description": "Suggests using Effect.mapError instead of Effect.catch + Effect.fail",
|
|
197081
|
+
"defaultSeverity": "suggestion",
|
|
197082
|
+
"fixable": true,
|
|
197083
|
+
"supportedEffect": ["v3", "v4"],
|
|
197084
|
+
"codes": [377010],
|
|
197085
|
+
"preview": {
|
|
197086
|
+
"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",
|
|
197087
|
+
"diagnostics": [{
|
|
197088
|
+
"start": 150,
|
|
197089
|
+
"end": 162,
|
|
197090
|
+
"text": "You can use Effect.mapError instead of Effect.catch + Effect.fail to transform the error type. effect(catchAllToMapError)"
|
|
197091
|
+
}]
|
|
197092
|
+
}
|
|
196616
197093
|
},
|
|
196617
197094
|
{
|
|
196618
|
-
"name": "
|
|
196619
|
-
"
|
|
196620
|
-
"
|
|
196621
|
-
"
|
|
197095
|
+
"name": "deterministicKeys",
|
|
197096
|
+
"group": "style",
|
|
197097
|
+
"description": "Enforces deterministic naming for service/tag/error identifiers based on class names",
|
|
197098
|
+
"defaultSeverity": "off",
|
|
197099
|
+
"fixable": true,
|
|
197100
|
+
"supportedEffect": ["v3", "v4"],
|
|
197101
|
+
"codes": [377049],
|
|
197102
|
+
"preview": {
|
|
197103
|
+
"sourceText": "import { ServiceMap } from \"effect\"\n\nexport class RenamedService\n extends ServiceMap.Service<RenamedService, {}>()(\"CustomIdentifier\") {}\n",
|
|
197104
|
+
"diagnostics": []
|
|
197105
|
+
}
|
|
196622
197106
|
},
|
|
196623
197107
|
{
|
|
196624
|
-
"name": "
|
|
196625
|
-
"
|
|
197108
|
+
"name": "effectFnOpportunity",
|
|
197109
|
+
"group": "style",
|
|
197110
|
+
"description": "Suggests using Effect.fn for functions that return an Effect",
|
|
196626
197111
|
"defaultSeverity": "suggestion",
|
|
196627
|
-
"
|
|
197112
|
+
"fixable": true,
|
|
197113
|
+
"supportedEffect": ["v3", "v4"],
|
|
197114
|
+
"codes": [377047],
|
|
197115
|
+
"preview": {
|
|
197116
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = () => Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n",
|
|
197117
|
+
"diagnostics": [{
|
|
197118
|
+
"start": 54,
|
|
197119
|
+
"end": 61,
|
|
197120
|
+
"text": "Can be rewritten as a reusable function: Effect.fn(function*() { ... }). effect(effectFnOpportunity)"
|
|
197121
|
+
}]
|
|
197122
|
+
}
|
|
196628
197123
|
},
|
|
196629
197124
|
{
|
|
196630
|
-
"name": "
|
|
196631
|
-
"
|
|
197125
|
+
"name": "effectMapVoid",
|
|
197126
|
+
"group": "style",
|
|
197127
|
+
"description": "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
196632
197128
|
"defaultSeverity": "suggestion",
|
|
196633
|
-
"
|
|
197129
|
+
"fixable": true,
|
|
197130
|
+
"supportedEffect": ["v3", "v4"],
|
|
197131
|
+
"codes": [377018],
|
|
197132
|
+
"preview": {
|
|
197133
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.succeed(1).pipe(\n Effect.map(() => undefined)\n)\n",
|
|
197134
|
+
"diagnostics": [{
|
|
197135
|
+
"start": 82,
|
|
197136
|
+
"end": 92,
|
|
197137
|
+
"text": "Effect.asVoid can be used instead to discard the success value. effect(effectMapVoid)"
|
|
197138
|
+
}]
|
|
197139
|
+
}
|
|
196634
197140
|
},
|
|
196635
197141
|
{
|
|
196636
|
-
"name": "
|
|
196637
|
-
"
|
|
197142
|
+
"name": "effectSucceedWithVoid",
|
|
197143
|
+
"group": "style",
|
|
197144
|
+
"description": "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
196638
197145
|
"defaultSeverity": "suggestion",
|
|
196639
|
-
"
|
|
197146
|
+
"fixable": true,
|
|
197147
|
+
"supportedEffect": ["v3", "v4"],
|
|
197148
|
+
"codes": [377016],
|
|
197149
|
+
"preview": {
|
|
197150
|
+
"sourceText": "import { Effect } from \"effect\"\n\nexport const preview = Effect.succeed(undefined)\n",
|
|
197151
|
+
"diagnostics": [{
|
|
197152
|
+
"start": 56,
|
|
197153
|
+
"end": 81,
|
|
197154
|
+
"text": "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0). effect(effectSucceedWithVoid)"
|
|
197155
|
+
}]
|
|
197156
|
+
}
|
|
196640
197157
|
},
|
|
196641
197158
|
{
|
|
196642
|
-
"name": "
|
|
196643
|
-
"
|
|
196644
|
-
"
|
|
196645
|
-
"
|
|
197159
|
+
"name": "missedPipeableOpportunity",
|
|
197160
|
+
"group": "style",
|
|
197161
|
+
"description": "Suggests using .pipe() for nested function calls",
|
|
197162
|
+
"defaultSeverity": "off",
|
|
197163
|
+
"fixable": true,
|
|
197164
|
+
"supportedEffect": ["v3", "v4"],
|
|
197165
|
+
"codes": [377050],
|
|
197166
|
+
"preview": {
|
|
197167
|
+
"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",
|
|
197168
|
+
"diagnostics": [{
|
|
197169
|
+
"start": 116,
|
|
197170
|
+
"end": 172,
|
|
197171
|
+
"text": "Nested function calls can be converted to pipeable style for better readability; consider using Schema.decodeEffect(User)({ id: 1 }).pipe(...) instead. effect(missedPipeableOpportunity)"
|
|
197172
|
+
}]
|
|
197173
|
+
}
|
|
196646
197174
|
},
|
|
196647
197175
|
{
|
|
196648
|
-
"name": "
|
|
196649
|
-
"
|
|
197176
|
+
"name": "missingEffectServiceDependency",
|
|
197177
|
+
"group": "style",
|
|
197178
|
+
"description": "Checks that Effect.Service dependencies satisfy all required layer inputs",
|
|
197179
|
+
"defaultSeverity": "off",
|
|
197180
|
+
"fixable": false,
|
|
197181
|
+
"supportedEffect": ["v3"],
|
|
197182
|
+
"codes": [377039, 377040],
|
|
197183
|
+
"preview": {
|
|
197184
|
+
"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",
|
|
197185
|
+
"diagnostics": [{
|
|
197186
|
+
"start": 128,
|
|
197187
|
+
"end": 132,
|
|
197188
|
+
"text": "Service 'Db' is required but not provided by dependencies. effect(missingEffectServiceDependency)"
|
|
197189
|
+
}]
|
|
197190
|
+
}
|
|
197191
|
+
},
|
|
197192
|
+
{
|
|
197193
|
+
"name": "redundantSchemaTagIdentifier",
|
|
197194
|
+
"group": "style",
|
|
197195
|
+
"description": "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
196650
197196
|
"defaultSeverity": "suggestion",
|
|
196651
|
-
"
|
|
197197
|
+
"fixable": true,
|
|
197198
|
+
"supportedEffect": ["v3", "v4"],
|
|
197199
|
+
"codes": [377045],
|
|
197200
|
+
"preview": {
|
|
197201
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport class Preview\n extends Schema.TaggedClass<Preview>(\"Preview\")(\"Preview\", {\n value: Schema.String\n }) {}\n",
|
|
197202
|
+
"diagnostics": [{
|
|
197203
|
+
"start": 100,
|
|
197204
|
+
"end": 109,
|
|
197205
|
+
"text": "Identifier 'Preview' is redundant since it equals the _tag value. effect(redundantSchemaTagIdentifier)"
|
|
197206
|
+
}]
|
|
197207
|
+
}
|
|
196652
197208
|
},
|
|
196653
197209
|
{
|
|
196654
|
-
"name": "
|
|
196655
|
-
"
|
|
197210
|
+
"name": "schemaStructWithTag",
|
|
197211
|
+
"group": "style",
|
|
197212
|
+
"description": "Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field",
|
|
196656
197213
|
"defaultSeverity": "suggestion",
|
|
196657
|
-
"
|
|
197214
|
+
"fixable": true,
|
|
197215
|
+
"supportedEffect": ["v3", "v4"],
|
|
197216
|
+
"codes": [377036],
|
|
197217
|
+
"preview": {
|
|
197218
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport const preview = Schema.Struct({\n _tag: Schema.Literal(\"User\"),\n name: Schema.String\n})\n",
|
|
197219
|
+
"diagnostics": [{
|
|
197220
|
+
"start": 64,
|
|
197221
|
+
"end": 136,
|
|
197222
|
+
"text": "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor. effect(schemaStructWithTag)"
|
|
197223
|
+
}]
|
|
197224
|
+
}
|
|
196658
197225
|
},
|
|
196659
197226
|
{
|
|
196660
197227
|
"name": "schemaUnionOfLiterals",
|
|
197228
|
+
"group": "style",
|
|
196661
197229
|
"description": "Suggests combining multiple Schema.Literal calls in Schema.Union into a single Schema.Literal",
|
|
196662
197230
|
"defaultSeverity": "off",
|
|
196663
|
-
"
|
|
196664
|
-
|
|
196665
|
-
|
|
196666
|
-
"
|
|
196667
|
-
|
|
196668
|
-
|
|
196669
|
-
|
|
197231
|
+
"fixable": true,
|
|
197232
|
+
"supportedEffect": ["v3"],
|
|
197233
|
+
"codes": [377038],
|
|
197234
|
+
"preview": {
|
|
197235
|
+
"sourceText": "import * as Schema from \"effect/Schema\"\n\nexport const preview = Schema.Union(Schema.Literal(\"a\"), Schema.Literal(\"b\"))\n",
|
|
197236
|
+
"diagnostics": [{
|
|
197237
|
+
"start": 64,
|
|
197238
|
+
"end": 118,
|
|
197239
|
+
"text": "A Schema.Union of multiple Schema.Literal calls can be simplified to a single Schema.Literal call. effect(schemaUnionOfLiterals)"
|
|
197240
|
+
}]
|
|
197241
|
+
}
|
|
196670
197242
|
},
|
|
196671
197243
|
{
|
|
196672
197244
|
"name": "serviceNotAsClass",
|
|
197245
|
+
"group": "style",
|
|
196673
197246
|
"description": "Warns when ServiceMap.Service is used as a variable instead of a class declaration",
|
|
196674
197247
|
"defaultSeverity": "off",
|
|
196675
|
-
"
|
|
197248
|
+
"fixable": true,
|
|
197249
|
+
"supportedEffect": ["v4"],
|
|
197250
|
+
"codes": [377056],
|
|
197251
|
+
"preview": {
|
|
197252
|
+
"sourceText": "import { ServiceMap } from \"effect\"\n\nexport const Preview = ServiceMap.Service<{ port: number }>(\"Preview\")\n",
|
|
197253
|
+
"diagnostics": [{
|
|
197254
|
+
"start": 60,
|
|
197255
|
+
"end": 107,
|
|
197256
|
+
"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)"
|
|
197257
|
+
}]
|
|
197258
|
+
}
|
|
196676
197259
|
},
|
|
196677
197260
|
{
|
|
196678
197261
|
"name": "strictBooleanExpressions",
|
|
197262
|
+
"group": "style",
|
|
196679
197263
|
"description": "Enforces boolean types in conditional expressions for type safety",
|
|
196680
197264
|
"defaultSeverity": "off",
|
|
196681
|
-
"
|
|
196682
|
-
|
|
196683
|
-
|
|
196684
|
-
"
|
|
196685
|
-
|
|
196686
|
-
|
|
196687
|
-
|
|
196688
|
-
|
|
196689
|
-
|
|
196690
|
-
|
|
196691
|
-
|
|
196692
|
-
|
|
196693
|
-
|
|
196694
|
-
|
|
196695
|
-
|
|
196696
|
-
"name": "unknownInEffectCatch",
|
|
196697
|
-
"description": "Warns when catch callbacks return unknown instead of typed errors",
|
|
196698
|
-
"defaultSeverity": "warning",
|
|
196699
|
-
"codes": [377021]
|
|
197265
|
+
"fixable": false,
|
|
197266
|
+
"supportedEffect": ["v3", "v4"],
|
|
197267
|
+
"codes": [377029],
|
|
197268
|
+
"preview": {
|
|
197269
|
+
"sourceText": "\ndeclare const value: string | undefined\nexport const preview = value ? 1 : 0\n",
|
|
197270
|
+
"diagnostics": [{
|
|
197271
|
+
"start": 64,
|
|
197272
|
+
"end": 69,
|
|
197273
|
+
"text": "Unexpected `string` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
197274
|
+
}, {
|
|
197275
|
+
"start": 64,
|
|
197276
|
+
"end": 69,
|
|
197277
|
+
"text": "Unexpected `undefined` type in condition, expected strictly a boolean instead. effect(strictBooleanExpressions)"
|
|
197278
|
+
}]
|
|
197279
|
+
}
|
|
196700
197280
|
},
|
|
196701
197281
|
{
|
|
196702
197282
|
"name": "unnecessaryEffectGen",
|
|
197283
|
+
"group": "style",
|
|
196703
197284
|
"description": "Suggests removing Effect.gen when it contains only a single return statement",
|
|
196704
197285
|
"defaultSeverity": "suggestion",
|
|
196705
|
-
"
|
|
197286
|
+
"fixable": true,
|
|
197287
|
+
"supportedEffect": ["v3", "v4"],
|
|
197288
|
+
"codes": [377017],
|
|
197289
|
+
"preview": {
|
|
197290
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.gen(function*() {\n return yield* Effect.succeed(1)\n})\n",
|
|
197291
|
+
"diagnostics": [{
|
|
197292
|
+
"start": 64,
|
|
197293
|
+
"end": 125,
|
|
197294
|
+
"text": "This Effect.gen contains a single return statement. effect(unnecessaryEffectGen)"
|
|
197295
|
+
}]
|
|
197296
|
+
}
|
|
196706
197297
|
},
|
|
196707
197298
|
{
|
|
196708
197299
|
"name": "unnecessaryFailYieldableError",
|
|
197300
|
+
"group": "style",
|
|
196709
197301
|
"description": "Suggests yielding yieldable errors directly instead of wrapping with Effect.fail",
|
|
196710
197302
|
"defaultSeverity": "suggestion",
|
|
196711
|
-
"
|
|
197303
|
+
"fixable": true,
|
|
197304
|
+
"supportedEffect": ["v3", "v4"],
|
|
197305
|
+
"codes": [377019],
|
|
197306
|
+
"preview": {
|
|
197307
|
+
"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",
|
|
197308
|
+
"diagnostics": [{
|
|
197309
|
+
"start": 178,
|
|
197310
|
+
"end": 183,
|
|
197311
|
+
"text": "This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead. effect(unnecessaryFailYieldableError)"
|
|
197312
|
+
}]
|
|
197313
|
+
}
|
|
196712
197314
|
},
|
|
196713
197315
|
{
|
|
196714
197316
|
"name": "unnecessaryPipe",
|
|
197317
|
+
"group": "style",
|
|
196715
197318
|
"description": "Removes pipe calls with no arguments",
|
|
196716
197319
|
"defaultSeverity": "suggestion",
|
|
196717
|
-
"
|
|
197320
|
+
"fixable": true,
|
|
197321
|
+
"supportedEffect": ["v3", "v4"],
|
|
197322
|
+
"codes": [377013],
|
|
197323
|
+
"preview": {
|
|
197324
|
+
"sourceText": "import { pipe } from \"effect/Function\"\n\nexport const preview = pipe(1)\n",
|
|
197325
|
+
"diagnostics": [{
|
|
197326
|
+
"start": 63,
|
|
197327
|
+
"end": 70,
|
|
197328
|
+
"text": "This pipe call contains no arguments. effect(unnecessaryPipe)"
|
|
197329
|
+
}]
|
|
197330
|
+
}
|
|
196718
197331
|
},
|
|
196719
197332
|
{
|
|
196720
197333
|
"name": "unnecessaryPipeChain",
|
|
197334
|
+
"group": "style",
|
|
196721
197335
|
"description": "Simplifies chained pipe calls into a single pipe call",
|
|
196722
197336
|
"defaultSeverity": "suggestion",
|
|
196723
|
-
"
|
|
197337
|
+
"fixable": true,
|
|
197338
|
+
"supportedEffect": ["v3", "v4"],
|
|
197339
|
+
"codes": [377015],
|
|
197340
|
+
"preview": {
|
|
197341
|
+
"sourceText": "import * as Effect from \"effect/Effect\"\n\nexport const preview = Effect.succeed(1).pipe(Effect.asVoid).pipe(Effect.as(\"done\"))\n",
|
|
197342
|
+
"diagnostics": [{
|
|
197343
|
+
"start": 64,
|
|
197344
|
+
"end": 125,
|
|
197345
|
+
"text": "Chained pipe calls can be simplified to a single pipe call. effect(unnecessaryPipeChain)"
|
|
197346
|
+
}]
|
|
197347
|
+
}
|
|
196724
197348
|
}
|
|
196725
197349
|
];
|
|
196726
197350
|
|
|
196727
197351
|
//#endregion
|
|
196728
197352
|
//#region src/setup/rule-info.ts
|
|
196729
197353
|
function getAllRules() {
|
|
196730
|
-
return
|
|
197354
|
+
return rules;
|
|
197355
|
+
}
|
|
197356
|
+
function getAllGroups() {
|
|
197357
|
+
return groups;
|
|
196731
197358
|
}
|
|
196732
197359
|
function cycleSeverity(current, direction) {
|
|
196733
197360
|
const order = [
|
|
@@ -196758,187 +197385,349 @@ function getSeverityShortName(severity) {
|
|
|
196758
197385
|
const RESET = "\x1B[0m";
|
|
196759
197386
|
const BOLD = "\x1B[0;1m";
|
|
196760
197387
|
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";
|
|
197388
|
+
const RED = "\x1B[0;31m";
|
|
197389
|
+
const YELLOW = "\x1B[0;33m";
|
|
197390
|
+
const BLUE = "\x1B[0;34m";
|
|
197391
|
+
const ITALIC = "\x1B[0;3m";
|
|
197392
|
+
const UNDERLINE = "\x1B[0;4m";
|
|
196769
197393
|
const ansi = (text, code) => `${code}${text}${RESET}`;
|
|
196770
197394
|
const ERASE_LINE = "\x1B[2K";
|
|
196771
197395
|
const CURSOR_LEFT = "\r";
|
|
196772
197396
|
const CURSOR_HIDE = "\x1B[?25l";
|
|
196773
197397
|
const CURSOR_TO_0 = "\x1B[G";
|
|
196774
197398
|
const BEEP = "\x07";
|
|
197399
|
+
const ANSI_ESCAPE_REGEX = new RegExp(String.raw`\u001b\[[0-?]*[ -/]*[@-~]`, "g");
|
|
197400
|
+
const stripAnsi = (text) => text.replace(ANSI_ESCAPE_REGEX, "");
|
|
197401
|
+
const visibleLength = (text) => stripAnsi(text).length;
|
|
196775
197402
|
|
|
196776
197403
|
//#endregion
|
|
196777
197404
|
//#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
|
-
}
|
|
197405
|
+
const diagnosticGroups = getAllGroups();
|
|
196787
197406
|
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);
|
|
197407
|
+
const SEARCH_ICON = "/";
|
|
197408
|
+
const MIN_PREVIEW_AND_MESSAGES_LINES = 18;
|
|
197409
|
+
function getControlsLegend(searchText) {
|
|
197410
|
+
return `\u2190/\u2192 change rule \u2191/\u2193 change severity ${searchText.length === 0 ? `${SEARCH_ICON} type to search` : `${SEARCH_ICON} searching: ${searchText}`}`;
|
|
196795
197411
|
}
|
|
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);
|
|
197412
|
+
function getSeveritySymbol(severity) {
|
|
196801
197413
|
return {
|
|
196802
|
-
|
|
196803
|
-
|
|
196804
|
-
|
|
197414
|
+
off: ".",
|
|
197415
|
+
suggestion: "?",
|
|
197416
|
+
message: "i",
|
|
197417
|
+
warning: "!",
|
|
197418
|
+
error: "x"
|
|
197419
|
+
}[severity];
|
|
196805
197420
|
}
|
|
196806
|
-
const figuresValue = {
|
|
196807
|
-
arrowUp: "↑",
|
|
196808
|
-
arrowDown: "↓",
|
|
196809
|
-
tick: "✔",
|
|
196810
|
-
pointerSmall: "›"
|
|
196811
|
-
};
|
|
196812
197421
|
function getSeverityStyle(severity) {
|
|
196813
197422
|
return {
|
|
196814
|
-
off:
|
|
196815
|
-
suggestion:
|
|
196816
|
-
message:
|
|
196817
|
-
warning:
|
|
196818
|
-
error:
|
|
197423
|
+
off: DIM,
|
|
197424
|
+
suggestion: DIM,
|
|
197425
|
+
message: BLUE,
|
|
197426
|
+
warning: YELLOW,
|
|
197427
|
+
error: RED
|
|
196819
197428
|
}[severity];
|
|
196820
197429
|
}
|
|
196821
|
-
function
|
|
196822
|
-
|
|
196823
|
-
|
|
196824
|
-
|
|
196825
|
-
|
|
196826
|
-
|
|
196827
|
-
|
|
197430
|
+
function renderEntry(entry, severity, isSelected) {
|
|
197431
|
+
return `${ansi(getSeveritySymbol(severity), getSeverityStyle(severity))} ${isSelected ? ansi(entry.name, UNDERLINE) : entry.name}`;
|
|
197432
|
+
}
|
|
197433
|
+
function rowsForLength(length, columns) {
|
|
197434
|
+
if (columns <= 0) return 1;
|
|
197435
|
+
return Math.max(1, 1 + Math.floor(Math.max(length - 1, 0) / columns));
|
|
197436
|
+
}
|
|
197437
|
+
function eraseRenderedLines(lines, columns) {
|
|
197438
|
+
let result = "";
|
|
197439
|
+
for (let lineIndex = lines.length - 1; lineIndex >= 0; lineIndex--) {
|
|
197440
|
+
const rows = rowsForLength(visibleLength(lines[lineIndex] ?? ""), columns);
|
|
197441
|
+
for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
|
|
197442
|
+
result += ERASE_LINE;
|
|
197443
|
+
if (!(lineIndex === 0 && rowIndex === rows - 1)) result += "\x1B[1A";
|
|
196828
197444
|
}
|
|
196829
|
-
}
|
|
197445
|
+
}
|
|
197446
|
+
if (lines.length > 0) result += CURSOR_LEFT;
|
|
197447
|
+
return result;
|
|
196830
197448
|
}
|
|
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);
|
|
197449
|
+
function wrapPaddedText(text, initialPadding, endPadding, columns) {
|
|
197450
|
+
if (text.length === 0) return [initialPadding + endPadding];
|
|
197451
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 1);
|
|
197452
|
+
const lines = [];
|
|
197453
|
+
let remaining = text;
|
|
197454
|
+
while (remaining.length > 0) {
|
|
197455
|
+
const chunk = remaining.slice(0, available);
|
|
197456
|
+
lines.push(initialPadding + chunk + endPadding);
|
|
197457
|
+
remaining = remaining.slice(chunk.length);
|
|
196853
197458
|
}
|
|
196854
|
-
return
|
|
197459
|
+
return lines;
|
|
196855
197460
|
}
|
|
196856
|
-
function
|
|
196857
|
-
|
|
196858
|
-
|
|
196859
|
-
|
|
196860
|
-
|
|
196861
|
-
|
|
197461
|
+
function wrapListItemText(text, firstLinePadding, continuationPadding, endPadding, columns) {
|
|
197462
|
+
if (text.length === 0) return [firstLinePadding + endPadding];
|
|
197463
|
+
const firstAvailable = Math.max(columns - visibleLength(firstLinePadding) - visibleLength(endPadding), 1);
|
|
197464
|
+
const continuationAvailable = Math.max(columns - visibleLength(continuationPadding) - visibleLength(endPadding), 1);
|
|
197465
|
+
const lines = [];
|
|
197466
|
+
let remaining = text;
|
|
197467
|
+
let isFirstLine = true;
|
|
197468
|
+
while (remaining.length > 0) {
|
|
197469
|
+
const padding = isFirstLine ? firstLinePadding : continuationPadding;
|
|
197470
|
+
const available = isFirstLine ? firstAvailable : continuationAvailable;
|
|
197471
|
+
const chunk = remaining.slice(0, available);
|
|
197472
|
+
lines.push(padding + chunk + endPadding);
|
|
197473
|
+
remaining = remaining.slice(chunk.length);
|
|
197474
|
+
isFirstLine = false;
|
|
197475
|
+
}
|
|
197476
|
+
return lines;
|
|
197477
|
+
}
|
|
197478
|
+
function renderPaddedLine(text, initialPadding, endPadding, columns) {
|
|
197479
|
+
const available = Math.max(columns - visibleLength(initialPadding) - visibleLength(endPadding), 0);
|
|
197480
|
+
const truncated = visibleLength(text) <= available ? text : text.slice(0, available);
|
|
197481
|
+
const padding = Math.max(available - visibleLength(truncated), 0);
|
|
197482
|
+
return initialPadding + truncated + " ".repeat(padding) + endPadding;
|
|
197483
|
+
}
|
|
197484
|
+
function mergeHighlightRanges(ranges) {
|
|
197485
|
+
const sorted = ranges.filter((range) => range.end > range.start).slice().sort((a, b) => a.start - b.start);
|
|
197486
|
+
const merged = [];
|
|
197487
|
+
for (const range of sorted) {
|
|
197488
|
+
const previous = merged[merged.length - 1];
|
|
197489
|
+
if (!previous || range.start > previous.end) {
|
|
197490
|
+
merged.push(range);
|
|
197491
|
+
continue;
|
|
197492
|
+
}
|
|
197493
|
+
merged[merged.length - 1] = {
|
|
197494
|
+
start: previous.start,
|
|
197495
|
+
end: Math.max(previous.end, range.end)
|
|
197496
|
+
};
|
|
197497
|
+
}
|
|
197498
|
+
return merged;
|
|
197499
|
+
}
|
|
197500
|
+
function stylePreviewLine(line, lineStart, ranges) {
|
|
197501
|
+
if (line.length === 0) return "";
|
|
197502
|
+
const lineEnd = lineStart + line.length;
|
|
197503
|
+
let cursor = 0;
|
|
197504
|
+
let rendered = "";
|
|
197505
|
+
for (const range of ranges) {
|
|
197506
|
+
const start = Math.max(range.start, lineStart);
|
|
197507
|
+
const end = Math.min(range.end, lineEnd);
|
|
197508
|
+
if (end <= start) continue;
|
|
197509
|
+
const startIndex = start - lineStart;
|
|
197510
|
+
const endIndex = end - lineStart;
|
|
197511
|
+
if (cursor < startIndex) rendered += ansi(line.slice(cursor, startIndex), DIM);
|
|
197512
|
+
rendered += ansi(line.slice(startIndex, endIndex), UNDERLINE);
|
|
197513
|
+
cursor = endIndex;
|
|
197514
|
+
}
|
|
197515
|
+
if (cursor < line.length) rendered += ansi(line.slice(cursor), DIM);
|
|
197516
|
+
return rendered;
|
|
197517
|
+
}
|
|
197518
|
+
function wrapSourceLine(line, lineStart, availableWidth) {
|
|
197519
|
+
if (line.length === 0) return [{
|
|
197520
|
+
text: "",
|
|
197521
|
+
start: lineStart,
|
|
197522
|
+
end: lineStart
|
|
197523
|
+
}];
|
|
197524
|
+
const width = Math.max(availableWidth, 1);
|
|
197525
|
+
const wrapped = [];
|
|
197526
|
+
let offset = 0;
|
|
197527
|
+
while (offset < line.length) {
|
|
197528
|
+
const text = line.slice(offset, offset + width);
|
|
197529
|
+
wrapped.push({
|
|
197530
|
+
text,
|
|
197531
|
+
start: lineStart + offset,
|
|
197532
|
+
end: lineStart + offset + text.length
|
|
197533
|
+
});
|
|
197534
|
+
offset += text.length;
|
|
197535
|
+
}
|
|
197536
|
+
return wrapped;
|
|
197537
|
+
}
|
|
197538
|
+
function wrapPreviewSourceText(sourceText, columns) {
|
|
197539
|
+
const availableWidth = Math.max(columns - 2, 1);
|
|
197540
|
+
const logicalLines = sourceText.split("\n");
|
|
197541
|
+
const wrapped = [];
|
|
197542
|
+
let offset = 0;
|
|
197543
|
+
for (const line of logicalLines) {
|
|
197544
|
+
for (const wrappedLine of wrapSourceLine(line, offset, availableWidth)) wrapped.push(wrappedLine);
|
|
197545
|
+
offset += line.length + 1;
|
|
197546
|
+
}
|
|
197547
|
+
return wrapped;
|
|
197548
|
+
}
|
|
197549
|
+
function renderPreviewSourceText(sourceText, diagnostics, columns) {
|
|
197550
|
+
const ranges = mergeHighlightRanges(diagnostics.map((diagnostic) => ({
|
|
197551
|
+
start: diagnostic.start,
|
|
197552
|
+
end: diagnostic.end
|
|
197553
|
+
})));
|
|
197554
|
+
return wrapPreviewSourceText(sourceText, columns).map((line) => {
|
|
197555
|
+
return CURSOR_TO_0 + renderPaddedLine(stylePreviewLine(line.text, line.start, ranges), " ", "", columns);
|
|
196862
197556
|
});
|
|
196863
197557
|
}
|
|
196864
|
-
function
|
|
196865
|
-
return
|
|
196866
|
-
|
|
196867
|
-
|
|
196868
|
-
|
|
196869
|
-
|
|
196870
|
-
|
|
196871
|
-
|
|
197558
|
+
function renderPreviewMessages(diagnostics, columns) {
|
|
197559
|
+
return Array.from(new Set(diagnostics.map((diagnostic) => diagnostic.text))).flatMap((message) => {
|
|
197560
|
+
let isFirstBlock = true;
|
|
197561
|
+
return message.split("\n").flatMap((line) => {
|
|
197562
|
+
const wrappedLines = wrapListItemText(line, isFirstBlock ? "- " : " ", " ", "", columns);
|
|
197563
|
+
isFirstBlock = false;
|
|
197564
|
+
return wrappedLines.map((wrappedLine) => CURSOR_TO_0 + ansi(wrappedLine, DIM + ITALIC));
|
|
197565
|
+
});
|
|
196872
197566
|
});
|
|
196873
197567
|
}
|
|
196874
|
-
function
|
|
196875
|
-
|
|
196876
|
-
|
|
196877
|
-
|
|
196878
|
-
|
|
196879
|
-
|
|
197568
|
+
function renderDivider(columns, legendText) {
|
|
197569
|
+
if (legendText === void 0) return ansi("─".repeat(Math.max(columns, 0)), DIM);
|
|
197570
|
+
const legend = ` ${legendText} `;
|
|
197571
|
+
const legendLength = visibleLength(legend);
|
|
197572
|
+
if (columns <= legendLength) return ansi(legend.slice(0, Math.max(columns, 0)), DIM);
|
|
197573
|
+
const remaining = columns - legendLength;
|
|
197574
|
+
const left = "─".repeat(Math.floor(remaining / 2));
|
|
197575
|
+
const right = "─".repeat(remaining - left.length);
|
|
197576
|
+
return ansi(left + legend + right, DIM);
|
|
196880
197577
|
}
|
|
196881
|
-
function
|
|
196882
|
-
|
|
196883
|
-
|
|
196884
|
-
|
|
196885
|
-
index: newIndex
|
|
196886
|
-
} }));
|
|
197578
|
+
function renderSelectedRuleDivider(selected, severities, columns) {
|
|
197579
|
+
if (!selected) return renderDivider(columns);
|
|
197580
|
+
const currentSeverity = severities[selected.name] ?? selected.defaultSeverity;
|
|
197581
|
+
return renderDivider(columns, `${selected.name} (currently set as ${getSeverityShortName(currentSeverity)})`);
|
|
196887
197582
|
}
|
|
196888
|
-
function
|
|
196889
|
-
|
|
196890
|
-
const
|
|
196891
|
-
return
|
|
196892
|
-
|
|
196893
|
-
|
|
196894
|
-
|
|
196895
|
-
|
|
196896
|
-
|
|
196897
|
-
|
|
197583
|
+
function matchesSearch(entry, searchText) {
|
|
197584
|
+
if (searchText.length === 0) return true;
|
|
197585
|
+
const normalized = searchText.toLowerCase();
|
|
197586
|
+
return entry.name.toLowerCase().includes(normalized) || entry.description.toLowerCase().includes(normalized) || entry.previewSourceText.toLowerCase().includes(normalized);
|
|
197587
|
+
}
|
|
197588
|
+
function getFilteredEntries(entries, searchText) {
|
|
197589
|
+
return entries.filter((entry) => matchesSearch(entry, searchText));
|
|
197590
|
+
}
|
|
197591
|
+
function normalizeStartIndex(length, startIndex) {
|
|
197592
|
+
if (length <= 0) return 0;
|
|
197593
|
+
return (startIndex % length + length) % length;
|
|
197594
|
+
}
|
|
197595
|
+
function isPrintableInput(input) {
|
|
197596
|
+
const printablePattern = new RegExp(String.raw`^[^\u0000-\u001F\u007F]+$`, "u");
|
|
197597
|
+
return !input.key.ctrl && !input.key.meta && input.input !== void 0 && input.input.length > 0 && printablePattern.test(input.input);
|
|
197598
|
+
}
|
|
197599
|
+
function buildVisibleEntries(entries, severities, startIndex, columns) {
|
|
197600
|
+
if (entries.length === 0) return {
|
|
197601
|
+
fullLine: renderPaddedLine(ansi("No matching rules", DIM), " ", " ", columns),
|
|
197602
|
+
visibleRules: []
|
|
197603
|
+
};
|
|
197604
|
+
const itemColumns = Math.max(columns - 4, 0);
|
|
197605
|
+
const separator = " ";
|
|
197606
|
+
const visibleEntries = [];
|
|
197607
|
+
const visibleRules = [];
|
|
197608
|
+
let currentLength = 0;
|
|
197609
|
+
let seenCount = 0;
|
|
197610
|
+
while (seenCount < entries.length) {
|
|
197611
|
+
const rule = entries[(startIndex + seenCount) % entries.length];
|
|
197612
|
+
const entry = renderEntry(rule, severities[rule.name] ?? rule.defaultSeverity, seenCount === 0);
|
|
197613
|
+
const nextLength = visibleEntries.length === 0 ? visibleLength(entry) : currentLength + 2 + visibleLength(entry);
|
|
197614
|
+
if (nextLength > itemColumns) break;
|
|
197615
|
+
visibleEntries.push(entry);
|
|
197616
|
+
visibleRules.push(rule);
|
|
197617
|
+
currentLength = nextLength;
|
|
197618
|
+
seenCount++;
|
|
197619
|
+
}
|
|
197620
|
+
const leftMarker = entries.length > 1 ? ansi("←", DIM) : " ";
|
|
197621
|
+
const rightMarker = entries.length > 1 ? ansi("→", DIM) : " ";
|
|
197622
|
+
return {
|
|
197623
|
+
fullLine: renderPaddedLine(visibleEntries.join(separator), `${leftMarker} `, ` ${rightMarker}`, columns),
|
|
197624
|
+
visibleRules
|
|
197625
|
+
};
|
|
196898
197626
|
}
|
|
196899
|
-
function
|
|
197627
|
+
function buildGroupLine(selected, groups, columns) {
|
|
197628
|
+
if (!selected) return renderPaddedLine("", " ", " ", columns);
|
|
197629
|
+
const selectedIndex = groups.findIndex((group) => group.id === selected.group);
|
|
197630
|
+
return renderPaddedLine(groups.map((_, index) => groups[(selectedIndex + index) % groups.length]).map((group, index) => {
|
|
197631
|
+
const label = group.name.toUpperCase();
|
|
197632
|
+
return index === 0 ? ansi(label, BOLD) : ansi(label, DIM);
|
|
197633
|
+
}).join(" "), " ", " ", columns);
|
|
197634
|
+
}
|
|
197635
|
+
function buildFrame(entries, groups, searchText, severities, startIndex, columns) {
|
|
197636
|
+
const visible = buildVisibleEntries(entries, severities, startIndex, columns);
|
|
197637
|
+
const selected = visible.visibleRules[0];
|
|
197638
|
+
const wrappedDescription = wrapPaddedText(selected?.description ?? "", " ", "", columns);
|
|
197639
|
+
const previewLines = renderPreviewSourceText(selected?.previewSourceText ?? "", selected?.previewDiagnostics ?? [], columns);
|
|
197640
|
+
const previewMessages = renderPreviewMessages(selected?.previewDiagnostics ?? [], columns);
|
|
197641
|
+
const previewSectionLines = [...previewLines, ...previewMessages];
|
|
197642
|
+
const paddingLines = Array.from({ length: Math.max(MIN_PREVIEW_AND_MESSAGES_LINES - previewSectionLines.length, 0) }, () => "");
|
|
197643
|
+
return [
|
|
197644
|
+
CURSOR_HIDE + renderSelectedRuleDivider(selected, severities, columns),
|
|
197645
|
+
...previewSectionLines,
|
|
197646
|
+
...paddingLines,
|
|
197647
|
+
CURSOR_TO_0 + renderDivider(columns),
|
|
197648
|
+
CURSOR_TO_0 + buildGroupLine(selected, groups, columns),
|
|
197649
|
+
CURSOR_TO_0 + visible.fullLine,
|
|
197650
|
+
...wrappedDescription.map((line) => CURSOR_TO_0 + ansi(line, DIM)),
|
|
197651
|
+
CURSOR_TO_0 + renderDivider(columns, getControlsLegend(searchText)),
|
|
197652
|
+
""
|
|
197653
|
+
];
|
|
197654
|
+
}
|
|
197655
|
+
function buildState(entries, groups, startIndex, searchText, severities) {
|
|
197656
|
+
return gen(function* () {
|
|
197657
|
+
const terminal = yield* Terminal;
|
|
197658
|
+
const columns = Math.max(yield* terminal.columns, 1);
|
|
197659
|
+
const filteredEntries = getFilteredEntries(entries, searchText);
|
|
197660
|
+
const normalizedStartIndex = normalizeStartIndex(filteredEntries.length, startIndex);
|
|
197661
|
+
return {
|
|
197662
|
+
startIndex: normalizedStartIndex,
|
|
197663
|
+
searchText,
|
|
197664
|
+
severities,
|
|
197665
|
+
renderedColumns: columns,
|
|
197666
|
+
renderedLines: buildFrame(filteredEntries, groups, searchText, severities, normalizedStartIndex, columns)
|
|
197667
|
+
};
|
|
197668
|
+
});
|
|
197669
|
+
}
|
|
197670
|
+
function renderSubmission(state, entries) {
|
|
197671
|
+
return succeed(CURSOR_TO_0 + JSON.stringify(Object.fromEntries(entries.flatMap((entry) => {
|
|
197672
|
+
const severity = state.severities[entry.name];
|
|
197673
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
197674
|
+
}))) + "\n");
|
|
197675
|
+
}
|
|
197676
|
+
function handleProcess(entries, groups) {
|
|
196900
197677
|
return (input, state) => {
|
|
196901
|
-
const
|
|
197678
|
+
const filteredEntries = getFilteredEntries(entries, state.searchText);
|
|
196902
197679
|
switch (input.key.name) {
|
|
196903
|
-
case "
|
|
196904
|
-
case "
|
|
196905
|
-
|
|
196906
|
-
|
|
196907
|
-
case "
|
|
196908
|
-
|
|
197680
|
+
case "backspace": return buildState(entries, groups, 0, state.searchText.slice(0, -1), state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197681
|
+
case "left":
|
|
197682
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197683
|
+
return buildState(entries, groups, state.startIndex - 1, state.searchText, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197684
|
+
case "right":
|
|
197685
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197686
|
+
return buildState(entries, groups, state.startIndex + 1, state.searchText, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
197687
|
+
case "up":
|
|
197688
|
+
case "down":
|
|
197689
|
+
if (filteredEntries.length === 0) return succeed(Action.Beep());
|
|
197690
|
+
return buildState(entries, groups, state.startIndex, state.searchText, {
|
|
197691
|
+
...state.severities,
|
|
197692
|
+
[filteredEntries[state.startIndex].name]: cycleSeverity(state.severities[filteredEntries[state.startIndex].name] ?? filteredEntries[state.startIndex].defaultSeverity, input.key.name === "up" ? "left" : "right")
|
|
197693
|
+
}).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
196909
197694
|
case "enter":
|
|
196910
|
-
case "return": return succeed(Action.Submit({ value:
|
|
196911
|
-
|
|
197695
|
+
case "return": return succeed(Action.Submit({ value: Object.fromEntries(entries.flatMap((entry) => {
|
|
197696
|
+
const severity = state.severities[entry.name];
|
|
197697
|
+
return severity !== void 0 && severity !== entry.defaultSeverity ? [[entry.name, severity]] : [];
|
|
197698
|
+
})) }));
|
|
197699
|
+
default:
|
|
197700
|
+
if (!isPrintableInput(input)) return succeed(Action.Beep());
|
|
197701
|
+
return buildState(entries, groups, 0, state.searchText + input.input, state.severities).pipe(map$2((nextState) => Action.NextFrame({ state: nextState })));
|
|
196912
197702
|
}
|
|
196913
197703
|
};
|
|
196914
197704
|
}
|
|
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
|
-
});
|
|
197705
|
+
function getPromptEntries(rules) {
|
|
197706
|
+
return rules.map((rule) => ({
|
|
197707
|
+
name: rule.name,
|
|
197708
|
+
group: rule.group,
|
|
197709
|
+
description: rule.description,
|
|
197710
|
+
previewSourceText: rule.preview.sourceText,
|
|
197711
|
+
previewDiagnostics: rule.preview.diagnostics,
|
|
197712
|
+
defaultSeverity: rule.defaultSeverity
|
|
197713
|
+
}));
|
|
196928
197714
|
}
|
|
196929
197715
|
function createRulePrompt(rules, initialSeverities) {
|
|
196930
|
-
const
|
|
196931
|
-
|
|
196932
|
-
|
|
196933
|
-
|
|
196934
|
-
|
|
196935
|
-
|
|
196936
|
-
|
|
196937
|
-
|
|
196938
|
-
|
|
196939
|
-
|
|
196940
|
-
process: handleProcess(
|
|
196941
|
-
clear: () =>
|
|
197716
|
+
const entries = getPromptEntries(rules);
|
|
197717
|
+
const groups = diagnosticGroups;
|
|
197718
|
+
return custom(buildState(entries, groups, 0, "", initialSeverities), {
|
|
197719
|
+
render: (state, action) => {
|
|
197720
|
+
switch (action._tag) {
|
|
197721
|
+
case "Beep": return succeed(BEEP);
|
|
197722
|
+
case "NextFrame": return succeed(action.state.renderedLines.join("\n"));
|
|
197723
|
+
case "Submit": return renderSubmission(state, entries);
|
|
197724
|
+
}
|
|
197725
|
+
},
|
|
197726
|
+
process: handleProcess(entries, groups),
|
|
197727
|
+
clear: (state) => gen(function* () {
|
|
197728
|
+
const columns = yield* (yield* Terminal).columns;
|
|
197729
|
+
return eraseRenderedLines(state.renderedLines, columns);
|
|
197730
|
+
})
|
|
196942
197731
|
});
|
|
196943
197732
|
}
|
|
196944
197733
|
|
|
@@ -196987,16 +197776,17 @@ const gatherTargetState = (assessment, context) => gen(function* () {
|
|
|
196987
197776
|
editors: []
|
|
196988
197777
|
};
|
|
196989
197778
|
const diagnosticSeverities = (yield* select({
|
|
196990
|
-
message: "Would you like to customize the
|
|
197779
|
+
message: "Would you like to customize the diagnostics that the language service will provide?",
|
|
196991
197780
|
choices: [{
|
|
196992
197781
|
title: "Yes",
|
|
196993
|
-
description: "
|
|
197782
|
+
description: "Manually review and select which diagnostics to enable",
|
|
196994
197783
|
value: true,
|
|
196995
197784
|
selected: true
|
|
196996
197785
|
}, {
|
|
196997
197786
|
title: "No",
|
|
196998
|
-
description: "Keep the
|
|
196999
|
-
value: false
|
|
197787
|
+
description: "Keep the defaults provided by the language service",
|
|
197788
|
+
value: false,
|
|
197789
|
+
selected: false
|
|
197000
197790
|
}]
|
|
197001
197791
|
})) ? some(yield* createRulePrompt(getAllRules(), getOrElse$1(assessment.tsconfig.currentDiagnosticSeverities, () => ({})))) : none$3();
|
|
197002
197792
|
const hasVscodeSettings = isSome(assessment.vscodeSettings);
|