/** * D89 A4: a 'map' callback that rebuilds its rows instead of carrying them * over, which is what a keyed list stops recognising — what one rebuild records, * and the module reading that decides whether a helper builds fresh records at * all. * * D115 P4 R3a: the advisory is raised from the analyzer's JSX pass, but every * reading it rests on is a reading of the program, so they read as a module. * * D115 P4 R3c: the four rules that record a rebuild and raise the advisory join * them, over a `KeyedRebuildHost`. */ import { type DiagnosticFix, type Span } from "@velarscript/compiler"; import { type Expression, type Program, type Statement, type ValueType } from "@velarscript/compiler/extension"; import { type WebComputedDeclaration as ComputedDeclaration } from "../ast.ts"; /** * D89 A4: what a `map` callback rebuilds, when it returns a newly built record * instead of the row it was handed. Null is the non-trigger answer the ruling * names — a callback that returns its parameter, or anything else that is not a * record literal, builds nothing and moves no identity. A record is reported * even when no field can be named, because it is the rebuilt row's own identity * that the keyed list stops recognising, not any one field it carries. * * A conditional is read through because the React spelling nearly always * carries one, `t.id == id ? {...t, done: true} : t`, and its `else` branch is * the returns-the-original case rather than a second shape. A branch that names * a field is preferred, since a suggestion that names one is the whole product. */ export declare function keyedRebuiltRecord(body: Expression, row: string): { readonly field: string | null; } | null; export type FunctionDeclarationStatement = Extract; /** * D89 A4, the wider proven shape: every `def` of the module by name, so a * `computed` whose initializer calls one can be read through to what that call * builds. A name declared twice maps to null — two bodies under one name make * the question unanswerable, and an advisory that cannot prove which body runs * stays silent rather than guessing. */ export declare function collectModuleFunctions(program: Program): ReadonlyMap; /** * D89 A4, the wider proven shape: whether a `def` answers a list it filled with * freshly constructed records — the `for`/`append` builder that spells the same * churn `list = list.map(item => {…})` spells with `map`, and the spelling the * P2b consumer actually wrote. * * Four things are proven, and any one of them missing leaves the advisory * silent, because the whole product is naming a rebuild that really happens: * * 1. the body's last statement returns a name it declared here, initialized to * an empty list literal — so the list is the `def`'s own, not one handed in; * 2. every value appended to that list is a record literal, so every element is * a new value on every call. One `append(row)` of a source record is the * identity-preserving spelling this advisory teaches, and one member call * the proof cannot read leaves the whole answer unproven; * 3. at least one such record reads a parameter of the `def` or the binding of * the `for` that appends it. A builder whose records are constant answers * the same content every call, so nothing it is derived from can move and * the `computed` over it never recomputes; * 4. nothing else touches the list — it is built and answered, nothing more. */ export declare function buildsFreshRecords(declaration: FunctionDeclarationStatement): boolean; /** * D89 A4: one rebuild of a list's rows, held until the module's JSX is analyzed * so the advisory is raised only where that same list is what a keyed list * renders. * * `kind` names which spelling wrote it, and the two differ only in the remedy * the message ends with. An `assignment` — `list = list.map(item => {…})` — owns * the list it rewrote, so the preserving alternative is the field write next to * it. A `derived` rebuild is a `computed` that builds its rows, and a derived * value owns nothing it could write, so the alternative names the source rows * instead. */ export interface KeyedListRebuild { readonly kind: "assignment" | "derived"; readonly source: string; readonly name: string; /** The rewritten field, or null when the rebuild names none. */ readonly field: string | null; readonly span: Span; } /** * What the rebuild rules ask of the analyzer that hosts them: the two module * tables the walk fills, the module's `def` bodies, and the two analyzer * operations a rebuild is recorded and advised through. * * D115 P4 R3c. `moduleFunctions` is replaced once per program, so it arrives * through a getter and a collaborator reading it mid-walk reads the live one. */ export interface KeyedRebuildHost { /** D89 A4: every row rebuild of this module — assigned or derived — in source order. */ readonly keyedListRebuilds: KeyedListRebuild[]; /** D89 A4: the binding identity of every list a keyed `.map(...)` interpolation renders. */ readonly keyedListSources: ReadonlySet; /** D89 A4: this module's `def` bodies by name, so a `computed` that calls one can be read through. Replaced per program. */ readonly moduleFunctions: ReadonlyMap; advise(code: string, message: string, adviceSpan: Span, fix?: DiagnosticFix): void; lookup(name: string): { readonly span: Span; readonly type: ValueType; } | null; } /** * D89 A4: records `list = list.map(item => {…})`, React's immutable update, * where the callback builds a new record rather than changing a field. * * D90 R2 stands — `__velarKeyed` compares identity and that does not move, * and the framework does not accommodate the idiom. This channel is not the * framework taking responsibility; it is the compiler telling the author that * the row he is typing into is about to be destroyed. Nothing is reported * yet: the advisory is owed only if the rewritten list is what a keyed list * renders, and the render usually sits below the update. */ export declare function recordKeyedListRebuild(host: KeyedRebuildHost, statement: Statement): void; /** * D89 A4, the wider proven shape: the same churn written as a derived value. * `computed rows = source.map(item => {…})` and `computed rows = build(...)` * over a `for`/`append` builder both hand a keyed position a fresh record for * every row on every recompute, which is exactly what the assignment shape * does — the reconciliation wave compiled both and found only one of them * named. It is one advisory with a wider proof, not a second code: the defect, * the consequence, and the suppression are the same. * * A `const` in a component body is deliberately not here. It is constructed * once, so its records never move, and advising it would be a guess rather * than a proof. */ export declare function recordDerivedKeyedListRebuild(host: KeyedRebuildHost, statement: ComputedDeclaration): void; /** * Whether a derived initializer constructs one fresh record per source * element. Two spellings are proven and everything else is silent: a `map` * whose callback answers a record literal — the same recognizer the * assignment shape uses — and a call to a `def` this module declares whose * whole answer is a list it filled with record literals. */ export declare function rebuildsRecordsPerElement(host: KeyedRebuildHost, value: Expression): boolean; /** * D89 A4: raises the advisory for the rebuilds whose list a keyed position * really renders. The advisory channel cannot reach `this.diagnostics`, so * nothing here fails a build, changes an emitted byte, or moves a semantic * rule; `// velar-allow A4: ` suppresses it where building the rows is * the only spelling, which a `readonly` list or one API response makes it. */ export declare function adviseKeyedListRebuilds(host: KeyedRebuildHost): void; //# sourceMappingURL=keyed-rebuild.d.ts.map