Skip to content
File

Blob: types/src/transforms/helpers.ts

typescript157 lines
1// Copyright (c) 2022-2023 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 ts from "typescript";
7import { printNode } from "../print";
8 
9// Checks whether the modifiers array contains a modifier of the specified kind
10export function hasModifier(
11 modifiers: ReadonlyArray<ts.Modifier> | undefined,
12 kind: ts.Modifier["kind"]
13): boolean {
14 if (modifiers === undefined) return false;
15 return modifiers.some((modifier) => modifier.kind === kind);
16}
17 
18// Ensure a modifiers array has the specified modifier, inserting it at the
19// start if it doesn't.
20export function ensureModifier(
21 ctx: ts.TransformationContext,
22 modifiers: ReadonlyArray<ts.Modifier> | undefined,
23 ensure: ts.SyntaxKind.ExportKeyword | ts.SyntaxKind.DeclareKeyword
24): ReadonlyArray<ts.Modifier> {
25 // If modifiers already contains the required modifier, return it as is...
26 if (modifiers !== undefined && hasModifier(modifiers, ensure)) {
27 return modifiers;
28 }
29 // ...otherwise, add the modifier to the start of the array
30 return [ctx.factory.createToken(ensure), ...(modifiers ?? [])];
31}
32 
33// Ensure a modifiers array doesn't have the specified modifier
34export function ensureNoModifier(
35 _ctx: ts.TransformationContext,
36 modifiers: ReadonlyArray<ts.Modifier> | undefined,
37 ensure: ts.SyntaxKind.ExportKeyword | ts.SyntaxKind.DeclareKeyword
38): ReadonlyArray<ts.Modifier> {
39 // If modifiers already doesn't contain the required modifier, return it as is...
40 if (modifiers !== undefined && !hasModifier(modifiers, ensure)) {
41 return modifiers;
42 }
43 // ...otherwise, remove the modifier
44 return modifiers?.filter((m) => m.kind !== ensure) ?? [];
45}
46 
47export interface ModifierRequirements {
48 export?: boolean;
49 declare?: boolean;
50}
51// Ensures a node satisfies the specified modifier requirements
52function ensureModifierRequirements(
53 ctx: ts.TransformationContext,
54 node: ts.HasModifiers,
55 reqs: ModifierRequirements
56): ReadonlyArray<ts.Modifier> | undefined {
57 let modifiers = ts.getModifiers(node);
58 if (reqs.declare !== undefined) {
59 modifiers = (reqs.declare ? ensureModifier : ensureNoModifier)(
60 ctx,
61 modifiers,
62 ts.SyntaxKind.DeclareKeyword
63 );
64 }
65 if (reqs.export !== undefined) {
66 modifiers = (reqs.export ? ensureModifier : ensureNoModifier)(
67 ctx,
68 modifiers,
69 ts.SyntaxKind.ExportKeyword
70 );
71 }
72 return modifiers;
73}
74 
75// Make sure replacement node is `export`ed, with the `declare` modifier if it's
76// a class, variable or function declaration.
77// If the `noExport` option is set, only ensure `declare` modifiers
78export function ensureStatementModifiers(
79 ctx: ts.TransformationContext,
80 node: ts.Node,
81 reqs: ModifierRequirements
82): ts.Statement {
83 if (ts.isClassDeclaration(node)) {
84 return ctx.factory.updateClassDeclaration(
85 node,
86 ensureModifierRequirements(ctx, node, reqs),
87 node.name,
88 node.typeParameters,
89 node.heritageClauses,
90 node.members
91 );
92 }
93 if (ts.isInterfaceDeclaration(node)) {
94 return ctx.factory.updateInterfaceDeclaration(
95 node,
96 ensureModifierRequirements(ctx, node, { ...reqs, declare: undefined }),
97 node.name,
98 node.typeParameters,
99 node.heritageClauses,
100 node.members
101 );
102 }
103 if (ts.isEnumDeclaration(node)) {
104 return ctx.factory.updateEnumDeclaration(
105 node,
106 ensureModifierRequirements(ctx, node, reqs),
107 node.name,
108 node.members
109 );
110 }
111 if (ts.isTypeAliasDeclaration(node)) {
112 return ctx.factory.updateTypeAliasDeclaration(
113 node,
114 ensureModifierRequirements(ctx, node, { ...reqs, declare: undefined }),
115 node.name,
116 node.typeParameters,
117 node.type
118 );
119 }
120 if (ts.isVariableStatement(node)) {
121 return ctx.factory.updateVariableStatement(
122 node,
123 ensureModifierRequirements(ctx, node, reqs),
124 node.declarationList
125 );
126 }
127 if (ts.isFunctionDeclaration(node)) {
128 return ctx.factory.updateFunctionDeclaration(
129 node,
130 ensureModifierRequirements(ctx, node, reqs),
131 node.asteriskToken,
132 node.name,
133 node.typeParameters,
134 node.parameters,
135 node.type,
136 node.body
137 );
138 }
139 if (ts.isModuleDeclaration(node)) {
140 return ctx.factory.updateModuleDeclaration(
141 node,
142 ensureModifierRequirements(ctx, node, reqs),
143 node.name,
144 node.body
145 );
146 }
147 if (
148 ts.isImportDeclaration(node) ||
149 ts.isImportEqualsDeclaration(node) ||
150 ts.isExportDeclaration(node) ||
151 ts.isExportAssignment(node)
152 ) {
153 return node;
154 }
155 assert.fail(`Expected statement, got "${printNode(node)}"`);
156}