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,459 @@
|
|
|
1
|
+
import { parse } from '@babel/parser'
|
|
2
|
+
import { tokenizer } from 'acorn'
|
|
3
|
+
import { types as t } from '@babel/core'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import { SourceMap } from 'node:module'
|
|
6
|
+
import { applySourceEdits, identitySourceMap, mappedSourceTransform, sourceLineStarts, sourceOffsetAt, visitSourceMappings } from './source-position-map.js'
|
|
7
|
+
import { createCallableSourceCatalog } from './callable-source-encoding.js'
|
|
8
|
+
import { regionInput, visitRegionSource } from './compiler-region-output.js'
|
|
9
|
+
import { lowerCallableSource, undecoratedMethodSource, decoratedClassSource } from './callable-source-lowering.js'
|
|
10
|
+
import { supportsNativeUsing, transformTypeScriptSource } from './typescript-transform.js'
|
|
11
|
+
|
|
12
|
+
const isCallable = node => t.isFunction(node) || t.isClass(node)
|
|
13
|
+
const isMethod = node => t.isObjectMethod(node) || t.isClassMethod(node) || t.isClassPrivateMethod(node)
|
|
14
|
+
const requiresTypeErasure = node => node.type.startsWith('TS') || node.type === 'Decorator' || node.type === 'ClassAccessorProperty'
|
|
15
|
+
|| node.accessibility !== undefined && node.accessibility !== null || node.abstract || node.declare
|
|
16
|
+
|| node.override || node.readonly || node.definite || node.optional
|
|
17
|
+
|
|
18
|
+
class CallableSourceFacts {
|
|
19
|
+
constructor(source) { this.buffers = [source]; this.entries = []; this.length = 0; this.aliases = [] }
|
|
20
|
+
add(marker, start, end, override) {
|
|
21
|
+
let buffer = 0
|
|
22
|
+
if (override !== undefined) { buffer = this.buffers.length; this.buffers.push(override); start = 0; end = override.length }
|
|
23
|
+
this.entries.push({ marker, buffer, start, end })
|
|
24
|
+
this.length++
|
|
25
|
+
}
|
|
26
|
+
*[Symbol.iterator]() { for (const entry of this.entries) yield [entry.marker, this.buffers[entry.buffer].slice(entry.start, entry.end)] }
|
|
27
|
+
map(callback) { return [...this].map(callback) }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const factMap = sources => sources instanceof CallableSourceFacts
|
|
31
|
+
? new Map(sources.entries.map(entry => [entry.marker, { entry, buffers: sources.buffers }]))
|
|
32
|
+
: new Map(sources.map(([marker, original]) => [marker, { original }]))
|
|
33
|
+
const originalText = fact => fact.entry === undefined ? fact.original
|
|
34
|
+
: fact.buffers[fact.entry.buffer].slice(fact.entry.start, fact.entry.end)
|
|
35
|
+
|
|
36
|
+
// Native method source starts after the static modifier and its trivia. Read
|
|
37
|
+
// tokens only at this parsed method boundary, without retaining file tokens.
|
|
38
|
+
function callableSourceStart(source, node) {
|
|
39
|
+
if (!(node.staticMethod ?? (node.static && node.type.endsWith('Method')))) return node.start
|
|
40
|
+
const start = node.decorators?.at(-1)?.end ?? node.start
|
|
41
|
+
const tokens = tokenizer(source.slice(start, node.end), { ecmaVersion: 'latest' })
|
|
42
|
+
while (tokens.getToken().value !== 'static') {}
|
|
43
|
+
return start + tokens.getToken().start
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function callableMarkerPosition(node, source, comments) {
|
|
47
|
+
if (node.type !== 'ArrowFunctionExpression') return node.body.start + 1
|
|
48
|
+
let start = source.lastIndexOf('=>', node.body.start - 1)
|
|
49
|
+
while (true) {
|
|
50
|
+
let low = 0, high = comments.length
|
|
51
|
+
while (low < high) {
|
|
52
|
+
const middle = (low + high) >>> 1
|
|
53
|
+
if (comments[middle].start <= start) low = middle + 1
|
|
54
|
+
else high = middle
|
|
55
|
+
}
|
|
56
|
+
const comment = comments[low - 1]
|
|
57
|
+
if (comment === undefined || comment.end <= start) return start
|
|
58
|
+
start = source.lastIndexOf('=>', comment.start - 1)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Carry source identity through transforms without wrapping callable values. */
|
|
63
|
+
export function markCallableSources(source, sourceMap = identitySourceMap(source.length), parserOptions = {}, {
|
|
64
|
+
lowerNativeSource, resolveOriginalSource, nativeUsing = supportsNativeUsing(),
|
|
65
|
+
} = {}) {
|
|
66
|
+
const options = { sourceType: 'unambiguous', allowAwaitOutsideFunction: true,
|
|
67
|
+
allowReturnOutsideFunction: true, errorRecovery: true, ...parserOptions }
|
|
68
|
+
let tree, nativeJavaScript = false
|
|
69
|
+
try {
|
|
70
|
+
tree = parse(source, { ...options, errorRecovery: false, plugins: ['importAttributes'] })
|
|
71
|
+
nativeJavaScript = true
|
|
72
|
+
} catch {
|
|
73
|
+
tree = parse(source, options)
|
|
74
|
+
}
|
|
75
|
+
const identity = createHash('sha256').update(source).digest('hex')
|
|
76
|
+
const sources = new CallableSourceFacts(source)
|
|
77
|
+
const typedCallables = new WeakSet()
|
|
78
|
+
const resourceCallables = new WeakSet()
|
|
79
|
+
const lowerResources = !nativeUsing && source.includes('using')
|
|
80
|
+
const typed = node => {
|
|
81
|
+
if (!node) return 0
|
|
82
|
+
let needed = requiresTypeErasure(node) ? 1 : 0
|
|
83
|
+
if (lowerResources && t.isVariableDeclaration(node) && (node.kind === 'using' || node.kind === 'await using')) needed |= 2
|
|
84
|
+
for (const key of t.VISITOR_KEYS[node.type] ?? []) {
|
|
85
|
+
const child = node[key]
|
|
86
|
+
if (Array.isArray(child)) { for (const value of child) needed |= typed(value) }
|
|
87
|
+
else needed |= typed(child)
|
|
88
|
+
}
|
|
89
|
+
if (needed && isCallable(node)) typedCallables.add(node)
|
|
90
|
+
if ((needed & 2) && isCallable(node)) resourceCallables.add(node)
|
|
91
|
+
return needed
|
|
92
|
+
}
|
|
93
|
+
if (!nativeJavaScript || lowerResources) typed(tree.program)
|
|
94
|
+
const edits = [], typedRecords = []
|
|
95
|
+
const contexts = new WeakMap()
|
|
96
|
+
const context = (node, privateNames = []) => {
|
|
97
|
+
if (!node) return
|
|
98
|
+
if (isCallable(node)) contexts.set(node, privateNames)
|
|
99
|
+
const names = t.isClass(node) ? [...new Set([...privateNames, ...node.body.body
|
|
100
|
+
.filter(member => t.isPrivateName(member.key)).map(member => member.key.id.name)])] : privateNames
|
|
101
|
+
for (const key of t.VISITOR_KEYS[node.type] ?? []) {
|
|
102
|
+
const child = node[key]
|
|
103
|
+
if (Array.isArray(child)) { for (const value of child) context(value, names) }
|
|
104
|
+
else context(child, names)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!nativeJavaScript || lowerResources) context(tree.program)
|
|
108
|
+
t.traverseFast(tree, node => {
|
|
109
|
+
if (t.isVariableDeclaration(node) && (node.kind === 'using' || node.kind === 'await using')) nativeJavaScript = false
|
|
110
|
+
if (!isCallable(node)) return
|
|
111
|
+
if (node.body === undefined || node.body === null) return
|
|
112
|
+
// A constructor method's callable value is the class itself. It cannot
|
|
113
|
+
// have a separate source identity from Class.prototype.constructor.
|
|
114
|
+
if (t.isClassMethod(node, { kind: 'constructor' })) return
|
|
115
|
+
const marker = `__dsh_ptc_callable_${identity}_${sources.length}__`
|
|
116
|
+
const startOffset = callableSourceStart(source, node)
|
|
117
|
+
sources.add(marker, startOffset, node.end, resolveOriginalSource?.(source.slice(startOffset, node.end)))
|
|
118
|
+
const start = callableMarkerPosition(node, source, tree.comments)
|
|
119
|
+
edits.push({ start, end: start, text: `/*${marker}*/` })
|
|
120
|
+
if (typedCallables.has(node)) typedRecords.push({ marker, node, entry: sources.entries.at(-1), privateNames: contexts.get(node),
|
|
121
|
+
resources: resourceCallables.has(node) })
|
|
122
|
+
})
|
|
123
|
+
const marked = applySourceEdits(source, sourceMap, edits)
|
|
124
|
+
if (typedRecords.length === 0) return { ...marked, callableSources: sources, nativeJavaScript }
|
|
125
|
+
// Every descendant has a source identity before its parent is lowered.
|
|
126
|
+
const markedTree = parse(marked.code, options)
|
|
127
|
+
const markedNodes = new Map([...markedCallableOwners(markedTree, sources)].map(([node, fact]) => [fact.marker, node]))
|
|
128
|
+
const originals = new Map(sources)
|
|
129
|
+
const nativeFunctions = typedRecords.map(record => markedNodes.get(record.marker))
|
|
130
|
+
.filter(node => t.isFunction(node) && !isMethod(node)).sort((left, right) => left.start - right.start)
|
|
131
|
+
const nativeRecipes = new Map()
|
|
132
|
+
const nativeFunctionIndex = start => {
|
|
133
|
+
let low = 0, high = nativeFunctions.length
|
|
134
|
+
while (low < high) {
|
|
135
|
+
const middle = (low + high) >>> 1
|
|
136
|
+
if (nativeFunctions[middle].start < start) low = middle + 1
|
|
137
|
+
else high = middle
|
|
138
|
+
}
|
|
139
|
+
return low
|
|
140
|
+
}
|
|
141
|
+
for (const record of typedRecords.sort((a, b) => a.node.end - a.node.start - (b.node.end - b.node.start))) {
|
|
142
|
+
const { marker, entry, privateNames } = record
|
|
143
|
+
const node = markedNodes.get(marker)
|
|
144
|
+
const start = callableSourceStart(marked.code, node)
|
|
145
|
+
let original = marked.code.slice(start, node.end)
|
|
146
|
+
const method = isMethod(node)
|
|
147
|
+
const classRecipe = t.isClass(node) && (node.decorators?.length || node.abstract)
|
|
148
|
+
const methodRecipe = method && (node.decorators?.length || node.params.some(parameter => parameter.decorators?.length))
|
|
149
|
+
const reused = []
|
|
150
|
+
if (classRecipe) original = decoratedClassSource(node)
|
|
151
|
+
else if (methodRecipe) original = undecoratedMethodSource(node)
|
|
152
|
+
else {
|
|
153
|
+
// A lowered function closes its generated helpers over its own source.
|
|
154
|
+
// Reuse that complete function in enclosing reflection recipes; class
|
|
155
|
+
// and method definition phases retain their separate grammar owners.
|
|
156
|
+
// Descendants are complete before their parent. Select the outermost
|
|
157
|
+
// reusable functions, jumping over the descendants each one already owns.
|
|
158
|
+
for (let index = nativeFunctionIndex(start); index < nativeFunctions.length;) {
|
|
159
|
+
const child = nativeFunctions[index]
|
|
160
|
+
if (child.start >= node.end) break
|
|
161
|
+
const replacement = nativeRecipes.get(child)
|
|
162
|
+
if (replacement === undefined) { index++; continue }
|
|
163
|
+
reused.push(replacement)
|
|
164
|
+
index = nativeFunctionIndex(child.end)
|
|
165
|
+
}
|
|
166
|
+
if (reused.length > 0) original = applySourceEdits(original, identitySourceMap(original.length), reused
|
|
167
|
+
.map(value => ({ start: value.start - start, end: value.end - start, text: value.text }))).code
|
|
168
|
+
}
|
|
169
|
+
const prefix = method ? 'class Source {' : '('
|
|
170
|
+
const suffix = method ? '}' : ')'
|
|
171
|
+
const lowerCallable = () => {
|
|
172
|
+
const lowered = lowerCallableSource(original, { method,
|
|
173
|
+
lowerNativeSource: (source, options) => lowerNativeSource(source, { ...options, nativeUsing }),
|
|
174
|
+
privateNames, sourceType: tree.program.sourceType })
|
|
175
|
+
original = lowered.source
|
|
176
|
+
const entries = [...originals].map(([key, text]) => [key, key === marker ? original : text])
|
|
177
|
+
for (const [node, fact] of markedCallableOwners(lowered.emitted, entries)) {
|
|
178
|
+
sources.aliases.push([lowered.emittedSource.slice(node.start, node.end), fact.original])
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Prefer source-preserving erasure. Runtime TypeScript constructs such as
|
|
182
|
+
// parameter properties require equivalent JavaScript initialization too.
|
|
183
|
+
let resources = record.resources
|
|
184
|
+
let erase = true
|
|
185
|
+
if (reused.length > 0) {
|
|
186
|
+
resources = false
|
|
187
|
+
erase = false
|
|
188
|
+
const reusedNodes = new Set(reused.map(value => value.node))
|
|
189
|
+
t.traverseFast(node, child => {
|
|
190
|
+
if (reusedNodes.has(child)) return t.traverseFast.skip
|
|
191
|
+
if (requiresTypeErasure(child)) erase = true
|
|
192
|
+
if (record.resources && t.isVariableDeclaration(child) && (child.kind === 'using' || child.kind === 'await using')) resources = true
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
// Replacing complete functions with proven native functions leaves native
|
|
196
|
+
// surrounding syntax intact. Their generated bodies need no erasure pass.
|
|
197
|
+
if (resources) lowerCallable()
|
|
198
|
+
else if (erase) try {
|
|
199
|
+
const erased = transformTypeScriptSource(`${prefix}${original}${suffix}`, { mode: 'strip-only' }).code
|
|
200
|
+
parse(erased, { errorRecovery: true })
|
|
201
|
+
original = erased.slice(prefix.length, erased.length - suffix.length)
|
|
202
|
+
} catch {
|
|
203
|
+
lowerCallable()
|
|
204
|
+
}
|
|
205
|
+
entry.buffer = sources.buffers.length
|
|
206
|
+
sources.buffers.push(original)
|
|
207
|
+
entry.start = 0
|
|
208
|
+
entry.end = original.length
|
|
209
|
+
originals.set(marker, original)
|
|
210
|
+
if (!method && t.isFunction(node)) nativeRecipes.set(node, { node, start, end: node.end, text: original })
|
|
211
|
+
}
|
|
212
|
+
return { ...marked, callableSources: sources, nativeJavaScript }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function markedCallableOwners(tree, sources) {
|
|
216
|
+
const declarations = []
|
|
217
|
+
t.traverseFast(tree, node => { if (isCallable(node)) declarations.push(node) })
|
|
218
|
+
declarations.sort((left, right) => left.start - right.start || right.end - left.end)
|
|
219
|
+
const facts = factMap(sources)
|
|
220
|
+
const originals = new Map()
|
|
221
|
+
const stack = []
|
|
222
|
+
let next = 0
|
|
223
|
+
for (const comment of tree.comments) {
|
|
224
|
+
const fact = facts.get(comment.value)
|
|
225
|
+
if (fact === undefined) continue
|
|
226
|
+
while (next < declarations.length && declarations[next].start <= comment.start) {
|
|
227
|
+
const node = declarations[next++]
|
|
228
|
+
while (stack.length > 0 && stack.at(-1).end < node.end) stack.pop()
|
|
229
|
+
stack.push(node)
|
|
230
|
+
}
|
|
231
|
+
while (stack.length > 0 && stack.at(-1).end < comment.end) stack.pop()
|
|
232
|
+
if (stack.length > 0) originals.set(stack.at(-1), { marker: comment.value, ...fact })
|
|
233
|
+
}
|
|
234
|
+
return originals
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function sourceOwnerPositions(code, sources, parserOptions) {
|
|
238
|
+
return new Map([...markedCallableOwners(parse(code, parserOptions), sources)]
|
|
239
|
+
.map(([node, fact]) => [`${node.type}:${node.start}`, fact]))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function callableSourceOwners(code, sources, parserOptions, transformation) {
|
|
243
|
+
const options = { sourceType: 'unambiguous', allowAwaitOutsideFunction: true,
|
|
244
|
+
allowReturnOutsideFunction: true, ...parserOptions }
|
|
245
|
+
if (transformation === undefined) {
|
|
246
|
+
const tree = parse(code, options)
|
|
247
|
+
return { tree, owners: markedCallableOwners(tree, sources) }
|
|
248
|
+
}
|
|
249
|
+
// Retain numeric owner facts, not a second complete AST while reading output.
|
|
250
|
+
const owners = sourceOwnerPositions(transformation.code, sources, options)
|
|
251
|
+
const sourceMap = new SourceMap(JSON.parse(transformation.map))
|
|
252
|
+
const starts = sourceLineStarts(transformation.code)
|
|
253
|
+
const emitted = parse(code, options)
|
|
254
|
+
const matched = new Map()
|
|
255
|
+
t.traverseFast(emitted, node => {
|
|
256
|
+
if (!isCallable(node)) return
|
|
257
|
+
const position = sourceMap.findEntry(node.loc.start.line - 1, node.loc.start.column)
|
|
258
|
+
// A preceding segment does not prove a generated callable's source owner.
|
|
259
|
+
if (position.generatedLine !== node.loc.start.line - 1 || position.generatedColumn !== node.loc.start.column
|
|
260
|
+
|| starts[position.originalLine] === undefined) return
|
|
261
|
+
const fact = owners.get(`${node.type}:${starts[position.originalLine] + position.originalColumn}`)
|
|
262
|
+
if (fact !== undefined) matched.set(node, fact)
|
|
263
|
+
})
|
|
264
|
+
return { tree: emitted, owners: matched }
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Source marks own callables before transforms; exact mappings retain that ownership afterward. */
|
|
268
|
+
export function collectCallableSources(code, sources, parserOptions = {}, transformation) {
|
|
269
|
+
if (sources.length === 0) return []
|
|
270
|
+
const { owners } = callableSourceOwners(code, sources, parserOptions, transformation)
|
|
271
|
+
return [...owners].map(([node, fact]) => {
|
|
272
|
+
return [code.slice(callableSourceStart(code, node), node.end), originalText(fact)]
|
|
273
|
+
}).concat(sources.aliases ?? [])
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Emission ranges come from typed nodes and exact generator mappings. */
|
|
277
|
+
export function regionCallableRanges(input, generated, sourceRanges, parserOptions) {
|
|
278
|
+
if (!sourceRanges?.length) return []
|
|
279
|
+
const sourceByStart = new Map(sourceRanges.map(fact => [fact.start, fact]))
|
|
280
|
+
const local = new Map()
|
|
281
|
+
t.traverseFast(input.selected, node => {
|
|
282
|
+
if (!isCallable(node)) return
|
|
283
|
+
const fact = sourceByStart.get(sourceOffsetAt(input.sourceMap, node.start))
|
|
284
|
+
if (fact && sourceOffsetAt(input.sourceMap, node.end - 1) + 1 === fact.end) local.set(node.start, fact)
|
|
285
|
+
})
|
|
286
|
+
if (!local.size) return []
|
|
287
|
+
const inputStarts = sourceLineStarts(input.code), generatedStarts = sourceLineStarts(generated.code)
|
|
288
|
+
const mapped = new Map()
|
|
289
|
+
for (const item of generated.rawMappings) if (item.original) mapped.set(
|
|
290
|
+
generatedStarts[item.generated.line - 1] + item.generated.column,
|
|
291
|
+
inputStarts[item.original.line - 1] + item.original.column)
|
|
292
|
+
const output = regionInput(generated.code, { ...input.region, start: 0, end: generated.code.length, children: [] },
|
|
293
|
+
{ ...parserOptions, attachComment: false })
|
|
294
|
+
const ranges = []
|
|
295
|
+
t.traverseFast(output.selected, node => {
|
|
296
|
+
if (!isCallable(node)) return
|
|
297
|
+
const start = node.start - output.prefix.length
|
|
298
|
+
const fact = local.get(mapped.get(start))
|
|
299
|
+
if (fact) ranges.push({ marker: fact.marker, start, end: node.end - output.prefix.length,
|
|
300
|
+
staticMethod: node.static && node.type.endsWith('Method') })
|
|
301
|
+
})
|
|
302
|
+
return ranges
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Parent shells and child bodies jointly prove ownership, without retaining
|
|
306
|
+
* their ASTs or parsing the expanded module as a whole. */
|
|
307
|
+
export function createRegionCallableCollector(sources) {
|
|
308
|
+
const facts = factMap(sources)
|
|
309
|
+
const collect = input => {
|
|
310
|
+
const ranges = []
|
|
311
|
+
t.traverseFast(input.selected, node => {
|
|
312
|
+
if (isCallable(node)) ranges.push({
|
|
313
|
+
start: sourceOffsetAt(input.sourceMap, node.start),
|
|
314
|
+
end: sourceOffsetAt(input.sourceMap, node.end - 1) + 1,
|
|
315
|
+
staticMethod: node.static && node.type.endsWith('Method'),
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
for (const comment of input.tree.comments) if (facts.has(comment.value)) ranges.push({
|
|
319
|
+
marker: comment.value, start: sourceOffsetAt(input.sourceMap, comment.start),
|
|
320
|
+
end: sourceOffsetAt(input.sourceMap, comment.end - 1) + 1,
|
|
321
|
+
})
|
|
322
|
+
return ranges
|
|
323
|
+
}
|
|
324
|
+
return { collect, resolve: resolveRegionCallableRanges }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function resolveRegionCallableRanges(ranges) {
|
|
328
|
+
const declarations = [], comments = []
|
|
329
|
+
for (const range of ranges) (range.marker === undefined ? declarations : comments).push(range)
|
|
330
|
+
declarations.sort((a, b) => a.start - b.start || b.end - a.end)
|
|
331
|
+
comments.sort((a, b) => a.start - b.start)
|
|
332
|
+
const owners = new Map(), stack = []
|
|
333
|
+
let next = 0
|
|
334
|
+
for (const comment of comments) {
|
|
335
|
+
while (next < declarations.length && declarations[next].start <= comment.start) {
|
|
336
|
+
const node = declarations[next++]
|
|
337
|
+
while (stack.length && stack.at(-1).end < node.end) stack.pop()
|
|
338
|
+
stack.push(node)
|
|
339
|
+
}
|
|
340
|
+
while (stack.length && stack.at(-1).end < comment.end) stack.pop()
|
|
341
|
+
const owner = stack.at(-1)
|
|
342
|
+
if (owner) owners.set(owner.start, { ...owner, marker: comment.marker })
|
|
343
|
+
}
|
|
344
|
+
return owners
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function collectRegionCallableRanges(mapped, sources, options) {
|
|
348
|
+
const collector = createRegionCallableCollector(sources)
|
|
349
|
+
const ranges = []
|
|
350
|
+
visitRegionSource(mapped, options, input => {
|
|
351
|
+
for (const range of collector.collect(input)) ranges.push(range)
|
|
352
|
+
})
|
|
353
|
+
return collector.resolve(ranges)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Restore identities while the maintained transform's bounded tree and exact
|
|
357
|
+
* source-map points are available; child slots remain structural output facts. */
|
|
358
|
+
export function emitRegionCallableSources(input, generated, sourceRanges, options) {
|
|
359
|
+
const originals = new Map()
|
|
360
|
+
t.traverseFast(input.selected, node => {
|
|
361
|
+
if (!isCallable(node)) return
|
|
362
|
+
const fact = sourceRanges.get(sourceOffsetAt(input.sourceMap, node.start))
|
|
363
|
+
if (fact && sourceOffsetAt(input.sourceMap, node.end - 1) + 1 === fact.end) originals.set(node.start, fact)
|
|
364
|
+
})
|
|
365
|
+
const inputStarts = sourceLineStarts(input.code), generatedStarts = sourceLineStarts(generated.code)
|
|
366
|
+
const points = [], origins = new Map()
|
|
367
|
+
visitSourceMappings(generated, (line, column, originalLine, originalColumn) => {
|
|
368
|
+
if (originalLine === undefined) return
|
|
369
|
+
const point = [generatedStarts[line] + column, inputStarts[originalLine] + originalColumn]
|
|
370
|
+
points.push(point)
|
|
371
|
+
origins.set(...point)
|
|
372
|
+
})
|
|
373
|
+
const tree = parse(generated.code, { ...options, plugins: ['importAttributes'],
|
|
374
|
+
allowSuperOutsideMethod: true, allowUndeclaredExports: options.sourceType === 'module', errorRecovery: true })
|
|
375
|
+
for (const error of tree.errors) {
|
|
376
|
+
if (error.reasonCode === 'InvalidPrivateFieldResolution' && input.region.privateNames?.includes(error.details.identifierName)) continue
|
|
377
|
+
throw error
|
|
378
|
+
}
|
|
379
|
+
let from = 0, to = generated.code.length
|
|
380
|
+
if (input.prefix) {
|
|
381
|
+
let statement = tree.program.body[0].body.body[0]
|
|
382
|
+
while (statement.type === 'LabeledStatement') statement = statement.body
|
|
383
|
+
const body = statement.body.body
|
|
384
|
+
from = body[0]?.start ?? statement.body.start + 1
|
|
385
|
+
to = body.at(-1)?.end ?? from
|
|
386
|
+
}
|
|
387
|
+
const owners = []
|
|
388
|
+
const slotStarts = new Map(input.slots.map(slot => [slot.start, slot]))
|
|
389
|
+
const slotEnds = new Map(input.slots.map(slot => [slot.end - 1, slot]))
|
|
390
|
+
const slots = []
|
|
391
|
+
t.traverseFast(tree, node => {
|
|
392
|
+
if (node.type === 'BlockStatement' && node.body.length === 0 && node.directives.length === 0) {
|
|
393
|
+
const slot = slotStarts.get(origins.get(node.start)) ?? slotEnds.get(origins.get(node.end - 1))
|
|
394
|
+
if (slot) slots.push({ sourceStart: slot.start, start: node.start, end: node.end, prefix: '' })
|
|
395
|
+
}
|
|
396
|
+
if (!isCallable(node) || node.start < from || node.end > to) return
|
|
397
|
+
const fact = originals.get(origins.get(node.start))
|
|
398
|
+
if (fact) owners.push({ node, fact })
|
|
399
|
+
})
|
|
400
|
+
const markers = new Set([...originals.values()].map(fact => fact.marker))
|
|
401
|
+
const edits = tree.comments.filter(comment => markers.has(comment.value))
|
|
402
|
+
.map(comment => ({ start: comment.start, end: comment.end, text: '' }))
|
|
403
|
+
for (const { node, fact } of owners) {
|
|
404
|
+
const start = callableMarkerPosition(node, generated.code, tree.comments)
|
|
405
|
+
const text = `/*${fact.marker}*/`
|
|
406
|
+
const slot = slots.find(slot => start > slot.start && start < slot.end)
|
|
407
|
+
if (slot) slot.prefix += text
|
|
408
|
+
else edits.push({ start, end: start, text })
|
|
409
|
+
}
|
|
410
|
+
if (from) edits.push({ start: 0, end: from, text: '' })
|
|
411
|
+
if (to < generated.code.length) edits.push({ start: to, end: generated.code.length, text: '' })
|
|
412
|
+
edits.sort((a, b) => a.start - b.start || a.end - b.end)
|
|
413
|
+
// Non-overlapping edits have ordered ends. Include every edit ending at a
|
|
414
|
+
// queried boundary, including all insertions at that same position.
|
|
415
|
+
const deltas = [0]
|
|
416
|
+
for (const edit of edits) deltas.push(deltas.at(-1) + edit.text.length - (edit.end - edit.start))
|
|
417
|
+
const position = offset => {
|
|
418
|
+
let low = 0, high = edits.length
|
|
419
|
+
while (low < high) {
|
|
420
|
+
const middle = (low + high) >>> 1
|
|
421
|
+
if (edits[middle].end > offset) high = middle
|
|
422
|
+
else low = middle + 1
|
|
423
|
+
}
|
|
424
|
+
return offset + deltas[low]
|
|
425
|
+
}
|
|
426
|
+
const emission = mappedSourceTransform(input.code, identitySourceMap(input.code.length), generated)
|
|
427
|
+
const restored = applySourceEdits(emission.code, emission.sourceMap, edits)
|
|
428
|
+
return { code: restored.code, sourceOffsets: restored.sourceMap,
|
|
429
|
+
mappingPoints: points.filter(([output]) => output >= from && output < to)
|
|
430
|
+
.map(([output, original]) => [position(output), original]),
|
|
431
|
+
slotRanges: slots.map(slot => ({ ...slot, start: position(slot.start), end: position(slot.end) })),
|
|
432
|
+
callableRanges: owners.map(({ node, fact }) => ({ marker: fact.marker,
|
|
433
|
+
start: position(node.start), end: position(node.end), staticMethod: node.static && node.type.endsWith('Method') })),
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function callableSourceCatalog(code, sources, ranges) {
|
|
438
|
+
const facts = factMap(sources)
|
|
439
|
+
const buffers = [code]
|
|
440
|
+
const indexes = new Map()
|
|
441
|
+
const bufferIndex = text => {
|
|
442
|
+
let index = indexes.get(text)
|
|
443
|
+
if (index === undefined) { index = buffers.length; indexes.set(text, index); buffers.push(text) }
|
|
444
|
+
return index
|
|
445
|
+
}
|
|
446
|
+
const entries = []
|
|
447
|
+
for (const range of ranges) {
|
|
448
|
+
const fact = facts.get(range.marker)
|
|
449
|
+
if (!fact) continue
|
|
450
|
+
const start = callableSourceStart(code, range)
|
|
451
|
+
const entry = fact.entry
|
|
452
|
+
entries.push([0, start, range.end, bufferIndex(entry ? fact.buffers[entry.buffer] : fact.original),
|
|
453
|
+
entry?.start ?? 0, entry?.end ?? fact.original.length])
|
|
454
|
+
}
|
|
455
|
+
for (const [generated, original] of sources.aliases ?? []) entries.push([
|
|
456
|
+
bufferIndex(generated), 0, generated.length, bufferIndex(original), 0, original.length,
|
|
457
|
+
])
|
|
458
|
+
return createCallableSourceCatalog(buffers, entries)
|
|
459
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { parse } from '@babel/parser'
|
|
2
|
+
import { types as t } from '@babel/core'
|
|
3
|
+
import generatorImport from '@babel/generator'
|
|
4
|
+
import traverseImport from '@babel/traverse'
|
|
5
|
+
import { callableDefinitionOwner, closeDefinitionExpression } from './callable-definition-environment.js'
|
|
6
|
+
|
|
7
|
+
const generate = generatorImport.default ?? generatorImport
|
|
8
|
+
const traverse = traverseImport.default ?? traverseImport
|
|
9
|
+
const nativeParserOptions = { sourceType: 'script', allowAwaitOutsideFunction: true, allowYieldOutsideFunction: true,
|
|
10
|
+
allowSuperOutsideMethod: true, allowNewTargetOutsideFunction: true }
|
|
11
|
+
const parserOptions = { sourceType: 'script', plugins: ['typescript', 'decorators', 'decoratorAutoAccessors'],
|
|
12
|
+
...nativeParserOptions, allowReturnOutsideFunction: true, errorRecovery: true }
|
|
13
|
+
const methodNode = node => t.isObjectMethod(node) || t.isClassMethod(node) || t.isClassPrivateMethod(node)
|
|
14
|
+
const text = node => generate(node).code
|
|
15
|
+
|
|
16
|
+
/** Definition-time decorators belong to the enclosing class, not the method value. */
|
|
17
|
+
export function undecoratedMethodSource(node) {
|
|
18
|
+
const method = t.cloneNode(node, true)
|
|
19
|
+
method.decorators = null
|
|
20
|
+
method.static = false
|
|
21
|
+
method.accessibility = null
|
|
22
|
+
for (const parameter of method.params) parameter.decorators = null
|
|
23
|
+
return text(method)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A decorated declaration's range may include its parent's export modifiers. */
|
|
27
|
+
export function decoratedClassSource(node) {
|
|
28
|
+
const value = t.cloneNode(node, true)
|
|
29
|
+
value.type = 'ClassExpression'
|
|
30
|
+
value.abstract = false
|
|
31
|
+
return text(value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A body owns only the helper bindings it can use, including dependencies of
|
|
35
|
+
* those helpers. Parameter and key activations obtain their own lexical set. */
|
|
36
|
+
function closeCallableHelpers(value, helpers) {
|
|
37
|
+
const callable = t.isMethod(value) ? { ...value, type: 'ObjectMethod' } : value
|
|
38
|
+
const tree = t.file(t.program([...helpers, t.expressionStatement(t.isMethod(callable)
|
|
39
|
+
? t.objectExpression([callable]) : callable)]))
|
|
40
|
+
const bodies = new Map([[callable, new Set()]])
|
|
41
|
+
const dependencies = new Map(helpers.map(node => [node, new Set()]))
|
|
42
|
+
const statement = path => {
|
|
43
|
+
while (!path.parentPath.isProgram()) path = path.parentPath
|
|
44
|
+
return path.node
|
|
45
|
+
}
|
|
46
|
+
traverse(tree, { Function(path) {
|
|
47
|
+
if (callableDefinitionOwner(path) && !dependencies.has(statement(path))) bodies.set(path.node, new Set())
|
|
48
|
+
} })
|
|
49
|
+
traverse(tree, { ReferencedIdentifier(path) {
|
|
50
|
+
const binding = path.scope.getBinding(path.node.name)
|
|
51
|
+
if (binding === undefined) return
|
|
52
|
+
const helper = statement(binding.path)
|
|
53
|
+
if (!dependencies.has(helper)) return
|
|
54
|
+
const consumer = dependencies.get(statement(path))
|
|
55
|
+
if (consumer) consumer.add(helper)
|
|
56
|
+
else {
|
|
57
|
+
let child = path
|
|
58
|
+
while (child.parentPath) {
|
|
59
|
+
const parent = child.parentPath
|
|
60
|
+
if (bodies.has(parent.node) && child.key === 'body') {
|
|
61
|
+
bodies.get(parent.node).add(helper)
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
child = parent
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} })
|
|
68
|
+
for (const [body, required] of bodies) {
|
|
69
|
+
for (const helper of required) for (const dependency of dependencies.get(helper)) required.add(dependency)
|
|
70
|
+
if (!t.isBlockStatement(body.body)) body.body = t.blockStatement([t.returnStatement(body.body)])
|
|
71
|
+
body.body.body.unshift(...helpers.filter(node => required.has(node)).map(node => t.cloneNode(node, true)))
|
|
72
|
+
}
|
|
73
|
+
value.body = callable.body
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Close generated language helpers over the source that actually needs them. */
|
|
77
|
+
export function lowerCallableSource(original, { method = false, lowerNativeSource, privateNames = [], sourceType = 'script' }) {
|
|
78
|
+
// Grammar-only containers must not acquire logical declaration identities.
|
|
79
|
+
// Every fragment crosses lowering as an expression, including method hosts.
|
|
80
|
+
const prefix = method ? '(class {' : '('
|
|
81
|
+
const suffix = method ? '})' : ')'
|
|
82
|
+
let input = prefix + original + suffix
|
|
83
|
+
const nativeOptions = { ...nativeParserOptions, sourceType, errorRecovery: true }
|
|
84
|
+
const tree = parse(input, { ...parserOptions, sourceType })
|
|
85
|
+
let contextual = privateNames.length > 0
|
|
86
|
+
t.traverseFast(tree, node => {
|
|
87
|
+
if (t.isSuper(node) || t.isMetaProperty(node) && node.meta.name === 'new') contextual = true
|
|
88
|
+
})
|
|
89
|
+
// Declaration ownership is assigned after dialect normalization. This
|
|
90
|
+
// inspection only selects the detached fragment's surrounding grammar.
|
|
91
|
+
traverse(tree, { noScope: true,
|
|
92
|
+
...traverse.visitors.environmentVisitor({ YieldExpression() { contextual = true } }),
|
|
93
|
+
})
|
|
94
|
+
if (!method && t.isClass(tree.program.body[0].expression) && tree.program.body[0].expression.id === null) {
|
|
95
|
+
input = `({[""]:${input}})[""]`
|
|
96
|
+
}
|
|
97
|
+
if (contextual) {
|
|
98
|
+
const body = `async *source(){return ${input}}`
|
|
99
|
+
input = privateNames.length > 0 ? `(class{${privateNames.map(name => `#${name};`).join('')}${body}})`
|
|
100
|
+
: `({${body}})`
|
|
101
|
+
}
|
|
102
|
+
const lowered = lowerNativeSource(input, { sourceType })
|
|
103
|
+
const native = parse(lowered, nativeOptions)
|
|
104
|
+
const helpers = native.program.body.slice(0, -1)
|
|
105
|
+
let expression = native.program.body.at(-1).expression
|
|
106
|
+
if (contextual) {
|
|
107
|
+
const body = (privateNames.length > 0 ? expression.body.body : expression.properties)
|
|
108
|
+
.find(methodNode).body.body
|
|
109
|
+
helpers.push(...body.slice(0, -1))
|
|
110
|
+
expression = body.at(-1).argument
|
|
111
|
+
}
|
|
112
|
+
const value = method ? expression.body.body.find(methodNode) : expression
|
|
113
|
+
let source
|
|
114
|
+
if (t.isFunction(value)) {
|
|
115
|
+
closeCallableHelpers(value, helpers)
|
|
116
|
+
source = text(value)
|
|
117
|
+
} else {
|
|
118
|
+
source = text(helpers.length === 0 ? value : closeDefinitionExpression(helpers, value))
|
|
119
|
+
}
|
|
120
|
+
const emittedSource = method ? `class Source{${source}}` : `(${source})`
|
|
121
|
+
const emitted = parse(emittedSource, nativeOptions)
|
|
122
|
+
return { source, emitted, emittedSource }
|
|
123
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function declarationSpan(node) {
|
|
2
|
+
const start = node.loc?.start
|
|
3
|
+
/* c8 ignore next */
|
|
4
|
+
const end = node.loc?.end ?? start
|
|
5
|
+
/* c8 ignore next */
|
|
6
|
+
if (start === undefined) return undefined
|
|
7
|
+
return {
|
|
8
|
+
line: start.line,
|
|
9
|
+
column: start.column + 1,
|
|
10
|
+
/* c8 ignore next */
|
|
11
|
+
...(end === undefined ? {} : {
|
|
12
|
+
end: {
|
|
13
|
+
line: end.line,
|
|
14
|
+
column: end.column + 1,
|
|
15
|
+
},
|
|
16
|
+
}),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class PreflightError extends Error {
|
|
21
|
+
constructor(message, node, span = undefined) {
|
|
22
|
+
super(message)
|
|
23
|
+
this.span = span ?? declarationSpan(node)
|
|
24
|
+
}
|
|
25
|
+
}
|