UNPKG

5.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const errors_1 = require("../errors");
4const locations_1 = require("../locations");
5const types_1 = require("../types");
6const util_1 = require("../util");
7const config_parser_1 = require("./config-parser");
8const endpoint_parser_1 = require("./endpoint-parser");
9const parser_helpers_1 = require("./parser-helpers");
10const security_header_parser_1 = require("./security-header-parser");
11/**
12 * Parse a root source file to return a contract.
13 */
14function parseContract(file) {
15 const typeTable = new types_1.TypeTable();
16 const lociTable = new locations_1.LociTable();
17 const klass = parser_helpers_1.getClassWithDecoratorOrThrow(file, "api");
18 const decorator = klass.getDecoratorOrThrow("api");
19 const decoratorConfig = parser_helpers_1.getDecoratorConfigOrThrow(decorator);
20 // Handle name
21 const nameProp = parser_helpers_1.getObjLiteralPropOrThrow(decoratorConfig, "name");
22 const nameLiteral = parser_helpers_1.getPropValueAsStringOrThrow(nameProp);
23 const name = nameLiteral.getLiteralText().trim();
24 if (name.length === 0) {
25 return util_1.err(new errors_1.ParserError("api name cannot be empty", {
26 file: nameLiteral.getSourceFile().getFilePath(),
27 position: nameLiteral.getPos()
28 }));
29 }
30 if (!/^[\w\s-]*$/.test(name)) {
31 return util_1.err(new errors_1.ParserError("api name may only contain alphanumeric, space, underscore and hyphen characters", {
32 file: nameLiteral.getSourceFile().getFilePath(),
33 position: nameLiteral.getPos()
34 }));
35 }
36 // Handle description
37 const descriptionDoc = parser_helpers_1.getJsDoc(klass);
38 const description = descriptionDoc === null || descriptionDoc === void 0 ? void 0 : descriptionDoc.getDescription().trim();
39 // Handle config
40 const configResult = resolveConfig(klass);
41 if (configResult.isErr())
42 return configResult;
43 const config = configResult.unwrap();
44 // Handle security
45 const securityHeaderProp = parser_helpers_1.getPropertyWithDecorator(klass, "securityHeader");
46 const securityResult = securityHeaderProp &&
47 security_header_parser_1.parseSecurityHeader(securityHeaderProp, typeTable, lociTable);
48 if (securityResult && securityResult.isErr())
49 return securityResult;
50 const security = securityResult === null || securityResult === void 0 ? void 0 : securityResult.unwrap();
51 // Add location data
52 lociTable.addMorphNode(locations_1.LociTable.apiClassKey(), klass);
53 lociTable.addMorphNode(locations_1.LociTable.apiDecoratorKey(), decorator);
54 lociTable.addMorphNode(locations_1.LociTable.apiNameKey(), nameProp);
55 if (descriptionDoc) {
56 lociTable.addMorphNode(locations_1.LociTable.apiDescriptionKey(), descriptionDoc);
57 }
58 // Resolve all related files
59 const projectFiles = parser_helpers_1.getSelfAndLocalDependencies(file);
60 // Parse all endpoints
61 const endpointClasses = projectFiles.reduce((acc, currentFile) => acc.concat(currentFile
62 .getClasses()
63 .filter(k => k.getDecorator("endpoint") !== undefined)), []);
64 const endpointsResult = extractEndpoints(endpointClasses, typeTable, lociTable);
65 if (endpointsResult.isErr())
66 return endpointsResult;
67 const endpoints = endpointsResult.unwrap();
68 // Handle Types
69 const types = typeTable.toArray();
70 const contract = { name, description, types, config, security, endpoints };
71 return util_1.ok({ contract, lociTable });
72}
73exports.parseContract = parseContract;
74function resolveConfig(klass) {
75 const hasConfigDecorator = klass.getDecorator("config") !== undefined;
76 if (hasConfigDecorator) {
77 return config_parser_1.parseConfig(klass);
78 }
79 else {
80 return util_1.ok(config_parser_1.defaultConfig());
81 }
82}
83function extractEndpoints(endpointClasses, typeTable, lociTable) {
84 const endpointNames = endpointClasses.map(k => k.getNameOrThrow());
85 const duplicateEndpointNames = [
86 ...new Set(endpointNames.filter((name, index) => endpointNames.indexOf(name) !== index))
87 ];
88 if (duplicateEndpointNames.length !== 0) {
89 const locations = duplicateEndpointNames.reduce((acc, name) => {
90 const nameLocations = endpointClasses
91 .filter(k => k.getNameOrThrow() === name)
92 .map(k => {
93 return {
94 file: k.getSourceFile().getFilePath(),
95 position: k.getPos()
96 };
97 });
98 return acc.concat(nameLocations);
99 }, []);
100 return util_1.err(new errors_1.ParserError("endpoints must have unique names", ...locations));
101 }
102 const endpoints = [];
103 for (const k of endpointClasses) {
104 const endpointResult = endpoint_parser_1.parseEndpoint(k, typeTable, lociTable);
105 if (endpointResult.isErr())
106 return endpointResult;
107 endpoints.push(endpointResult.unwrap());
108 }
109 return util_1.ok(endpoints.sort((a, b) => (b.name > a.name ? -1 : 1)));
110}