File
Blob: types/src/index.ts
| 1 | // Copyright (c) 2026 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | import assert from 'node:assert'; |
| 6 | import { StructureGroups } from '@workerd/jsg/rtti'; |
| 7 | import ts from 'typescript'; |
| 8 | import { generateDefinitions } from './generator'; |
| 9 | import { printNodeList, printer } from './print'; |
| 10 | import { SourcesMap, createMemoryProgram } from './program'; |
| 11 | import { |
| 12 | CommentsData, |
| 13 | compileOverridesDefines, |
| 14 | createAmbientTransformer, |
| 15 | createCommentsTransformer, |
| 16 | createGlobalScopeTransformer, |
| 17 | createImportResolveTransformer, |
| 18 | createImportableTransformer, |
| 19 | createIteratorTransformer, |
| 20 | createOverrideDefineTransformer, |
| 21 | } from './transforms'; |
| 22 | import { createClassToInterfaceTransformer } from './transforms/class-to-interface'; |
| 23 | import { createAddOnMessageDeclarationTransformer } from './transforms/onmessage-declaration'; |
| 24 | |
| 25 | const definitionsHeader = `/*! ***************************************************************************** |
| 26 | Copyright (c) Cloudflare. All rights reserved. |
| 27 | Copyright (c) Microsoft Corporation. All rights reserved. |
| 28 | |
| 29 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use |
| 30 | this file except in compliance with the License. You may obtain a copy of the |
| 31 | License at http://www.apache.org/licenses/LICENSE-2.0 |
| 32 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 33 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED |
| 34 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, |
| 35 | MERCHANTABLITY OR NON-INFRINGEMENT. |
| 36 | See the Apache Version 2.0 License for specific language governing permissions |
| 37 | and limitations under the License. |
| 38 | ***************************************************************************** */ |
| 39 | /* eslint-disable */ |
| 40 | // noinspection JSUnusedGlobalSymbols |
| 41 | `; |
| 42 | |
| 43 | function transform( |
| 44 | sources: SourcesMap, |
| 45 | sourcePath: string, |
| 46 | transforms: ( |
| 47 | program: ts.Program, |
| 48 | checker: ts.TypeChecker |
| 49 | ) => ts.TransformerFactory<ts.SourceFile>[] |
| 50 | ): string { |
| 51 | const program = createMemoryProgram(sources); |
| 52 | const checker = program.getTypeChecker(); |
| 53 | const sourceFile = program.getSourceFile(sourcePath); |
| 54 | assert(sourceFile !== undefined); |
| 55 | const result = ts.transform(sourceFile, transforms(program, checker)); |
| 56 | assert.strictEqual(result.transformed.length, 1); |
| 57 | return printer.printFile(result.transformed[0]); |
| 58 | } |
| 59 | |
| 60 | export function printDefinitions( |
| 61 | root: StructureGroups, |
| 62 | commentData: CommentsData, |
| 63 | extraDefinitions: string |
| 64 | ): { ambient: string; importable: string } { |
| 65 | // Generate TypeScript nodes from capnp request |
| 66 | const { nodes } = generateDefinitions(root); |
| 67 | |
| 68 | // Assemble partial overrides and defines to valid TypeScript source files |
| 69 | const [sources, replacements] = compileOverridesDefines(root); |
| 70 | // Add source file containing generated nodes |
| 71 | const sourcePath = '/$virtual/source.ts'; |
| 72 | let source = printNodeList(nodes); |
| 73 | sources.set(sourcePath, printNodeList(nodes)); |
| 74 | |
| 75 | // Run post-processing transforms on program |
| 76 | source = transform(sources, sourcePath, (program, checker) => [ |
| 77 | // Run iterator transformer before overrides so iterator-like interfaces are |
| 78 | // still removed if they're replaced in overrides |
| 79 | createIteratorTransformer(checker), |
| 80 | createOverrideDefineTransformer(program, replacements), |
| 81 | // Run global scope transformer after overrides so members added in |
| 82 | // overrides are extracted |
| 83 | createGlobalScopeTransformer(checker), |
| 84 | createClassToInterfaceTransformer(['Request', 'Response', 'WebSocket']), |
| 85 | // TODO: enable this once we've figured out how not to expose internal modules |
| 86 | // createInternalNamespaceTransformer(root, structureMap), |
| 87 | createCommentsTransformer(commentData), |
| 88 | createAddOnMessageDeclarationTransformer(), |
| 89 | ]); |
| 90 | |
| 91 | // TODO: enable this once we've figured out how not to expose internal modules |
| 92 | // source += collectTypeScriptModules(root) + extraDefinitions; |
| 93 | source += extraDefinitions; |
| 94 | |
| 95 | // We need the type checker to respect our updated definitions after applying |
| 96 | // overrides (e.g. to find the correct nodes when traversing heritage), so |
| 97 | // rebuild the program to re-run type checking. We also want to include our |
| 98 | // additional definitions. |
| 99 | source = transform(new SourcesMap([[sourcePath, source]]), sourcePath, () => [ |
| 100 | createImportResolveTransformer(), |
| 101 | createAmbientTransformer(), |
| 102 | ]); |
| 103 | |
| 104 | const importable = transform( |
| 105 | new SourcesMap([[sourcePath, source]]), |
| 106 | sourcePath, |
| 107 | () => [createImportableTransformer()] |
| 108 | ); |
| 109 | |
| 110 | // Print program to string |
| 111 | return { |
| 112 | ambient: definitionsHeader + source, |
| 113 | importable: definitionsHeader + importable, |
| 114 | }; |
| 115 | } |