@intentius/tsad-reference 1.4.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.
Files changed (55) hide show
  1. package/CAVEATS.md +95 -0
  2. package/README.md +42 -0
  3. package/dist/adapter.d.ts +5 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +50 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/fnbody.d.ts +11 -0
  8. package/dist/fnbody.d.ts.map +1 -0
  9. package/dist/fnbody.js +68 -0
  10. package/dist/fnbody.js.map +1 -0
  11. package/dist/fold.d.ts +86 -0
  12. package/dist/fold.d.ts.map +1 -0
  13. package/dist/fold.js +558 -0
  14. package/dist/fold.js.map +1 -0
  15. package/dist/foldable-helpers.d.ts +23 -0
  16. package/dist/foldable-helpers.d.ts.map +1 -0
  17. package/dist/foldable-helpers.js +25 -0
  18. package/dist/foldable-helpers.js.map +1 -0
  19. package/dist/host.d.ts +45 -0
  20. package/dist/host.d.ts.map +1 -0
  21. package/dist/host.js +5 -0
  22. package/dist/host.js.map +1 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +9 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/module.d.ts +20 -0
  28. package/dist/module.d.ts.map +1 -0
  29. package/dist/module.js +74 -0
  30. package/dist/module.js.map +1 -0
  31. package/dist/project.d.ts +22 -0
  32. package/dist/project.d.ts.map +1 -0
  33. package/dist/project.js +481 -0
  34. package/dist/project.js.map +1 -0
  35. package/dist/revive.d.ts +16 -0
  36. package/dist/revive.d.ts.map +1 -0
  37. package/dist/revive.js +109 -0
  38. package/dist/revive.js.map +1 -0
  39. package/dist/subset.d.ts +42 -0
  40. package/dist/subset.d.ts.map +1 -0
  41. package/dist/subset.js +214 -0
  42. package/dist/subset.js.map +1 -0
  43. package/package.json +37 -0
  44. package/src/adapter.ts +48 -0
  45. package/src/fnbody.ts +61 -0
  46. package/src/fold.ts +581 -0
  47. package/src/foldable-helpers.ts +38 -0
  48. package/src/host.ts +38 -0
  49. package/src/index.ts +8 -0
  50. package/src/module.ts +52 -0
  51. package/src/no-own-execution.test.ts +86 -0
  52. package/src/prebuild.test.ts +68 -0
  53. package/src/project.ts +495 -0
  54. package/src/revive.ts +120 -0
  55. package/src/subset.ts +224 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revive.js","sourceRoot":"","sources":["../src/revive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The shape classifier: grammar.md §2, one branch per S-* production.
3
+ *
4
+ * Written from the specification text, not derived from any implementation
5
+ * (#50). Decides from syntax alone and never resolves a name, which is the
6
+ * direction F-Direction permits: this may accept what the folder rejects and
7
+ * must never reject what the folder accepts, outside F-Exc-Lazy and
8
+ * F-Exc-Registry.
9
+ */
10
+ import * as ts from "typescript";
11
+ import { type IntrinsicDef } from "./host.js";
12
+ export interface ShapeViolation {
13
+ /** The S-* production that refused it. */
14
+ readonly rule: string;
15
+ readonly node: ts.Node;
16
+ readonly message: string;
17
+ }
18
+ /** ⟨LiteralKey⟩ ::= ⟨Identifier⟩ | ⟨StringLiteral⟩ | ⟨NumericLiteral⟩ */
19
+ export declare function isLiteralKey(n: ts.PropertyName): n is ts.Identifier | ts.StringLiteral | ts.NumericLiteral;
20
+ /** S-Index's key: a string or numeric literal only. */
21
+ export declare function isLiteralElementKey(n: ts.Expression): n is ts.StringLiteral | ts.NumericLiteral;
22
+ /**
23
+ * S-CompositeStep's callee condition.
24
+ *
25
+ * SPEC GAP (#51): the normative text calls this callee "unclaimed" in four
26
+ * places and defines it in none; all four cite inventory row L3.20, which is
27
+ * a ledger entry. Implemented here per that row's intent, which is that the
28
+ * callee is a bare identifier claimed by no other S-Call form and not
29
+ * shadowed by a local const. The registry and the local scope are resolution,
30
+ * so at shape level only the bare-identifier part is checkable.
31
+ */
32
+ export declare function isUnclaimedCallee(call: ts.CallExpression, intrinsics?: readonly IntrinsicDef[]): boolean;
33
+ /**
34
+ * Classify `node`'s shape. Returns the first violation in evaluation order,
35
+ * or undefined when the whole shape is admissible.
36
+ *
37
+ * `intrinsics` is the registry ρ. Absent, S-CallIntrinsic and S-CallEager are
38
+ * unrecognisable and their shapes fall to S-Reject, which is F-Exc-Registry:
39
+ * the one place this classifier is stricter than the folder.
40
+ */
41
+ export declare function findShapeViolation(node: ts.Node, intrinsics?: readonly IntrinsicDef[], localCallees?: ReadonlySet<string>): ShapeViolation | undefined;
42
+ //# sourceMappingURL=subset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subset.d.ts","sourceRoot":"","sources":["../src/subset.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAiD,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAG7F,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAsBD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,GAAG,CAAC,IAAI,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAE1G;AACD,uDAAuD;AACvD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,GAAG,CAAC,IAAI,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,cAAc,CAE/F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAMxG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,SAAS,YAAY,EAAE,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,cAAc,GAAG,SAAS,CAoJtJ"}
package/dist/subset.js ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The shape classifier: grammar.md §2, one branch per S-* production.
3
+ *
4
+ * Written from the specification text, not derived from any implementation
5
+ * (#50). Decides from syntax alone and never resolves a name, which is the
6
+ * direction F-Direction permits: this may accept what the folder rejects and
7
+ * must never reject what the folder accepts, outside F-Exc-Lazy and
8
+ * F-Exc-Registry.
9
+ */
10
+ import * as ts from "typescript";
11
+ import { intrinsicCallFolds, intrinsicCallFoldsEagerly } from "./host.js";
12
+ import { isFoldableHelperName } from "./foldable-helpers.js";
13
+ const no = (rule, node, message) => ({ rule, node, message });
14
+ /** ⟨BinOp⟩, grammar.md §2. */
15
+ const BIN_OPS = new Set([
16
+ ts.SyntaxKind.AmpersandAmpersandToken,
17
+ ts.SyntaxKind.BarBarToken,
18
+ ts.SyntaxKind.QuestionQuestionToken,
19
+ ts.SyntaxKind.PlusToken,
20
+ ts.SyntaxKind.MinusToken,
21
+ ts.SyntaxKind.AsteriskToken,
22
+ ts.SyntaxKind.SlashToken,
23
+ ts.SyntaxKind.EqualsEqualsEqualsToken,
24
+ ts.SyntaxKind.ExclamationEqualsEqualsToken,
25
+ ts.SyntaxKind.GreaterThanToken,
26
+ ts.SyntaxKind.LessThanToken,
27
+ ts.SyntaxKind.GreaterThanEqualsToken,
28
+ ts.SyntaxKind.LessThanEqualsToken,
29
+ ]);
30
+ /** S-Unary's operator set. */
31
+ const UNARY_OPS = new Set([ts.SyntaxKind.ExclamationToken, ts.SyntaxKind.MinusToken]);
32
+ /** ⟨LiteralKey⟩ ::= ⟨Identifier⟩ | ⟨StringLiteral⟩ | ⟨NumericLiteral⟩ */
33
+ export function isLiteralKey(n) {
34
+ return ts.isIdentifier(n) || ts.isStringLiteral(n) || ts.isNumericLiteral(n);
35
+ }
36
+ /** S-Index's key: a string or numeric literal only. */
37
+ export function isLiteralElementKey(n) {
38
+ return ts.isStringLiteral(n) || ts.isNumericLiteral(n);
39
+ }
40
+ /**
41
+ * S-CompositeStep's callee condition.
42
+ *
43
+ * SPEC GAP (#51): the normative text calls this callee "unclaimed" in four
44
+ * places and defines it in none; all four cite inventory row L3.20, which is
45
+ * a ledger entry. Implemented here per that row's intent, which is that the
46
+ * callee is a bare identifier claimed by no other S-Call form and not
47
+ * shadowed by a local const. The registry and the local scope are resolution,
48
+ * so at shape level only the bare-identifier part is checkable.
49
+ */
50
+ export function isUnclaimedCallee(call, intrinsics) {
51
+ if (!ts.isIdentifier(call.expression))
52
+ return false;
53
+ const name = call.expression.text;
54
+ if (isFoldableHelperName(name))
55
+ return false;
56
+ if (intrinsics?.some((i) => i.name === name && (intrinsicCallFolds(i) || intrinsicCallFoldsEagerly(i))))
57
+ return false;
58
+ return true;
59
+ }
60
+ /**
61
+ * Classify `node`'s shape. Returns the first violation in evaluation order,
62
+ * or undefined when the whole shape is admissible.
63
+ *
64
+ * `intrinsics` is the registry ρ. Absent, S-CallIntrinsic and S-CallEager are
65
+ * unrecognisable and their shapes fall to S-Reject, which is F-Exc-Registry:
66
+ * the one place this classifier is stricter than the folder.
67
+ */
68
+ export function findShapeViolation(node, intrinsics, localCallees) {
69
+ const recur = (n) => findShapeViolation(n, intrinsics, localCallees);
70
+ // S-Unwrap
71
+ if (ts.isParenthesizedExpression(node) ||
72
+ ts.isAsExpression(node) ||
73
+ ts.isSatisfiesExpression(node) ||
74
+ ts.isNonNullExpression(node)) {
75
+ return recur(node.expression);
76
+ }
77
+ // S-Literal
78
+ if (ts.isStringLiteral(node) ||
79
+ ts.isNoSubstitutionTemplateLiteral(node) ||
80
+ ts.isNumericLiteral(node) ||
81
+ node.kind === ts.SyntaxKind.TrueKeyword ||
82
+ node.kind === ts.SyntaxKind.FalseKeyword ||
83
+ node.kind === ts.SyntaxKind.NullKeyword) {
84
+ return undefined;
85
+ }
86
+ // S-Ident, which S-Undefined is an instance of. Always shape-valid:
87
+ // whether it resolves is F-Eval-Ident's question (F-Div-Ident).
88
+ if (ts.isIdentifier(node))
89
+ return undefined;
90
+ // S-Tagged. The interior is opaque and is deliberately not recursed into:
91
+ // an intrinsic's interpolations legitimately hold shapes this classifier
92
+ // would refuse, and only the folder has the registry (F-Div-Tag).
93
+ if (ts.isTaggedTemplateExpression(node))
94
+ return undefined;
95
+ // S-Template
96
+ if (ts.isTemplateExpression(node)) {
97
+ for (const span of node.templateSpans) {
98
+ const v = recur(span.expression);
99
+ if (v)
100
+ return v;
101
+ }
102
+ return undefined;
103
+ }
104
+ // S-Object
105
+ if (ts.isObjectLiteralExpression(node)) {
106
+ for (const member of node.properties) {
107
+ if (ts.isPropertyAssignment(member)) {
108
+ if (!isLiteralKey(member.name))
109
+ return no("S-Prop", member.name, "computed or dynamic property name");
110
+ const v = recur(member.initializer);
111
+ if (v)
112
+ return v;
113
+ }
114
+ else if (ts.isShorthandPropertyAssignment(member)) {
115
+ continue; // S-Shorthand: always shape-valid
116
+ }
117
+ else if (ts.isSpreadAssignment(member)) {
118
+ const v = recur(member.expression);
119
+ if (v)
120
+ return v;
121
+ }
122
+ else {
123
+ return no("S-Object", member, "unsupported object member");
124
+ }
125
+ }
126
+ return undefined;
127
+ }
128
+ // S-Array
129
+ if (ts.isArrayLiteralExpression(node)) {
130
+ for (const el of node.elements) {
131
+ const v = recur(ts.isSpreadElement(el) ? el.expression : el);
132
+ if (v)
133
+ return v;
134
+ }
135
+ return undefined;
136
+ }
137
+ // S-Member, with S-CompositeStep taking precedence when the member is
138
+ // `step` and the object is a call.
139
+ if (ts.isPropertyAccessExpression(node)) {
140
+ if (node.name.text === "step" && ts.isCallExpression(node.expression))
141
+ return undefined;
142
+ return recur(node.expression);
143
+ }
144
+ // S-Index
145
+ if (ts.isElementAccessExpression(node)) {
146
+ if (!isLiteralElementKey(node.argumentExpression)) {
147
+ return no("S-Index", node.argumentExpression, "element-access key must be a string or numeric literal");
148
+ }
149
+ return recur(node.expression);
150
+ }
151
+ // S-Unary
152
+ if (ts.isPrefixUnaryExpression(node)) {
153
+ if (!UNARY_OPS.has(node.operator))
154
+ return no("S-Unary", node, `unsupported unary operator: ${ts.SyntaxKind[node.operator]}`);
155
+ return recur(node.operand);
156
+ }
157
+ // S-Binary. Flow-insensitive by construction: both sides must be
158
+ // shape-valid even where the folder would evaluate only one (F-Exc-Lazy).
159
+ if (ts.isBinaryExpression(node)) {
160
+ const op = node.operatorToken.kind;
161
+ if (!BIN_OPS.has(op))
162
+ return no("S-Binary", node, `unsupported binary operator: ${ts.SyntaxKind[op]}`);
163
+ return recur(node.left) ?? recur(node.right);
164
+ }
165
+ // S-Conditional. Flow-insensitive for the same reason.
166
+ if (ts.isConditionalExpression(node)) {
167
+ return recur(node.condition) ?? recur(node.whenTrue) ?? recur(node.whenFalse);
168
+ }
169
+ // S-New. The callee's shape is unconstrained here; that it must be a plain
170
+ // identifier is resolution-adjacent and belongs to F-Eval-New (F-Div-NsNew).
171
+ if (ts.isNewExpression(node)) {
172
+ for (const arg of node.arguments ?? []) {
173
+ const v = recur(arg);
174
+ if (v)
175
+ return v;
176
+ }
177
+ return undefined;
178
+ }
179
+ // S-Call
180
+ if (ts.isCallExpression(node)) {
181
+ const args = () => {
182
+ for (const a of node.arguments) {
183
+ const v = recur(a);
184
+ if (v)
185
+ return v;
186
+ }
187
+ return undefined;
188
+ };
189
+ // S-CallMethod: receiver ∈ ⟨Expr⟩, method name unconstrained.
190
+ if (ts.isPropertyAccessExpression(node.expression))
191
+ return recur(node.expression.expression) ?? args();
192
+ if (ts.isIdentifier(node.expression)) {
193
+ const name = node.expression.text;
194
+ if (isFoldableHelperName(name))
195
+ return args(); // S-CallHelper
196
+ // S-CallLocal (spec 1.2): the callee is bound in this file by
197
+ // S-LocalFunction or by an import from a project specifier. The body's
198
+ // admissibility is the fold's question (S-FnBody at the call).
199
+ if (localCallees?.has(name))
200
+ return args();
201
+ const def = intrinsics?.find((i) => i.name === name);
202
+ // S-CallIntrinsic / S-CallEager, registry-gated. Without ρ neither is
203
+ // recognisable and the call falls to S-Reject (F-Exc-Registry).
204
+ if (def && (intrinsicCallFolds(def) || intrinsicCallFoldsEagerly(def)))
205
+ return args();
206
+ }
207
+ return no("S-Reject", node, "function call as a value is not foldable");
208
+ }
209
+ // S-Reject. This is where the explicitly-outside list lands: a function or
210
+ // arrow expression used as a value, class expressions, await, yield,
211
+ // assignment, the comma operator, and every operator outside ⟨BinOp⟩.
212
+ return no("S-Reject", node, `unsupported expression: ${ts.SyntaxKind[node.kind]}`);
213
+ }
214
+ //# sourceMappingURL=subset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subset.js","sourceRoot":"","sources":["../src/subset.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@intentius/tsad-reference",
3
+ "version": "1.4.0",
4
+ "description": "The reference implementation of the typescript-as-data specification, written from the specification text and nothing else.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/INTENTIUS/typescript-as-data.git",
9
+ "directory": "packages/reference"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "development": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
22
+ "types": "./dist/index.d.ts",
23
+ "main": "./dist/index.js",
24
+ "files": [
25
+ "src/",
26
+ "dist/",
27
+ "CAVEATS.md"
28
+ ],
29
+ "scripts": {
30
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
31
+ "prepack": "npm run build"
32
+ },
33
+ "dependencies": {
34
+ "@intentius/tsad-conformance": "^1.4.0",
35
+ "typescript": "^5.9.0"
36
+ }
37
+ }
package/src/adapter.ts ADDED
@@ -0,0 +1,48 @@
1
+ import type { ConformanceAdapter, ConformanceHost, ProjectResult } from "@intentius/tsad-conformance";
2
+ import { EMPTY_HOST, type Host, type Profile } from "./host.js";
3
+ import { shapeOfExport, foldExport } from "./module.js";
4
+ import { foldProject } from "./project.js";
5
+
6
+ /** A named conformance host, in this implementation's own terms. */
7
+ function hostOf(h: ConformanceHost | undefined, profile: Profile): Host {
8
+ if (!h) return { ...EMPTY_HOST, profile };
9
+ return {
10
+ profile,
11
+ intrinsics: h.intrinsics.map((i) => ({ name: i.name, isTag: i.isTag, foldsAsCall: i.foldsAsCall, foldsEagerly: i.foldsEagerly, outputKey: i.outputKey })),
12
+ helpers: h.helpers,
13
+ ownedSpecifierPrefixes: h.ownedSpecifierPrefixes,
14
+ values: h.values,
15
+ };
16
+ }
17
+
18
+ /** The reference reports spec rule identifiers directly: it is written from the spec. One adapter per profile (F-Profile). */
19
+ function adapterFor(profile: Profile): ConformanceAdapter {
20
+ return {
21
+ name: profile === "full" ? "reference" : `reference/${profile}`,
22
+ // Bumped by hand when the rule set this package implements moves; the
23
+ // conformance suite fails when it and spec/VERSION disagree (#18).
24
+ specVersion: "1.4",
25
+ shape(source, exportName) {
26
+ const v = shapeOfExport(source, exportName, { ...EMPTY_HOST, profile });
27
+ if (v === "no-such-export") return { accepted: false, line: 1, column: 1, message: `no export named ${exportName}` };
28
+ if (!v) return { accepted: true };
29
+ const { line, character } = v.node.getSourceFile().getLineAndCharacterOfPosition(v.node.getStart());
30
+ return { accepted: false, rule: v.rule, line: line + 1, column: character + 1, message: v.message };
31
+ },
32
+ foldExport(source, exportName) { return foldExport(source, exportName, { ...EMPTY_HOST, profile }); },
33
+ foldProject(files, host) {
34
+ const r = foldProject(files, hostOf(host, profile));
35
+ const out: ProjectResult = { verdicts: {}, tentative: {}, taintedBy: {} };
36
+ for (const [path, v] of r.verdicts) {
37
+ out.verdicts[path] = v.kind === "fold" ? { kind: "fold", exports: Object.fromEntries(v.exports) } : { kind: "run", rule: v.rule, reason: v.reason };
38
+ }
39
+ for (const [path, v] of r.tentative) out.tentative![path] = v.kind;
40
+ for (const [path, from] of r.taintSource) out.taintedBy![path] = from;
41
+ return out;
42
+ },
43
+ };
44
+ }
45
+
46
+ export const referenceAdapter: ConformanceAdapter = adapterFor("full");
47
+ /** The same implementation judged in the data-host profile: no runtime, and S-ExportDefault admitted. */
48
+ export const referenceDataHostAdapter: ConformanceAdapter = adapterFor("data-host");
package/src/fnbody.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * S-FnBody, grammar.md §3: the statement sub-grammar a project-local function
3
+ * must satisfy before J1's F-Eval-CallLocal will evaluate it.
4
+ */
5
+ import * as ts from "typescript";
6
+
7
+ export type FnDecl = ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression;
8
+
9
+ /** A binding element the sub-grammar admits: `{ a }` or `{ a: b }`, nothing else. */
10
+ export function plainBindingKey(el: ts.BindingElement): string | undefined {
11
+ if (el.dotDotDotToken || el.initializer || !ts.isIdentifier(el.name)) return undefined;
12
+ const key = el.propertyName ?? el.name;
13
+ return ts.isIdentifier(key) || ts.isStringLiteral(key) || ts.isNumericLiteral(key) ? key.text : undefined;
14
+ }
15
+
16
+ /** A reason the function is outside S-FnBody, or undefined when it is admissible. */
17
+ export function findFnBodyViolation(fn: FnDecl): string | undefined {
18
+ if (fn.asteriskToken) return "a generator function is not foldable";
19
+ if (fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) return "an async function is not foldable";
20
+ if (!fn.body) return "a function without a body is not foldable";
21
+
22
+ for (const p of fn.parameters) {
23
+ if (p.dotDotDotToken) return "a rest parameter is not foldable";
24
+ if (ts.isIdentifier(p.name)) continue;
25
+ if (ts.isObjectBindingPattern(p.name)) {
26
+ for (const el of p.name.elements) {
27
+ if (plainBindingKey(el) === undefined) return "a destructured parameter with a rest, default, or nested element is not foldable";
28
+ }
29
+ continue;
30
+ }
31
+ return "an array-destructured parameter is not foldable";
32
+ }
33
+
34
+ if (!ts.isBlock(fn.body)) return undefined; // a concise expression body
35
+
36
+ const statements = fn.body.statements;
37
+ for (let i = 0; i < statements.length; i += 1) {
38
+ const st = statements[i];
39
+ const last = i === statements.length - 1;
40
+ if (ts.isReturnStatement(st)) {
41
+ if (!last) return "an early `return` is not foldable";
42
+ continue;
43
+ }
44
+ if (!ts.isVariableStatement(st)) {
45
+ return `\`${ts.SyntaxKind[st.kind]}\` in a function body is not foldable, only \`const\` declarations and a final \`return\` are`;
46
+ }
47
+ if ((st.declarationList.flags & ts.NodeFlags.Const) === 0) return "`let`/`var` in a function body is not foldable";
48
+ for (const d of st.declarationList.declarations) {
49
+ if (!d.initializer) return "an uninitialized `const` in a function body is not foldable";
50
+ if (ts.isIdentifier(d.name)) continue;
51
+ if (ts.isObjectBindingPattern(d.name)) {
52
+ for (const el of d.name.elements) {
53
+ if (plainBindingKey(el) === undefined) return "a destructured `const` with a rest, default, or nested element is not foldable";
54
+ }
55
+ continue;
56
+ }
57
+ return "an array-destructured `const` in a function body is not foldable";
58
+ }
59
+ }
60
+ return undefined;
61
+ }