|
| 1 | +import ts from "typescript"; |
| 2 | +import {ChangeAction, ChangeSet} from "../../../autofix/autofix.js"; |
| 3 | +import {PositionInfo} from "../../LinterContext.js"; |
| 4 | +import {FixHelpers} from "./Fix.js"; |
| 5 | +import PropertyAssignmentBaseFix from "./PropertyAssignmentBaseFix.js"; |
| 6 | +import {Attribute, Position} from "sax-wasm"; |
| 7 | + |
| 8 | +export interface PropertyAssignmentGeneratorFixParams<GeneratorContext extends object> { |
| 9 | + /** |
| 10 | + * Validate the property assignment, optionally using the checker provided in the fix helpers. |
| 11 | + * |
| 12 | + * This hook may also collect information that affects the generator. For that, it can store |
| 13 | + * information in the provided context object, which can be retrieved later in the generator function |
| 14 | + */ |
| 15 | + validatePropertyAssignment?: ( |
| 16 | + ctx: GeneratorContext, helpers: FixHelpers, propertyAssignment: ts.PropertyAssignment |
| 17 | + ) => boolean; |
| 18 | + |
| 19 | + /** |
| 20 | + * The generator function will be used to determine the value of the replacement, affecting |
| 21 | + * the whole call expression |
| 22 | + * |
| 23 | + * If the return value is undefined, no change will be generated. |
| 24 | + * |
| 25 | + * @param ctx - The context object that can be used to store information between validation and generation |
| 26 | + * @param propertyName - |
| 27 | + * The name of the property that is being accessed, as a string representation of the source code, |
| 28 | + * @param propertyInitializer - |
| 29 | + * The initializer of the property assignment, as a string representation of the source code. |
| 30 | + */ |
| 31 | + generator: ( |
| 32 | + ctx: GeneratorContext, propertyName: string, propertyInitializer: string |
| 33 | + ) => string | undefined; |
| 34 | +}; |
| 35 | + |
| 36 | +/** |
| 37 | + * Fix a global property access. Requires a module name which will be imported and replaces the defined property access. |
| 38 | + * The property access is in the order of the AST, e.g. ["core", "ui", "sap"] |
| 39 | + */ |
| 40 | +export default class PropertyAssignmentGeneratorFix<GeneratorContext extends object> extends PropertyAssignmentBaseFix { |
| 41 | + protected generatorContext = {} as GeneratorContext; |
| 42 | + protected propertyName: string | undefined; |
| 43 | + protected propertyInitializer: string | undefined; |
| 44 | + |
| 45 | + constructor(private params: PropertyAssignmentGeneratorFixParams<GeneratorContext>) { |
| 46 | + super(); |
| 47 | + } |
| 48 | + |
| 49 | + visitLinterNode(node: ts.Node, sourcePosition: PositionInfo, helpers: FixHelpers) { |
| 50 | + if (!super.visitLinterNode(node, sourcePosition, helpers)) { |
| 51 | + return false; |
| 52 | + } |
| 53 | + if (!ts.isPropertyAssignment(node) || !ts.isIdentifier(node.name)) { |
| 54 | + return false; |
| 55 | + } |
| 56 | + if (this.params.validatePropertyAssignment) { |
| 57 | + if (!this.params.validatePropertyAssignment(this.generatorContext, helpers, node)) { |
| 58 | + return false; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + this.sourcePosition = sourcePosition; |
| 63 | + return true; |
| 64 | + } |
| 65 | + |
| 66 | + visitAutofixNode(node: ts.Node, position: number, sourceFile: ts.SourceFile) { |
| 67 | + if (!super.visitAutofixNode(node, position, sourceFile)) { |
| 68 | + return false; |
| 69 | + } |
| 70 | + if (!ts.isPropertyAssignment(node)) { |
| 71 | + return false; |
| 72 | + } |
| 73 | + |
| 74 | + this.propertyName = node.name.getFullText(); |
| 75 | + this.propertyInitializer = node.initializer.getFullText(); |
| 76 | + |
| 77 | + return true; |
| 78 | + } |
| 79 | + |
| 80 | + visitAutofixXmlNode(node: Attribute, toPosition: (pos: Position) => number) { |
| 81 | + if (!super.visitAutofixXmlNode(node, toPosition)) { |
| 82 | + return false; |
| 83 | + } |
| 84 | + this.propertyName = node.name.value; |
| 85 | + this.propertyInitializer = node.value.value; |
| 86 | + return true; |
| 87 | + } |
| 88 | + |
| 89 | + generateChanges(): ChangeSet | ChangeSet[] | undefined { |
| 90 | + if (this.startPos === undefined || this.endPos === undefined) { |
| 91 | + throw new Error("Start and end position are not defined"); |
| 92 | + } |
| 93 | + if (this.propertyName === undefined || this.propertyInitializer === undefined) { |
| 94 | + throw new Error("Property name or initializer is not defined"); |
| 95 | + } |
| 96 | + const value = this.params.generator(this.generatorContext, this.propertyName, this.propertyInitializer); |
| 97 | + if (value === undefined) { |
| 98 | + return; |
| 99 | + } |
| 100 | + return { |
| 101 | + action: ChangeAction.REPLACE, |
| 102 | + start: this.startPos, |
| 103 | + end: this.endPos, |
| 104 | + value, |
| 105 | + }; |
| 106 | + } |
| 107 | +} |
0 commit comments