dsh-ptc-plus 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/README.en.md +66 -73
  2. package/README.md +66 -73
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30610 -722
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +82 -22
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +96 -20
  51. package/docs/durability-design.md +61 -24
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/program-data-plane.md +6 -0
  55. package/docs/publishing.md +5 -3
  56. package/docs/runtime-reference.md +79 -25
  57. package/docs/semantic-validation.md +29 -0
  58. package/docs/stateful-binding-updates-proposal.md +755 -0
  59. package/docs/user-bindings.en.md +64 -0
  60. package/docs/user-bindings.md +64 -0
  61. package/docs/value-wire.md +3 -3
  62. package/docs/verification.md +57 -0
  63. package/index.js +374 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/binding-descriptors.js +1 -0
  66. package/internal/binding-pattern-traversal.js +25 -0
  67. package/internal/binding-pattern.js +26 -29
  68. package/internal/binding-source-analysis.js +391 -0
  69. package/internal/binding-update-policy.js +66 -0
  70. package/internal/bounded-worker-round.js +56 -0
  71. package/internal/callable-definition-environment.js +42 -0
  72. package/internal/callable-source-catalog.js +129 -0
  73. package/internal/callable-source-encoding.js +47 -0
  74. package/internal/callable-source-facts.js +459 -0
  75. package/internal/callable-source-format.js +2 -0
  76. package/internal/callable-source-lowering.js +123 -0
  77. package/internal/cell-analysis-contract.js +25 -0
  78. package/internal/cell-analysis.js +252 -242
  79. package/internal/cell-completion.js +15 -0
  80. package/internal/cell-error.js +7 -0
  81. package/internal/cell-parser.js +66 -0
  82. package/internal/cell-rewriter.js +3 -553
  83. package/internal/commonjs-export-evidence.js +63 -0
  84. package/internal/commonjs-source-evidence.js +72 -0
  85. package/internal/compiler-amaro.cjs +2 -0
  86. package/internal/compiler-compression.js +12 -0
  87. package/internal/compiler-data.js +58 -0
  88. package/internal/compiler-descriptors.js +54 -0
  89. package/internal/compiler-dynamic-cache.js +48 -0
  90. package/internal/compiler-entry.js +35 -0
  91. package/internal/compiler-generated-names.js +17 -0
  92. package/internal/compiler-intrinsics.js +84 -0
  93. package/internal/compiler-module-links.js +34 -0
  94. package/internal/compiler-operations.js +61 -0
  95. package/internal/compiler-platform-factory.js +84 -0
  96. package/internal/compiler-platform.js +136 -0
  97. package/internal/compiler-record-roles.js +53 -0
  98. package/internal/compiler-region-output.js +259 -0
  99. package/internal/compiler-resource-lowering.js +103 -0
  100. package/internal/compiler-scope-facts.js +115 -0
  101. package/internal/compiler-service.js +109 -0
  102. package/internal/compiler-source-cache.js +32 -0
  103. package/internal/compiler-source-pieces.js +93 -0
  104. package/internal/compiler-source-regions.js +132 -0
  105. package/internal/compiler-storage-source.js +54 -0
  106. package/internal/compiler-text.js +15 -0
  107. package/internal/compiler-typescript.cjs +2 -0
  108. package/internal/config-spec.js +91 -25
  109. package/internal/cordis-tools-owner.js +130 -21
  110. package/internal/diagnostic.js +27 -1
  111. package/internal/direct-surface-owner.js +76 -23
  112. package/internal/dynamic-binding-evidence.js +16 -0
  113. package/internal/dynamic-environment-compiler.js +779 -0
  114. package/internal/dynamic-environment-integration.js +87 -0
  115. package/internal/dynamic-environment-runtime.js +362 -0
  116. package/internal/dynamic-native-calls.js +150 -0
  117. package/internal/dynamic-native-runtime.js +248 -0
  118. package/internal/dynamic-scope-analysis.js +90 -0
  119. package/internal/edit-transport-owner.js +28 -3
  120. package/internal/execution-seam-compat.js +193 -0
  121. package/internal/failure-reporting.js +118 -18
  122. package/internal/host-rpc.js +66 -0
  123. package/internal/isolated-worker.js +548 -0
  124. package/internal/kernel-child.js +139 -0
  125. package/internal/kernel-worker.js +911 -116
  126. package/internal/language-semantics.js +14 -0
  127. package/internal/legacy-binding-catalog.js +48 -0
  128. package/internal/legacy-cell-analysis.js +600 -0
  129. package/internal/legacy-cell-parser.js +50 -0
  130. package/internal/legacy-cell-rewriter.js +631 -0
  131. package/internal/legacy-repl-convenience.js +291 -0
  132. package/internal/managed-module-operations.js +170 -0
  133. package/internal/module-compilation.js +321 -0
  134. package/internal/module-runtime-platform.js +19 -0
  135. package/internal/module-transform-contract.js +12 -0
  136. package/internal/native-await-declarations.js +68 -0
  137. package/internal/native-root-compilation.js +54 -0
  138. package/internal/native-root-dynamic.js +67 -0
  139. package/internal/recovery-tips.js +27 -15
  140. package/internal/rejected-cell-editor.js +13 -8
  141. package/internal/repl-convenience.js +2 -201
  142. package/internal/repl-memory-projection.js +81 -12
  143. package/internal/repl-observation-interest.js +70 -0
  144. package/internal/repl-rewrite-contract.js +21 -0
  145. package/internal/repl-scope-normalizer.js +2091 -0
  146. package/internal/repl-value-observer.js +114 -0
  147. package/internal/rpc-contract.js +52 -0
  148. package/internal/runtime-bridge-owner.js +172 -28
  149. package/internal/runtime-config.js +48 -51
  150. package/internal/runtime-contexts.js +82 -10
  151. package/internal/runtime-messages.js +111 -0
  152. package/internal/session-cell-executor.js +455 -91
  153. package/internal/session-journal-recovery.js +684 -0
  154. package/internal/session-journal-schema.js +106 -0
  155. package/internal/session-journal.js +237 -440
  156. package/internal/session-log-view.js +42 -9
  157. package/internal/session-runtime.js +325 -80
  158. package/internal/session-state.js +249 -40
  159. package/internal/source-position-map.js +211 -71
  160. package/internal/stateful-module-compiler.js +154 -0
  161. package/internal/stateful-module-runtime.js +303 -0
  162. package/internal/stateful-root-compiler.js +598 -0
  163. package/internal/stateful-root-runtime.js +340 -0
  164. package/internal/typert-codec-compat.js +28 -0
  165. package/internal/typescript-transform.js +29 -0
  166. package/internal/user-binding-authoring.js +70 -0
  167. package/internal/user-binding-console-worker.js +184 -0
  168. package/internal/user-binding-console.js +148 -0
  169. package/internal/user-binding-draft-projection.js +335 -0
  170. package/internal/user-binding-model-context.js +31 -0
  171. package/internal/user-binding-runner.js +56 -0
  172. package/internal/user-bindings-owner.js +1065 -0
  173. package/internal/user-bindings-store.js +293 -0
  174. package/internal/user-bindings.js +353 -0
  175. package/internal/value-wire-schema.js +28 -0
  176. package/internal/value-wire.js +29 -20
  177. package/internal/worker-client.js +57 -67
  178. package/internal/worker-environment.js +45 -0
  179. package/internal/worker-output-relay.js +29 -0
  180. package/internal/worker-shutdown.js +14 -0
  181. package/package.json +81 -11
  182. package/src/client-activity.js +109 -45
  183. package/src/client-authoring-view.js +580 -0
  184. package/src/client-binding-review.js +230 -0
  185. package/src/client-bindings-data.js +29 -0
  186. package/src/client-catalog.js +127 -0
  187. package/src/client-code-editor.js +84 -0
  188. package/src/client-console.js +112 -0
  189. package/src/client-copy.js +370 -0
  190. package/src/client-feature-gates.js +62 -0
  191. package/src/client-host-compat.js +150 -0
  192. package/src/client-indicator-view.js +267 -0
  193. package/src/client-repl-view.js +292 -0
  194. package/src/client-rpc.js +20 -0
  195. package/src/client-settings-view.js +221 -0
  196. package/src/client-styles.js +106 -0
  197. package/src/client-tool-view.js +108 -0
  198. package/src/client-transport.js +159 -0
  199. package/src/client-workbench.js +521 -0
  200. package/src/client.js +233 -615
@@ -0,0 +1,7 @@
1
+ /** Source diagnostics retain their native SyntaxError identity at runtime. */
2
+ export class ModuleRewriteError extends SyntaxError {
3
+ constructor(message, cellPosition) {
4
+ super(message)
5
+ if (cellPosition !== undefined) this.cellPosition = cellPosition
6
+ }
7
+ }
@@ -0,0 +1,66 @@
1
+ import { stripTypeScriptTypes } from 'node:module'
2
+ import { getLineInfo, parse } from 'acorn'
3
+ import { walkAst } from './ast-traversal.js'
4
+ import { ModuleRewriteError } from './cell-rewriter.js'
5
+ import { transformTypeScriptSource } from './typescript-transform.js'
6
+
7
+ const CELL_PREFIX = 'async function __ptc_cell__(){\n'
8
+ const CELL_SUFFIX = '\n}'
9
+ const PARSER_OPTIONS = { ecmaVersion: 'latest', sourceType: 'script', locations: true }
10
+ const STRIP_OPTIONS = { mode: 'strip-only' }
11
+
12
+ /** Erase TypeScript syntax without moving any source position.
13
+ * The host stripper is experimental and rejects TypeScript early-error forms
14
+ * the compiler's own lowering produces (Node 22.19 refuses `delete` on a
15
+ * sequence expression the same input erases on Node 24), so the pinned eraser
16
+ * owns erasure whenever the host stripper refuses the input or leaves text
17
+ * this parser still cannot read. */
18
+ function eraseTypeScript(source) {
19
+ try {
20
+ const executable = stripTypeScriptTypes(source)
21
+ return { executable, tree: parse(executable, PARSER_OPTIONS) }
22
+ } catch {
23
+ const executable = transformTypeScriptSource(source, STRIP_OPTIONS).code
24
+ return { executable, tree: parse(executable, PARSER_OPTIONS) }
25
+ }
26
+ }
27
+
28
+ /** Parse the async body once and expose only cell-relative source coordinates. */
29
+ export function parseExecutableCell(source, { eraseTypes = false } = {}) {
30
+ const wrapped = CELL_PREFIX + source + CELL_SUFFIX
31
+ let executable = wrapped
32
+ let tree
33
+ try {
34
+ tree = parse(executable, PARSER_OPTIONS)
35
+ } catch (javascriptError) {
36
+ if (eraseTypes) {
37
+ try {
38
+ ({ executable, tree } = eraseTypeScript(wrapped))
39
+ } catch {
40
+ tree = undefined
41
+ }
42
+ }
43
+ if (tree === undefined) {
44
+ const offset = Math.max(0, Math.min(source.length, javascriptError.pos - CELL_PREFIX.length))
45
+ const position = getLineInfo(source, offset)
46
+ throw new ModuleRewriteError(javascriptError.message.replace(/ \(\d+:\d+\)$/, ''),
47
+ { line: position.line, column: position.column + 1 })
48
+ }
49
+ }
50
+ const code = executable.slice(CELL_PREFIX.length, -CELL_SUFFIX.length)
51
+ const body = tree.body[0].body
52
+ walkAst(body, node => {
53
+ if (typeof node.start !== 'number') return
54
+ node.start -= CELL_PREFIX.length
55
+ node.end -= CELL_PREFIX.length
56
+ node.loc = {
57
+ start: { line: node.loc.start.line - 1, column: node.loc.start.column },
58
+ end: { line: node.loc.end.line - 1, column: node.loc.end.column },
59
+ }
60
+ })
61
+ body.start = 0
62
+ body.end = code.length
63
+ body.loc.start = getLineInfo(code, 0)
64
+ body.loc.end = getLineInfo(code, code.length)
65
+ return { code, body }
66
+ }
@@ -1,554 +1,4 @@
1
- /** Adapts TypeScript module declarations to the async function-body grammar used by PTC cells. */
2
- import { parse } from '@babel/parser'
3
- import traverseModule, { Hub, NodePath } from '@babel/traverse'
4
- import { bindingNodes, createGeneratedNameAllocator } from './binding-pattern.js'
5
- import { applySourceEdits, createMappedTextBuilder, identitySourceMap } from './source-position-map.js'
1
+ /** Historical module adapter; new cells use the versioned logical compiler. */
2
+ export { ModuleRewriteError } from './cell-error.js'
6
3
 
7
- export const STRIP_PREFIX = 'async function __ptc_cell__(){\n'
8
- export const STRIP_SUFFIX = '\n}'
9
-
10
- export class ModuleRewriteError extends SyntaxError {
11
- constructor(message, cellPosition) {
12
- super(message)
13
- if (cellPosition !== undefined) this.cellPosition = cellPosition
14
- }
15
- }
16
-
17
- const PARSER_PLUGINS = ['typescript', 'importAttributes']
18
- const TYPE_DECLARATIONS = new Set([
19
- 'TSDeclareFunction',
20
- 'TSEnumDeclaration',
21
- 'TSInterfaceDeclaration',
22
- 'TSModuleDeclaration',
23
- 'TSTypeAliasDeclaration',
24
- ])
25
- const traverse = traverseModule.default ?? traverseModule
26
-
27
- function declaredTypeNames(program) {
28
- const names = new Set()
29
- for (const node of program.body) {
30
- const declaration = node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration'
31
- ? node.declaration
32
- : node
33
- if (TYPE_DECLARATIONS.has(declaration?.type) && declaration.id?.type === 'Identifier') {
34
- names.add(declaration.id.name)
35
- }
36
- }
37
- return names
38
- }
39
-
40
- function createProgramScope(file) {
41
- const hub = new Hub()
42
- hub.buildError = (node, message, ErrorType = TypeError) => {
43
- const error = new ErrorType(message)
44
- error.loc = node.loc?.start
45
- return error
46
- }
47
- const path = NodePath.get({ hub, parent: file, container: file, key: 'program' })
48
- path.setContext()
49
- path.scope = path.getScope()
50
- path.scope.init()
51
- return path.scope
52
- }
53
-
54
- function validateLocalExports(file, programScope) {
55
- const localExports = file.program.body.filter(node => node.type === 'ExportNamedDeclaration'
56
- && node.source === null && node.declaration === null)
57
- const valueSpecifiers = localExports.flatMap(node => node.specifiers
58
- .filter(specifier => specifier.exportKind !== 'type'))
59
- if (valueSpecifiers.length === 0) return
60
- const typeNames = declaredTypeNames(file.program)
61
- const missing = valueSpecifiers.find(specifier => (
62
- programScope.getBinding(specifier.local.name) === undefined && !typeNames.has(specifier.local.name)
63
- ))
64
- if (missing === undefined) return
65
- throw new ModuleRewriteError(`Export '${missing.local.name}' is not defined.`, {
66
- line: missing.local.loc.start.line,
67
- column: missing.local.loc.start.column + 1,
68
- })
69
- }
70
-
71
- function parseCell(code) {
72
- try {
73
- const file = parse(code, {
74
- sourceType: 'script',
75
- allowAwaitOutsideFunction: true,
76
- allowImportExportEverywhere: true,
77
- allowReturnOutsideFunction: true,
78
- allowUndeclaredExports: true,
79
- plugins: PARSER_PLUGINS,
80
- })
81
- validateLocalExports(file, createProgramScope(file))
82
- return file.program
83
- } catch (error) {
84
- if (error instanceof ModuleRewriteError) throw error
85
- const loc = error?.loc
86
- throw new ModuleRewriteError(error instanceof Error ? error.message : String(error),
87
- Number.isSafeInteger(loc?.line) && Number.isSafeInteger(loc?.column)
88
- ? { line: loc.line, column: loc.column + 1 }
89
- : undefined)
90
- }
91
- }
92
-
93
- function parseSource(node) { return node.source.value }
94
- function importOptions(node) {
95
- if (!node.attributes || node.attributes.length === 0) return undefined
96
- const attributes = Object.fromEntries(node.attributes.map((attribute) => {
97
- const key = attribute.key.type === 'Identifier' ? attribute.key.name : attribute.key.value
98
- return [key, attribute.value.value]
99
- }))
100
- const keyword = node.attributes[0].type === 'ImportAttribute' ? 'with' : 'assert'
101
- return { [keyword]: attributes }
102
- }
103
- function moduleLoad(node, source, global = undefined, requiredExports = []) {
104
- return {
105
- source,
106
- ...(global === undefined ? {} : { global }),
107
- ...(requiredExports.length === 0 ? {} : { requiredExports }),
108
- ...(importOptions(node) === undefined ? {} : { options: importOptions(node) }),
109
- position: {
110
- line: node.source.loc.start.line,
111
- column: node.source.loc.start.column + 1,
112
- end: {
113
- line: node.source.loc.end.line,
114
- column: node.source.loc.end.column + 1,
115
- },
116
- },
117
- }
118
- }
119
- function record(kind, description, node, source) {
120
- return { kind, description, ...(source === undefined ? {} : { source }), at: node.start }
121
- }
122
- function preserveLines(original, replacement = '') {
123
- const missing = (original.match(/\n/g)?.length ?? 0) - (replacement.match(/\n/g)?.length ?? 0)
124
- return missing > 0 ? replacement + '\n'.repeat(missing) : replacement
125
- }
126
- function editNode(edits, node, code, replacement = '') {
127
- const mapped = typeof replacement === 'string' ? { text: replacement } : replacement
128
- const separated = node.end < code.length && code[node.end] !== '\n' && code[node.end] !== '\r'
129
- ? `${mapped.text}\n`
130
- : mapped.text
131
- edits.push({
132
- start: node.start,
133
- end: node.end,
134
- text: preserveLines(code.slice(node.start, node.end), separated),
135
- ...(mapped.mappings === undefined ? {} : { mappings: mapped.mappings }),
136
- })
137
- }
138
- function edit(edits, start, end, text) { edits.push({ start, end, text }) }
139
- function localName(specifier) { return specifier.local.name }
140
- function importedName(specifier) {
141
- if (specifier.type === 'ImportDefaultSpecifier') return 'default'
142
- if (specifier.type === 'ImportNamespaceSpecifier') return undefined
143
- return specifier.imported.type === 'Identifier' ? specifier.imported.name : specifier.imported.value
144
- }
145
-
146
- function importEdits(
147
- node,
148
- code,
149
- edits,
150
- rewrites,
151
- imports,
152
- importDeclarations,
153
- moduleLoads,
154
- namespaceCaptures,
155
- allocateNamespace,
156
- allocateGlobal,
157
- ) {
158
- const source = parseSource(node)
159
- const rewrite = record('import', `adapted the static import of ${JSON.stringify(source)} for REPL execution`, node, source)
160
- if (node.importKind === 'type') {
161
- editNode(edits, node, code)
162
- rewrites.push(record('import', `removed the type-only import of ${JSON.stringify(source)}`, node, source))
163
- return
164
- }
165
- if (node.specifiers.length > 0 && node.specifiers.every(specifier => specifier.importKind === 'type')) {
166
- editNode(edits, node, code)
167
- rewrites.push(record('import', `removed the type-only import of ${JSON.stringify(source)}`, node, source))
168
- return
169
- }
170
- const specifiers = node.specifiers.filter(specifier => specifier.importKind !== 'type')
171
- if (specifiers.length === 0) {
172
- moduleLoads.push(moduleLoad(node, source))
173
- editNode(edits, node, code)
174
- rewrites.push(rewrite)
175
- return
176
- }
177
- const namespace = allocateNamespace('import_namespace')
178
- const global = allocateGlobal('import_global')
179
- const requiredExports = []
180
- for (const specifier of specifiers) {
181
- const imported = importedName(specifier)
182
- imports.set(localName(specifier), { namespace, imported })
183
- if (imported !== undefined && !requiredExports.includes(imported)) requiredExports.push(imported)
184
- importDeclarations.push({
185
- name: localName(specifier),
186
- kind: 'import',
187
- span: {
188
- line: specifier.local.loc.start.line,
189
- column: specifier.local.loc.start.column + 1,
190
- end: {
191
- line: specifier.local.loc.end.line,
192
- column: specifier.local.loc.end.column + 1,
193
- },
194
- },
195
- definitionSpan: {
196
- line: node.loc.start.line,
197
- column: node.loc.start.column + 1,
198
- end: {
199
- line: node.loc.end.line,
200
- column: node.loc.end.column + 1,
201
- },
202
- },
203
- original: true,
204
- })
205
- }
206
- moduleLoads.push(moduleLoad(node, source, global, requiredExports))
207
- namespaceCaptures.push({ namespace, global, source: node.source })
208
- editNode(edits, node, code)
209
- rewrites.push(rewrite)
210
- }
211
-
212
- function importPrologueEdit(program, tree, namespaceCaptures) {
213
- if (namespaceCaptures.length === 0) return undefined
214
- const directive = tree.directives.at(-1)
215
- let insertion = directive?.end ?? tree.interpreter?.end ?? 0
216
- if (directive === undefined && tree.interpreter !== null && tree.interpreter !== undefined) {
217
- if (program[insertion] === '\r' && program[insertion + 1] === '\n') insertion += 2
218
- else if (program[insertion] === '\r' || program[insertion] === '\n') insertion += 1
219
- }
220
- const builder = createMappedTextBuilder(program)
221
- if (directive !== undefined && program[insertion - 1] !== ';') builder.append(';')
222
- for (const capture of namespaceCaptures) {
223
- builder.append('const ')
224
- builder.appendMapped(capture.namespace, capture.source.start, capture.source.end)
225
- builder.append(' = ')
226
- builder.appendMapped(capture.global, capture.source.start, capture.source.end)
227
- builder.append(';')
228
- }
229
- return { start: insertion, end: insertion, ...builder.result() }
230
- }
231
-
232
- function importMember(namespace, imported) {
233
- return imported === undefined ? namespace
234
- : /^[A-Za-z_$][\w$]*$/.test(imported) ? `${namespace}.${imported}`
235
- : `${namespace}[${JSON.stringify(imported)}]`
236
- }
237
-
238
- function appendReadonlyImportWrite(builder, appendValue) {
239
- builder.append("((__dsh_ptc_import_value__) => { throw new TypeError('Assignment to constant variable.'); })(")
240
- appendValue()
241
- builder.append(')')
242
- }
243
-
244
- function importedBindingFor(path, identifier, imports) {
245
- const binding = imports.get(identifier.name)
246
- return binding !== undefined && path.scope.getBinding(identifier.name) === undefined ? binding : undefined
247
- }
248
-
249
- function importedWriteBinding(path, imports) {
250
- if (path.isAssignmentExpression() && path.node.left.type === 'Identifier') {
251
- return importedBindingFor(path, path.node.left, imports)
252
- }
253
- return undefined
254
- }
255
-
256
- function assertNoImportedWriteAncestor(path, imports) {
257
- if (path.findParent(parent => importedWriteBinding(parent, imports) !== undefined) !== null) {
258
- throw new ModuleRewriteError('nested assignment to an imported binding is not supported')
259
- }
260
- }
261
-
262
- function preserveExpressionStatementBoundary(path, edits) {
263
- const statement = path.findParent(parent => parent.isExpressionStatement())
264
- if (statement?.node.expression.start === path.node.start) {
265
- edits.push({ start: path.node.start, end: path.node.start, text: ';' })
266
- }
267
- }
268
-
269
- function assertNoDynamicImportResolution(tree, imports) {
270
- if (imports.size === 0) return
271
- traverse(tree, {
272
- WithStatement() {
273
- throw new ModuleRewriteError('with statement is not supported while imported bindings are active')
274
- },
275
- CallExpression(path) {
276
- if (path.node.callee.type === 'Identifier' && path.node.callee.name === 'eval'
277
- && !imports.has('eval') && path.scope.getBinding('eval') === undefined) {
278
- throw new ModuleRewriteError('direct eval is not supported while imported bindings are active')
279
- }
280
- },
281
- })
282
- }
283
-
284
- function rewriteImportReferences(code, sourceMap, imports) {
285
- if (imports.size === 0) return { code, sourceMap }
286
- const parseRewritten = source => parse(source, {
287
- sourceType: 'script',
288
- allowAwaitOutsideFunction: true,
289
- allowReturnOutsideFunction: true,
290
- plugins: PARSER_PLUGINS,
291
- })
292
- let file = parseRewritten(code)
293
- const writeEdits = []
294
- traverse(file, {
295
- AssignmentExpression(path) {
296
- if (path.node.left.type !== 'Identifier') {
297
- const names = bindingNodes(path.node.left).map(node => node.name)
298
- if (names.some(name => imports.has(name) && path.scope.getBinding(name) === undefined)) {
299
- throw new ModuleRewriteError('destructuring assignment to an imported binding is not supported')
300
- }
301
- return
302
- }
303
- const binding = importedBindingFor(path, path.node.left, imports)
304
- if (binding === undefined) return
305
- assertNoImportedWriteAncestor(path, imports)
306
- const member = importMember(binding.namespace, binding.imported)
307
- const builder = createMappedTextBuilder(code)
308
- const appendMember = () => builder.appendMapped(member, path.node.left.start, path.node.left.end)
309
- const appendRight = () => builder.appendSource(path.node.right.start, path.node.right.end)
310
- if (path.node.operator === '=') appendReadonlyImportWrite(builder, appendRight)
311
- else if (['&&=', '||=', '??='].includes(path.node.operator)) {
312
- const operator = path.node.operator.slice(0, -1)
313
- appendMember()
314
- builder.append(` ${operator} `)
315
- appendReadonlyImportWrite(builder, appendRight)
316
- } else {
317
- appendReadonlyImportWrite(builder, () => {
318
- appendMember()
319
- builder.append(` ${path.node.operator.slice(0, -1)} (`)
320
- appendRight()
321
- builder.append(')')
322
- })
323
- }
324
- writeEdits.push({
325
- start: path.node.start,
326
- end: path.node.end,
327
- ...builder.result(),
328
- })
329
- },
330
- UpdateExpression(path) {
331
- if (path.node.argument.type !== 'Identifier') return
332
- const binding = importedBindingFor(path, path.node.argument, imports)
333
- if (binding === undefined) return
334
- assertNoImportedWriteAncestor(path, imports)
335
- const member = importMember(binding.namespace, binding.imported)
336
- const builder = createMappedTextBuilder(code)
337
- builder.append('((__dsh_ptc_import_value__) => { ')
338
- builder.append(path.node.operator === '++' ? '__dsh_ptc_import_value__++' : '__dsh_ptc_import_value__--')
339
- builder.append("; throw new TypeError('Assignment to constant variable.'); })(")
340
- builder.appendMapped(member, path.node.argument.start, path.node.argument.end)
341
- builder.append(')')
342
- writeEdits.push({
343
- start: path.node.start,
344
- end: path.node.end,
345
- ...builder.result(),
346
- })
347
- },
348
- UnaryExpression(path) {
349
- if (path.node.operator !== 'delete' || path.node.argument.type !== 'Identifier') return
350
- if (importedBindingFor(path, path.node.argument, imports) !== undefined) {
351
- throw new ModuleRewriteError('delete of an imported binding is not supported')
352
- }
353
- },
354
- ForInStatement(path) {
355
- if (path.node.left.type === 'VariableDeclaration') return
356
- const names = bindingNodes(path.node.left)
357
- if (names.some(node => importedBindingFor(path, node, imports) !== undefined)) {
358
- throw new ModuleRewriteError('for-in assignment to an imported binding is not supported')
359
- }
360
- },
361
- ForOfStatement(path) {
362
- if (path.node.left.type === 'VariableDeclaration') return
363
- const names = bindingNodes(path.node.left)
364
- if (names.some(node => importedBindingFor(path, node, imports) !== undefined)) {
365
- throw new ModuleRewriteError('for-of assignment to an imported binding is not supported')
366
- }
367
- },
368
- })
369
- const written = applySourceEdits(code, sourceMap, writeEdits)
370
- file = parseRewritten(written.code)
371
- const readEdits = []
372
- traverse(file, {
373
- ReferencedIdentifier(path) {
374
- const binding = imports.get(path.node.name)
375
- if (binding === undefined || path.scope.getBinding(path.node.name) !== undefined) return
376
- const member = importMember(binding.namespace, binding.imported)
377
- const parent = path.parent
378
- if (((parent.type === 'CallExpression' || parent.type === 'OptionalCallExpression') && parent.callee === path.node)
379
- || (parent.type === 'TaggedTemplateExpression' && parent.tag === path.node)) {
380
- preserveExpressionStatementBoundary(path, readEdits)
381
- readEdits.push({ start: path.node.start, end: path.node.end, text: `(0, ${member})` })
382
- return
383
- }
384
- if (parent.type === 'ObjectProperty' && parent.shorthand && parent.value === path.node) {
385
- readEdits.push({ start: parent.start, end: parent.end, text: `${path.node.name}: ${member}` })
386
- return
387
- }
388
- readEdits.push({ start: path.node.start, end: path.node.end, text: member })
389
- },
390
- })
391
- return applySourceEdits(written.code, written.sourceMap, readEdits)
392
- }
393
-
394
- function exportNamedEdits(node, code, edits, rewrites, moduleLoads) {
395
- const onlyTypeSpecifiers = node.specifiers.length > 0
396
- && node.specifiers.every(specifier => specifier.exportKind === 'type')
397
- if (node.exportKind === 'type' || onlyTypeSpecifiers
398
- || (node.declaration && ['TSInterfaceDeclaration', 'TSTypeAliasDeclaration', 'TSDeclareFunction'].includes(node.declaration.type))) {
399
- editNode(edits, node, code)
400
- const source = node.exportKind === 'type' || onlyTypeSpecifiers ? 'type' : node.declaration.type
401
- rewrites.push(record('export', 'removed a type-only export declaration', node, source))
402
- return
403
- }
404
- if (node.source !== null && node.source !== undefined) {
405
- const source = parseSource(node)
406
- moduleLoads.push(moduleLoad(node, source))
407
- editNode(edits, node, code)
408
- rewrites.push(record('export', `converted the re-export of ${JSON.stringify(source)} into a side-effect import`, node, source))
409
- return
410
- }
411
- if (node.declaration !== null && node.declaration !== undefined) {
412
- edit(edits, node.start, node.declaration.start, preserveLines(code.slice(node.start, node.declaration.start)))
413
- rewrites.push(record('export', 'stripped the export modifier from a top-level declaration', node))
414
- return
415
- }
416
- editNode(edits, node, code)
417
- rewrites.push(record('export', 'removed a local re-export declaration', node))
418
- }
419
-
420
- function exportAllEdits(node, code, edits, rewrites, moduleLoads) {
421
- if (node.exportKind === 'type') {
422
- editNode(edits, node, code)
423
- rewrites.push(record('export', 'removed a type-only export declaration', node, 'type'))
424
- return
425
- }
426
- const source = parseSource(node)
427
- moduleLoads.push(moduleLoad(node, source))
428
- editNode(edits, node, code)
429
- rewrites.push(record('export', `converted the re-export of ${JSON.stringify(source)} into a side-effect import`, node, source))
430
- }
431
-
432
- function exportDefaultEdits(node, code, edits, rewrites, defaultNameAvailable, exportDeclarations) {
433
- const declaration = node.declaration
434
- if (declaration.type === 'TSInterfaceDeclaration') {
435
- editNode(edits, node, code)
436
- rewrites.push(record('export', 'removed a type-only export declaration', node, 'interface'))
437
- return
438
- }
439
- if (declaration.id?.name === '__default') {
440
- edit(edits, node.start, declaration.start, preserveLines(code.slice(node.start, declaration.start)))
441
- rewrites.push(record('export', 'stripped the export modifier from the __default declaration', node))
442
- return
443
- }
444
- if (!defaultNameAvailable) {
445
- throw new ModuleRewriteError(
446
- 'export default cannot be exposed as __default because that name is already declared',
447
- { line: node.loc.start.line, column: node.loc.start.column + 1 },
448
- )
449
- }
450
- exportDeclarations.push({
451
- name: '__default',
452
- kind: declaration.type === 'FunctionDeclaration'
453
- ? 'function'
454
- : declaration.type === 'ClassDeclaration' ? 'class' : 'variable',
455
- span: {
456
- line: node.loc.start.line,
457
- column: node.loc.start.column + 1,
458
- end: {
459
- line: node.loc.end.line,
460
- column: node.loc.end.column + 1,
461
- },
462
- },
463
- definitionSpan: {
464
- line: node.loc.start.line,
465
- column: node.loc.start.column + 1,
466
- end: {
467
- line: node.loc.end.line,
468
- column: node.loc.end.column + 1,
469
- },
470
- },
471
- original: true,
472
- })
473
- const rewrite = record('export', 'converted the default export into a local __default binding', node)
474
- if (declaration.type === 'FunctionDeclaration' && declaration.id !== null) {
475
- const prefix = code.slice(node.start, declaration.start)
476
- edit(edits, node.start, declaration.start, preserveLines(prefix, `const __default = ${declaration.id.name}; `))
477
- } else if (declaration.type === 'ClassDeclaration' && declaration.id !== null) {
478
- edit(edits, node.start, declaration.start, preserveLines(code.slice(node.start, declaration.start)))
479
- edit(edits, node.end, node.end, `; const __default = ${declaration.id.name};`)
480
- } else if (declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') {
481
- const prefix = code.slice(node.start, declaration.start)
482
- edit(edits, node.start, declaration.start, preserveLines(prefix, 'const __default = '))
483
- edit(edits, node.end, node.end, ';')
484
- } else {
485
- const prefix = code.slice(node.start, declaration.start)
486
- edit(edits, node.start, declaration.start, preserveLines(prefix, 'const __default = '))
487
- }
488
- rewrites.push(rewrite)
489
- }
490
-
491
- function ascending(left, right) { return left.at - right.at }
492
-
493
- export function rewriteModuleImportsExports(program, enabled, existingImports = new Map(), existingNamespaces = new Set(), unavailableNames = new Set()) {
494
- const tree = parseCell(program)
495
- const edits = []
496
- const rewrites = []
497
- const imports = new Map(existingImports)
498
- const importDeclarations = []
499
- const exportDeclarations = []
500
- const moduleLoads = []
501
- const importNamespaces = new Set(existingNamespaces)
502
- const allocateName = createGeneratedNameAllocator(tree, [...unavailableNames, ...importNamespaces])
503
- const generatedNamespaces = new Set()
504
- const allocateImportName = purpose => {
505
- const name = allocateName(purpose)
506
- importNamespaces.add(name)
507
- generatedNamespaces.add(name)
508
- return name
509
- }
510
- const namespaceCaptures = []
511
- const defaultNameAvailable = !tree.body.some((node) => {
512
- const declaration = node.type === 'ExportNamedDeclaration' ? node.declaration : node
513
- if (declaration?.type === 'VariableDeclaration' && declaration.declare !== true) {
514
- return declaration.declarations.some(entry => bindingNodes(entry.id).some(binding => binding.name === '__default'))
515
- }
516
- if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
517
- return declaration.id?.name === '__default'
518
- }
519
- return node.type === 'ImportDeclaration' && node.importKind !== 'type' && node.specifiers.some(specifier => (
520
- specifier.importKind !== 'type' && specifier.local.name === '__default'
521
- ))
522
- })
523
- for (const node of tree.body) {
524
- if (node.type === 'ImportDeclaration' && enabled.autoRewriteImports) {
525
- importEdits(node, program, edits, rewrites, imports, importDeclarations,
526
- moduleLoads, namespaceCaptures, allocateImportName, allocateName)
527
- }
528
- else if (node.type === 'ExportNamedDeclaration' && enabled.autoStripExports) {
529
- exportNamedEdits(node, program, edits, rewrites, moduleLoads)
530
- }
531
- else if (node.type === 'ExportAllDeclaration' && enabled.autoStripExports) {
532
- exportAllEdits(node, program, edits, rewrites, moduleLoads)
533
- }
534
- else if (node.type === 'ExportDefaultDeclaration' && enabled.autoStripExports) {
535
- exportDefaultEdits(node, program, edits, rewrites, defaultNameAvailable, exportDeclarations)
536
- }
537
- }
538
- assertNoDynamicImportResolution(tree, imports)
539
- const prologue = importPrologueEdit(program, tree, namespaceCaptures)
540
- if (prologue !== undefined) edits.push(prologue)
541
- const applied = applySourceEdits(program, identitySourceMap(program.length), edits)
542
- const rewritten = rewriteImportReferences(applied.code, applied.sourceMap, imports)
543
- rewrites.sort(ascending)
544
- return {
545
- ...rewritten,
546
- imports,
547
- importNamespaces,
548
- generatedNamespaces,
549
- importDeclarations,
550
- exportDeclarations,
551
- moduleLoads,
552
- rewrites: rewrites.map(({ at: _at, ...rest }) => rest),
553
- }
554
- }
4
+ export { rewriteModuleImportsExports } from './legacy-cell-rewriter.js'