dsh-ptc-plus 0.3.2 → 0.4.2
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/README.en.md +63 -80
- package/README.md +63 -80
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30827 -742
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +556 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +7 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +77 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +28 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +76 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +759 -0
- package/docs/user-bindings.en.md +66 -0
- package/docs/user-bindings.md +66 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +71 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/async-local-storage-intrinsics.js +38 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +708 -0
- package/internal/binding-update-policy.js +72 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +253 -290
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -677
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +50 -0
- package/internal/compiler-module-links.js +37 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +83 -27
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +91 -0
- package/internal/dynamic-environment-runtime.js +383 -0
- package/internal/dynamic-native-calls.js +153 -0
- package/internal/dynamic-native-runtime.js +262 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +136 -28
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +555 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +1179 -233
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +322 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +42 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/record-utils.js +24 -15
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +117 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-intrinsics.js +101 -0
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +402 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +123 -0
- package/internal/session-journal.js +199 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +257 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +230 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +600 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +31 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +244 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +361 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +130 -91
- package/internal/worker-client.js +57 -67
- package/internal/worker-cwd-virtualization.js +80 -53
- package/internal/worker-environment.js +65 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-repl-realm.js +134 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +76 -7
- package/src/client-activity.js +95 -53
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
import { parse } from '@babel/parser'
|
|
2
|
+
import { AMBIENT_GLOBALS, renderDurabilityReasons } from './module-policy.js'
|
|
3
|
+
import { CELL_PARSER_PLUGINS, normalizeTypeScriptValues } from './repl-scope-normalizer.js'
|
|
4
|
+
import { identitySourceMap } from './source-position-map.js'
|
|
5
|
+
import { LEGACY_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM, transformTypeScriptSource } from './typescript-transform.js'
|
|
6
|
+
import { compileStatefulModule } from './module-compilation.js'
|
|
7
|
+
import { prepareProgram, classifyDurability } from './cell-analysis.js'
|
|
8
|
+
import { createCompilerSourceCache } from './compiler-source-cache.js'
|
|
9
|
+
import { bindingTypeNamespaceToken } from './binding-declaration-contract.js'
|
|
10
|
+
|
|
11
|
+
const MAX_NAME_LENGTH = 128
|
|
12
|
+
const LEGACY_GLOBAL_TYPE_NAMES = new Set([
|
|
13
|
+
'Array', 'ArrayBuffer', 'ArrayBufferView', 'AsyncIterable', 'AsyncIterableIterator',
|
|
14
|
+
'AsyncIterator', 'Awaited', 'BigInt', 'BigInt64Array', 'BigUint64Array', 'Boolean',
|
|
15
|
+
'Buffer', 'ConstructorParameters', 'DataView', 'Date', 'Error', 'EvalError', 'Exclude',
|
|
16
|
+
'Extract', 'Float32Array', 'Float64Array', 'Function', 'Generator', 'GeneratorFunction',
|
|
17
|
+
'InstanceType', 'Int8Array', 'Int16Array', 'Int32Array', 'Iterable', 'IterableIterator',
|
|
18
|
+
'Iterator', 'Map', 'NonNullable', 'Number', 'Object', 'Omit', 'OmitThisParameter',
|
|
19
|
+
'Parameters', 'Partial', 'Pick', 'Promise', 'PromiseLike', 'RangeError', 'Readonly',
|
|
20
|
+
'ReadonlyArray', 'ReadonlyMap', 'ReadonlySet', 'Record', 'ReferenceError', 'RegExp',
|
|
21
|
+
'Required', 'ReturnType', 'Set', 'SharedArrayBuffer', 'String', 'Symbol', 'SyntaxError',
|
|
22
|
+
'ThisParameterType', 'ThisType', 'TypeError', 'URIError', 'Uint8Array', 'Uint8ClampedArray',
|
|
23
|
+
'Uint16Array', 'Uint32Array', 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize',
|
|
24
|
+
'WeakMap', 'WeakSet',
|
|
25
|
+
])
|
|
26
|
+
const RESERVED_BINDINGS = new Set([
|
|
27
|
+
...AMBIENT_GLOBALS,
|
|
28
|
+
'capabilities',
|
|
29
|
+
'code',
|
|
30
|
+
'repl',
|
|
31
|
+
'tools',
|
|
32
|
+
'CapabilityExplorationError',
|
|
33
|
+
'CodeExecutionError',
|
|
34
|
+
'ToolCallError',
|
|
35
|
+
'Buffer',
|
|
36
|
+
'console',
|
|
37
|
+
'global',
|
|
38
|
+
'globalThis',
|
|
39
|
+
'module',
|
|
40
|
+
'process',
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
export function identifier(value, subject, reserved = false) {
|
|
44
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > MAX_NAME_LENGTH) {
|
|
45
|
+
throw new TypeError(`${subject} must be a non-empty identifier of at most ${MAX_NAME_LENGTH} characters`)
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const { program } = parse(`const ${value} = 0`, { sourceType: 'module' })
|
|
49
|
+
const declaration = program.body[0]
|
|
50
|
+
const binding = declaration.declarations[0].id
|
|
51
|
+
if (program.body.length !== 1 || declaration.declarations.length !== 1
|
|
52
|
+
|| binding.type !== 'Identifier' || binding.name !== value) {
|
|
53
|
+
throw new TypeError('identifier spelling must match the complete binding name')
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
throw new TypeError(`${subject} must be a valid JavaScript identifier`)
|
|
57
|
+
}
|
|
58
|
+
if (reserved && RESERVED_BINDINGS.has(value)) {
|
|
59
|
+
throw new TypeError(`${subject} conflicts with reserved REPL binding ${JSON.stringify(value)}`)
|
|
60
|
+
}
|
|
61
|
+
return value
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function sourceSlice(source, node) {
|
|
65
|
+
return Number.isSafeInteger(node?.start) && Number.isSafeInteger(node?.end)
|
|
66
|
+
? source.slice(node.start, node.end)
|
|
67
|
+
: ''
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function bindingNames(pattern, names = []) {
|
|
71
|
+
if (pattern?.type === 'Identifier') names.push(pattern.name)
|
|
72
|
+
else if (pattern?.type === 'RestElement') bindingNames(pattern.argument, names)
|
|
73
|
+
else if (pattern?.type === 'AssignmentPattern') bindingNames(pattern.left, names)
|
|
74
|
+
else if (pattern?.type === 'ArrayPattern') {
|
|
75
|
+
for (const element of pattern.elements) bindingNames(element, names)
|
|
76
|
+
} else if (pattern?.type === 'ObjectPattern') {
|
|
77
|
+
for (const property of pattern.properties) {
|
|
78
|
+
bindingNames(property.type === 'RestElement' ? property.argument : property.value, names)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return names
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function typeParameterNames(node) {
|
|
85
|
+
return (node?.params ?? [])
|
|
86
|
+
.map(parameter => parameter?.name)
|
|
87
|
+
.filter(name => typeof name === 'string')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function typeReferenceRoot(node) {
|
|
91
|
+
let current = node
|
|
92
|
+
while (current?.type === 'TSQualifiedName') current = current.left
|
|
93
|
+
return current?.type === 'Identifier' ? current.name : undefined
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function typeReferenceRootNode(node) {
|
|
97
|
+
let current = node
|
|
98
|
+
while (current?.type === 'TSQualifiedName') current = current.left
|
|
99
|
+
return current?.type === 'Identifier' ? current : undefined
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function projectedType(source, node, outerTypeNames, typeContext, usedTypeNames) {
|
|
103
|
+
const replacements = []
|
|
104
|
+
const commentRanges = new Set()
|
|
105
|
+
const visit = (current, inheritedNames) => {
|
|
106
|
+
if (current === null || typeof current !== 'object') return
|
|
107
|
+
if (current.type === 'CommentBlock' || current.type === 'CommentLine') {
|
|
108
|
+
if (Number.isSafeInteger(current.start) && Number.isSafeInteger(current.end)
|
|
109
|
+
&& current.start >= node.start && current.end <= node.end) {
|
|
110
|
+
const key = `${current.start}:${current.end}`
|
|
111
|
+
if (!commentRanges.has(key)) {
|
|
112
|
+
commentRanges.add(key)
|
|
113
|
+
replacements.push({ start: current.start, end: current.end, text: ' ' })
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
let typeNames = inheritedNames
|
|
119
|
+
if (current.typeParameters?.type === 'TSTypeParameterDeclaration') {
|
|
120
|
+
typeNames = new Set([...inheritedNames, ...typeParameterNames(current.typeParameters)])
|
|
121
|
+
}
|
|
122
|
+
if (current.type === 'TSMappedType' && typeof current.typeParameter?.name === 'string') {
|
|
123
|
+
typeNames = new Set([...typeNames, current.typeParameter.name])
|
|
124
|
+
}
|
|
125
|
+
if (!typeContext.modern && (current.type === 'TSImportType' || current.type === 'TSTypeQuery')) {
|
|
126
|
+
replacements.push({ start: current.start, end: current.end, text: 'unknown' })
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
if (current.type === 'TSTypeQuery') {
|
|
130
|
+
const reference = current.exprName
|
|
131
|
+
const name = typeReferenceRoot(reference)
|
|
132
|
+
if (typeContext.modern && name !== undefined && !typeNames.has(name)) {
|
|
133
|
+
if (typeContext.importedNames.has(name)) {
|
|
134
|
+
throw new TypeError(`imported value ${JSON.stringify(name)} cannot be represented in the binding interface; use an explicit import(...) type query`)
|
|
135
|
+
}
|
|
136
|
+
if (typeContext.localValues.has(name)) {
|
|
137
|
+
const declaration = typeContext.localDeclarations.get(name)
|
|
138
|
+
if (declaration === undefined
|
|
139
|
+
|| !['ClassDeclaration', 'TSEnumDeclaration'].includes(declaration.type)
|
|
140
|
+
|| (reference.type !== 'Identifier' && declaration.type !== 'TSEnumDeclaration')) {
|
|
141
|
+
throw new TypeError(`local value query ${JSON.stringify(name)} cannot be represented in the binding interface; use an explicit structural type`)
|
|
142
|
+
}
|
|
143
|
+
usedTypeNames.add(name)
|
|
144
|
+
const root = typeReferenceRootNode(reference)
|
|
145
|
+
replacements.push({ start: root.start, end: root.end,
|
|
146
|
+
text: `${typeContext.typeNamespaceToken}.${name}` })
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} else if (current.type === 'TSTypeReference' || current.type === 'TSExpressionWithTypeArguments') {
|
|
150
|
+
const reference = current.typeName ?? current.expression
|
|
151
|
+
const name = typeReferenceRoot(reference)
|
|
152
|
+
if (!typeContext.modern && (name === undefined || !typeNames.has(name)
|
|
153
|
+
&& (typeContext.sourceTypeNames.has(name) || !LEGACY_GLOBAL_TYPE_NAMES.has(name)))) {
|
|
154
|
+
replacements.push({ start: current.start, end: current.end, text: 'unknown' })
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
if (typeContext.modern && name !== undefined && !typeNames.has(name)) {
|
|
158
|
+
if (typeContext.importedNames.has(name)) {
|
|
159
|
+
throw new TypeError(`imported type ${JSON.stringify(name)} cannot be represented in the binding interface; use an explicit import(...) type or a local structural type`)
|
|
160
|
+
}
|
|
161
|
+
if (typeContext.localDeclarations.has(name)) {
|
|
162
|
+
usedTypeNames.add(name)
|
|
163
|
+
const root = typeReferenceRootNode(reference)
|
|
164
|
+
replacements.push({ start: root.start, end: root.end,
|
|
165
|
+
text: `${typeContext.typeNamespaceToken}.${name}` })
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const [key, value] of Object.entries(current)) {
|
|
170
|
+
if (key === 'loc' || key === 'start' || key === 'end'
|
|
171
|
+
|| key === 'typeParameters' || key === 'typeName' || key === 'expression' || key === 'exprName') continue
|
|
172
|
+
if (Array.isArray(value)) value.forEach(item => visit(item, typeNames))
|
|
173
|
+
else visit(value, typeNames)
|
|
174
|
+
}
|
|
175
|
+
if (current.typeParameters !== undefined) visit(current.typeParameters, typeNames)
|
|
176
|
+
}
|
|
177
|
+
visit(node, outerTypeNames)
|
|
178
|
+
let text = sourceSlice(source, node) || 'unknown'
|
|
179
|
+
const start = node.start ?? 0
|
|
180
|
+
for (const replacement of replacements.sort((left, right) => right.start - left.start)) {
|
|
181
|
+
text = text.slice(0, replacement.start - start)
|
|
182
|
+
+ replacement.text
|
|
183
|
+
+ text.slice(replacement.end - start)
|
|
184
|
+
}
|
|
185
|
+
return text
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function typeParameters(source, node, outerTypeNames, typeContext, usedTypeNames) {
|
|
189
|
+
const declaration = node?.typeParameters
|
|
190
|
+
if (declaration?.type !== 'TSTypeParameterDeclaration') {
|
|
191
|
+
return { text: '', names: outerTypeNames }
|
|
192
|
+
}
|
|
193
|
+
const names = new Set([...outerTypeNames, ...typeParameterNames(declaration)])
|
|
194
|
+
return { text: projectedType(source, declaration, names, typeContext, usedTypeNames), names }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function annotation(source, node, typeNames, typeContext, usedTypeNames) {
|
|
198
|
+
const type = node?.typeAnnotation?.typeAnnotation
|
|
199
|
+
return type === undefined ? 'unknown' : projectedType(source, type, typeNames, typeContext, usedTypeNames)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parameter(source, node, index, typeNames, typeContext, usedTypeNames) {
|
|
203
|
+
if (node?.type === 'TSParameterProperty') {
|
|
204
|
+
return parameter(source, node.parameter, index, typeNames, typeContext, usedTypeNames)
|
|
205
|
+
}
|
|
206
|
+
if (node?.type === 'RestElement') {
|
|
207
|
+
const argument = node.argument
|
|
208
|
+
return argument?.type === 'Identifier'
|
|
209
|
+
? `...${argument.name}: ${node.typeAnnotation === undefined ? 'unknown[]' : annotation(source, node, typeNames, typeContext, usedTypeNames)}`
|
|
210
|
+
: `...args${index}: unknown[]`
|
|
211
|
+
}
|
|
212
|
+
const optional = node?.type === 'AssignmentPattern'
|
|
213
|
+
const target = optional ? node.left : node
|
|
214
|
+
if (target?.type !== 'Identifier') return `arg${index}: unknown`
|
|
215
|
+
return `${target.name}${optional || target.optional === true ? '?' : ''}: ${annotation(source, target, typeNames, typeContext, usedTypeNames)}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function returnType(source, node, typeNames, typeContext, usedTypeNames) {
|
|
219
|
+
const explicit = node?.returnType?.typeAnnotation
|
|
220
|
+
if (explicit !== undefined) return projectedType(source, explicit, typeNames, typeContext, usedTypeNames)
|
|
221
|
+
return node?.async === true ? 'Promise<unknown>' : 'unknown'
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function functionType(source, node, outerTypeNames, typeContext, usedTypeNames) {
|
|
225
|
+
const generics = typeParameters(source, node, outerTypeNames, typeContext, usedTypeNames)
|
|
226
|
+
const parameters = (node.params ?? [])
|
|
227
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
228
|
+
return `${generics.text}(${parameters}) => ${returnType(source, node, generics.names, typeContext, usedTypeNames)}`
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function inferredType(source, node, depth, typeContext, usedTypeNames) {
|
|
232
|
+
if (depth > 2 || node === null || node === undefined) return 'unknown'
|
|
233
|
+
if (node.type === 'StringLiteral' || node.type === 'TemplateLiteral') return 'string'
|
|
234
|
+
if (node.type === 'NumericLiteral') return 'number'
|
|
235
|
+
if (node.type === 'BooleanLiteral') return 'boolean'
|
|
236
|
+
if (node.type === 'BigIntLiteral') return 'bigint'
|
|
237
|
+
if (node.type === 'NullLiteral') return 'null'
|
|
238
|
+
if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
|
|
239
|
+
return functionType(source, node, new Set(), typeContext, usedTypeNames)
|
|
240
|
+
}
|
|
241
|
+
if (node.type === 'ArrayExpression') return 'unknown[]'
|
|
242
|
+
if (node.type === 'ObjectExpression') {
|
|
243
|
+
const members = []
|
|
244
|
+
for (const property of node.properties) {
|
|
245
|
+
if (property.type !== 'ObjectProperty' || property.computed || property.method) return 'Record<string, unknown>'
|
|
246
|
+
const name = property.key.type === 'Identifier'
|
|
247
|
+
? property.key.name
|
|
248
|
+
: property.key.type === 'StringLiteral' ? JSON.stringify(property.key.value) : undefined
|
|
249
|
+
if (name === undefined) return 'Record<string, unknown>'
|
|
250
|
+
members.push(`${name}: ${inferredType(source, property.value, depth + 1, typeContext, usedTypeNames)}`)
|
|
251
|
+
}
|
|
252
|
+
return `{ ${members.join('; ')} }`
|
|
253
|
+
}
|
|
254
|
+
return 'unknown'
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function classMemberName(member) {
|
|
258
|
+
return member.key?.type === 'Identifier'
|
|
259
|
+
? member.key.name
|
|
260
|
+
: member.key?.type === 'StringLiteral' ? JSON.stringify(member.key.value) : undefined
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function modernClassMemberName(member) {
|
|
264
|
+
const key = classMemberKey(member)
|
|
265
|
+
return classMemberName(member) ?? (key === undefined ? undefined : JSON.stringify(key))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function classMemberKey(member) {
|
|
269
|
+
if (member.computed && !['StringLiteral', 'NumericLiteral', 'BigIntLiteral'].includes(member.key?.type)) {
|
|
270
|
+
throw new TypeError('computed class member key cannot be represented in the binding interface; use a literal property key')
|
|
271
|
+
}
|
|
272
|
+
return member.key?.type === 'Identifier'
|
|
273
|
+
? member.key.name
|
|
274
|
+
: member.key?.type === 'StringLiteral' ? member.key.value
|
|
275
|
+
: member.key?.type === 'NumericLiteral' ? String(member.key.value)
|
|
276
|
+
: member.key?.type === 'BigIntLiteral' ? String(BigInt(member.key.value)) : undefined
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isPublicClassMember(member) {
|
|
280
|
+
return !member.static
|
|
281
|
+
&& member.accessibility !== 'private' && member.accessibility !== 'protected'
|
|
282
|
+
&& member.key?.type !== 'PrivateName'
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function parameterPropertyMember(source, node, classTypeNames, typeContext, usedTypeNames) {
|
|
286
|
+
if (node?.type !== 'TSParameterProperty' || node.accessibility === 'private' || node.accessibility === 'protected') return undefined
|
|
287
|
+
const parameterNode = node.parameter
|
|
288
|
+
const optional = parameterNode?.type === 'AssignmentPattern'
|
|
289
|
+
const target = optional ? parameterNode.left : parameterNode
|
|
290
|
+
if (target?.type !== 'Identifier') return undefined
|
|
291
|
+
return `${node.readonly === true ? 'readonly ' : ''}${target.name}${optional || target.optional === true ? '?' : ''}: ${annotation(source, target, classTypeNames, typeContext, usedTypeNames)}`
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function parameterPropertyName(node) {
|
|
295
|
+
if (node?.type !== 'TSParameterProperty' || node.accessibility === 'private' || node.accessibility === 'protected') return undefined
|
|
296
|
+
const parameter = node.parameter?.type === 'AssignmentPattern' ? node.parameter.left : node.parameter
|
|
297
|
+
return parameter?.type === 'Identifier' ? parameter.name : undefined
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function legacyClassMembers(source, node, classTypeNames, typeContext, usedTypeNames) {
|
|
301
|
+
const members = []
|
|
302
|
+
const memberNames = new Set()
|
|
303
|
+
let constructorParameters = '...args: unknown[]'
|
|
304
|
+
for (const member of node.body?.body ?? []) {
|
|
305
|
+
if (!['ClassMethod', 'TSDeclareMethod'].includes(member.type)
|
|
306
|
+
|| member.static || member.computed || member.accessibility === 'private' || member.key?.type === 'PrivateName') continue
|
|
307
|
+
const name = classMemberName(member)
|
|
308
|
+
if (name === undefined) continue
|
|
309
|
+
const generics = typeParameters(source, member, classTypeNames, typeContext, usedTypeNames)
|
|
310
|
+
const parameters = (member.params ?? [])
|
|
311
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
312
|
+
if (member.kind === 'constructor') {
|
|
313
|
+
constructorParameters = parameters
|
|
314
|
+
}
|
|
315
|
+
else if (member.kind === 'get' || member.kind === 'set') {
|
|
316
|
+
const signature = `${member.kind}:${name}`
|
|
317
|
+
if (memberNames.has(signature)) continue
|
|
318
|
+
members.push(member.kind === 'get'
|
|
319
|
+
? `get ${name}(): ${returnType(source, member, generics.names, typeContext, usedTypeNames)}`
|
|
320
|
+
: `set ${name}(${parameters})`)
|
|
321
|
+
memberNames.add(signature)
|
|
322
|
+
}
|
|
323
|
+
else if (member.kind === 'method') {
|
|
324
|
+
members.push(`${name}${member.optional === true ? '?' : ''}${generics.text}(${parameters}): ${returnType(source, member, generics.names, typeContext, usedTypeNames)}`)
|
|
325
|
+
memberNames.add(name)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { constructorParameters, members, instance: `{ ${members.join('; ')} }` }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function classMembers(source, node, classTypeNames, typeContext, usedTypeNames) {
|
|
332
|
+
if (!typeContext.modern) return legacyClassMembers(source, node, classTypeNames, typeContext, usedTypeNames)
|
|
333
|
+
const body = node.body?.body ?? []
|
|
334
|
+
const fields = new Map()
|
|
335
|
+
const parameterProperties = new Map()
|
|
336
|
+
const prototypeMembers = new Map()
|
|
337
|
+
let constructorNode
|
|
338
|
+
for (const member of body) {
|
|
339
|
+
if (['ClassProperty', 'TSAbstractPropertyDefinition'].includes(member.type)) {
|
|
340
|
+
if (!isPublicClassMember(member)) continue
|
|
341
|
+
const key = classMemberKey(member)
|
|
342
|
+
if (key !== undefined) fields.set(key, member)
|
|
343
|
+
continue
|
|
344
|
+
}
|
|
345
|
+
if (member.type === 'ClassAccessorProperty') {
|
|
346
|
+
if (!isPublicClassMember(member)) continue
|
|
347
|
+
const key = classMemberKey(member)
|
|
348
|
+
if (key !== undefined) prototypeMembers.set(key, { kind: 'accessor', get: member, set: member })
|
|
349
|
+
continue
|
|
350
|
+
}
|
|
351
|
+
if (!['ClassMethod', 'TSDeclareMethod'].includes(member.type) || !isPublicClassMember(member)) continue
|
|
352
|
+
const key = classMemberKey(member)
|
|
353
|
+
if (key === undefined) continue
|
|
354
|
+
if (member.kind === 'constructor') {
|
|
355
|
+
constructorNode = member
|
|
356
|
+
} else if (member.kind === 'get' || member.kind === 'set') {
|
|
357
|
+
const current = prototypeMembers.get(key)
|
|
358
|
+
const descriptor = current?.kind === 'accessor' ? { ...current } : { kind: 'accessor' }
|
|
359
|
+
descriptor[member.kind] = member
|
|
360
|
+
prototypeMembers.set(key, descriptor)
|
|
361
|
+
} else if (member.kind === 'method') {
|
|
362
|
+
prototypeMembers.set(key, { kind: 'method', node: member })
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
for (const parameter of constructorNode?.params ?? []) {
|
|
366
|
+
const name = parameterPropertyName(parameter)
|
|
367
|
+
if (name !== undefined) parameterProperties.set(name, parameter)
|
|
368
|
+
}
|
|
369
|
+
const ownMember = name => parameterProperties.get(name) ?? fields.get(name)
|
|
370
|
+
const members = []
|
|
371
|
+
let constructorParameters = '...args: unknown[]'
|
|
372
|
+
for (const member of body) {
|
|
373
|
+
if (['ClassProperty', 'TSAbstractPropertyDefinition'].includes(member.type)) {
|
|
374
|
+
if (!isPublicClassMember(member)) continue
|
|
375
|
+
const key = classMemberKey(member)
|
|
376
|
+
if (key === undefined || ownMember(key) !== member) continue
|
|
377
|
+
const name = modernClassMemberName(member)
|
|
378
|
+
members.push(`${member.readonly === true ? 'readonly ' : ''}${name}${member.optional === true ? '?' : ''}: ${annotation(source, member, classTypeNames, typeContext, usedTypeNames)}`)
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
if (member.type === 'ClassAccessorProperty') {
|
|
382
|
+
if (!isPublicClassMember(member)) continue
|
|
383
|
+
const key = classMemberKey(member)
|
|
384
|
+
const name = modernClassMemberName(member)
|
|
385
|
+
const selected = key === undefined || ownMember(key) !== undefined ? undefined : prototypeMembers.get(key)
|
|
386
|
+
if (selected?.kind !== 'accessor') continue
|
|
387
|
+
if (selected.get === member && selected.set === member) {
|
|
388
|
+
members.push(`${name}${member.optional === true ? '?' : ''}: ${annotation(source, member, classTypeNames, typeContext, usedTypeNames)}`)
|
|
389
|
+
} else {
|
|
390
|
+
const type = annotation(source, member, classTypeNames, typeContext, usedTypeNames)
|
|
391
|
+
if (selected.get === member) members.push(`get ${name}(): ${type}`)
|
|
392
|
+
if (selected.set === member) members.push(`set ${name}(value: ${type})`)
|
|
393
|
+
}
|
|
394
|
+
continue
|
|
395
|
+
}
|
|
396
|
+
if (!['ClassMethod', 'TSDeclareMethod'].includes(member.type) || !isPublicClassMember(member)) continue
|
|
397
|
+
const key = classMemberKey(member)
|
|
398
|
+
if (key === undefined) continue
|
|
399
|
+
const name = modernClassMemberName(member)
|
|
400
|
+
if (member.kind === 'constructor') {
|
|
401
|
+
if (member !== constructorNode) continue
|
|
402
|
+
const generics = typeParameters(source, member, classTypeNames, typeContext, usedTypeNames)
|
|
403
|
+
const parameters = (member.params ?? [])
|
|
404
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
405
|
+
constructorParameters = parameters
|
|
406
|
+
for (const item of member.params ?? []) {
|
|
407
|
+
const fieldName = parameterPropertyName(item)
|
|
408
|
+
if (fieldName === undefined || ownMember(fieldName) !== item) continue
|
|
409
|
+
members.push(parameterPropertyMember(source, item, classTypeNames, typeContext, usedTypeNames))
|
|
410
|
+
}
|
|
411
|
+
continue
|
|
412
|
+
}
|
|
413
|
+
if (ownMember(key) !== undefined) continue
|
|
414
|
+
const selected = prototypeMembers.get(key)
|
|
415
|
+
if (member.kind === 'get' || member.kind === 'set') {
|
|
416
|
+
if (selected?.kind !== 'accessor' || selected[member.kind] !== member) continue
|
|
417
|
+
const generics = typeParameters(source, member, classTypeNames, typeContext, usedTypeNames)
|
|
418
|
+
const parameters = (member.params ?? [])
|
|
419
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
420
|
+
members.push(member.kind === 'get'
|
|
421
|
+
? `get ${name}(): ${returnType(source, member, generics.names, typeContext, usedTypeNames)}`
|
|
422
|
+
: `set ${name}(${parameters})`)
|
|
423
|
+
} else if (member.kind === 'method' && selected?.kind === 'method' && selected.node === member) {
|
|
424
|
+
const generics = typeParameters(source, member, classTypeNames, typeContext, usedTypeNames)
|
|
425
|
+
const parameters = (member.params ?? [])
|
|
426
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
427
|
+
members.push(`${name}${member.optional === true ? '?' : ''}${generics.text}(${parameters}): ${returnType(source, member, generics.names, typeContext, usedTypeNames)}`)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return { constructorParameters, members, instance: `{ ${members.join('; ')} }` }
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function valueReferenceRootNode(node) {
|
|
434
|
+
let current = node
|
|
435
|
+
while (current?.type === 'MemberExpression' && !current.computed) current = current.object
|
|
436
|
+
return current?.type === 'Identifier' ? current : undefined
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function classHeritage(source, node, classTypeNames, typeContext, usedTypeNames) {
|
|
440
|
+
const base = node.superClass
|
|
441
|
+
if (base === null || base === undefined) return { clause: '', instance: '' }
|
|
442
|
+
const root = valueReferenceRootNode(base)
|
|
443
|
+
if (root === undefined || !['Identifier', 'MemberExpression'].includes(base.type)) {
|
|
444
|
+
throw new TypeError('class heritage cannot be represented in the binding interface; use a named class base')
|
|
445
|
+
}
|
|
446
|
+
if (typeContext.importedNames.has(root.name)) {
|
|
447
|
+
throw new TypeError(`imported class ${JSON.stringify(root.name)} cannot be represented in the binding interface; use an explicit structural type`)
|
|
448
|
+
}
|
|
449
|
+
let reference = sourceSlice(source, base)
|
|
450
|
+
if (base.type === 'Identifier' && typeContext.localDeclarations.has(root.name)) {
|
|
451
|
+
const declaration = typeContext.localDeclarations.get(root.name)
|
|
452
|
+
if (declaration.type !== 'ClassDeclaration') {
|
|
453
|
+
throw new TypeError(`local class base ${JSON.stringify(root.name)} cannot be represented in the binding interface; use a class declaration`)
|
|
454
|
+
}
|
|
455
|
+
usedTypeNames.add(root.name)
|
|
456
|
+
reference = `${typeContext.typeNamespaceToken}.${root.name}`
|
|
457
|
+
} else if (typeContext.localValues.has(root.name)) {
|
|
458
|
+
throw new TypeError(`local class base ${JSON.stringify(root.name)} cannot be represented in the binding interface; use a class declaration`)
|
|
459
|
+
}
|
|
460
|
+
const argumentsNode = node.superTypeParameters ?? node.superTypeArguments
|
|
461
|
+
const typeArguments = argumentsNode === undefined ? ''
|
|
462
|
+
: projectedType(source, argumentsNode, classTypeNames, typeContext, usedTypeNames)
|
|
463
|
+
const instance = `${reference}${typeArguments}`
|
|
464
|
+
return { clause: ` extends ${instance}`, instance }
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function localTypeDeclaration(source, name, node, typeContext, usedTypeNames) {
|
|
468
|
+
if (node.type === 'TSInterfaceDeclaration' || node.type === 'TSTypeAliasDeclaration'
|
|
469
|
+
|| node.type === 'TSEnumDeclaration') {
|
|
470
|
+
const ownTypeNames = new Set(typeParameterNames(node.typeParameters))
|
|
471
|
+
const text = projectedType(source, node, ownTypeNames, typeContext, usedTypeNames)
|
|
472
|
+
.replace(/^declare\s+/, '')
|
|
473
|
+
return `export ${text}`
|
|
474
|
+
}
|
|
475
|
+
if (node.type === 'ClassDeclaration') {
|
|
476
|
+
const generics = typeParameters(source, node, new Set(), typeContext, usedTypeNames)
|
|
477
|
+
const heritage = classHeritage(source, node, generics.names, typeContext, usedTypeNames)
|
|
478
|
+
const shape = classMembers(source, node, generics.names, typeContext, usedTypeNames)
|
|
479
|
+
return `export ${node.abstract === true ? 'abstract ' : ''}class ${name}${generics.text}${heritage.clause} { constructor(${shape.constructorParameters});${shape.members.length === 0 ? '' : ` ${shape.members.join('; ')};`} }`
|
|
480
|
+
}
|
|
481
|
+
throw new TypeError(`local type ${JSON.stringify(name)} cannot be represented in the binding interface; use a local interface, type alias, enum, or class`)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function typeDependencyDeclarations(source, roots, typeContext) {
|
|
485
|
+
if (!typeContext.modern) return []
|
|
486
|
+
const pending = [...roots]
|
|
487
|
+
const seen = new Set()
|
|
488
|
+
const declarations = []
|
|
489
|
+
while (pending.length > 0) {
|
|
490
|
+
const name = pending.shift()
|
|
491
|
+
if (seen.has(name)) continue
|
|
492
|
+
seen.add(name)
|
|
493
|
+
const node = typeContext.localDeclarations.get(name)
|
|
494
|
+
if (node === undefined) continue
|
|
495
|
+
const dependencies = new Set()
|
|
496
|
+
declarations.push(localTypeDeclaration(source, name, node, typeContext, dependencies))
|
|
497
|
+
for (const dependency of dependencies) if (!seen.has(dependency)) pending.push(dependency)
|
|
498
|
+
}
|
|
499
|
+
return declarations
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function jsdocPurpose(...nodes) {
|
|
503
|
+
const comment = nodes.flatMap(node => node?.leadingComments ?? [])
|
|
504
|
+
.find(item => item.type === 'CommentBlock' && item.value.startsWith('*'))
|
|
505
|
+
if (comment === undefined) return ''
|
|
506
|
+
return comment.value.slice(1).split(/\r?\n/)
|
|
507
|
+
.map(line => line.replace(/^\s*\*?\s?/, '').trim())
|
|
508
|
+
.find(line => line !== '' && !line.startsWith('@')) ?? ''
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function symbolDescriptor(source, name, node, exportNode, typeContext) {
|
|
512
|
+
const usedTypeNames = new Set()
|
|
513
|
+
const complete = descriptor => ({
|
|
514
|
+
...descriptor,
|
|
515
|
+
typeNamespaceToken: typeContext.typeNamespaceToken,
|
|
516
|
+
typeDeclarations: typeDependencyDeclarations(source, usedTypeNames, typeContext),
|
|
517
|
+
})
|
|
518
|
+
if (node?.type === 'FunctionDeclaration') {
|
|
519
|
+
const generics = typeParameters(source, node, new Set(), typeContext, usedTypeNames)
|
|
520
|
+
const parameters = (node.params ?? [])
|
|
521
|
+
.map((item, index) => parameter(source, item, index, generics.names, typeContext, usedTypeNames)).join(', ')
|
|
522
|
+
return complete({
|
|
523
|
+
name,
|
|
524
|
+
kind: 'function',
|
|
525
|
+
purpose: jsdocPurpose(exportNode, node),
|
|
526
|
+
declaration: `function ${name}${generics.text}(${parameters}): ${returnType(source, node, generics.names, typeContext, usedTypeNames)}`,
|
|
527
|
+
member: `${name}${generics.text}(${parameters}): ${returnType(source, node, generics.names, typeContext, usedTypeNames)}`,
|
|
528
|
+
})
|
|
529
|
+
}
|
|
530
|
+
if (node?.type === 'ClassDeclaration') {
|
|
531
|
+
const generics = typeParameters(source, node, new Set(), typeContext, usedTypeNames)
|
|
532
|
+
const heritage = classHeritage(source, node, generics.names, typeContext, usedTypeNames)
|
|
533
|
+
const shape = classMembers(source, node, generics.names, typeContext, usedTypeNames)
|
|
534
|
+
const instance = heritage.instance === '' ? shape.instance : `${heritage.instance} & ${shape.instance}`
|
|
535
|
+
return complete({
|
|
536
|
+
name,
|
|
537
|
+
kind: 'class',
|
|
538
|
+
purpose: jsdocPurpose(exportNode, node),
|
|
539
|
+
declaration: `${node.abstract === true ? 'abstract ' : ''}class ${name}${generics.text}${heritage.clause} { constructor(${shape.constructorParameters});${shape.members.length === 0 ? '' : ` ${shape.members.join('; ')};`} }`,
|
|
540
|
+
member: node.abstract === true
|
|
541
|
+
? `${name}: abstract new${generics.text}(${shape.constructorParameters}) => ${instance}`
|
|
542
|
+
: `${name}: { new${generics.text}(${shape.constructorParameters}): ${instance} }`,
|
|
543
|
+
})
|
|
544
|
+
}
|
|
545
|
+
const declarator = node?.type === 'VariableDeclarator' ? node : undefined
|
|
546
|
+
const type = declarator?.id?.type !== 'Identifier'
|
|
547
|
+
? 'unknown'
|
|
548
|
+
: declarator.id.typeAnnotation !== undefined
|
|
549
|
+
? annotation(source, declarator.id, new Set(), typeContext, usedTypeNames)
|
|
550
|
+
: inferredType(source, declarator.init, 0, typeContext, usedTypeNames)
|
|
551
|
+
return complete({
|
|
552
|
+
name,
|
|
553
|
+
kind: 'variable',
|
|
554
|
+
purpose: jsdocPurpose(exportNode, node),
|
|
555
|
+
declaration: `const ${name}: ${type}`,
|
|
556
|
+
member: `${name}: ${type}`,
|
|
557
|
+
})
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export const exportedSymbols = createCompilerSourceCache(analyzeExportedSymbols)
|
|
561
|
+
|
|
562
|
+
function analyzeExportedSymbols(source, transform) {
|
|
563
|
+
const metadataSource = source
|
|
564
|
+
let metadataAst
|
|
565
|
+
try {
|
|
566
|
+
metadataAst = parse(metadataSource, {
|
|
567
|
+
sourceType: 'module',
|
|
568
|
+
plugins: transform === LEGACY_USER_BINDING_TRANSFORM
|
|
569
|
+
? ['typescript', 'topLevelAwait', 'importAttributes'] : CELL_PARSER_PLUGINS,
|
|
570
|
+
errorRecovery: transform !== LEGACY_USER_BINDING_TRANSFORM,
|
|
571
|
+
})
|
|
572
|
+
} catch (error) {
|
|
573
|
+
throw new SyntaxError(`binding source could not be parsed: ${error.message}`)
|
|
574
|
+
}
|
|
575
|
+
const importedNames = new Set()
|
|
576
|
+
const localDeclarations = new Map()
|
|
577
|
+
const localValueNames = new Set()
|
|
578
|
+
const metadataLocals = new Map()
|
|
579
|
+
const metadataExportNodes = new Map()
|
|
580
|
+
for (const statement of metadataAst.program.body) {
|
|
581
|
+
const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement
|
|
582
|
+
if (statement.type === 'ImportDeclaration') {
|
|
583
|
+
for (const specifier of statement.specifiers) importedNames.add(specifier.local.name)
|
|
584
|
+
}
|
|
585
|
+
if (declaration?.type === 'TSTypeAliasDeclaration'
|
|
586
|
+
|| declaration?.type === 'TSInterfaceDeclaration'
|
|
587
|
+
|| declaration?.type === 'TSEnumDeclaration'
|
|
588
|
+
|| declaration?.type === 'TSModuleDeclaration'
|
|
589
|
+
|| declaration?.type === 'ClassDeclaration') {
|
|
590
|
+
if (declaration.id?.type === 'Identifier') localDeclarations.set(declaration.id.name, declaration)
|
|
591
|
+
}
|
|
592
|
+
if (declaration?.type === 'TSEnumDeclaration' || declaration?.type === 'TSModuleDeclaration') {
|
|
593
|
+
if (declaration.id?.type === 'Identifier') localValueNames.add(declaration.id.name)
|
|
594
|
+
}
|
|
595
|
+
if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
|
|
596
|
+
if (declaration.id !== null) {
|
|
597
|
+
localValueNames.add(declaration.id.name)
|
|
598
|
+
metadataLocals.set(declaration.id.name, declaration)
|
|
599
|
+
if (statement.type === 'ExportNamedDeclaration') metadataExportNodes.set(declaration.id.name, statement)
|
|
600
|
+
}
|
|
601
|
+
} else if (declaration?.type === 'VariableDeclaration') {
|
|
602
|
+
for (const item of declaration.declarations) {
|
|
603
|
+
for (const name of bindingNames(item.id)) {
|
|
604
|
+
localValueNames.add(name)
|
|
605
|
+
metadataLocals.set(name, item)
|
|
606
|
+
if (statement.type === 'ExportNamedDeclaration') metadataExportNodes.set(name, statement)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (statement.type === 'ExportNamedDeclaration') {
|
|
611
|
+
for (const specifier of statement.specifiers) {
|
|
612
|
+
if (specifier.type === 'ExportSpecifier' && specifier.local.type === 'Identifier') {
|
|
613
|
+
metadataExportNodes.set(specifier.local.name, statement)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const typeContext = {
|
|
619
|
+
modern: transform === USER_BINDING_TRANSFORM,
|
|
620
|
+
importedNames,
|
|
621
|
+
localDeclarations,
|
|
622
|
+
localValues: localValueNames,
|
|
623
|
+
sourceTypeNames: new Set([...importedNames, ...localDeclarations.keys()]),
|
|
624
|
+
typeNamespaceToken: bindingTypeNamespaceToken(metadataSource),
|
|
625
|
+
}
|
|
626
|
+
let ast = metadataAst
|
|
627
|
+
if (transform !== LEGACY_USER_BINDING_TRANSFORM) {
|
|
628
|
+
source = normalizeTypeScriptValues(source, identitySourceMap(source.length), 'module').code
|
|
629
|
+
ast = parse(source, { sourceType: 'module', plugins: CELL_PARSER_PLUGINS, errorRecovery: true })
|
|
630
|
+
}
|
|
631
|
+
const locals = new Map()
|
|
632
|
+
for (const statement of ast.program.body) {
|
|
633
|
+
const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement
|
|
634
|
+
if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
|
|
635
|
+
if (declaration.id !== null) locals.set(declaration.id.name, declaration)
|
|
636
|
+
} else if (declaration?.type === 'VariableDeclaration') {
|
|
637
|
+
for (const item of declaration.declarations) {
|
|
638
|
+
for (const name of bindingNames(item.id)) locals.set(name, item)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const exports = new Map()
|
|
643
|
+
const descriptor = (name, runtimeNode, exportNode, localName = name) => {
|
|
644
|
+
const metadataNode = metadataLocals.get(localName)
|
|
645
|
+
return metadataNode === undefined
|
|
646
|
+
? symbolDescriptor(source, name, runtimeNode, exportNode, typeContext)
|
|
647
|
+
: symbolDescriptor(metadataSource, name, metadataNode,
|
|
648
|
+
metadataExportNodes.get(localName) ?? metadataNode, typeContext)
|
|
649
|
+
}
|
|
650
|
+
for (const statement of ast.program.body) {
|
|
651
|
+
if (statement.type === 'ExportDefaultDeclaration' || statement.type === 'ExportAllDeclaration') {
|
|
652
|
+
throw new TypeError('binding source supports named exports only')
|
|
653
|
+
}
|
|
654
|
+
if (statement.type !== 'ExportNamedDeclaration' || statement.exportKind === 'type') continue
|
|
655
|
+
if (statement.source !== null && statement.source !== undefined) {
|
|
656
|
+
throw new TypeError('binding source cannot re-export from another module')
|
|
657
|
+
}
|
|
658
|
+
const declaration = statement.declaration
|
|
659
|
+
if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
|
|
660
|
+
if (declaration.id === null) throw new TypeError('binding source exports must be named')
|
|
661
|
+
exports.set(declaration.id.name, descriptor(declaration.id.name, declaration, statement))
|
|
662
|
+
continue
|
|
663
|
+
}
|
|
664
|
+
if (declaration?.type === 'VariableDeclaration') {
|
|
665
|
+
for (const item of declaration.declarations) {
|
|
666
|
+
for (const name of bindingNames(item.id)) {
|
|
667
|
+
exports.set(name, descriptor(name, item, statement))
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
continue
|
|
671
|
+
}
|
|
672
|
+
for (const specifier of statement.specifiers) {
|
|
673
|
+
if (specifier.exportKind === 'type' || specifier.type !== 'ExportSpecifier') continue
|
|
674
|
+
const exported = specifier.exported.type === 'Identifier' ? specifier.exported.name : specifier.exported.value
|
|
675
|
+
const local = specifier.local.type === 'Identifier' ? specifier.local.name : specifier.local.value
|
|
676
|
+
identifier(exported, 'exported binding name')
|
|
677
|
+
const target = locals.get(local)
|
|
678
|
+
if (target === undefined) throw new TypeError(`export ${JSON.stringify(exported)} has no local value declaration`)
|
|
679
|
+
exports.set(exported, descriptor(exported, target, target, local))
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (exports.size === 0) throw new TypeError('binding source must contain at least one named value export')
|
|
683
|
+
return exports
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export const sourceDurability = createCompilerSourceCache(analyzeSourceDurability)
|
|
687
|
+
|
|
688
|
+
function analyzeSourceDurability(source, transform) {
|
|
689
|
+
const compiled = compileStatefulModule(source, { transform })
|
|
690
|
+
// Keep source imports visible to durability analysis after runtime adaptation.
|
|
691
|
+
const javascript = transformTypeScriptSource(compiled.classificationCode,
|
|
692
|
+
transform === LEGACY_USER_BINDING_TRANSFORM ? {} : { module: true, transform: { tsEnumIsMutable: true } }).code
|
|
693
|
+
const prepared = transform !== LEGACY_USER_BINDING_TRANSFORM
|
|
694
|
+
? classifyDurability(javascript, new Set(), { sourceType: 'module',
|
|
695
|
+
internalModules: new Set([new URL('./stateful-module-runtime.js', import.meta.url).href]) })
|
|
696
|
+
: prepareProgram(javascript, {
|
|
697
|
+
languageSemantics: 'legacy-v1',
|
|
698
|
+
knownBindings: new Set(),
|
|
699
|
+
bindingPolicy: { variableRedeclarations: true, functionClassRedeclarations: true },
|
|
700
|
+
reservedBindings: new Set(),
|
|
701
|
+
rewritesEnabled: { autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true },
|
|
702
|
+
})
|
|
703
|
+
const reason = prepared.reason ?? renderDurabilityReasons(prepared.reasons)
|
|
704
|
+
return {
|
|
705
|
+
durability: prepared.durability,
|
|
706
|
+
...(reason === '' ? {} : { volatileReason: reason }),
|
|
707
|
+
}
|
|
708
|
+
}
|