diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-changed b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-changed index 03c0b597..2800a20c 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-changed +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-changed @@ -769,6 +769,29 @@ class Changed: if not lam or (m and m[3] != 'module' and m[0] <= lam[0] and lam[1] <= m[1]): return None if any(k in ('field', 'const', 'enum_member', 'variable') and a <= lam[0] <= max(b, a) for a, b, i, k, d, n in decls): return None return lam + FIELD_KINDS = ('field', 'const', 'enum_member', 'variable') + def field_spanning(ln, first=False): + """A FIELD'S INITIALIZER IS THE FIELD'S, however many lines it spans. Only a field's first line was charged to + it, so an edit to the second line of `export const A = {\n x: 1,\n};` (a schema object, a token table, a + multi-line static initializer) was "body " or "inside ", and test-impact seeded from every + declaration of the module instead of the const. The narrowest field whose span holds ln after its first line + (from it too with first=True, for an insertion after that line); None when a callable written inside the + initializer (an object literal's method, a table's lambda) holds ln itself: that callable's body is its own. + A Python module-level name is recorded from its first binding to its last (`x = 0` at the top, `x = 5` at the + bottom spans the file), not by its statement: there the span is the statement's own lines, while a bracket + it opens is still open or a line ends in a backslash""" + def end(x): + if not is_py: return x[1] + depth, j = 0, x[0] + while j <= len(OS): + t = OS[j - 1]; depth += sum(t.count(c) for c in '([{') - sum(t.count(c) for c in ')]}') + if depth <= 0 and not t.rstrip().endswith('\\'): return j + j += 1 + return x[0] + inside = lambda x: x[0] <= ln < end(x) if first else x[0] < ln <= end(x) + fx = min((x for x in decls if x[3] in FIELD_KINDS and inside(x)), key=lambda x: end(x) - x[0], default=None) + inner = (narrowest(ln, {'method', 'function', 'constructor'}) or narrowest(ln, None, lambdas=True)) if fx else None + return None if inner and fx[0] <= inner[0] and inner[1] <= end(fx) else fx def decl_after(ln, L): """the declaration whose header starts within 3 lines after ln (an annotation / decorator line belongs to it)""" for a, b, i, k, d, n in decls: @@ -785,6 +808,10 @@ class Changed: m = narrowest(after, {'method', 'function', 'constructor'}) if after else None if m and m[0] <= after < self.header_end(OL, m[0]): hits.setdefault(('signature', m), set()).add(after); header_ins.add((after, j1, j2)) + # and a line inserted into a field's initializer (a member added to a multi-line object) is that field's + for ins in list(added): + fx = field_spanning(ins[0], first=True) if ins[0] and ins not in header_ins else None + if fx: hits.setdefault(('field', fx), set()).add(ins[0]); added.remove(ins) def counterpart(ln): """the new line an old changed line became: the most similar line of its replaced block; None when deleted""" if ln in removed_lines: return None @@ -816,7 +843,7 @@ class Changed: f = None # a lambda's lines inside a field's initializer are the field's elif not f: f = next(((a, b, i, k, d, n) for a, b, i, k, d, n in decls if k in ('field', 'const', 'enum_member', 'variable') and a < ln - and any(is_lam(x) and x[0] <= ln <= x[1] and a <= x[0] <= max(b, a) for x in decls)), None) + and any(is_lam(x) and x[0] <= ln <= x[1] and a <= x[0] <= max(b, a) for x in decls)), None) or field_spanning(ln) m = narrowest(ln, {'method', 'function', 'constructor', 'module'}) # A FUNCTION WRITTEN IN ANOTHER'S PARAMETER LIST IS PART OF THAT HEADER. A default value # (`clock = { millis: () => Date.now() }`) holds a callable the graph records on its own; an edit inside the @@ -1042,6 +1069,12 @@ class Changed: detail = (f'may have changed: still assigned at line {again}' if again else f'may have changed: no assignment of it near its line, but the new text still names it' if any(re.search(rf'\b{re.escape(n)}\b', x) for x in NS) else 'removed or renamed') + # A DATA KEY OF A CONST OBJECT (`Settings.retries` in `const Settings = { retries: 3 }`) is written `key: value`: + # read as a typed field, `retries: 3` → `retries: 4` was "type 3, → 4,", and on a one-line const the key + # line's declared name (`LIMIT`) made `LIMIT.max` "renamed → LIMIT". Its value is its initializer + elif '.' in d and any(kk in FIELD_KINDS and dd == d.split('.')[0] for aa, bb, ii, kk, dd, nn in decls): + detail = ('initializer changed' if re.search(rf'(?:^|[{{,\s])["\']?{re.escape(n)}["\']?\s*:', nline) else + 'removed or renamed') elif ot and nt and ot != nt: detail = f'type {ot} → {nt}' elif nn_ and nn_ != n: detail = f'renamed → {nn_}' else: detail = 'initializer / modifiers changed' @@ -1061,7 +1094,7 @@ class Changed: # holds it. Never a signature, never nothing; a comment in a module's top level belongs to no declaration for ln in sorted(comment_only): x = next(((a, b, i, k, d, n) for a, b, i, k, d, n in decls if a == ln and k in ('field', 'const', 'enum_member', 'variable')), None) or \ - next((m for m in [narrowest(ln, {'method', 'function', 'constructor'})] if m), None) or \ + field_spanning(ln) or next((m for m in [narrowest(ln, {'method', 'function', 'constructor'})] if m), None) or \ narrowest(ln, {'class', 'interface', 'enum', 'type'}) if not x or any(e.get('id') == x[2] for e in out): continue a, b, i, k, d, n = x diff --git a/tests/cases/csharp/one-line-statements/case.json b/tests/cases/csharp/one-line-statements/case.json index d363cc13..6dad10ce 100644 --- a/tests/cases/csharp/one-line-statements/case.json +++ b/tests/cases/csharp/one-line-statements/case.json @@ -81,6 +81,27 @@ "avoid": [ " field " ] + }, + { + "why": "an edit inside a static readonly initializer written over several lines (a collection initializer) is that field's, not 'inside Conf'", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/Conf.cs", + "--new", + "{repo}/new_conf.cs", + "--file", + "src/Conf.cs" + ], + "want": [ + "field Conf.Settings", + "initializer" + ], + "avoid": [ + "inside Conf", + "Conf.Limit" + ] } ] } \ No newline at end of file diff --git a/tests/cases/csharp/one-line-statements/new_conf.cs b/tests/cases/csharp/one-line-statements/new_conf.cs new file mode 100644 index 00000000..f4b0f8ad --- /dev/null +++ b/tests/cases/csharp/one-line-statements/new_conf.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace A +{ + public class Conf + { + static readonly Dictionary Settings = new Dictionary + { + ["retries"] = 4, + ["timeout"] = 10, + }; + static readonly int Limit = 1; + + public int Get() + { + return Settings["retries"] + Limit; + } + } +} diff --git a/tests/cases/csharp/one-line-statements/src/Conf.cs b/tests/cases/csharp/one-line-statements/src/Conf.cs new file mode 100644 index 00000000..8f11af76 --- /dev/null +++ b/tests/cases/csharp/one-line-statements/src/Conf.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace A +{ + public class Conf + { + static readonly Dictionary Settings = new Dictionary + { + ["retries"] = 3, + ["timeout"] = 10, + }; + static readonly int Limit = 1; + + public int Get() + { + return Settings["retries"] + Limit; + } + } +} diff --git a/tests/cases/java/one-line-statements/New_conf.java b/tests/cases/java/one-line-statements/New_conf.java new file mode 100644 index 00000000..ed99655a --- /dev/null +++ b/tests/cases/java/one-line-statements/New_conf.java @@ -0,0 +1,15 @@ +package a; + +import java.util.List; +import java.util.Map; + +public class Conf { + static final Map SETTINGS = Map.of( + "retries", 4, + "timeout", 10); + static final int LIMIT = 1; + + int get() { + return SETTINGS.get("retries") + LIMIT; + } +} diff --git a/tests/cases/java/one-line-statements/case.json b/tests/cases/java/one-line-statements/case.json index a021ad92..991a9f99 100644 --- a/tests/cases/java/one-line-statements/case.json +++ b/tests/cases/java/one-line-statements/case.json @@ -143,6 +143,27 @@ "Box.lo", "added Box.high" ] + }, + { + "why": "an edit on a later line of a static final initializer written over several lines is that field's, not 'inside Conf'", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/main/java/a/Conf.java", + "--new", + "{repo}/New_conf.java", + "--file", + "src/main/java/a/Conf.java" + ], + "want": [ + "field Conf.SETTINGS", + "initializer" + ], + "avoid": [ + "inside Conf", + "Conf.LIMIT" + ] } ] } \ No newline at end of file diff --git a/tests/cases/java/one-line-statements/src/main/java/a/Conf.java b/tests/cases/java/one-line-statements/src/main/java/a/Conf.java new file mode 100644 index 00000000..214da4cd --- /dev/null +++ b/tests/cases/java/one-line-statements/src/main/java/a/Conf.java @@ -0,0 +1,15 @@ +package a; + +import java.util.List; +import java.util.Map; + +public class Conf { + static final Map SETTINGS = Map.of( + "retries", 3, + "timeout", 10); + static final int LIMIT = 1; + + int get() { + return SETTINGS.get("retries") + LIMIT; + } +} diff --git a/tests/cases/javascript/edits-in-place/case.json b/tests/cases/javascript/edits-in-place/case.json index b595e6e8..8b6ce905 100644 --- a/tests/cases/javascript/edits-in-place/case.json +++ b/tests/cases/javascript/edits-in-place/case.json @@ -200,6 +200,27 @@ "signature ", "removed " ] + }, + { + "why": "an edit on a later line of a const whose object initializer spans lines is that const's, not the module's body", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/conf.js", + "--new", + "{repo}/new-conf.js", + "--file", + "src/conf.js" + ], + "want": [ + "field Settings", + "initializer" + ], + "avoid": [ + "", + "LIMIT" + ] } ] } \ No newline at end of file diff --git a/tests/cases/javascript/edits-in-place/new-conf.js b/tests/cases/javascript/edits-in-place/new-conf.js new file mode 100644 index 00000000..6c9bd63b --- /dev/null +++ b/tests/cases/javascript/edits-in-place/new-conf.js @@ -0,0 +1,8 @@ +export const Settings = { + retries: 4, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export function total() { + return Settings.retries + LIMIT.max; +} diff --git a/tests/cases/javascript/edits-in-place/src/conf.js b/tests/cases/javascript/edits-in-place/src/conf.js new file mode 100644 index 00000000..63bae002 --- /dev/null +++ b/tests/cases/javascript/edits-in-place/src/conf.js @@ -0,0 +1,8 @@ +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export function total() { + return Settings.retries + LIMIT.max; +} diff --git a/tests/cases/python/one-line-statements/case.json b/tests/cases/python/one-line-statements/case.json index 678bb2dc..a2052746 100644 --- a/tests/cases/python/one-line-statements/case.json +++ b/tests/cases/python/one-line-statements/case.json @@ -167,6 +167,66 @@ "variable lo", "variable j" ] + }, + { + "why": "an edit inside a module-level dict written over several lines is that name's, not the module's body", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/conf.py", + "--new", + "{repo}/new_conf_mod.py", + "--file", + "src/conf.py" + ], + "want": [ + "field SETTINGS", + "initializer" + ], + "avoid": [ + "", + "LIMIT" + ] + }, + { + "why": "and a list item added to a class attribute written over several lines is that attribute's, not the class body's", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/conf.py", + "--new", + "{repo}/new_conf_cls.py", + "--file", + "src/conf.py" + ], + "want": [ + "field Store.ROUTES", + "initializer" + ], + "avoid": [ + "" + ] + }, + { + "why": "control: a statement between two bindings of a module-level name is not that name's (its recorded span runs from the first binding to the last)", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/script.py", + "--new", + "{repo}/new_script.py", + "--file", + "src/script.py" + ], + "want": [ + "body script." + ], + "avoid": [ + "count" + ] } ] } \ No newline at end of file diff --git a/tests/cases/python/one-line-statements/new_conf_cls.py b/tests/cases/python/one-line-statements/new_conf_cls.py new file mode 100644 index 00000000..cdf0f602 --- /dev/null +++ b/tests/cases/python/one-line-statements/new_conf_cls.py @@ -0,0 +1,16 @@ +SETTINGS = { + "retries": 3, + "timeout": 10, +} +LIMIT = {"max": 1} + + +class Store: + ROUTES = [ + "a", + "b", + "c", + ] + + def get(self): + return SETTINGS["retries"] + LIMIT["max"] diff --git a/tests/cases/python/one-line-statements/new_conf_mod.py b/tests/cases/python/one-line-statements/new_conf_mod.py new file mode 100644 index 00000000..9273a097 --- /dev/null +++ b/tests/cases/python/one-line-statements/new_conf_mod.py @@ -0,0 +1,15 @@ +SETTINGS = { + "retries": 4, + "timeout": 10, +} +LIMIT = {"max": 1} + + +class Store: + ROUTES = [ + "a", + "b", + ] + + def get(self): + return SETTINGS["retries"] + LIMIT["max"] diff --git a/tests/cases/python/one-line-statements/new_script.py b/tests/cases/python/one-line-statements/new_script.py new file mode 100644 index 00000000..da6e1e13 --- /dev/null +++ b/tests/cases/python/one-line-statements/new_script.py @@ -0,0 +1,4 @@ +count = 0 +print("start") +print("the middle") +count = 5 diff --git a/tests/cases/python/one-line-statements/src/conf.py b/tests/cases/python/one-line-statements/src/conf.py new file mode 100644 index 00000000..315fe390 --- /dev/null +++ b/tests/cases/python/one-line-statements/src/conf.py @@ -0,0 +1,15 @@ +SETTINGS = { + "retries": 3, + "timeout": 10, +} +LIMIT = {"max": 1} + + +class Store: + ROUTES = [ + "a", + "b", + ] + + def get(self): + return SETTINGS["retries"] + LIMIT["max"] diff --git a/tests/cases/python/one-line-statements/src/script.py b/tests/cases/python/one-line-statements/src/script.py new file mode 100644 index 00000000..7a5620c2 --- /dev/null +++ b/tests/cases/python/one-line-statements/src/script.py @@ -0,0 +1,4 @@ +count = 0 +print("start") +print("middle") +count = 5 diff --git a/tests/cases/typescript/multi-line-initializer/case.json b/tests/cases/typescript/multi-line-initializer/case.json new file mode 100644 index 00000000..88ae0ed5 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/case.json @@ -0,0 +1,170 @@ +{ + "lang": "typescript", + "src": "src", + "checks": [ + { + "why": "an edit on the second line of a const whose object initializer spans lines is that const's (here its declared key's) initializer, not the module's body, and a key's value is no type", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_member_edited.ts", + "--file", + "src/config.ts" + ], + "want": [ + "field Settings.retries", + "initializer changed" + ], + "avoid": [ + "", + "type 3" + ] + }, + { + "why": "a member inserted into a multi-line `as const` object is that const's change", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_member_added.ts", + "--file", + "src/config.ts" + ], + "want": [ + "field Tokens", + "initializer" + ], + "avoid": [ + "", + "new field" + ] + }, + { + "why": "a member removed from it is that key's removal, not the module's body", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_member_removed.ts", + "--file", + "src/config.ts" + ], + "want": [ + "removed Tokens.Store" + ], + "avoid": [ + "" + ] + }, + { + "why": "a member added to an object handed to a call (a schema builder) is the const the call initializes", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_call_argument.ts", + "--file", + "src/config.ts" + ], + "want": [ + "field OrderShape", + "initializer" + ], + "avoid": [ + "" + ] + }, + { + "why": "a comment added inside the initializer is that const's, as a comment only", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_comment.ts", + "--file", + "src/config.ts" + ], + "want": [ + "OrderShape", + "comment only" + ], + "avoid": [ + "" + ] + }, + { + "why": "control: an edit to a one-line const's value is still its initializer, never a rename of its key to the const's name", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_one_line.ts", + "--file", + "src/config.ts" + ], + "want": [ + "LIMIT", + "initializer changed" + ], + "avoid": [ + "", + "Settings", + "renamed" + ] + }, + { + "why": "control: a method written in an object literal keeps its own body", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_method_in_object.ts", + "--file", + "src/config.ts" + ], + "want": [ + "body run src/config.ts:17" + ], + "avoid": [ + "", + "field api" + ] + }, + { + "why": "control: a function below the consts is its own body, not a const above it", + "run": [ + "changed", + "{repo}", + "--old", + "{repo}/src/config.ts", + "--new", + "{repo}/new_function.ts", + "--file", + "src/config.ts" + ], + "want": [ + "body total" + ], + "avoid": [ + "", + "field Settings", + "field LIMIT" + ] + } + ] +} diff --git a/tests/cases/typescript/multi-line-initializer/new_call_argument.ts b/tests/cases/typescript/multi-line-initializer/new_call_argument.ts new file mode 100644 index 00000000..40b7f1c8 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_call_argument.ts @@ -0,0 +1,24 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', + channel: 'string', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_comment.ts b/tests/cases/typescript/multi-line-initializer/new_comment.ts new file mode 100644 index 00000000..fb659a71 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_comment.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', // the order's key + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_function.ts b/tests/cases/typescript/multi-line-initializer/new_function.ts new file mode 100644 index 00000000..6433f153 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_function.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs - LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_member_added.ts b/tests/cases/typescript/multi-line-initializer/new_member_added.ts new file mode 100644 index 00000000..7281b874 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_member_added.ts @@ -0,0 +1,24 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', + Cache: 'Cache', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_member_edited.ts b/tests/cases/typescript/multi-line-initializer/new_member_edited.ts new file mode 100644 index 00000000..c724701d --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_member_edited.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 4, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_member_removed.ts b/tests/cases/typescript/multi-line-initializer/new_member_removed.ts new file mode 100644 index 00000000..d52d9352 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_member_removed.ts @@ -0,0 +1,22 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_method_in_object.ts b/tests/cases/typescript/multi-line-initializer/new_method_in_object.ts new file mode 100644 index 00000000..cced1ac6 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_method_in_object.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries + 1; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/new_one_line.ts b/tests/cases/typescript/multi-line-initializer/new_one_line.ts new file mode 100644 index 00000000..db8e6479 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/new_one_line.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 2 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/src/config.ts b/tests/cases/typescript/multi-line-initializer/src/config.ts new file mode 100644 index 00000000..a574aedd --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/src/config.ts @@ -0,0 +1,23 @@ +function shape(fields: T): T { return fields; } + +export const Settings = { + retries: 3, + timeoutMs: 1000, +}; +export const LIMIT = { max: 1 }; +export const Tokens = { + Clock: 'Clock', + Store: 'Store', +} as const; +export const OrderShape = shape({ + id: 'string', + total: 'number', +}); +export const api = { + run(): number { + return Settings.retries; + }, +}; +export function total(): number { + return Settings.timeoutMs + LIMIT.max; +} diff --git a/tests/cases/typescript/multi-line-initializer/src/use.test.ts b/tests/cases/typescript/multi-line-initializer/src/use.test.ts new file mode 100644 index 00000000..25d6e778 --- /dev/null +++ b/tests/cases/typescript/multi-line-initializer/src/use.test.ts @@ -0,0 +1,2 @@ +import { total, Tokens, OrderShape } from './config'; +test('total', () => { expect(total()).toBe(1001); expect(Tokens.Clock).toBe('Clock'); expect(OrderShape.id).toBe('string'); });