Skip to content
File

Blob: types/src/index.ts

typescript116 lines
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 
5import assert from 'node:assert';
6import { StructureGroups } from '@workerd/jsg/rtti';
7import ts from 'typescript';
8import { generateDefinitions } from './generator';
9import { printNodeList, printer } from './print';
10import { SourcesMap, createMemoryProgram } from './program';
11import {
12 CommentsData,
13 compileOverridesDefines,
14 createAmbientTransformer,
15 createCommentsTransformer,
16 createGlobalScopeTransformer,
17 createImportResolveTransformer,
18 createImportableTransformer,
19 createIteratorTransformer,
20 createOverrideDefineTransformer,
21} from './transforms';
22import { createClassToInterfaceTransformer } from './transforms/class-to-interface';
23import { createAddOnMessageDeclarationTransformer } from './transforms/onmessage-declaration';
24 
25const definitionsHeader = `/*! *****************************************************************************
26Copyright (c) Cloudflare. All rights reserved.
27Copyright (c) Microsoft Corporation. All rights reserved.
28
29Licensed under the Apache License, Version 2.0 (the "License"); you may not use
30this file except in compliance with the License. You may obtain a copy of the
31License at http://www.apache.org/licenses/LICENSE-2.0
32THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
34WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
35MERCHANTABLITY OR NON-INFRINGEMENT.
36See the Apache Version 2.0 License for specific language governing permissions
37and limitations under the License.
38***************************************************************************** */
39/* eslint-disable */
40// noinspection JSUnusedGlobalSymbols
41`;
42 
43function 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 
60export 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}