UNPKG

6.24 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const functions = require("@bearer/functions");
4const fs = require("fs");
5const globby = require("globby");
6const path = require("path");
7const ts = require("typescript");
8const TJS = require("typescript-json-schema");
9const openapi_generator_1 = require("@bearer/openapi-generator");
10const config = ts.readConfigFile(path.join(__dirname, '../../templates/start', 'tsconfig.json'), ts.sys.readFile);
11const NON_FUNCTION_NAMES = ['DBClient'];
12const FUNCTION_NAMES = Object.keys(functions).filter(functionName => !NON_FUNCTION_NAMES.includes(functionName));
13const FUNCTION_TYPE_IDENTIFIER = 'functionType';
14const functionEntries = [];
15function bodySchema(tsType, generator) {
16 return {};
17}
18class FunctionNodeAdapter {
19 constructor(functionName, node,
20 // @ts-ignore
21 generator) {
22 this.functionName = functionName;
23 this.node = node;
24 this.generator = generator;
25 }
26 get functionClassName() {
27 return getIdentifier(this.node).escapedText.toString();
28 }
29 get functionType() {
30 return getPropertyValue(this.node, FUNCTION_TYPE_IDENTIFIER);
31 }
32 get paramsSchema() {
33 return [...DEFAULT_PARAMS];
34 }
35 adaptParamsSchema({ properties = {} }) {
36 if (properties) {
37 return Object.keys(properties).map(propName => {
38 const { schema, required, name, description } = properties[propName];
39 return {
40 schema: schema || { type: 'string' },
41 in: 'query',
42 description: description || propName,
43 required: required !== undefined ? required : true,
44 name: name || propName
45 };
46 });
47 }
48 return [];
49 }
50 get bodySchema() {
51 return {
52 properties: bodySchema(this.node, this.generator)
53 };
54 }
55 get outputSchema() {
56 return {};
57 }
58 get adapt() {
59 return {
60 functionClassName: this.functionClassName,
61 functionName: this.functionName,
62 functionType: this.functionType,
63 paramsSchema: this.paramsSchema,
64 bodySchema: this.bodySchema,
65 outputSchema: this.outputSchema
66 };
67 }
68}
69function isFunctionClass(tsNode) {
70 if (!ts.isClassDeclaration(tsNode)) {
71 return false;
72 }
73 return extendsFunctionType(tsNode) && implementsAction(tsNode);
74}
75exports.isFunctionClass = isFunctionClass;
76function extendsFunctionType(tsClass) {
77 const extendedClasses = (tsClass.heritageClauses || []).filter(hc => hc.token === ts.SyntaxKind.ExtendsKeyword);
78 return Boolean(extendedClasses.find(hc => Boolean(hc.types.find(t => FUNCTION_NAMES.includes(t.expression.escapedText.toString())))));
79}
80function implementsAction(tsClass) {
81 return Boolean(tsClass.members.filter(m => {
82 return ts.isMethodDeclaration(m);
83 }));
84}
85function getIdentifier(tsNode) {
86 return tsNode.name;
87}
88exports.getIdentifier = getIdentifier;
89function getFunctionName(tsSourceFile) {
90 return path.basename(tsSourceFile.fileName).split('.')[0];
91}
92exports.getFunctionName = getFunctionName;
93function getPropertyValue(tsNode, propertyName) {
94 if (tsNode.members) {
95 const declaration = tsNode.members.find(node => {
96 return ts.isPropertyDeclaration(node) && node.name.escapedText.toString() === propertyName;
97 });
98 if (declaration && declaration.initializer) {
99 return declaration.initializer.text;
100 }
101 }
102}
103exports.getPropertyValue = getPropertyValue;
104function transformer(generator) {
105 return (context) => {
106 return (tsSourceFile) => {
107 function visit(tsNode) {
108 if (isFunctionClass(tsNode)) {
109 const adapter = new FunctionNodeAdapter(getFunctionName(tsSourceFile), tsNode, generator);
110 functionEntries.push(adapter.adapt);
111 }
112 return tsNode;
113 }
114 return ts.visitEachChild(tsSourceFile, visit, context);
115 };
116 };
117}
118exports.transformer = transformer;
119class FunctionCodeProcessor {
120 constructor(srcFunctionsDir, transformer) {
121 this.srcFunctionsDir = srcFunctionsDir;
122 this.transformer = transformer;
123 }
124 async run() {
125 const files = await globby(`${this.srcFunctionsDir}/*.ts`);
126 files.forEach(file => {
127 const sourceFile = ts.createSourceFile(file, fs.readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
128 ts.transform(sourceFile, [this.transformer]);
129 });
130 }
131}
132exports.FunctionCodeProcessor = FunctionCodeProcessor;
133class OpenApiSpecGenerator {
134 constructor(srcFunctionsDir, bearerConfig) {
135 this.srcFunctionsDir = srcFunctionsDir;
136 this.bearerConfig = bearerConfig;
137 }
138 async build() {
139 const files = await globby(`${this.srcFunctionsDir}/*.ts`);
140 const programGenerator = TJS.getProgramFromFiles(files, Object.assign({}, config.config.compilerOptions, { allowUnusedLabels: true,
141 // be indulgent
142 noUnusedParameters: false, noUnusedLocals: false }), './ok');
143 const generator = TJS.buildGenerator(programGenerator, {
144 required: true
145 });
146 if (!generator) {
147 throw new Error('Please fix above issues before');
148 }
149 files.forEach(file => {
150 const sourceFile = ts.createSourceFile(file, fs.readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
151 ts.transform(sourceFile, [transformer(generator)]);
152 });
153 return openapi_generator_1.default({
154 functions: functionEntries.map(entry => entry.functionName),
155 functionsDir: this.srcFunctionsDir,
156 buid: this.bearerConfig.buid,
157 integrationName: this.bearerConfig.integrationTitle || ''
158 });
159 }
160}
161exports.OpenApiSpecGenerator = OpenApiSpecGenerator;
162const DEFAULT_PARAMS = [
163 {
164 in: 'header',
165 name: 'authorization',
166 schema: {
167 type: 'string'
168 },
169 description: 'API Key',
170 required: true
171 }
172];