@effect/language-service 0.65.0 → 0.67.0

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/cli.js CHANGED
@@ -945,7 +945,7 @@ var require_createProjectService = __commonJS({
945
945
  return mod && mod.__esModule ? mod : { "default": mod };
946
946
  };
947
947
  Object.defineProperty(exports2, "__esModule", { value: true });
948
- exports2.createProjectService = createProjectService5;
948
+ exports2.createProjectService = createProjectService6;
949
949
  var debug_1 = __importDefault(require_src());
950
950
  var getParsedConfigFileFromTSServer_js_1 = require_getParsedConfigFileFromTSServer();
951
951
  var DEFAULT_PROJECT_MATCHED_FILES_THRESHOLD = 8;
@@ -959,7 +959,7 @@ var require_createProjectService = __commonJS({
959
959
  var createStubFileWatcher = () => ({
960
960
  close: doNothing
961
961
  });
962
- function createProjectService5({ jsDocParsingMode, options: optionsRaw = {}, tsconfigRootDir } = {}) {
962
+ function createProjectService6({ jsDocParsingMode, options: optionsRaw = {}, tsconfigRootDir } = {}) {
963
963
  const options3 = {
964
964
  defaultProject: "tsconfig.json",
965
965
  ...optionsRaw
@@ -30211,7 +30211,7 @@ var runMain3 = runMain2;
30211
30211
  // package.json
30212
30212
  var package_default = {
30213
30213
  name: "@effect/language-service",
30214
- version: "0.65.0",
30214
+ version: "0.67.0",
30215
30215
  packageManager: "pnpm@8.11.0",
30216
30216
  publishConfig: {
30217
30217
  access: "public",
@@ -33315,6 +33315,97 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
33315
33315
  "TypeParser.effectFnGen",
33316
33316
  (node) => node
33317
33317
  );
33318
+ const findEnclosingScopes = fn2("TypeParser.findEnclosingScopes")(function* (startNode) {
33319
+ let currentParent = startNode.parent;
33320
+ let scopeNode = void 0;
33321
+ let effectGenResult = void 0;
33322
+ while (currentParent) {
33323
+ const nodeToCheck = currentParent;
33324
+ if (!scopeNode) {
33325
+ if (ts.isFunctionExpression(nodeToCheck) || ts.isFunctionDeclaration(nodeToCheck) || ts.isMethodDeclaration(nodeToCheck) || ts.isArrowFunction(nodeToCheck) || ts.isGetAccessorDeclaration(nodeToCheck) || ts.isSetAccessorDeclaration(nodeToCheck)) {
33326
+ scopeNode = nodeToCheck;
33327
+ }
33328
+ }
33329
+ if (!effectGenResult) {
33330
+ const isEffectGen = yield* pipe(
33331
+ effectGen(nodeToCheck),
33332
+ map34((result) => ({
33333
+ node: result.node,
33334
+ effectModule: result.effectModule,
33335
+ generatorFunction: result.generatorFunction,
33336
+ body: result.body
33337
+ })),
33338
+ orElse15(
33339
+ () => pipe(
33340
+ effectFnUntracedGen(nodeToCheck),
33341
+ map34((result) => ({
33342
+ node: result.node,
33343
+ effectModule: result.effectModule,
33344
+ generatorFunction: result.generatorFunction,
33345
+ body: result.body,
33346
+ pipeArguments: result.pipeArguments
33347
+ }))
33348
+ )
33349
+ ),
33350
+ orElse15(
33351
+ () => pipe(
33352
+ effectFnGen(nodeToCheck),
33353
+ map34((result) => ({
33354
+ node: result.node,
33355
+ effectModule: result.effectModule,
33356
+ generatorFunction: result.generatorFunction,
33357
+ body: result.body,
33358
+ pipeArguments: result.pipeArguments
33359
+ }))
33360
+ )
33361
+ ),
33362
+ option5
33363
+ );
33364
+ if (isSome2(isEffectGen)) {
33365
+ effectGenResult = isEffectGen.value;
33366
+ }
33367
+ }
33368
+ if (scopeNode && effectGenResult) {
33369
+ break;
33370
+ }
33371
+ currentParent = nodeToCheck.parent;
33372
+ }
33373
+ return { scopeNode, effectGen: effectGenResult };
33374
+ });
33375
+ const effectFn = cachedBy(
33376
+ function(node) {
33377
+ if (!ts.isCallExpression(node)) {
33378
+ return typeParserIssue("Node is not a call expression", void 0, node);
33379
+ }
33380
+ if (node.arguments.length === 0) {
33381
+ return typeParserIssue("Node has no arguments", void 0, node);
33382
+ }
33383
+ const regularFunction = node.arguments[0];
33384
+ if (!ts.isFunctionExpression(regularFunction) && !ts.isArrowFunction(regularFunction)) {
33385
+ return typeParserIssue("Node is not a function expression or arrow function", void 0, node);
33386
+ }
33387
+ if (ts.isFunctionExpression(regularFunction) && regularFunction.asteriskToken !== void 0) {
33388
+ return typeParserIssue("Node is a generator function, not a regular function", void 0, node);
33389
+ }
33390
+ const expressionToTest = ts.isCallExpression(node.expression) ? node.expression.expression : node.expression;
33391
+ if (!ts.isPropertyAccessExpression(expressionToTest)) {
33392
+ return typeParserIssue("Node is not a property access expression", void 0, node);
33393
+ }
33394
+ const propertyAccess = expressionToTest;
33395
+ const pipeArguments2 = node.arguments.slice(1);
33396
+ return pipe(
33397
+ isNodeReferenceToEffectModuleApi("fn")(propertyAccess),
33398
+ map34(() => ({
33399
+ node,
33400
+ effectModule: propertyAccess.expression,
33401
+ regularFunction,
33402
+ pipeArguments: pipeArguments2
33403
+ }))
33404
+ );
33405
+ },
33406
+ "TypeParser.effectFn",
33407
+ (node) => node
33408
+ );
33318
33409
  const unnecessaryEffectGen2 = cachedBy(
33319
33410
  fn2("TypeParser.unnecessaryEffectGen")(function* (node) {
33320
33411
  const { body } = yield* effectGen(node);
@@ -33426,6 +33517,28 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
33426
33517
  `TypeParser.isNodeReferenceToEffectSchemaModuleApi(${memberName})`,
33427
33518
  (node) => node
33428
33519
  );
33520
+ const isEffectParseResultSourceFile = cachedBy(
33521
+ fn2("TypeParser.isEffectParseResultSourceFile")(function* (sourceFile) {
33522
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
33523
+ if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
33524
+ const parseIssueSymbol = typeChecker.tryGetMemberInModuleExports("ParseIssue", moduleSymbol);
33525
+ if (!parseIssueSymbol) return yield* typeParserIssue("ParseIssue type not found", void 0, sourceFile);
33526
+ const decodeSyncSymbol = typeChecker.tryGetMemberInModuleExports("decodeSync", moduleSymbol);
33527
+ if (!decodeSyncSymbol) return yield* typeParserIssue("decodeSync not found", void 0, sourceFile);
33528
+ const encodeSyncSymbol = typeChecker.tryGetMemberInModuleExports("encodeSync", moduleSymbol);
33529
+ if (!encodeSyncSymbol) return yield* typeParserIssue("encodeSync not found", void 0, sourceFile);
33530
+ return sourceFile;
33531
+ }),
33532
+ "TypeParser.isEffectParseResultSourceFile",
33533
+ (sourceFile) => sourceFile
33534
+ );
33535
+ const isNodeReferenceToEffectParseResultModuleApi = (memberName) => cachedBy(
33536
+ fn2("TypeParser.isNodeReferenceToEffectParseResultModuleApi")(function* (node) {
33537
+ return yield* isNodeReferenceToExportOfPackageModule(node, "effect", isEffectParseResultSourceFile, memberName);
33538
+ }),
33539
+ `TypeParser.isNodeReferenceToEffectParseResultModuleApi(${memberName})`,
33540
+ (node) => node
33541
+ );
33429
33542
  const contextTagVarianceStruct = (type2, atLocation) => map34(
33430
33543
  all9(
33431
33544
  varianceStructInvariantType(type2, atLocation, "_Identifier"),
@@ -34206,11 +34319,65 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
34206
34319
  if (includeEffectFn) {
34207
34320
  const effectFnGenParsed = yield* pipe(effectFnGen(node), option5);
34208
34321
  const effectFnUntracedGenParsed = isNone2(effectFnGenParsed) ? yield* pipe(effectFnUntracedGen(node), option5) : none2();
34209
- const isEffectFn = isSome2(effectFnGenParsed);
34210
- const effectFnParsed = isEffectFn ? effectFnGenParsed : effectFnUntracedGenParsed;
34211
- const transformationKind = isEffectFn ? "effectFn" : "effectFnUntraced";
34212
- if (isSome2(effectFnParsed) && effectFnParsed.value.pipeArguments.length > 0) {
34213
- const fnResult = effectFnParsed.value;
34322
+ const effectFnNonGenParsed = isNone2(effectFnGenParsed) && isNone2(effectFnUntracedGenParsed) ? yield* pipe(effectFn(node), option5) : none2();
34323
+ const isEffectFnGen = isSome2(effectFnGenParsed);
34324
+ const isEffectFnUntracedGen = isSome2(effectFnUntracedGenParsed);
34325
+ const isEffectFnNonGen = isSome2(effectFnNonGenParsed);
34326
+ const transformationKind = isEffectFnUntracedGen ? "effectFnUntraced" : "effectFn";
34327
+ if (isEffectFnGen || isEffectFnUntracedGen) {
34328
+ const effectFnParsed = isEffectFnGen ? effectFnGenParsed : effectFnUntracedGenParsed;
34329
+ if (isSome2(effectFnParsed) && effectFnParsed.value.pipeArguments.length > 0) {
34330
+ const fnResult = effectFnParsed.value;
34331
+ const pipeArgs = fnResult.pipeArguments;
34332
+ const transformations = [];
34333
+ let subjectType;
34334
+ for (let i = 0; i < pipeArgs.length; i++) {
34335
+ const arg = pipeArgs[i];
34336
+ const contextualType = typeChecker.getContextualType(arg);
34337
+ const callSigs = contextualType ? typeChecker.getSignaturesOfType(contextualType, ts.SignatureKind.Call) : [];
34338
+ const outType = callSigs.length > 0 ? typeChecker.getReturnTypeOfSignature(callSigs[0]) : void 0;
34339
+ if (i === 0 && callSigs.length > 0) {
34340
+ const params = callSigs[0].parameters;
34341
+ if (params.length > 0) {
34342
+ subjectType = typeChecker.getTypeOfSymbol(params[0]);
34343
+ }
34344
+ }
34345
+ if (ts.isCallExpression(arg)) {
34346
+ transformations.push({
34347
+ callee: arg.expression,
34348
+ args: Array.from(arg.arguments),
34349
+ outType,
34350
+ kind: transformationKind
34351
+ });
34352
+ } else {
34353
+ transformations.push({
34354
+ callee: arg,
34355
+ args: void 0,
34356
+ outType,
34357
+ kind: transformationKind
34358
+ });
34359
+ }
34360
+ }
34361
+ const newFlow = {
34362
+ node,
34363
+ subject: {
34364
+ node,
34365
+ outType: subjectType
34366
+ },
34367
+ transformations
34368
+ };
34369
+ result.push(newFlow);
34370
+ workQueue.push([fnResult.body, void 0]);
34371
+ for (const arg of pipeArgs) {
34372
+ ts.forEachChild(arg, (c) => {
34373
+ workQueue.push([c, void 0]);
34374
+ });
34375
+ }
34376
+ continue;
34377
+ }
34378
+ }
34379
+ if (isEffectFnNonGen && isSome2(effectFnNonGenParsed) && effectFnNonGenParsed.value.pipeArguments.length > 0) {
34380
+ const fnResult = effectFnNonGenParsed.value;
34214
34381
  const pipeArgs = fnResult.pipeArguments;
34215
34382
  const transformations = [];
34216
34383
  let subjectType;
@@ -34230,14 +34397,14 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
34230
34397
  callee: arg.expression,
34231
34398
  args: Array.from(arg.arguments),
34232
34399
  outType,
34233
- kind: transformationKind
34400
+ kind: "effectFn"
34234
34401
  });
34235
34402
  } else {
34236
34403
  transformations.push({
34237
34404
  callee: arg,
34238
34405
  args: void 0,
34239
34406
  outType,
34240
- kind: transformationKind
34407
+ kind: "effectFn"
34241
34408
  });
34242
34409
  }
34243
34410
  }
@@ -34250,7 +34417,16 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
34250
34417
  transformations
34251
34418
  };
34252
34419
  result.push(newFlow);
34253
- workQueue.push([fnResult.body, void 0]);
34420
+ const regularFn = fnResult.regularFunction;
34421
+ if (ts.isArrowFunction(regularFn)) {
34422
+ if (ts.isBlock(regularFn.body)) {
34423
+ workQueue.push([regularFn.body, void 0]);
34424
+ } else {
34425
+ workQueue.push([regularFn.body, void 0]);
34426
+ }
34427
+ } else if (regularFn.body) {
34428
+ workQueue.push([regularFn.body, void 0]);
34429
+ }
34254
34430
  for (const arg of pipeArgs) {
34255
34431
  ts.forEachChild(arg, (c) => {
34256
34432
  workQueue.push([c, void 0]);
@@ -34313,6 +34489,7 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
34313
34489
  return {
34314
34490
  isNodeReferenceToEffectModuleApi,
34315
34491
  isNodeReferenceToEffectSchemaModuleApi,
34492
+ isNodeReferenceToEffectParseResultModuleApi,
34316
34493
  isNodeReferenceToEffectDataModuleApi,
34317
34494
  isNodeReferenceToEffectContextModuleApi,
34318
34495
  isNodeReferenceToEffectSqlModelModuleApi,
@@ -34326,6 +34503,8 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
34326
34503
  effectGen,
34327
34504
  effectFnUntracedGen,
34328
34505
  effectFnGen,
34506
+ findEnclosingScopes,
34507
+ effectFn,
34329
34508
  extendsCauseYieldableError,
34330
34509
  unnecessaryEffectGen: unnecessaryEffectGen2,
34331
34510
  effectSchemaType,
@@ -36085,106 +36264,195 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
36085
36264
  });
36086
36265
 
36087
36266
  // src/diagnostics/effectFnOpportunity.ts
36088
- var parseEffectFnOpportunityTarget = (node, sourceFile) => gen3(function* () {
36089
- const ts = yield* service2(TypeScriptApi);
36090
- const typeChecker = yield* service2(TypeCheckerApi);
36091
- const typeParser = yield* service2(TypeParser);
36092
- const tsUtils = yield* service2(TypeScriptUtils);
36093
- if (!ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isFunctionDeclaration(node)) {
36094
- return yield* TypeParserIssue.issue;
36095
- }
36096
- if ((ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) && node.asteriskToken) {
36097
- return yield* TypeParserIssue.issue;
36098
- }
36099
- if (ts.isFunctionExpression(node) && node.name) {
36100
- return yield* TypeParserIssue.issue;
36101
- }
36102
- let bodyExpression;
36103
- if (ts.isArrowFunction(node)) {
36104
- if (ts.isBlock(node.body)) {
36105
- const returnStatement = findSingleReturnStatement(ts, node.body);
36106
- if (returnStatement?.expression) {
36107
- bodyExpression = returnStatement.expression;
36108
- }
36109
- } else {
36110
- bodyExpression = node.body;
36111
- }
36112
- } else if ((ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) && node.body) {
36113
- const returnStatement = findSingleReturnStatement(ts, node.body);
36114
- if (returnStatement?.expression) {
36115
- bodyExpression = returnStatement.expression;
36116
- }
36117
- }
36118
- if (!bodyExpression) return yield* TypeParserIssue.issue;
36119
- const { pipeArguments: pipeArguments2, subject } = yield* pipe(
36120
- typeParser.pipeCall(bodyExpression),
36121
- map34(({ args: args3, subject: subject2 }) => ({ subject: subject2, pipeArguments: args3 })),
36122
- orElse15(() => succeed17({ subject: bodyExpression, pipeArguments: [] }))
36123
- );
36124
- const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
36125
- const functionType = typeChecker.getTypeAtLocation(node);
36126
- if (!functionType) return yield* TypeParserIssue.issue;
36127
- const callSignatures = typeChecker.getSignaturesOfType(functionType, ts.SignatureKind.Call);
36128
- if (callSignatures.length !== 1) return yield* TypeParserIssue.issue;
36129
- const signature = callSignatures[0];
36130
- const returnType = typeChecker.getReturnTypeOfSignature(signature);
36131
- const { A, E, R } = yield* typeParser.strictEffectType(returnType, node);
36132
- const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
36133
- sourceFile,
36134
- "effect",
36135
- "Effect"
36136
- ) || "Effect";
36137
- const nameIdentifier = getNameIdentifier(ts, node);
36138
- const traceName = nameIdentifier ? ts.isIdentifier(nameIdentifier) ? ts.idText(nameIdentifier) : nameIdentifier.text : void 0;
36139
- const hasReturnTypeAnnotation = !!node.type;
36140
- return {
36141
- node,
36142
- nameIdentifier,
36143
- effectModule,
36144
- generatorFunction,
36145
- effectModuleName,
36146
- traceName,
36147
- hasReturnTypeAnnotation,
36148
- effectTypes: { A, E, R },
36149
- pipeArguments: pipeArguments2
36150
- };
36151
- });
36152
36267
  var effectFnOpportunity = createDiagnostic({
36153
36268
  name: "effectFnOpportunity",
36154
36269
  code: 41,
36155
- description: "Suggests using Effect.fn for functions that return Effect.gen",
36270
+ description: "Suggests using Effect.fn for functions that returns an Effect",
36156
36271
  severity: "suggestion",
36157
36272
  apply: fn2("effectFnOpportunity.apply")(function* (sourceFile, report) {
36158
36273
  const ts = yield* service2(TypeScriptApi);
36159
36274
  const typeChecker = yield* service2(TypeCheckerApi);
36275
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
36276
+ const typeParser = yield* service2(TypeParser);
36160
36277
  const tsUtils = yield* service2(TypeScriptUtils);
36161
- const createReturnTypeAnnotation = (effectModuleName, effectTypes, enclosingNode) => {
36162
- const { A, E, R } = effectTypes;
36163
- const aTypeNode = typeChecker.typeToTypeNode(A, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
36164
- const eTypeNode = typeChecker.typeToTypeNode(E, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
36165
- const rTypeNode = typeChecker.typeToTypeNode(R, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
36166
- if (!aTypeNode || !eTypeNode || !rTypeNode) return void 0;
36167
- return ts.factory.createTypeReferenceNode(
36168
- ts.factory.createQualifiedName(
36169
- ts.factory.createQualifiedName(
36170
- ts.factory.createIdentifier(effectModuleName),
36171
- "fn"
36172
- ),
36173
- "Return"
36174
- ),
36175
- [aTypeNode, eTypeNode, rTypeNode]
36278
+ const sourceEffectModuleName = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
36279
+ sourceFile,
36280
+ "effect",
36281
+ "Effect"
36282
+ ) || "Effect";
36283
+ const findSingleReturnStatement = (block) => {
36284
+ if (block.statements.length !== 1) return void 0;
36285
+ const statement = block.statements[0];
36286
+ if (!ts.isReturnStatement(statement)) return void 0;
36287
+ return statement;
36288
+ };
36289
+ const getBodyExpression = (fnNode) => {
36290
+ if (ts.isArrowFunction(fnNode)) {
36291
+ if (ts.isBlock(fnNode.body)) {
36292
+ return findSingleReturnStatement(fnNode.body)?.expression;
36293
+ }
36294
+ return fnNode.body;
36295
+ } else if ((ts.isFunctionExpression(fnNode) || ts.isFunctionDeclaration(fnNode)) && fnNode.body) {
36296
+ return findSingleReturnStatement(fnNode.body)?.expression;
36297
+ }
36298
+ return void 0;
36299
+ };
36300
+ const getNameIdentifier = (node) => {
36301
+ if (ts.isFunctionDeclaration(node) && node.name) {
36302
+ return node.name;
36303
+ }
36304
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
36305
+ return node.parent.name;
36306
+ }
36307
+ if (node.parent && ts.isPropertyAssignment(node.parent)) {
36308
+ const name = node.parent.name;
36309
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
36310
+ return name;
36311
+ }
36312
+ }
36313
+ if (node.parent && ts.isPropertyDeclaration(node.parent)) {
36314
+ const name = node.parent.name;
36315
+ if (ts.isIdentifier(name)) {
36316
+ return name;
36317
+ }
36318
+ }
36319
+ return void 0;
36320
+ };
36321
+ const areParametersReferencedIn = (fnNode, nodes2) => {
36322
+ if (fnNode.parameters.length === 0 || nodes2.length === 0) return false;
36323
+ const firstParam = fnNode.parameters[0];
36324
+ const lastParam = fnNode.parameters[fnNode.parameters.length - 1];
36325
+ const paramsStart = firstParam.pos;
36326
+ const paramsEnd = lastParam.end;
36327
+ const isSymbolDeclaredInParams = (symbol3) => {
36328
+ const declarations = symbol3.declarations;
36329
+ if (!declarations) return false;
36330
+ return declarations.some((decl) => decl.pos >= paramsStart && decl.end <= paramsEnd);
36331
+ };
36332
+ const nodesToVisit = [...nodes2];
36333
+ while (nodesToVisit.length > 0) {
36334
+ const node = nodesToVisit.shift();
36335
+ if (ts.isIdentifier(node)) {
36336
+ const symbol3 = typeChecker.getSymbolAtLocation(node);
36337
+ if (symbol3 && isSymbolDeclaredInParams(symbol3)) {
36338
+ return true;
36339
+ }
36340
+ }
36341
+ if (ts.isShorthandPropertyAssignment(node)) {
36342
+ const valueSymbol = typeChecker.getShorthandAssignmentValueSymbol(node);
36343
+ if (valueSymbol && isSymbolDeclaredInParams(valueSymbol)) {
36344
+ return true;
36345
+ }
36346
+ }
36347
+ ts.forEachChild(node, (child) => {
36348
+ nodesToVisit.push(child);
36349
+ return void 0;
36350
+ });
36351
+ }
36352
+ return false;
36353
+ };
36354
+ const tryParseGenOpportunity = (fnNode) => gen3(function* () {
36355
+ const bodyExpression = getBodyExpression(fnNode);
36356
+ if (!bodyExpression) return yield* TypeParserIssue.issue;
36357
+ const { pipeArguments: pipeArguments2, subject } = yield* pipe(
36358
+ typeParser.pipeCall(bodyExpression),
36359
+ map34(({ args: args3, subject: subject2 }) => ({ subject: subject2, pipeArguments: args3 })),
36360
+ orElse15(() => succeed17({ subject: bodyExpression, pipeArguments: [] }))
36361
+ );
36362
+ const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
36363
+ const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
36364
+ return { effectModuleName, generatorFunction, pipeArguments: pipeArguments2 };
36365
+ });
36366
+ const isInsideEffectFn = (fnNode) => {
36367
+ const parent = fnNode.parent;
36368
+ if (!parent || !ts.isCallExpression(parent)) {
36369
+ return succeed17(false);
36370
+ }
36371
+ if (parent.arguments[0] !== fnNode) {
36372
+ return succeed17(false);
36373
+ }
36374
+ return pipe(
36375
+ typeParser.effectFn(parent),
36376
+ orElse15(() => typeParser.effectFnGen(parent)),
36377
+ orElse15(() => typeParser.effectFnUntracedGen(parent)),
36378
+ map34(() => true),
36379
+ orElse15(() => succeed17(false))
36176
36380
  );
36177
36381
  };
36178
- const createEffectFnNode = (originalNode, generatorFunction, effectModuleName, traceName, effectTypes, pipeArguments2) => {
36179
- const returnTypeAnnotation = effectTypes ? createReturnTypeAnnotation(effectModuleName, effectTypes, originalNode) : void 0;
36180
- const newGeneratorFunction = ts.factory.createFunctionExpression(
36382
+ const parseEffectFnOpportunityTarget = (node) => gen3(function* () {
36383
+ if (!ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isFunctionDeclaration(node)) {
36384
+ return yield* TypeParserIssue.issue;
36385
+ }
36386
+ if ((ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) && node.asteriskToken) {
36387
+ return yield* TypeParserIssue.issue;
36388
+ }
36389
+ if (ts.isFunctionExpression(node) && node.name) {
36390
+ return yield* TypeParserIssue.issue;
36391
+ }
36392
+ if (yield* isInsideEffectFn(node)) {
36393
+ return yield* TypeParserIssue.issue;
36394
+ }
36395
+ const functionType = typeChecker.getTypeAtLocation(node);
36396
+ if (!functionType) return yield* TypeParserIssue.issue;
36397
+ const callSignatures = typeChecker.getSignaturesOfType(functionType, ts.SignatureKind.Call);
36398
+ if (callSignatures.length !== 1) return yield* TypeParserIssue.issue;
36399
+ const signature = callSignatures[0];
36400
+ const returnType = typeChecker.getReturnTypeOfSignature(signature);
36401
+ const unionMembers = typeCheckerUtils.unrollUnionMembers(returnType);
36402
+ yield* all9(...unionMembers.map((member) => typeParser.strictEffectType(member, node)));
36403
+ const nameIdentifier = getNameIdentifier(node);
36404
+ const traceName = nameIdentifier ? ts.isIdentifier(nameIdentifier) ? ts.idText(nameIdentifier) : nameIdentifier.text : void 0;
36405
+ if (!traceName) return yield* TypeParserIssue.issue;
36406
+ const opportunity = yield* pipe(
36407
+ tryParseGenOpportunity(node),
36408
+ orElse15(() => {
36409
+ if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {
36410
+ return TypeParserIssue.issue;
36411
+ }
36412
+ const body = ts.isArrowFunction(node) ? node.body : node.body;
36413
+ if (!body || !ts.isBlock(body) || body.statements.length <= 5) {
36414
+ return TypeParserIssue.issue;
36415
+ }
36416
+ return succeed17({
36417
+ effectModuleName: sourceEffectModuleName,
36418
+ pipeArguments: [],
36419
+ generatorFunction: void 0
36420
+ });
36421
+ })
36422
+ );
36423
+ return {
36424
+ node,
36425
+ nameIdentifier,
36426
+ effectModuleName: opportunity.effectModuleName,
36427
+ traceName,
36428
+ pipeArguments: opportunity.pipeArguments,
36429
+ generatorFunction: opportunity.generatorFunction,
36430
+ hasParamsInPipeArgs: areParametersReferencedIn(node, opportunity.pipeArguments)
36431
+ };
36432
+ });
36433
+ const getFunctionBodyBlock = (node) => {
36434
+ if (ts.isArrowFunction(node)) {
36435
+ if (ts.isBlock(node.body)) {
36436
+ return node.body;
36437
+ }
36438
+ return ts.factory.createBlock([ts.factory.createReturnStatement(node.body)], true);
36439
+ }
36440
+ return node.body;
36441
+ };
36442
+ const isGeneratorFunction2 = (node) => {
36443
+ if (ts.isArrowFunction(node)) return false;
36444
+ return node.asteriskToken !== void 0;
36445
+ };
36446
+ const createEffectFnNode = (originalNode, innerFunction, effectModuleName, traceName, pipeArguments2) => {
36447
+ const isGenerator = isGeneratorFunction2(innerFunction);
36448
+ const newFunction = ts.factory.createFunctionExpression(
36181
36449
  void 0,
36182
- ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
36450
+ isGenerator ? ts.factory.createToken(ts.SyntaxKind.AsteriskToken) : void 0,
36183
36451
  void 0,
36184
36452
  originalNode.typeParameters,
36185
36453
  originalNode.parameters,
36186
- returnTypeAnnotation,
36187
- generatorFunction.body
36454
+ void 0,
36455
+ getFunctionBodyBlock(innerFunction)
36188
36456
  );
36189
36457
  let fnExpression = ts.factory.createPropertyAccessExpression(
36190
36458
  ts.factory.createIdentifier(effectModuleName),
@@ -36197,34 +36465,27 @@ var effectFnOpportunity = createDiagnostic({
36197
36465
  [ts.factory.createStringLiteral(traceName)]
36198
36466
  );
36199
36467
  }
36200
- const effectFnCall = ts.factory.createCallExpression(
36201
- fnExpression,
36202
- void 0,
36203
- [newGeneratorFunction, ...pipeArguments2]
36204
- );
36468
+ const effectFnCall = ts.factory.createCallExpression(fnExpression, void 0, [newFunction, ...pipeArguments2]);
36205
36469
  if (ts.isFunctionDeclaration(originalNode)) {
36206
36470
  return tsUtils.tryPreserveDeclarationSemantics(originalNode, effectFnCall, false);
36207
36471
  }
36208
36472
  return effectFnCall;
36209
36473
  };
36210
- const createEffectFnUntracedNode = (originalNode, generatorFunction, effectModuleName, effectTypes, pipeArguments2) => {
36211
- const returnTypeAnnotation = effectTypes ? createReturnTypeAnnotation(effectModuleName, effectTypes, originalNode) : void 0;
36212
- const newGeneratorFunction = ts.factory.createFunctionExpression(
36474
+ const createEffectFnUntracedNode = (originalNode, innerFunction, effectModuleName, pipeArguments2) => {
36475
+ const isGenerator = isGeneratorFunction2(innerFunction);
36476
+ const newFunction = ts.factory.createFunctionExpression(
36213
36477
  void 0,
36214
- ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
36478
+ isGenerator ? ts.factory.createToken(ts.SyntaxKind.AsteriskToken) : void 0,
36215
36479
  void 0,
36216
36480
  originalNode.typeParameters,
36217
36481
  originalNode.parameters,
36218
- returnTypeAnnotation,
36219
- generatorFunction.body
36482
+ void 0,
36483
+ getFunctionBodyBlock(innerFunction)
36220
36484
  );
36221
36485
  const effectFnCall = ts.factory.createCallExpression(
36222
- ts.factory.createPropertyAccessExpression(
36223
- ts.factory.createIdentifier(effectModuleName),
36224
- "fnUntraced"
36225
- ),
36486
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(effectModuleName), "fnUntraced"),
36226
36487
  void 0,
36227
- [newGeneratorFunction, ...pipeArguments2]
36488
+ [newFunction, ...pipeArguments2]
36228
36489
  );
36229
36490
  if (ts.isFunctionDeclaration(originalNode)) {
36230
36491
  return tsUtils.tryPreserveDeclarationSemantics(originalNode, effectFnCall, false);
@@ -36240,88 +36501,41 @@ var effectFnOpportunity = createDiagnostic({
36240
36501
  while (nodeToVisit.length > 0) {
36241
36502
  const node = nodeToVisit.shift();
36242
36503
  ts.forEachChild(node, appendNodeToVisit);
36243
- const target = yield* pipe(
36244
- parseEffectFnOpportunityTarget(node, sourceFile),
36245
- option5
36246
- );
36504
+ const target = yield* pipe(parseEffectFnOpportunityTarget(node), option5);
36247
36505
  if (isNone2(target)) continue;
36248
- const {
36249
- effectModuleName,
36250
- effectTypes,
36251
- generatorFunction,
36252
- hasReturnTypeAnnotation,
36253
- nameIdentifier,
36254
- node: targetNode,
36255
- pipeArguments: pipeArguments2,
36256
- traceName
36257
- } = target.value;
36506
+ if (target.value.hasParamsInPipeArgs) continue;
36507
+ const { effectModuleName, nameIdentifier, node: targetNode, pipeArguments: pipeArguments2, traceName } = target.value;
36508
+ const innerFunction = target.value.generatorFunction ?? targetNode;
36258
36509
  const fixes = [];
36259
36510
  fixes.push({
36260
36511
  fixName: "effectFnOpportunity_toEffectFn",
36261
36512
  description: traceName ? `Convert to Effect.fn("${traceName}")` : "Convert to Effect.fn",
36262
36513
  apply: gen3(function* () {
36263
36514
  const changeTracker = yield* service2(ChangeTracker);
36264
- const newNode = createEffectFnNode(
36265
- targetNode,
36266
- generatorFunction,
36267
- effectModuleName,
36268
- traceName,
36269
- hasReturnTypeAnnotation ? effectTypes : void 0,
36270
- pipeArguments2
36271
- );
36272
- changeTracker.replaceNode(sourceFile, targetNode, newNode);
36273
- })
36274
- });
36275
- fixes.push({
36276
- fixName: "effectFnOpportunity_toEffectFnUntraced",
36277
- description: "Convert to Effect.fnUntraced",
36278
- apply: gen3(function* () {
36279
- const changeTracker = yield* service2(ChangeTracker);
36280
- const newNode = createEffectFnUntracedNode(
36281
- targetNode,
36282
- generatorFunction,
36283
- effectModuleName,
36284
- hasReturnTypeAnnotation ? effectTypes : void 0,
36285
- pipeArguments2
36286
- );
36515
+ const newNode = createEffectFnNode(targetNode, innerFunction, effectModuleName, traceName, pipeArguments2);
36287
36516
  changeTracker.replaceNode(sourceFile, targetNode, newNode);
36288
36517
  })
36289
36518
  });
36519
+ if (target.value.generatorFunction) {
36520
+ fixes.push({
36521
+ fixName: "effectFnOpportunity_toEffectFnUntraced",
36522
+ description: "Convert to Effect.fnUntraced",
36523
+ apply: gen3(function* () {
36524
+ const changeTracker = yield* service2(ChangeTracker);
36525
+ const newNode = createEffectFnUntracedNode(targetNode, innerFunction, effectModuleName, pipeArguments2);
36526
+ changeTracker.replaceNode(sourceFile, targetNode, newNode);
36527
+ })
36528
+ });
36529
+ }
36530
+ const pipeArgsSuffix = pipeArguments2.length > 0 ? ` Effect.fn also accepts the piped transformations as additional arguments.` : ``;
36290
36531
  report({
36291
36532
  location: nameIdentifier ?? targetNode,
36292
- messageText: `This function could benefit from Effect.fn's automatic tracing and concise syntax, or Effect.fnUntraced to get just a more concise syntax.`,
36533
+ messageText: target.value.generatorFunction ? `This function could benefit from Effect.fn's automatic tracing and concise syntax, or Effect.fnUntraced to get just a more concise syntax.${pipeArgsSuffix}` : `This function could benefit from Effect.fn's automatic tracing and concise syntax.${pipeArgsSuffix}`,
36293
36534
  fixes
36294
36535
  });
36295
36536
  }
36296
36537
  })
36297
36538
  });
36298
- function findSingleReturnStatement(ts, block) {
36299
- if (block.statements.length !== 1) return void 0;
36300
- const statement = block.statements[0];
36301
- if (!ts.isReturnStatement(statement)) return void 0;
36302
- return statement;
36303
- }
36304
- function getNameIdentifier(ts, node) {
36305
- if (ts.isFunctionDeclaration(node) && node.name) {
36306
- return node.name;
36307
- }
36308
- if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
36309
- return node.parent.name;
36310
- }
36311
- if (node.parent && ts.isPropertyAssignment(node.parent)) {
36312
- const name = node.parent.name;
36313
- if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
36314
- return name;
36315
- }
36316
- }
36317
- if (node.parent && ts.isPropertyDeclaration(node.parent)) {
36318
- const name = node.parent.name;
36319
- if (ts.isIdentifier(name)) {
36320
- return name;
36321
- }
36322
- }
36323
- return void 0;
36324
- }
36325
36539
 
36326
36540
  // src/diagnostics/effectGenUsesAdapter.ts
36327
36541
  var effectGenUsesAdapter = createDiagnostic({
@@ -37084,6 +37298,52 @@ var missedPipeableOpportunity = createDiagnostic({
37084
37298
  const typeChecker = yield* service2(TypeCheckerApi);
37085
37299
  const typeParser = yield* service2(TypeParser);
37086
37300
  const options3 = yield* service2(LanguageServicePluginOptions);
37301
+ const isSafelyPipeableCallee = (callee) => {
37302
+ if (ts.isCallExpression(callee)) {
37303
+ return true;
37304
+ }
37305
+ if (ts.isArrowFunction(callee)) {
37306
+ return true;
37307
+ }
37308
+ if (ts.isFunctionExpression(callee)) {
37309
+ return true;
37310
+ }
37311
+ if (ts.isParenthesizedExpression(callee)) {
37312
+ return isSafelyPipeableCallee(callee.expression);
37313
+ }
37314
+ if (ts.isIdentifier(callee)) {
37315
+ const symbol3 = typeChecker.getSymbolAtLocation(callee);
37316
+ if (!symbol3) return false;
37317
+ if (symbol3.flags & (ts.SymbolFlags.Module | ts.SymbolFlags.Namespace | ts.SymbolFlags.ValueModule)) {
37318
+ return true;
37319
+ }
37320
+ const declarations = symbol3.declarations;
37321
+ if (declarations && declarations.length > 0) {
37322
+ const decl = declarations[0];
37323
+ if (ts.isFunctionDeclaration(decl) || ts.isVariableDeclaration(decl) || ts.isImportSpecifier(decl) || ts.isImportClause(decl) || ts.isNamespaceImport(decl)) {
37324
+ return true;
37325
+ }
37326
+ }
37327
+ return false;
37328
+ }
37329
+ if (ts.isPropertyAccessExpression(callee)) {
37330
+ const subject = callee.expression;
37331
+ const symbol3 = typeChecker.getSymbolAtLocation(subject);
37332
+ if (!symbol3) return false;
37333
+ if (symbol3.flags & (ts.SymbolFlags.Module | ts.SymbolFlags.Namespace | ts.SymbolFlags.ValueModule)) {
37334
+ return true;
37335
+ }
37336
+ const declarations = symbol3.declarations;
37337
+ if (declarations && declarations.length > 0) {
37338
+ const decl = declarations[0];
37339
+ if (ts.isNamespaceImport(decl) || ts.isSourceFile(decl) || ts.isModuleDeclaration(decl)) {
37340
+ return true;
37341
+ }
37342
+ }
37343
+ return false;
37344
+ }
37345
+ return false;
37346
+ };
37087
37347
  const flows = yield* typeParser.pipingFlows(false)(sourceFile);
37088
37348
  for (const flow2 of flows) {
37089
37349
  if (flow2.transformations.length < options3.pipeableMinArgCount) {
@@ -37097,76 +37357,92 @@ var missedPipeableOpportunity = createDiagnostic({
37097
37357
  if (callSigs.length > 0) {
37098
37358
  continue;
37099
37359
  }
37100
- let firstPipeableIndex = -1;
37101
- const subjectType = flow2.subject.outType;
37102
- if (!subjectType) {
37103
- continue;
37104
- }
37105
- const subjectIsPipeable = yield* pipe(
37106
- typeParser.pipeableType(subjectType, flow2.subject.node),
37107
- option5
37108
- );
37109
- if (subjectIsPipeable._tag === "Some") {
37110
- firstPipeableIndex = 0;
37111
- } else {
37112
- for (let i = 0; i < flow2.transformations.length; i++) {
37113
- const t = flow2.transformations[i];
37114
- if (t.outType) {
37115
- const isPipeable = yield* pipe(
37116
- typeParser.pipeableType(t.outType, flow2.node),
37117
- option5
37118
- );
37119
- if (isPipeable._tag === "Some") {
37120
- firstPipeableIndex = i + 1;
37121
- break;
37122
- }
37360
+ const isPipeableAtIndex = function* (index) {
37361
+ if (index === 0) {
37362
+ const subjectType = flow2.subject.outType;
37363
+ if (!subjectType) return false;
37364
+ const result = yield* pipe(
37365
+ typeParser.pipeableType(subjectType, flow2.subject.node),
37366
+ option5
37367
+ );
37368
+ return result._tag === "Some";
37369
+ } else {
37370
+ const t = flow2.transformations[index - 1];
37371
+ if (!t.outType) return false;
37372
+ const result = yield* pipe(
37373
+ typeParser.pipeableType(t.outType, flow2.node),
37374
+ option5
37375
+ );
37376
+ return result._tag === "Some";
37377
+ }
37378
+ };
37379
+ let searchStartIndex = 0;
37380
+ while (searchStartIndex <= flow2.transformations.length) {
37381
+ let firstPipeableIndex = -1;
37382
+ for (let i = searchStartIndex; i <= flow2.transformations.length; i++) {
37383
+ if (yield* isPipeableAtIndex(i)) {
37384
+ firstPipeableIndex = i;
37385
+ break;
37123
37386
  }
37124
37387
  }
37125
- }
37126
- if (firstPipeableIndex === -1) {
37127
- continue;
37128
- }
37129
- const transformationsAfterPipeable = flow2.transformations.slice(firstPipeableIndex);
37130
- const callKindCount = transformationsAfterPipeable.filter((t) => t.kind === "call").length;
37131
- if (callKindCount < options3.pipeableMinArgCount) {
37132
- continue;
37133
- }
37134
- const pipeableSubjectNode = firstPipeableIndex === 0 ? flow2.subject.node : typeParser.reconstructPipingFlow({
37135
- subject: flow2.subject,
37136
- transformations: flow2.transformations.slice(0, firstPipeableIndex)
37137
- });
37138
- const pipeableTransformations = flow2.transformations.slice(firstPipeableIndex);
37139
- report({
37140
- location: flow2.node,
37141
- messageText: `Nested function calls can be converted to pipeable style for better readability.`,
37142
- fixes: [{
37143
- fixName: "missedPipeableOpportunity_fix",
37144
- description: "Convert to pipe style",
37145
- apply: gen3(function* () {
37146
- const changeTracker = yield* service2(ChangeTracker);
37147
- const pipeArgs = pipeableTransformations.map((t) => {
37148
- if (t.args) {
37149
- return ts.factory.createCallExpression(
37150
- t.callee,
37388
+ if (firstPipeableIndex === -1) {
37389
+ break;
37390
+ }
37391
+ const pipeableTransformations = [];
37392
+ for (let i = firstPipeableIndex; i < flow2.transformations.length; i++) {
37393
+ const t = flow2.transformations[i];
37394
+ if (!isSafelyPipeableCallee(t.callee)) {
37395
+ break;
37396
+ }
37397
+ pipeableTransformations.push(t);
37398
+ }
37399
+ const callKindCount = pipeableTransformations.filter((t) => t.kind === "call").length;
37400
+ if (callKindCount >= options3.pipeableMinArgCount) {
37401
+ const pipeableEndIndex = firstPipeableIndex + pipeableTransformations.length;
37402
+ const pipeableSubjectNode = firstPipeableIndex === 0 ? flow2.subject.node : typeParser.reconstructPipingFlow({
37403
+ subject: flow2.subject,
37404
+ transformations: flow2.transformations.slice(0, firstPipeableIndex)
37405
+ });
37406
+ const afterTransformations = flow2.transformations.slice(pipeableEndIndex);
37407
+ report({
37408
+ location: flow2.node,
37409
+ messageText: `Nested function calls can be converted to pipeable style for better readability.`,
37410
+ fixes: [{
37411
+ fixName: "missedPipeableOpportunity_fix",
37412
+ description: "Convert to pipe style",
37413
+ apply: gen3(function* () {
37414
+ const changeTracker = yield* service2(ChangeTracker);
37415
+ const pipeArgs = pipeableTransformations.map((t) => {
37416
+ if (t.args) {
37417
+ return ts.factory.createCallExpression(
37418
+ t.callee,
37419
+ void 0,
37420
+ t.args
37421
+ );
37422
+ } else {
37423
+ return t.callee;
37424
+ }
37425
+ });
37426
+ const pipeNode = ts.factory.createCallExpression(
37427
+ ts.factory.createPropertyAccessExpression(
37428
+ pipeableSubjectNode,
37429
+ "pipe"
37430
+ ),
37151
37431
  void 0,
37152
- t.args
37432
+ pipeArgs
37153
37433
  );
37154
- } else {
37155
- return t.callee;
37156
- }
37157
- });
37158
- const newNode = ts.factory.createCallExpression(
37159
- ts.factory.createPropertyAccessExpression(
37160
- pipeableSubjectNode,
37161
- "pipe"
37162
- ),
37163
- void 0,
37164
- pipeArgs
37165
- );
37166
- changeTracker.replaceNode(sourceFile, flow2.node, newNode);
37167
- })
37168
- }]
37169
- });
37434
+ const newNode = afterTransformations.length > 0 ? typeParser.reconstructPipingFlow({
37435
+ subject: { node: pipeNode, outType: void 0 },
37436
+ transformations: afterTransformations
37437
+ }) : pipeNode;
37438
+ changeTracker.replaceNode(sourceFile, flow2.node, newNode);
37439
+ })
37440
+ }]
37441
+ });
37442
+ break;
37443
+ }
37444
+ searchStartIndex = firstPipeableIndex + pipeableTransformations.length + 1;
37445
+ }
37170
37446
  }
37171
37447
  })
37172
37448
  });
@@ -38012,6 +38288,90 @@ var overriddenSchemaConstructor = createDiagnostic({
38012
38288
  })
38013
38289
  });
38014
38290
 
38291
+ // src/diagnostics/preferSchemaOverJson.ts
38292
+ var preferSchemaOverJson = createDiagnostic({
38293
+ name: "preferSchemaOverJson",
38294
+ code: 44,
38295
+ description: "Suggests using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify which may throw",
38296
+ severity: "suggestion",
38297
+ apply: fn2("preferSchemaOverJson.apply")(function* (sourceFile, report) {
38298
+ const ts = yield* service2(TypeScriptApi);
38299
+ const typeParser = yield* service2(TypeParser);
38300
+ const parseJsonMethod = (node) => gen3(function* () {
38301
+ if (!ts.isCallExpression(node)) return yield* fail18("node is not a call expression");
38302
+ const expression = node.expression;
38303
+ if (!ts.isPropertyAccessExpression(expression)) return yield* fail18("expression is not a property access");
38304
+ const objectExpr = expression.expression;
38305
+ const methodName = ts.idText(expression.name);
38306
+ if (!ts.isIdentifier(objectExpr) || ts.idText(objectExpr) !== "JSON") {
38307
+ return yield* fail18("object is not JSON");
38308
+ }
38309
+ if (methodName !== "parse" && methodName !== "stringify") {
38310
+ return yield* fail18("method is not parse or stringify");
38311
+ }
38312
+ return { node, methodName };
38313
+ });
38314
+ const effectTrySimple = (node) => gen3(function* () {
38315
+ if (!ts.isCallExpression(node)) return yield* fail18("node is not a call expression");
38316
+ yield* typeParser.isNodeReferenceToEffectModuleApi("try")(node.expression);
38317
+ if (node.arguments.length === 0) return yield* fail18("Effect.try has no arguments");
38318
+ const lazyFn = yield* typeParser.lazyExpression(node.arguments[0]);
38319
+ const jsonMethod = yield* parseJsonMethod(lazyFn.expression);
38320
+ return { node: jsonMethod.node, methodName: jsonMethod.methodName };
38321
+ });
38322
+ const effectTryObject = (node) => gen3(function* () {
38323
+ if (!ts.isCallExpression(node)) return yield* fail18("node is not a call expression");
38324
+ yield* typeParser.isNodeReferenceToEffectModuleApi("try")(node.expression);
38325
+ if (node.arguments.length === 0) return yield* fail18("Effect.try has no arguments");
38326
+ const arg = node.arguments[0];
38327
+ if (!ts.isObjectLiteralExpression(arg)) return yield* fail18("argument is not an object literal");
38328
+ const tryProp = arg.properties.find(
38329
+ (p3) => ts.isPropertyAssignment(p3) && ts.isIdentifier(p3.name) && ts.idText(p3.name) === "try"
38330
+ );
38331
+ if (!tryProp) return yield* fail18("object has no 'try' property");
38332
+ const lazyFn = yield* typeParser.lazyExpression(tryProp.initializer);
38333
+ const jsonMethod = yield* parseJsonMethod(lazyFn.expression);
38334
+ return { node: jsonMethod.node, methodName: jsonMethod.methodName };
38335
+ });
38336
+ const jsonMethodInEffectGen = (node) => gen3(function* () {
38337
+ const jsonMethod = yield* parseJsonMethod(node);
38338
+ const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
38339
+ if (!effectGen || effectGen.body.statements.length === 0) {
38340
+ return yield* fail18("not inside an Effect generator");
38341
+ }
38342
+ if (scopeNode && scopeNode !== effectGen.generatorFunction) {
38343
+ return yield* fail18("inside a nested function scope");
38344
+ }
38345
+ return { node: jsonMethod.node, methodName: jsonMethod.methodName };
38346
+ });
38347
+ const nodeToVisit = [];
38348
+ const appendNodeToVisit = (node) => {
38349
+ nodeToVisit.push(node);
38350
+ return void 0;
38351
+ };
38352
+ ts.forEachChild(sourceFile, appendNodeToVisit);
38353
+ while (nodeToVisit.length > 0) {
38354
+ const node = nodeToVisit.shift();
38355
+ ts.forEachChild(node, appendNodeToVisit);
38356
+ const match18 = yield* pipe(
38357
+ firstSuccessOf2([
38358
+ effectTrySimple(node),
38359
+ effectTryObject(node),
38360
+ jsonMethodInEffectGen(node)
38361
+ ]),
38362
+ option5
38363
+ );
38364
+ if (isSome2(match18)) {
38365
+ report({
38366
+ location: match18.value.node,
38367
+ messageText: "Consider using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify",
38368
+ fixes: []
38369
+ });
38370
+ }
38371
+ }
38372
+ })
38373
+ });
38374
+
38015
38375
  // src/diagnostics/redundantSchemaTagIdentifier.ts
38016
38376
  var redundantSchemaTagIdentifier = createDiagnostic({
38017
38377
  name: "redundantSchemaTagIdentifier",
@@ -38164,108 +38524,91 @@ var runEffectInsideEffect = createDiagnostic({
38164
38524
  option5
38165
38525
  );
38166
38526
  if (isNone2(isEffectRunCall)) continue;
38167
- let currentParent = node.parent;
38168
- let nodeIntroduceScope = void 0;
38169
- while (currentParent) {
38170
- const possiblyEffectGen = currentParent;
38171
- if (!nodeIntroduceScope) {
38172
- if (ts.isFunctionExpression(possiblyEffectGen) || ts.isFunctionDeclaration(possiblyEffectGen) || ts.isMethodDeclaration(possiblyEffectGen) || ts.isArrowFunction(possiblyEffectGen)) {
38173
- nodeIntroduceScope = possiblyEffectGen;
38174
- continue;
38175
- }
38176
- }
38177
- const isInEffectGen = yield* pipe(
38178
- typeParser.effectGen(possiblyEffectGen),
38179
- orElse15(() => typeParser.effectFnUntracedGen(possiblyEffectGen)),
38180
- orElse15(() => typeParser.effectFnGen(possiblyEffectGen)),
38181
- option5
38527
+ const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
38528
+ if (effectGen && effectGen.body.statements.length > 0) {
38529
+ const nodeText = sourceFile.text.substring(
38530
+ ts.getTokenPosOfNode(node.expression, sourceFile),
38531
+ node.expression.end
38182
38532
  );
38183
- if (isSome2(isInEffectGen) && isInEffectGen.value.body.statements.length > 0) {
38184
- const nodeText = sourceFile.text.substring(
38185
- ts.getTokenPosOfNode(node.expression, sourceFile),
38186
- node.expression.end
38187
- );
38188
- if (nodeIntroduceScope && nodeIntroduceScope !== isInEffectGen.value.generatorFunction) {
38189
- const fixAddRuntime = gen3(function* () {
38190
- const changeTracker = yield* service2(ChangeTracker);
38191
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
38192
- const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
38193
- let runtimeIdentifier = void 0;
38194
- for (const statement of isInEffectGen.value.generatorFunction.body.statements) {
38195
- if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
38196
- const declaration = statement.declarationList.declarations[0];
38197
- if (declaration.initializer && ts.isYieldExpression(declaration.initializer) && declaration.initializer.asteriskToken && declaration.initializer.expression) {
38198
- const yieldedExpression = declaration.initializer.expression;
38199
- if (ts.isCallExpression(yieldedExpression)) {
38200
- const maybeEffectRuntime = yield* pipe(
38201
- typeParser.isNodeReferenceToEffectModuleApi("runtime")(yieldedExpression.expression),
38202
- option5
38203
- );
38204
- if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
38205
- runtimeIdentifier = ts.idText(declaration.name);
38206
- }
38533
+ if (scopeNode && scopeNode !== effectGen.generatorFunction) {
38534
+ const fixAddRuntime = gen3(function* () {
38535
+ const changeTracker = yield* service2(ChangeTracker);
38536
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
38537
+ const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
38538
+ let runtimeIdentifier = void 0;
38539
+ for (const statement of effectGen.generatorFunction.body.statements) {
38540
+ if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
38541
+ const declaration = statement.declarationList.declarations[0];
38542
+ if (declaration.initializer && ts.isYieldExpression(declaration.initializer) && declaration.initializer.asteriskToken && declaration.initializer.expression) {
38543
+ const yieldedExpression = declaration.initializer.expression;
38544
+ if (ts.isCallExpression(yieldedExpression)) {
38545
+ const maybeEffectRuntime = yield* pipe(
38546
+ typeParser.isNodeReferenceToEffectModuleApi("runtime")(yieldedExpression.expression),
38547
+ option5
38548
+ );
38549
+ if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
38550
+ runtimeIdentifier = ts.idText(declaration.name);
38207
38551
  }
38208
38552
  }
38209
38553
  }
38210
38554
  }
38211
- if (!runtimeIdentifier) {
38212
- changeTracker.insertNodeAt(
38213
- sourceFile,
38214
- isInEffectGen.value.body.statements[0].pos,
38215
- ts.factory.createVariableStatement(
38555
+ }
38556
+ if (!runtimeIdentifier) {
38557
+ changeTracker.insertNodeAt(
38558
+ sourceFile,
38559
+ effectGen.body.statements[0].pos,
38560
+ ts.factory.createVariableStatement(
38561
+ void 0,
38562
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
38563
+ "effectRuntime",
38216
38564
  void 0,
38217
- ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
38218
- "effectRuntime",
38219
- void 0,
38220
- void 0,
38221
- ts.factory.createYieldExpression(
38222
- ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
38223
- ts.factory.createCallExpression(
38224
- ts.factory.createPropertyAccessExpression(
38225
- ts.factory.createIdentifier(effectModuleIdentifier),
38226
- "runtime"
38227
- ),
38228
- [ts.factory.createTypeReferenceNode("never")],
38229
- []
38230
- )
38565
+ void 0,
38566
+ ts.factory.createYieldExpression(
38567
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
38568
+ ts.factory.createCallExpression(
38569
+ ts.factory.createPropertyAccessExpression(
38570
+ ts.factory.createIdentifier(effectModuleIdentifier),
38571
+ "runtime"
38572
+ ),
38573
+ [ts.factory.createTypeReferenceNode("never")],
38574
+ []
38231
38575
  )
38232
- )], ts.NodeFlags.Const)
38233
- ),
38234
- {
38235
- prefix: "\n",
38236
- suffix: "\n"
38237
- }
38238
- );
38239
- }
38240
- changeTracker.deleteRange(sourceFile, {
38241
- pos: ts.getTokenPosOfNode(node.expression, sourceFile),
38242
- end: node.arguments[0].pos
38243
- });
38244
- changeTracker.insertText(
38245
- sourceFile,
38246
- node.arguments[0].pos,
38247
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
38576
+ )
38577
+ )], ts.NodeFlags.Const)
38578
+ ),
38579
+ {
38580
+ prefix: "\n",
38581
+ suffix: "\n"
38582
+ }
38248
38583
  );
38584
+ }
38585
+ changeTracker.deleteRange(sourceFile, {
38586
+ pos: ts.getTokenPosOfNode(node.expression, sourceFile),
38587
+ end: node.arguments[0].pos
38249
38588
  });
38250
- report({
38251
- location: node.expression,
38252
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
38589
+ changeTracker.insertText(
38590
+ sourceFile,
38591
+ node.arguments[0].pos,
38592
+ `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
38593
+ );
38594
+ });
38595
+ report({
38596
+ location: node.expression,
38597
+ messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
38253
38598
  Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
38254
- fixes: [{
38255
- fixName: "runEffectInsideEffect_fix",
38256
- description: "Use a runtime to run the Effect",
38257
- apply: fixAddRuntime
38258
- }]
38259
- });
38260
- } else {
38261
- report({
38262
- location: node.expression,
38263
- messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
38264
- fixes: []
38265
- });
38266
- }
38599
+ fixes: [{
38600
+ fixName: "runEffectInsideEffect_fix",
38601
+ description: "Use a runtime to run the Effect",
38602
+ apply: fixAddRuntime
38603
+ }]
38604
+ });
38605
+ } else {
38606
+ report({
38607
+ location: node.expression,
38608
+ messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
38609
+ fixes: []
38610
+ });
38267
38611
  }
38268
- currentParent = currentParent.parent;
38269
38612
  }
38270
38613
  }
38271
38614
  })
@@ -38351,6 +38694,59 @@ var schemaStructWithTag = createDiagnostic({
38351
38694
  })
38352
38695
  });
38353
38696
 
38697
+ // src/diagnostics/schemaSyncInEffect.ts
38698
+ var syncToEffectMethod = {
38699
+ decodeSync: "decode",
38700
+ decodeUnknownSync: "decodeUnknown",
38701
+ encodeSync: "encode",
38702
+ encodeUnknownSync: "encodeUnknown"
38703
+ };
38704
+ var schemaSyncInEffect = createDiagnostic({
38705
+ name: "schemaSyncInEffect",
38706
+ code: 43,
38707
+ description: "Suggests using Effect-based Schema methods instead of sync methods inside Effect generators",
38708
+ severity: "suggestion",
38709
+ apply: fn2("schemaSyncInEffect.apply")(function* (sourceFile, report) {
38710
+ const ts = yield* service2(TypeScriptApi);
38711
+ const typeParser = yield* service2(TypeParser);
38712
+ const parseSchemaSyncMethod = (node, methodName) => pipe(
38713
+ typeParser.isNodeReferenceToEffectParseResultModuleApi(methodName)(node),
38714
+ map34(() => ({ node, methodName }))
38715
+ );
38716
+ const nodeToVisit = [];
38717
+ const appendNodeToVisit = (node) => {
38718
+ nodeToVisit.push(node);
38719
+ return void 0;
38720
+ };
38721
+ ts.forEachChild(sourceFile, appendNodeToVisit);
38722
+ while (nodeToVisit.length > 0) {
38723
+ const node = nodeToVisit.shift();
38724
+ ts.forEachChild(node, appendNodeToVisit);
38725
+ if (!ts.isCallExpression(node)) continue;
38726
+ const isSchemaSyncCall = yield* pipe(
38727
+ firstSuccessOf2(
38728
+ Object.keys(syncToEffectMethod).map((methodName) => parseSchemaSyncMethod(node.expression, methodName))
38729
+ ),
38730
+ option5
38731
+ );
38732
+ if (isNone2(isSchemaSyncCall)) continue;
38733
+ const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
38734
+ if (!effectGen || effectGen.body.statements.length === 0) continue;
38735
+ if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
38736
+ const nodeText = sourceFile.text.substring(
38737
+ ts.getTokenPosOfNode(node.expression, sourceFile),
38738
+ node.expression.end
38739
+ );
38740
+ const effectMethodName = syncToEffectMethod[isSchemaSyncCall.value.methodName];
38741
+ report({
38742
+ location: node.expression,
38743
+ messageText: `Using ${nodeText} inside an Effect generator is not recommended. Use Schema.${effectMethodName} instead to get properly typed ParseError in the error channel.`,
38744
+ fixes: []
38745
+ });
38746
+ }
38747
+ })
38748
+ });
38749
+
38354
38750
  // src/diagnostics/schemaUnionOfLiterals.ts
38355
38751
  var schemaUnionOfLiterals = createDiagnostic({
38356
38752
  name: "schemaUnionOfLiterals",
@@ -39065,7 +39461,9 @@ var diagnostics = [
39065
39461
  layerMergeAllWithDependencies,
39066
39462
  effectMapVoid,
39067
39463
  effectFnOpportunity,
39068
- redundantSchemaTagIdentifier
39464
+ redundantSchemaTagIdentifier,
39465
+ schemaSyncInEffect,
39466
+ preferSchemaOverJson
39069
39467
  ];
39070
39468
 
39071
39469
  // src/cli/diagnostics.ts
@@ -39991,7 +40389,7 @@ var collectSourceFileExportedSymbols = (sourceFile, tsInstance, typeChecker, max
39991
40389
  }
39992
40390
  const exports2 = typeChecker.getExportsOfModule(moduleSymbol);
39993
40391
  const workQueue = exports2.map((s) => {
39994
- const declarations = s.getDeclarations();
40392
+ const declarations = s.declarations;
39995
40393
  const location = declarations && declarations.length > 0 ? getLocationFromDeclaration(declarations[0], tsInstance) : void 0;
39996
40394
  return [s, tsInstance.symbolName(s), location, 0];
39997
40395
  });
@@ -40153,7 +40551,7 @@ function collectExportedItems(sourceFile, tsInstance, typeChecker, maxSymbolDept
40153
40551
  maxSymbolDepth
40154
40552
  );
40155
40553
  for (const { description, location, name, symbol: symbol3, type: type2 } of exportedSymbols) {
40156
- const declarations = symbol3.getDeclarations();
40554
+ const declarations = symbol3.declarations;
40157
40555
  const declaration = declarations && declarations.length > 0 ? declarations[0] : sourceFile;
40158
40556
  const contextTagResult = yield* pipe(
40159
40557
  typeParser.contextTag(type2, declaration),
@@ -40971,6 +41369,523 @@ var patch9 = make58(
40971
41369
  )
40972
41370
  );
40973
41371
 
41372
+ // src/cli/quickfixes.ts
41373
+ var import_project_service5 = __toESM(require_dist2());
41374
+
41375
+ // src/cli/setup/text-diff-renderer.ts
41376
+ function getLines(text10) {
41377
+ return text10.split("\n");
41378
+ }
41379
+ function getLineAndCharacterOfPosition(text10, position) {
41380
+ const lines3 = text10.split("\n");
41381
+ let currentPos = 0;
41382
+ for (let lineIndex = 0; lineIndex < lines3.length; lineIndex++) {
41383
+ const lineLength = lines3[lineIndex].length;
41384
+ const lineEndPos = currentPos + lineLength;
41385
+ if (position <= lineEndPos) {
41386
+ return { line: lineIndex, character: position - currentPos };
41387
+ }
41388
+ currentPos = lineEndPos + 1;
41389
+ }
41390
+ const lastLineIndex = lines3.length - 1;
41391
+ return { line: lastLineIndex, character: lines3[lastLineIndex]?.length ?? 0 };
41392
+ }
41393
+ function renderLine(lineNum, symbol3, text10, color3) {
41394
+ const lineNumPart = lineNum !== void 0 ? String(lineNum).padStart(4, " ") : " ";
41395
+ return annotate2(text3(`${lineNumPart} ${symbol3} ${text10}`), color3);
41396
+ }
41397
+ function renderPlainTextChange(text10, textChange) {
41398
+ const startPos = textChange.span.start;
41399
+ const endPos = textChange.span.start + textChange.span.length;
41400
+ const startLineAndChar = getLineAndCharacterOfPosition(text10, startPos);
41401
+ const endLineAndChar = getLineAndCharacterOfPosition(text10, endPos);
41402
+ const startLine = startLineAndChar.line;
41403
+ const endLine = endLineAndChar.line;
41404
+ const startCol = startLineAndChar.character;
41405
+ const endCol = endLineAndChar.character;
41406
+ const lines3 = [];
41407
+ const allLines = getLines(text10);
41408
+ if (startLine > 0) {
41409
+ const contextBefore = allLines[startLine - 1];
41410
+ lines3.push(renderLine(startLine, "|", contextBefore, blackBright2));
41411
+ }
41412
+ if (startLine <= endLine) {
41413
+ const firstLineText = allLines[startLine];
41414
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41415
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41416
+ if (hasNonWhitespaceKept) {
41417
+ lines3.push(renderLine(startLine + 1, "|", keptBeforeDeletion, blackBright2));
41418
+ }
41419
+ const deletedOnFirstLine = startLine === endLine ? firstLineText.slice(startCol, endCol) : firstLineText.slice(startCol);
41420
+ if (deletedOnFirstLine.length > 0) {
41421
+ const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
41422
+ lines3.push(renderLine(void 0, "-", `${spacePadding}${deletedOnFirstLine}`, red4));
41423
+ }
41424
+ }
41425
+ for (let i = startLine + 1; i < endLine; i++) {
41426
+ const lineText = allLines[i];
41427
+ if (lineText !== void 0) {
41428
+ lines3.push(renderLine(void 0, "-", lineText, red4));
41429
+ }
41430
+ }
41431
+ if (endLine > startLine) {
41432
+ const lastLineText = allLines[endLine];
41433
+ const deletedOnLastLine = lastLineText.slice(0, endCol);
41434
+ if (deletedOnLastLine.length > 0) {
41435
+ lines3.push(renderLine(void 0, "-", deletedOnLastLine, red4));
41436
+ }
41437
+ }
41438
+ if (textChange.newText.length > 0) {
41439
+ const newTextLines = textChange.newText.split("\n");
41440
+ const firstLineText = allLines[startLine];
41441
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41442
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41443
+ const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
41444
+ for (let i = 0; i < newTextLines.length; i++) {
41445
+ const newLine = newTextLines[i];
41446
+ if (i === newTextLines.length - 1 && newLine.length === 0 && newTextLines.length > 1) {
41447
+ continue;
41448
+ }
41449
+ const padding = i === 0 && hasNonWhitespaceKept ? spacePadding : "";
41450
+ lines3.push(renderLine(void 0, "+", `${padding}${newLine}`, green3));
41451
+ }
41452
+ }
41453
+ let alignmentForKeptPart = 0;
41454
+ if (textChange.newText.length > 0) {
41455
+ const newTextLines = textChange.newText.split("\n");
41456
+ const lastNewLine = newTextLines[newTextLines.length - 1];
41457
+ if (lastNewLine.length === 0 && newTextLines.length > 1) {
41458
+ alignmentForKeptPart = 0;
41459
+ } else {
41460
+ const firstLineText = allLines[startLine];
41461
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41462
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41463
+ if (hasNonWhitespaceKept) {
41464
+ if (newTextLines.length === 1) {
41465
+ alignmentForKeptPart = keptBeforeDeletion.length + lastNewLine.length;
41466
+ } else {
41467
+ alignmentForKeptPart = lastNewLine.length;
41468
+ }
41469
+ } else {
41470
+ alignmentForKeptPart = lastNewLine.length;
41471
+ }
41472
+ }
41473
+ } else {
41474
+ alignmentForKeptPart = endCol;
41475
+ }
41476
+ if (endLine > startLine) {
41477
+ const lastLineText = allLines[endLine];
41478
+ const keptAfterDeletion = lastLineText.slice(endCol);
41479
+ if (keptAfterDeletion.trim().length > 0) {
41480
+ const alignment = " ".repeat(alignmentForKeptPart);
41481
+ lines3.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
41482
+ }
41483
+ } else if (startLine === endLine) {
41484
+ const firstLineText = allLines[startLine];
41485
+ const keptAfterDeletion = firstLineText.slice(endCol);
41486
+ if (keptAfterDeletion.trim().length > 0) {
41487
+ const alignment = " ".repeat(alignmentForKeptPart);
41488
+ lines3.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
41489
+ }
41490
+ }
41491
+ if (endLine + 1 < allLines.length) {
41492
+ const contextAfter = allLines[endLine + 1];
41493
+ lines3.push(renderLine(endLine + 2, "|", contextAfter, blackBright2));
41494
+ }
41495
+ return lines3;
41496
+ }
41497
+ function renderPlainTextFileChanges(text10, textChanges) {
41498
+ const lines3 = [];
41499
+ const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start);
41500
+ for (let i = 0; i < sortedChanges.length; i++) {
41501
+ const change = sortedChanges[i];
41502
+ const changeLines = renderPlainTextChange(text10, change);
41503
+ for (const line4 of changeLines) {
41504
+ lines3.push(line4);
41505
+ }
41506
+ if (i < sortedChanges.length - 1) {
41507
+ lines3.push(text3(""));
41508
+ }
41509
+ }
41510
+ return lines3;
41511
+ }
41512
+
41513
+ // src/cli/setup/diff-renderer.ts
41514
+ function getLines2(text10) {
41515
+ return text10.split("\n");
41516
+ }
41517
+ function renderLine2(lineNum, symbol3, text10, color3) {
41518
+ const lineNumPart = lineNum !== void 0 ? String(lineNum).padStart(4, " ") : " ";
41519
+ return annotate2(text3(`${lineNumPart} ${symbol3} ${text10}`), color3);
41520
+ }
41521
+ function renderTextChange(sourceFile, textChange) {
41522
+ const startPos = textChange.span.start;
41523
+ const endPos = textChange.span.start + textChange.span.length;
41524
+ const startLineAndChar = sourceFile.getLineAndCharacterOfPosition(startPos);
41525
+ const endLineAndChar = sourceFile.getLineAndCharacterOfPosition(endPos);
41526
+ const startLine = startLineAndChar.line;
41527
+ const endLine = endLineAndChar.line;
41528
+ const startCol = startLineAndChar.character;
41529
+ const endCol = endLineAndChar.character;
41530
+ const lines3 = [];
41531
+ const allLines = getLines2(sourceFile.text);
41532
+ if (startLine > 0) {
41533
+ const contextBefore = allLines[startLine - 1];
41534
+ lines3.push(renderLine2(startLine, "|", contextBefore, blackBright2));
41535
+ }
41536
+ if (startLine <= endLine) {
41537
+ const firstLineText = allLines[startLine];
41538
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41539
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41540
+ if (hasNonWhitespaceKept) {
41541
+ lines3.push(renderLine2(startLine + 1, "|", keptBeforeDeletion, blackBright2));
41542
+ }
41543
+ const deletedOnFirstLine = startLine === endLine ? firstLineText.slice(startCol, endCol) : firstLineText.slice(startCol);
41544
+ if (deletedOnFirstLine.length > 0) {
41545
+ const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
41546
+ lines3.push(renderLine2(void 0, "-", `${spacePadding}${deletedOnFirstLine}`, red4));
41547
+ }
41548
+ }
41549
+ for (let i = startLine + 1; i < endLine; i++) {
41550
+ const lineText = allLines[i];
41551
+ if (lineText !== void 0) {
41552
+ lines3.push(renderLine2(void 0, "-", lineText, red4));
41553
+ }
41554
+ }
41555
+ if (endLine > startLine) {
41556
+ const lastLineText = allLines[endLine];
41557
+ const deletedOnLastLine = lastLineText.slice(0, endCol);
41558
+ if (deletedOnLastLine.length > 0) {
41559
+ lines3.push(renderLine2(void 0, "-", deletedOnLastLine, red4));
41560
+ }
41561
+ }
41562
+ if (textChange.newText.length > 0) {
41563
+ const newTextLines = textChange.newText.split("\n");
41564
+ const firstLineText = allLines[startLine];
41565
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41566
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41567
+ const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
41568
+ for (let i = 0; i < newTextLines.length; i++) {
41569
+ const newLine = newTextLines[i];
41570
+ if (i === newTextLines.length - 1 && newLine.length === 0 && newTextLines.length > 1) {
41571
+ continue;
41572
+ }
41573
+ const padding = i === 0 && hasNonWhitespaceKept ? spacePadding : "";
41574
+ lines3.push(renderLine2(void 0, "+", `${padding}${newLine}`, green3));
41575
+ }
41576
+ }
41577
+ let alignmentForKeptPart = 0;
41578
+ if (textChange.newText.length > 0) {
41579
+ const newTextLines = textChange.newText.split("\n");
41580
+ const lastNewLine = newTextLines[newTextLines.length - 1];
41581
+ if (lastNewLine.length === 0 && newTextLines.length > 1) {
41582
+ alignmentForKeptPart = 0;
41583
+ } else {
41584
+ const firstLineText = allLines[startLine];
41585
+ const keptBeforeDeletion = firstLineText.slice(0, startCol);
41586
+ const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
41587
+ if (hasNonWhitespaceKept) {
41588
+ if (newTextLines.length === 1) {
41589
+ alignmentForKeptPart = keptBeforeDeletion.length + lastNewLine.length;
41590
+ } else {
41591
+ alignmentForKeptPart = lastNewLine.length;
41592
+ }
41593
+ } else {
41594
+ alignmentForKeptPart = lastNewLine.length;
41595
+ }
41596
+ }
41597
+ } else {
41598
+ alignmentForKeptPart = endCol;
41599
+ }
41600
+ if (endLine > startLine) {
41601
+ const lastLineText = allLines[endLine];
41602
+ const keptAfterDeletion = lastLineText.slice(endCol);
41603
+ if (keptAfterDeletion.trim().length > 0) {
41604
+ const alignment = " ".repeat(alignmentForKeptPart);
41605
+ lines3.push(renderLine2(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
41606
+ }
41607
+ } else if (startLine === endLine) {
41608
+ const firstLineText = allLines[startLine];
41609
+ const keptAfterDeletion = firstLineText.slice(endCol);
41610
+ if (keptAfterDeletion.trim().length > 0) {
41611
+ const alignment = " ".repeat(alignmentForKeptPart);
41612
+ lines3.push(renderLine2(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
41613
+ }
41614
+ }
41615
+ if (endLine + 1 < allLines.length) {
41616
+ const contextAfter = allLines[endLine + 1];
41617
+ lines3.push(renderLine2(endLine + 2, "|", contextAfter, blackBright2));
41618
+ }
41619
+ return lines3;
41620
+ }
41621
+ function renderFileChanges(sourceFile, textChanges) {
41622
+ const lines3 = [];
41623
+ const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start);
41624
+ for (let i = 0; i < sortedChanges.length; i++) {
41625
+ const change = sortedChanges[i];
41626
+ const changeLines = renderTextChange(sourceFile, change);
41627
+ for (const line4 of changeLines) {
41628
+ lines3.push(line4);
41629
+ }
41630
+ if (i < sortedChanges.length - 1) {
41631
+ lines3.push(text3(""));
41632
+ }
41633
+ }
41634
+ return lines3;
41635
+ }
41636
+ var renderCodeActions = (result, assessmentState) => gen2(function* () {
41637
+ if (result.codeActions.length === 0) {
41638
+ const noChanges = annotate2(
41639
+ text3("No changes needed - your configuration is already up to date!"),
41640
+ green3
41641
+ );
41642
+ const noChangesStr = noChanges.pipe(render3({ style: "pretty" }));
41643
+ yield* log3(noChangesStr);
41644
+ return;
41645
+ }
41646
+ const sourceFiles = [
41647
+ assessmentState.packageJson.sourceFile,
41648
+ assessmentState.tsconfig.sourceFile
41649
+ ];
41650
+ if (isSome2(assessmentState.vscodeSettings)) {
41651
+ sourceFiles.push(assessmentState.vscodeSettings.value.sourceFile);
41652
+ }
41653
+ const plainTextFiles = [];
41654
+ if (isSome2(assessmentState.agentsMd)) {
41655
+ plainTextFiles.push({
41656
+ path: assessmentState.agentsMd.value.path,
41657
+ text: assessmentState.agentsMd.value.text
41658
+ });
41659
+ }
41660
+ if (isSome2(assessmentState.claudeMd)) {
41661
+ plainTextFiles.push({
41662
+ path: assessmentState.claudeMd.value.path,
41663
+ text: assessmentState.claudeMd.value.text
41664
+ });
41665
+ }
41666
+ for (const codeAction of result.codeActions) {
41667
+ for (const fileChange of codeAction.changes) {
41668
+ const sourceFile = sourceFiles.find((sf) => sf.fileName === fileChange.fileName);
41669
+ const plainTextFile = plainTextFiles.find((pf) => pf.path === fileChange.fileName);
41670
+ const header = vsep2([
41671
+ empty32,
41672
+ annotate2(text3(codeAction.description), bold2),
41673
+ annotate2(text3(fileChange.fileName), cyan4),
41674
+ empty32
41675
+ ]);
41676
+ const headerStr = header.pipe(render3({ style: "pretty" }));
41677
+ yield* log3(headerStr);
41678
+ if (sourceFile) {
41679
+ const diffLines = renderFileChanges(sourceFile, fileChange.textChanges);
41680
+ const diff8 = vsep2(diffLines);
41681
+ const diffStr = diff8.pipe(render3({ style: "pretty" }));
41682
+ yield* log3(diffStr);
41683
+ } else if (plainTextFile) {
41684
+ const diffLines = renderPlainTextFileChanges(plainTextFile.text, fileChange.textChanges);
41685
+ const diff8 = vsep2(diffLines);
41686
+ const diffStr = diff8.pipe(render3({ style: "pretty" }));
41687
+ yield* log3(diffStr);
41688
+ } else {
41689
+ const noticeStr = text3(" (file will be modified)").pipe(render3({ style: "pretty" }));
41690
+ yield* log3(noticeStr);
41691
+ }
41692
+ }
41693
+ }
41694
+ if (result.messages.length > 0) {
41695
+ yield* log3("");
41696
+ for (const message of result.messages) {
41697
+ const messageDoc = message.includes("WARNING") ? annotate2(text3(message), yellow3) : text3(message);
41698
+ const messageStr = messageDoc.pipe(render3({ style: "pretty" }));
41699
+ yield* log3(messageStr);
41700
+ }
41701
+ }
41702
+ });
41703
+
41704
+ // src/cli/quickfixes.ts
41705
+ var isSkipFix = (fixName) => fixName.endsWith("_skipNextLine") || fixName.endsWith("_skipFile");
41706
+ var renderQuickFix = (sourceFile, fix) => {
41707
+ const lines3 = [];
41708
+ lines3.push(empty32);
41709
+ lines3.push(
41710
+ cat2(
41711
+ cat2(
41712
+ cat2(
41713
+ annotate2(text3(" Fix: "), bold2),
41714
+ annotate2(text3(fix.fixName), cyan4)
41715
+ ),
41716
+ text3(" - ")
41717
+ ),
41718
+ text3(fix.description)
41719
+ )
41720
+ );
41721
+ lines3.push(annotate2(text3(" " + "\u2500".repeat(60)), blackBright2));
41722
+ for (const fileChange of fix.changes) {
41723
+ if (fileChange.fileName === sourceFile.fileName) {
41724
+ for (const textChange of fileChange.textChanges) {
41725
+ const diffLines = renderTextChange(sourceFile, textChange);
41726
+ for (const diffLine of diffLines) {
41727
+ lines3.push(cat2(text3(" "), diffLine));
41728
+ }
41729
+ }
41730
+ }
41731
+ }
41732
+ return vsep2(lines3);
41733
+ };
41734
+ var renderDiagnosticWithFixes = (sourceFile, info2, tsInstance) => {
41735
+ const lines3 = [];
41736
+ const { character, line: line4 } = tsInstance.getLineAndCharacterOfPosition(sourceFile, info2.diagnostic.start);
41737
+ const locationStr = `${sourceFile.fileName}:${line4 + 1}:${character + 1}`;
41738
+ lines3.push(
41739
+ cat2(
41740
+ cat2(
41741
+ cat2(
41742
+ cat2(
41743
+ annotate2(text3(locationStr), cyan4),
41744
+ text3(" ")
41745
+ ),
41746
+ annotate2(text3(`effect(${info2.diagnostic.ruleName})`), yellow3)
41747
+ ),
41748
+ text3(": ")
41749
+ ),
41750
+ text3(info2.diagnostic.messageText)
41751
+ )
41752
+ );
41753
+ for (const fix of info2.fixes) {
41754
+ lines3.push(renderQuickFix(sourceFile, fix));
41755
+ }
41756
+ lines3.push(empty32);
41757
+ return vsep2(lines3);
41758
+ };
41759
+ var BATCH_SIZE4 = 50;
41760
+ var quickfixes = make58(
41761
+ "quickfixes",
41762
+ {
41763
+ file: file3("file").pipe(
41764
+ optional4,
41765
+ withDescription3("The full path of the file to check for quick fixes.")
41766
+ ),
41767
+ project: file3("project").pipe(
41768
+ optional4,
41769
+ withDescription3("The full path of the project tsconfig.json file to check for quick fixes.")
41770
+ )
41771
+ },
41772
+ fn("quickfixes")(function* ({ file: file5, project: project3 }) {
41773
+ const path2 = yield* Path2;
41774
+ const tsInstance = yield* TypeScriptContext;
41775
+ const filesToCheck = isSome2(project3) ? yield* getFileNamesInTsConfig(project3.value) : /* @__PURE__ */ new Set();
41776
+ if (isSome2(file5)) {
41777
+ filesToCheck.add(path2.resolve(file5.value));
41778
+ }
41779
+ if (filesToCheck.size === 0) {
41780
+ return yield* new NoFilesToCheckError();
41781
+ }
41782
+ let totalDiagnosticsWithFixes = 0;
41783
+ for (const batch of chunksOf(filesToCheck, BATCH_SIZE4)) {
41784
+ const { service: service3 } = (0, import_project_service5.createProjectService)({ options: { loadTypeScriptPlugins: false } });
41785
+ for (const filePath of batch) {
41786
+ service3.openClientFile(filePath);
41787
+ try {
41788
+ const scriptInfo = service3.getScriptInfo(filePath);
41789
+ if (!scriptInfo) continue;
41790
+ const projectInfo = scriptInfo.getDefaultProject();
41791
+ const languageService = projectInfo.getLanguageService(true);
41792
+ const program = languageService.getProgram();
41793
+ if (!program) continue;
41794
+ const sourceFile = program.getSourceFile(filePath);
41795
+ if (!sourceFile) continue;
41796
+ const pluginConfig = extractEffectLspOptions(program.getCompilerOptions());
41797
+ if (!pluginConfig) continue;
41798
+ const result = pipe(
41799
+ getSemanticDiagnosticsWithCodeFixes(diagnostics, sourceFile),
41800
+ nanoLayer3,
41801
+ nanoLayer2,
41802
+ nanoLayer,
41803
+ provideService7(TypeCheckerApi, program.getTypeChecker()),
41804
+ provideService7(TypeScriptProgram, program),
41805
+ provideService7(TypeScriptApi, tsInstance),
41806
+ provideService7(
41807
+ LanguageServicePluginOptions,
41808
+ { ...parse4(pluginConfig), diagnosticsName: false }
41809
+ ),
41810
+ run9,
41811
+ getOrElse(() => ({ diagnostics: [], codeFixes: [] }))
41812
+ );
41813
+ const diagnosticMap = /* @__PURE__ */ new Map();
41814
+ for (const diagnostic of result.diagnostics) {
41815
+ if (diagnostic.start === void 0) continue;
41816
+ const key = `${diagnostic.start}-${diagnostic.start + (diagnostic.length ?? 0)}-${diagnostic.code}`;
41817
+ const ruleName = Object.values(diagnostics).find((_) => _.code === diagnostic.code)?.name ?? `unknown(${diagnostic.code})`;
41818
+ if (!diagnosticMap.has(key)) {
41819
+ diagnosticMap.set(key, {
41820
+ diagnostic: {
41821
+ start: diagnostic.start,
41822
+ end: diagnostic.start + (diagnostic.length ?? 0),
41823
+ messageText: tsInstance.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
41824
+ code: diagnostic.code,
41825
+ ruleName
41826
+ },
41827
+ fixes: []
41828
+ });
41829
+ }
41830
+ }
41831
+ const formatContext = tsInstance.formatting.getFormatContext(
41832
+ tsInstance.getDefaultFormatCodeSettings(),
41833
+ { getNewLine: () => "\n" }
41834
+ );
41835
+ for (const codeFix of result.codeFixes) {
41836
+ if (isSkipFix(codeFix.fixName)) continue;
41837
+ const key = `${codeFix.start}-${codeFix.end}-${codeFix.code}`;
41838
+ const info2 = diagnosticMap.get(key);
41839
+ if (!info2) continue;
41840
+ const changes2 = tsInstance.textChanges.ChangeTracker.with(
41841
+ {
41842
+ formatContext,
41843
+ host: { getNewLine: () => "\n" },
41844
+ preferences: {}
41845
+ },
41846
+ (changeTracker) => pipe(
41847
+ codeFix.apply,
41848
+ provideService7(ChangeTracker, changeTracker),
41849
+ run9
41850
+ )
41851
+ );
41852
+ info2.fixes.push({
41853
+ fixName: codeFix.fixName,
41854
+ description: codeFix.description,
41855
+ changes: changes2
41856
+ });
41857
+ }
41858
+ const diagnosticsWithFixes = Array.from(diagnosticMap.values()).filter(
41859
+ (info2) => info2.fixes.length > 0
41860
+ );
41861
+ if (diagnosticsWithFixes.length === 0) continue;
41862
+ diagnosticsWithFixes.sort((a, b) => a.diagnostic.start - b.diagnostic.start);
41863
+ totalDiagnosticsWithFixes += diagnosticsWithFixes.length;
41864
+ for (const info2 of diagnosticsWithFixes) {
41865
+ const doc = renderDiagnosticWithFixes(sourceFile, info2, tsInstance);
41866
+ yield* log3(doc.pipe(render3({ style: "pretty" })));
41867
+ }
41868
+ } finally {
41869
+ service3.closeClientFile(filePath);
41870
+ }
41871
+ }
41872
+ yield* yieldNow4();
41873
+ }
41874
+ if (totalDiagnosticsWithFixes === 0) {
41875
+ yield* log3("No quick fixes available.");
41876
+ } else {
41877
+ yield* log3(
41878
+ annotate2(
41879
+ text3(`Found ${totalDiagnosticsWithFixes} diagnostic(s) with quick fixes.`),
41880
+ bold2
41881
+ ).pipe(render3({ style: "pretty" }))
41882
+ );
41883
+ }
41884
+ })
41885
+ ).pipe(
41886
+ withDescription6("Shows diagnostics with available quick fixes and their proposed changes.")
41887
+ );
41888
+
40974
41889
  // node_modules/.pnpm/@effect+cli@0.73.0_@effect+platform@0.94.1_@effect+printer-ansi@0.47.0_@effect+printer@0.47.0_effect@3.19.14/node_modules/@effect/cli/dist/esm/internal/prompt/confirm.js
40975
41890
  var renderBeep7 = /* @__PURE__ */ render3(beep3, {
40976
41891
  style: "pretty"
@@ -42047,335 +42962,6 @@ var computeVSCodeSettingsChanges = (current, target) => {
42047
42962
  });
42048
42963
  };
42049
42964
 
42050
- // src/cli/setup/text-diff-renderer.ts
42051
- function getLines(text10) {
42052
- return text10.split("\n");
42053
- }
42054
- function getLineAndCharacterOfPosition(text10, position) {
42055
- const lines3 = text10.split("\n");
42056
- let currentPos = 0;
42057
- for (let lineIndex = 0; lineIndex < lines3.length; lineIndex++) {
42058
- const lineLength = lines3[lineIndex].length;
42059
- const lineEndPos = currentPos + lineLength;
42060
- if (position <= lineEndPos) {
42061
- return { line: lineIndex, character: position - currentPos };
42062
- }
42063
- currentPos = lineEndPos + 1;
42064
- }
42065
- const lastLineIndex = lines3.length - 1;
42066
- return { line: lastLineIndex, character: lines3[lastLineIndex]?.length ?? 0 };
42067
- }
42068
- function renderLine(lineNum, symbol3, text10, color3) {
42069
- const lineNumPart = lineNum !== void 0 ? String(lineNum).padStart(4, " ") : " ";
42070
- return annotate2(text3(`${lineNumPart} ${symbol3} ${text10}`), color3);
42071
- }
42072
- function renderPlainTextChange(text10, textChange) {
42073
- const startPos = textChange.span.start;
42074
- const endPos = textChange.span.start + textChange.span.length;
42075
- const startLineAndChar = getLineAndCharacterOfPosition(text10, startPos);
42076
- const endLineAndChar = getLineAndCharacterOfPosition(text10, endPos);
42077
- const startLine = startLineAndChar.line;
42078
- const endLine = endLineAndChar.line;
42079
- const startCol = startLineAndChar.character;
42080
- const endCol = endLineAndChar.character;
42081
- const lines3 = [];
42082
- const allLines = getLines(text10);
42083
- if (startLine > 0) {
42084
- const contextBefore = allLines[startLine - 1];
42085
- lines3.push(renderLine(startLine, "|", contextBefore, blackBright2));
42086
- }
42087
- if (startLine <= endLine) {
42088
- const firstLineText = allLines[startLine];
42089
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42090
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42091
- if (hasNonWhitespaceKept) {
42092
- lines3.push(renderLine(startLine + 1, "|", keptBeforeDeletion, blackBright2));
42093
- }
42094
- const deletedOnFirstLine = startLine === endLine ? firstLineText.slice(startCol, endCol) : firstLineText.slice(startCol);
42095
- if (deletedOnFirstLine.length > 0) {
42096
- const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
42097
- lines3.push(renderLine(void 0, "-", `${spacePadding}${deletedOnFirstLine}`, red4));
42098
- }
42099
- }
42100
- for (let i = startLine + 1; i < endLine; i++) {
42101
- const lineText = allLines[i];
42102
- if (lineText !== void 0) {
42103
- lines3.push(renderLine(void 0, "-", lineText, red4));
42104
- }
42105
- }
42106
- if (endLine > startLine) {
42107
- const lastLineText = allLines[endLine];
42108
- const deletedOnLastLine = lastLineText.slice(0, endCol);
42109
- if (deletedOnLastLine.length > 0) {
42110
- lines3.push(renderLine(void 0, "-", deletedOnLastLine, red4));
42111
- }
42112
- }
42113
- if (textChange.newText.length > 0) {
42114
- const newTextLines = textChange.newText.split("\n");
42115
- const firstLineText = allLines[startLine];
42116
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42117
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42118
- const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
42119
- for (let i = 0; i < newTextLines.length; i++) {
42120
- const newLine = newTextLines[i];
42121
- if (i === newTextLines.length - 1 && newLine.length === 0 && newTextLines.length > 1) {
42122
- continue;
42123
- }
42124
- const padding = i === 0 && hasNonWhitespaceKept ? spacePadding : "";
42125
- lines3.push(renderLine(void 0, "+", `${padding}${newLine}`, green3));
42126
- }
42127
- }
42128
- let alignmentForKeptPart = 0;
42129
- if (textChange.newText.length > 0) {
42130
- const newTextLines = textChange.newText.split("\n");
42131
- const lastNewLine = newTextLines[newTextLines.length - 1];
42132
- if (lastNewLine.length === 0 && newTextLines.length > 1) {
42133
- alignmentForKeptPart = 0;
42134
- } else {
42135
- const firstLineText = allLines[startLine];
42136
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42137
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42138
- if (hasNonWhitespaceKept) {
42139
- if (newTextLines.length === 1) {
42140
- alignmentForKeptPart = keptBeforeDeletion.length + lastNewLine.length;
42141
- } else {
42142
- alignmentForKeptPart = lastNewLine.length;
42143
- }
42144
- } else {
42145
- alignmentForKeptPart = lastNewLine.length;
42146
- }
42147
- }
42148
- } else {
42149
- alignmentForKeptPart = endCol;
42150
- }
42151
- if (endLine > startLine) {
42152
- const lastLineText = allLines[endLine];
42153
- const keptAfterDeletion = lastLineText.slice(endCol);
42154
- if (keptAfterDeletion.trim().length > 0) {
42155
- const alignment = " ".repeat(alignmentForKeptPart);
42156
- lines3.push(renderLine(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
42157
- }
42158
- } else if (startLine === endLine) {
42159
- const firstLineText = allLines[startLine];
42160
- const keptAfterDeletion = firstLineText.slice(endCol);
42161
- if (keptAfterDeletion.trim().length > 0) {
42162
- const alignment = " ".repeat(alignmentForKeptPart);
42163
- lines3.push(renderLine(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
42164
- }
42165
- }
42166
- if (endLine + 1 < allLines.length) {
42167
- const contextAfter = allLines[endLine + 1];
42168
- lines3.push(renderLine(endLine + 2, "|", contextAfter, blackBright2));
42169
- }
42170
- return lines3;
42171
- }
42172
- function renderPlainTextFileChanges(text10, textChanges) {
42173
- const lines3 = [];
42174
- const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start);
42175
- for (let i = 0; i < sortedChanges.length; i++) {
42176
- const change = sortedChanges[i];
42177
- const changeLines = renderPlainTextChange(text10, change);
42178
- for (const line4 of changeLines) {
42179
- lines3.push(line4);
42180
- }
42181
- if (i < sortedChanges.length - 1) {
42182
- lines3.push(text3(""));
42183
- }
42184
- }
42185
- return lines3;
42186
- }
42187
-
42188
- // src/cli/setup/diff-renderer.ts
42189
- function getLines2(text10) {
42190
- return text10.split("\n");
42191
- }
42192
- function renderLine2(lineNum, symbol3, text10, color3) {
42193
- const lineNumPart = lineNum !== void 0 ? String(lineNum).padStart(4, " ") : " ";
42194
- return annotate2(text3(`${lineNumPart} ${symbol3} ${text10}`), color3);
42195
- }
42196
- function renderTextChange(sourceFile, textChange) {
42197
- const startPos = textChange.span.start;
42198
- const endPos = textChange.span.start + textChange.span.length;
42199
- const startLineAndChar = sourceFile.getLineAndCharacterOfPosition(startPos);
42200
- const endLineAndChar = sourceFile.getLineAndCharacterOfPosition(endPos);
42201
- const startLine = startLineAndChar.line;
42202
- const endLine = endLineAndChar.line;
42203
- const startCol = startLineAndChar.character;
42204
- const endCol = endLineAndChar.character;
42205
- const lines3 = [];
42206
- const allLines = getLines2(sourceFile.text);
42207
- if (startLine > 0) {
42208
- const contextBefore = allLines[startLine - 1];
42209
- lines3.push(renderLine2(startLine, "|", contextBefore, blackBright2));
42210
- }
42211
- if (startLine <= endLine) {
42212
- const firstLineText = allLines[startLine];
42213
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42214
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42215
- if (hasNonWhitespaceKept) {
42216
- lines3.push(renderLine2(startLine + 1, "|", keptBeforeDeletion, blackBright2));
42217
- }
42218
- const deletedOnFirstLine = startLine === endLine ? firstLineText.slice(startCol, endCol) : firstLineText.slice(startCol);
42219
- if (deletedOnFirstLine.length > 0) {
42220
- const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
42221
- lines3.push(renderLine2(void 0, "-", `${spacePadding}${deletedOnFirstLine}`, red4));
42222
- }
42223
- }
42224
- for (let i = startLine + 1; i < endLine; i++) {
42225
- const lineText = allLines[i];
42226
- if (lineText !== void 0) {
42227
- lines3.push(renderLine2(void 0, "-", lineText, red4));
42228
- }
42229
- }
42230
- if (endLine > startLine) {
42231
- const lastLineText = allLines[endLine];
42232
- const deletedOnLastLine = lastLineText.slice(0, endCol);
42233
- if (deletedOnLastLine.length > 0) {
42234
- lines3.push(renderLine2(void 0, "-", deletedOnLastLine, red4));
42235
- }
42236
- }
42237
- if (textChange.newText.length > 0) {
42238
- const newTextLines = textChange.newText.split("\n");
42239
- const firstLineText = allLines[startLine];
42240
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42241
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42242
- const spacePadding = hasNonWhitespaceKept ? " ".repeat(keptBeforeDeletion.length) : "";
42243
- for (let i = 0; i < newTextLines.length; i++) {
42244
- const newLine = newTextLines[i];
42245
- if (i === newTextLines.length - 1 && newLine.length === 0 && newTextLines.length > 1) {
42246
- continue;
42247
- }
42248
- const padding = i === 0 && hasNonWhitespaceKept ? spacePadding : "";
42249
- lines3.push(renderLine2(void 0, "+", `${padding}${newLine}`, green3));
42250
- }
42251
- }
42252
- let alignmentForKeptPart = 0;
42253
- if (textChange.newText.length > 0) {
42254
- const newTextLines = textChange.newText.split("\n");
42255
- const lastNewLine = newTextLines[newTextLines.length - 1];
42256
- if (lastNewLine.length === 0 && newTextLines.length > 1) {
42257
- alignmentForKeptPart = 0;
42258
- } else {
42259
- const firstLineText = allLines[startLine];
42260
- const keptBeforeDeletion = firstLineText.slice(0, startCol);
42261
- const hasNonWhitespaceKept = keptBeforeDeletion.trim().length > 0;
42262
- if (hasNonWhitespaceKept) {
42263
- if (newTextLines.length === 1) {
42264
- alignmentForKeptPart = keptBeforeDeletion.length + lastNewLine.length;
42265
- } else {
42266
- alignmentForKeptPart = lastNewLine.length;
42267
- }
42268
- } else {
42269
- alignmentForKeptPart = lastNewLine.length;
42270
- }
42271
- }
42272
- } else {
42273
- alignmentForKeptPart = endCol;
42274
- }
42275
- if (endLine > startLine) {
42276
- const lastLineText = allLines[endLine];
42277
- const keptAfterDeletion = lastLineText.slice(endCol);
42278
- if (keptAfterDeletion.trim().length > 0) {
42279
- const alignment = " ".repeat(alignmentForKeptPart);
42280
- lines3.push(renderLine2(endLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
42281
- }
42282
- } else if (startLine === endLine) {
42283
- const firstLineText = allLines[startLine];
42284
- const keptAfterDeletion = firstLineText.slice(endCol);
42285
- if (keptAfterDeletion.trim().length > 0) {
42286
- const alignment = " ".repeat(alignmentForKeptPart);
42287
- lines3.push(renderLine2(startLine + 1, "|", `${alignment}${keptAfterDeletion}`, blackBright2));
42288
- }
42289
- }
42290
- if (endLine + 1 < allLines.length) {
42291
- const contextAfter = allLines[endLine + 1];
42292
- lines3.push(renderLine2(endLine + 2, "|", contextAfter, blackBright2));
42293
- }
42294
- return lines3;
42295
- }
42296
- function renderFileChanges(sourceFile, textChanges) {
42297
- const lines3 = [];
42298
- const sortedChanges = [...textChanges].sort((a, b) => a.span.start - b.span.start);
42299
- for (let i = 0; i < sortedChanges.length; i++) {
42300
- const change = sortedChanges[i];
42301
- const changeLines = renderTextChange(sourceFile, change);
42302
- for (const line4 of changeLines) {
42303
- lines3.push(line4);
42304
- }
42305
- if (i < sortedChanges.length - 1) {
42306
- lines3.push(text3(""));
42307
- }
42308
- }
42309
- return lines3;
42310
- }
42311
- var renderCodeActions = (result, assessmentState) => gen2(function* () {
42312
- if (result.codeActions.length === 0) {
42313
- const noChanges = annotate2(
42314
- text3("No changes needed - your configuration is already up to date!"),
42315
- green3
42316
- );
42317
- const noChangesStr = noChanges.pipe(render3({ style: "pretty" }));
42318
- yield* log3(noChangesStr);
42319
- return;
42320
- }
42321
- const sourceFiles = [
42322
- assessmentState.packageJson.sourceFile,
42323
- assessmentState.tsconfig.sourceFile
42324
- ];
42325
- if (isSome2(assessmentState.vscodeSettings)) {
42326
- sourceFiles.push(assessmentState.vscodeSettings.value.sourceFile);
42327
- }
42328
- const plainTextFiles = [];
42329
- if (isSome2(assessmentState.agentsMd)) {
42330
- plainTextFiles.push({
42331
- path: assessmentState.agentsMd.value.path,
42332
- text: assessmentState.agentsMd.value.text
42333
- });
42334
- }
42335
- if (isSome2(assessmentState.claudeMd)) {
42336
- plainTextFiles.push({
42337
- path: assessmentState.claudeMd.value.path,
42338
- text: assessmentState.claudeMd.value.text
42339
- });
42340
- }
42341
- for (const codeAction of result.codeActions) {
42342
- for (const fileChange of codeAction.changes) {
42343
- const sourceFile = sourceFiles.find((sf) => sf.fileName === fileChange.fileName);
42344
- const plainTextFile = plainTextFiles.find((pf) => pf.path === fileChange.fileName);
42345
- const header = vsep2([
42346
- empty32,
42347
- annotate2(text3(codeAction.description), bold2),
42348
- annotate2(text3(fileChange.fileName), cyan4),
42349
- empty32
42350
- ]);
42351
- const headerStr = header.pipe(render3({ style: "pretty" }));
42352
- yield* log3(headerStr);
42353
- if (sourceFile) {
42354
- const diffLines = renderFileChanges(sourceFile, fileChange.textChanges);
42355
- const diff8 = vsep2(diffLines);
42356
- const diffStr = diff8.pipe(render3({ style: "pretty" }));
42357
- yield* log3(diffStr);
42358
- } else if (plainTextFile) {
42359
- const diffLines = renderPlainTextFileChanges(plainTextFile.text, fileChange.textChanges);
42360
- const diff8 = vsep2(diffLines);
42361
- const diffStr = diff8.pipe(render3({ style: "pretty" }));
42362
- yield* log3(diffStr);
42363
- } else {
42364
- const noticeStr = text3(" (file will be modified)").pipe(render3({ style: "pretty" }));
42365
- yield* log3(noticeStr);
42366
- }
42367
- }
42368
- }
42369
- if (result.messages.length > 0) {
42370
- yield* log3("");
42371
- for (const message of result.messages) {
42372
- const messageDoc = message.includes("WARNING") ? annotate2(text3(message), yellow3) : text3(message);
42373
- const messageStr = messageDoc.pipe(render3({ style: "pretty" }));
42374
- yield* log3(messageStr);
42375
- }
42376
- }
42377
- });
42378
-
42379
42965
  // src/cli/setup/errors.ts
42380
42966
  var PackageJsonNotFoundError = class extends TaggedError("PackageJsonNotFoundError") {
42381
42967
  get message() {
@@ -43017,7 +43603,7 @@ var cliCommand = make58(
43017
43603
  "effect-language-service",
43018
43604
  {},
43019
43605
  () => log3("Please select a command or run --help.")
43020
- ).pipe(withSubcommands3([setup, patch9, unpatch, check2, diagnostics2, codegen, overview, layerInfo]));
43606
+ ).pipe(withSubcommands3([setup, patch9, unpatch, check2, diagnostics2, quickfixes, codegen, overview, layerInfo]));
43021
43607
  var main = run8(cliCommand, {
43022
43608
  name: "effect-language-service",
43023
43609
  version: package_default.version