-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
generateTsDefs.py
executable file
·137 lines (114 loc) · 4.96 KB
/
generateTsDefs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python
#
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Generates TypeScript defs from Closure externs.
This uses the Clutz tool from https://github.com/angular/clutz and then performs
additional transformations to make the generated defs independent of Clutz.
"""
import argparse
import os
import re
import sys
import shakaBuildHelpers
def GenerateTsDefs(inputs, output):
"""Generates TypeScript defs from Closure externs.
Args:
inputs: A list of paths to Closure extern files.
output: A path to a TypeScript def output file.
"""
clutz = shakaBuildHelpers.get_node_binary('@teppeis/clutz', 'clutz')
command = clutz + [
'--closure_env', 'BROWSER',
'--externs',
] + inputs + [
'-o', '-',
]
# Get the output of Clutz, then transform it to make it independent of Clutz
# and usable directly in TypeScript projects.
contents = shakaBuildHelpers.execute_get_output(command)
# Remove the prefix clutz puts on all namespaces.
contents = contents.replace(b'\xe0\xb2\xa0_\xe0\xb2\xa0.clutz.', b'')
# Replace "GlobalObject" (from Clutz) with TypeScript-native "object".
contents = re.sub(br'\bGlobalObject\b', b'object', contents)
# Remove "Global" from Clutz's Global{Date,Element,Event,EventTarget} and use
# their original definitions instead.
contents = re.sub(
br'\bGlobal(Date|Element|Event|EventTarget)\b', br'\1', contents)
# Remove "protected", which appears on some fields, and is only supported on
# methods.
contents = re.sub(br'^\s*protected ', b'', contents, flags=re.MULTILINE)
# Clutz really likes EventTargets, so it wants IAdManager to extend it twice.
contents = contents.replace(
b'extends EventTarget extends EventTarget',
b'extends EventTarget')
# Some types allow you to return a Promise or nothing, but the Clutz output of
# "Promise | null | undefined" doesn't work in TypeScript. We need to use
# "Promise | void" instead.
contents = contents.replace(b'| null | undefined', b'| void')
# shaka.util.Error extends Error and implements shaka.extern.Error, but this
# confuses TypeScript when both are called "Error" in context of the namespace
# declaration for "shaka.util". Therefore we need to declare this
# "GlobalError" type, which is already referenced by Clutz but never defined.
global_error_def = b'declare class GlobalError extends Error {}\n\n'
contents = global_error_def + contents
# There are some types that implement multiple interfaces, such as IReleasable
# and Iteratable. Also, there are tools used inside Google that (for some
# reason) want to convert TS defs _BACK_ into Closure Compiler externs. When
# that happens, these multiple implementors generate externs with broken
# @implements annotations. Since this team can't control that process or
# those tools, we need to adjust our TS defs instead. In these particular
# cases, thankfully, we can just remove the reference to the IReleasable
# interface, which is not needed outside the library.
# TODO: This only covers one very specific pattern, and could be brittle.
contents = contents.replace(
b'implements shaka.util.IReleasable , ', b'implements ')
# Finally, Clutz includes a bunch of basic defs for a browser environment
# generated from Closure compiler's builtins. Remove these.
sections = re.split(br'\n(?=// Generated from .*)', contents)
sections = filter(
lambda s: not s.startswith(b'// Generated from externs.zip'),
sections)
contents = b'\n'.join(sections) + b'\n'
license_header_path = os.path.join(
shakaBuildHelpers.get_source_base(), 'build/license-header')
with open(license_header_path, 'rb') as f:
license_header = f.read()
with open(output, 'wb') as f:
f.write(license_header)
f.write(b'\n')
f.write(contents)
def CreateParser():
"""Create the argument parser for this application."""
base = shakaBuildHelpers.get_source_base()
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'--output',
type=str,
help='The file path for TypeScripts defs output')
parser.add_argument(
'input',
type=str,
nargs='+',
help='The Closure extern files to be converted to TypeScript defs')
return parser
def main(args):
parser = CreateParser()
args = parser.parse_args(args)
GenerateTsDefs(args.input, args.output)
return 0
if __name__ == '__main__':
main(sys.argv[1:])