1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Generator = void 0;
|
4 | const spec = require("@jsii/spec");
|
5 | const clone = require("clone");
|
6 | const codemaker_1 = require("codemaker");
|
7 | const crypto = require("crypto");
|
8 | const fs = require("fs-extra");
|
9 | const path = require("path");
|
10 | const version_1 = require("./version");
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | class Generator {
|
16 | constructor(options) {
|
17 | this.options = options;
|
18 | this.excludeTypes = new Array();
|
19 | this.code = new codemaker_1.CodeMaker();
|
20 | }
|
21 | get runtimeTypeChecking() {
|
22 | return this.options.runtimeTypeChecking;
|
23 | }
|
24 | get assembly() {
|
25 | if (!this._assembly) {
|
26 | throw new Error('No assembly has been loaded! The #load() method must be called first!');
|
27 | }
|
28 | return this._assembly;
|
29 | }
|
30 | get reflectAssembly() {
|
31 | if (!this._reflectAssembly) {
|
32 | throw new Error('Call load() first');
|
33 | }
|
34 | return this._reflectAssembly;
|
35 | }
|
36 | get metadata() {
|
37 | return { fingerprint: this.fingerprint };
|
38 | }
|
39 | async load(_packageRoot, assembly) {
|
40 | this._reflectAssembly = assembly;
|
41 | this._assembly = assembly.spec;
|
42 |
|
43 | this.fingerprint = crypto
|
44 | .createHash('sha256')
|
45 | .update(version_1.VERSION_DESC)
|
46 | .update('\0')
|
47 | .update(this.assembly.fingerprint)
|
48 | .digest('base64');
|
49 | return Promise.resolve();
|
50 | }
|
51 | |
52 |
|
53 |
|
54 | generate(fingerprint) {
|
55 | this.onBeginAssembly(this.assembly, fingerprint);
|
56 | this.visit(spec.NameTree.of(this.assembly));
|
57 | this.onEndAssembly(this.assembly, fingerprint);
|
58 | }
|
59 | async upToDate(_) {
|
60 | return Promise.resolve(false);
|
61 | }
|
62 | |
63 |
|
64 |
|
65 | getAssemblyFileName() {
|
66 | let name = this.assembly.name;
|
67 | const parts = name.split('/');
|
68 | if (parts.length === 1) {
|
69 | name = parts[0];
|
70 | }
|
71 | else if (parts.length === 2 && parts[0].startsWith('@')) {
|
72 | name = parts[1];
|
73 | }
|
74 | else {
|
75 | throw new Error('Malformed assembly name. Expecting either <name> or @<scope>/<name>');
|
76 | }
|
77 | return `${name}@${this.assembly.version}.jsii.tgz`;
|
78 | }
|
79 | |
80 |
|
81 |
|
82 | async save(outdir, tarball, { license, notice }) {
|
83 | const assemblyDir = this.getAssemblyOutputDir(this.assembly);
|
84 | if (assemblyDir) {
|
85 | const fullPath = path.resolve(path.join(outdir, assemblyDir, this.getAssemblyFileName()));
|
86 | await fs.mkdirp(path.dirname(fullPath));
|
87 | await fs.copy(tarball, fullPath, { overwrite: true });
|
88 | if (license) {
|
89 | await fs.writeFile(path.resolve(outdir, 'LICENSE'), license, {
|
90 | encoding: 'utf8',
|
91 | });
|
92 | }
|
93 | if (notice) {
|
94 | await fs.writeFile(path.resolve(outdir, 'NOTICE'), notice, {
|
95 | encoding: 'utf8',
|
96 | });
|
97 | }
|
98 | }
|
99 | return this.code.save(outdir);
|
100 | }
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | |
106 |
|
107 |
|
108 | getAssemblyOutputDir(_mod) {
|
109 | return undefined;
|
110 | }
|
111 |
|
112 |
|
113 | onBeginAssembly(_assm, _fingerprint) {
|
114 |
|
115 | }
|
116 | onEndAssembly(_assm, _fingerprint) {
|
117 |
|
118 | }
|
119 |
|
120 |
|
121 | onBeginNamespace(_ns) {
|
122 |
|
123 | }
|
124 | onEndNamespace(_ns) {
|
125 |
|
126 | }
|
127 |
|
128 |
|
129 | onBeginClass(_cls, _abstract) {
|
130 |
|
131 | }
|
132 | onEndClass(_cls) {
|
133 |
|
134 | }
|
135 |
|
136 |
|
137 | onInitializer(_cls, _initializer) {
|
138 |
|
139 | }
|
140 | onInitializerOverload(_cls, _overload, _originalInitializer) {
|
141 |
|
142 | }
|
143 |
|
144 |
|
145 | onBeginProperties(_cls) {
|
146 |
|
147 | }
|
148 | onEndProperties(_cls) {
|
149 |
|
150 | }
|
151 | onExpandedUnionProperty(_cls, _prop, _primaryName) {
|
152 | return;
|
153 | }
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | onBeginMethods(_cls) {
|
159 |
|
160 | }
|
161 | onEndMethods(_cls) {
|
162 |
|
163 | }
|
164 |
|
165 |
|
166 | onBeginEnum(_enm) {
|
167 |
|
168 | }
|
169 | onEndEnum(_enm) {
|
170 |
|
171 | }
|
172 | onEnumMember(_enm, _member) {
|
173 |
|
174 | }
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | hasField(_cls, _prop) {
|
180 | return false;
|
181 | }
|
182 | onField(_cls, _prop, _union) {
|
183 |
|
184 | }
|
185 | visit(node, names = new Array()) {
|
186 | const namespace = !node.fqn && names.length > 0 ? names.join('.') : undefined;
|
187 | if (namespace) {
|
188 | this.onBeginNamespace(namespace);
|
189 | }
|
190 | const visitChildren = () => {
|
191 | Object.keys(node.children)
|
192 | .sort()
|
193 | .forEach((name) => {
|
194 | this.visit(node.children[name], names.concat(name));
|
195 | });
|
196 | };
|
197 | if (node.fqn) {
|
198 | const type = this.assembly.types?.[node.fqn];
|
199 | if (!type) {
|
200 | throw new Error(`Malformed jsii file. Cannot find type: ${node.fqn}`);
|
201 | }
|
202 | if (!this.shouldExcludeType(type.name)) {
|
203 | switch (type.kind) {
|
204 | case spec.TypeKind.Class:
|
205 | const classSpec = type;
|
206 | const abstract = classSpec.abstract;
|
207 | if (abstract && this.options.addBasePostfixToAbstractClassNames) {
|
208 | this.addAbstractPostfixToClassName(classSpec);
|
209 | }
|
210 | this.onBeginClass(classSpec, abstract);
|
211 | this.visitClass(classSpec);
|
212 | visitChildren();
|
213 | this.onEndClass(classSpec);
|
214 | break;
|
215 | case spec.TypeKind.Enum:
|
216 | const enumSpec = type;
|
217 | this.onBeginEnum(enumSpec);
|
218 | this.visitEnum(enumSpec);
|
219 | visitChildren();
|
220 | this.onEndEnum(enumSpec);
|
221 | break;
|
222 | case spec.TypeKind.Interface:
|
223 | const interfaceSpec = type;
|
224 | this.onBeginInterface(interfaceSpec);
|
225 | this.visitInterface(interfaceSpec);
|
226 | visitChildren();
|
227 | this.onEndInterface(interfaceSpec);
|
228 | break;
|
229 | default:
|
230 | throw new Error(`Unsupported type kind: ${type.kind}`);
|
231 | }
|
232 | }
|
233 | }
|
234 | else {
|
235 | visitChildren();
|
236 | }
|
237 | if (namespace) {
|
238 | this.onEndNamespace(namespace);
|
239 | }
|
240 | }
|
241 | |
242 |
|
243 |
|
244 | addAbstractPostfixToClassName(cls) {
|
245 | cls.name = `${cls.name}Base`;
|
246 | const components = cls.fqn.split('.');
|
247 | cls.fqn = components
|
248 | .map((x, i) => (i < components.length - 1 ? x : `${x}Base`))
|
249 | .join('.');
|
250 | }
|
251 | excludeType(...names) {
|
252 | for (const n of names) {
|
253 | this.excludeTypes.push(n);
|
254 | }
|
255 | }
|
256 | shouldExcludeType(name) {
|
257 | return this.excludeTypes.includes(name);
|
258 | }
|
259 | |
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 | createOverloadsForOptionals(method) {
|
269 | const overloads = new Array();
|
270 |
|
271 | if (!this.options.generateOverloadsForMethodWithOptionals ||
|
272 | !method.parameters) {
|
273 | return overloads;
|
274 | }
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 | const remaining = clone(method.parameters);
|
281 | let next;
|
282 | next = remaining.pop();
|
283 |
|
284 | while (next?.optional) {
|
285 |
|
286 | const cloned = clone(method);
|
287 | cloned.parameters = clone(remaining);
|
288 | overloads.push(cloned);
|
289 |
|
290 | next = remaining.pop();
|
291 | }
|
292 | return overloads;
|
293 | }
|
294 | visitInterface(ifc) {
|
295 | if (ifc.properties) {
|
296 | ifc.properties.forEach((prop) => {
|
297 | this.onInterfaceProperty(ifc, prop);
|
298 | });
|
299 | }
|
300 | if (ifc.methods) {
|
301 | ifc.methods.forEach((method) => {
|
302 | this.onInterfaceMethod(ifc, method);
|
303 | for (const overload of this.createOverloadsForOptionals(method)) {
|
304 | this.onInterfaceMethodOverload(ifc, overload, method);
|
305 | }
|
306 | });
|
307 | }
|
308 | }
|
309 | visitClass(cls) {
|
310 | const initializer = cls.initializer;
|
311 | if (initializer) {
|
312 | this.onInitializer(cls, initializer);
|
313 |
|
314 | for (const overload of this.createOverloadsForOptionals(initializer)) {
|
315 | this.onInitializerOverload(cls, overload, initializer);
|
316 | }
|
317 | }
|
318 |
|
319 | if (cls.methods) {
|
320 | this.onBeginMethods(cls);
|
321 | cls.methods.forEach((method) => {
|
322 | if (!method.static) {
|
323 | this.onMethod(cls, method);
|
324 | for (const overload of this.createOverloadsForOptionals(method)) {
|
325 | this.onMethodOverload(cls, overload, method);
|
326 | }
|
327 | }
|
328 | else {
|
329 | this.onStaticMethod(cls, method);
|
330 | for (const overload of this.createOverloadsForOptionals(method)) {
|
331 | this.onStaticMethodOverload(cls, overload, method);
|
332 | }
|
333 | }
|
334 | });
|
335 | this.onEndMethods(cls);
|
336 | }
|
337 | if (cls.properties) {
|
338 | this.onBeginProperties(cls);
|
339 | cls.properties.forEach((prop) => {
|
340 | if (this.hasField(cls, prop)) {
|
341 | this.onField(cls, prop, spec.isUnionTypeReference(prop.type) ? prop.type : undefined);
|
342 | }
|
343 | });
|
344 | cls.properties.forEach((prop) => {
|
345 | if (!spec.isUnionTypeReference(prop.type)) {
|
346 | if (!prop.static) {
|
347 | this.onProperty(cls, prop);
|
348 | }
|
349 | else {
|
350 | this.onStaticProperty(cls, prop);
|
351 | }
|
352 | }
|
353 | else {
|
354 |
|
355 |
|
356 |
|
357 |
|
358 | this.onUnionProperty(cls, prop, prop.type);
|
359 |
|
360 | if (this.options.expandUnionProperties) {
|
361 | for (const [index, type] of prop.type.union.types.entries()) {
|
362 |
|
363 | const propClone = clone(prop);
|
364 | const primary = this.isPrimaryExpandedUnionProperty(prop.type, index);
|
365 | const propertyName = primary
|
366 | ? prop.name
|
367 | : `${prop.name}As${this.displayNameForType(type)}`;
|
368 | propClone.type = type;
|
369 | propClone.optional = prop.optional;
|
370 | propClone.name = propertyName;
|
371 | this.onExpandedUnionProperty(cls, propClone, prop.name);
|
372 | }
|
373 | }
|
374 | }
|
375 | });
|
376 | this.onEndProperties(cls);
|
377 | }
|
378 | }
|
379 | |
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 | isPrimaryExpandedUnionProperty(ref, index) {
|
389 | if (!ref) {
|
390 | return false;
|
391 | }
|
392 | return (index ===
|
393 | ref.union.types.findIndex((t) => {
|
394 | if (spec.isPrimitiveTypeReference(t)) {
|
395 | return true;
|
396 | }
|
397 | return false;
|
398 | }));
|
399 | }
|
400 | visitEnum(enumSpec) {
|
401 | if (enumSpec.members) {
|
402 | enumSpec.members.forEach((spec) => this.onEnumMember(enumSpec, spec));
|
403 | }
|
404 | }
|
405 | displayNameForType(type) {
|
406 |
|
407 | if (spec.isNamedTypeReference(type)) {
|
408 | const comps = type.fqn.split('.');
|
409 | const last = comps[comps.length - 1];
|
410 | return this.code.toPascalCase(last);
|
411 | }
|
412 |
|
413 | if (spec.isPrimitiveTypeReference(type)) {
|
414 | return this.code.toPascalCase(type.primitive);
|
415 | }
|
416 |
|
417 | const coll = spec.isCollectionTypeReference(type) && type.collection;
|
418 | if (coll) {
|
419 | return `${this.code.toPascalCase(coll.kind)}Of${this.displayNameForType(coll.elementtype)}`;
|
420 | }
|
421 | const union = spec.isUnionTypeReference(type) && type.union;
|
422 | if (union) {
|
423 | return union.types.map((t) => this.displayNameForType(t)).join('Or');
|
424 | }
|
425 | throw new Error(`Cannot determine display name for type: ${JSON.stringify(type)}`);
|
426 | }
|
427 | |
428 |
|
429 |
|
430 |
|
431 | findModule(name) {
|
432 |
|
433 | if (this.assembly.name === name) {
|
434 | return this.assembly;
|
435 | }
|
436 | const found = (this.assembly.dependencyClosure ?? {})[name];
|
437 | if (found) {
|
438 | return found;
|
439 | }
|
440 | throw new Error(`Unable to find module ${name} as a dependency of ${this.assembly.name}`);
|
441 | }
|
442 | findType(fqn) {
|
443 | const ret = this.reflectAssembly.system.tryFindFqn(fqn);
|
444 | if (!ret) {
|
445 | throw new Error(`Cannot find type '${fqn}' either as internal or external type`);
|
446 | }
|
447 | return ret.spec;
|
448 | }
|
449 | }
|
450 | exports.Generator = Generator;
|
451 |
|
\ | No newline at end of file |