diff --git a/packages/stainless/src/stl.ts b/packages/stainless/src/stl.ts index 6d857ca9..344e8fe7 100644 --- a/packages/stainless/src/stl.ts +++ b/packages/stainless/src/stl.ts @@ -2,6 +2,7 @@ import * as z from "./z"; import { type toZod } from "ts-to-zod"; import { openapiSpec } from "./openapiSpec"; import type { OpenAPIObject } from "zod-openapi/lib-types/openapi3-ts/dist/oas31"; +import { Config } from "prettier"; export { SelectTree, parseSelect } from "./parseSelect"; export { z }; export { createClient } from "./client"; @@ -650,6 +651,32 @@ export class Stl { params: Params, context: StlContext ): Promise> { + if ( + !context.endpoint.query && + !context.endpoint.path && + !context.endpoint.body && + !context.endpoint.response + ) { + try { + const module = await eval?.("import('@stl-api/gen/__endpointMap.js')"); + const schemas = await module.map[context.endpoint.endpoint]; + if (schemas) { + context.endpoint.path = schemas.path; + context.endpoint.query = schemas.query; + context.endpoint.body = schemas.body; + context.endpoint.response = schemas.response; + } else { + throw new Error( + "error encountered while handling endpoint " + + context.endpoint.endpoint + + ": no schema found. run the cli on the project to generate the schema for the endpoint." + ); + } + } catch (e) { + console.error(e); + } + } + for (const plugin of Object.values(this.stainlessPlugins)) { const middleware = plugin.middleware; if (middleware) await middleware(params, context); @@ -747,7 +774,7 @@ export class Stl { ): Endpoint { const { config, response, path, query, body, ...rest } = params; return { - stl: this as any, + stl: this, config: config as Config, response: (response || z.void()) as Response, path: path as Path, @@ -885,4 +912,95 @@ export class Stl { magic(schema: toZod): toZod { return schema; } + + types(): TypeEndpointBuilder< + TypeArgToZod, + TypeArgToZod, + TypeArgToZod, + "response" extends keyof T ? toZod : z.ZodVoid + > { + return { + endpoint: < + MethodAndUrl extends HttpEndpoint, + Config extends EndpointConfig | undefined, + Path extends ZodObjectSchema | undefined, + Query extends ZodObjectSchema | undefined, + Body extends ZodObjectSchema | undefined, + Response extends z.ZodTypeAny = z.ZodVoid + >({ + endpoint, + config, + handler, + }: TypeEndpointParams< + MethodAndUrl, + Config, + Path, + Query, + Body, + Response + >): Endpoint => { + return { + stl: this, + endpoint, + config: config as Config, + handler, + path: undefined as Path, + query: undefined as Query, + body: undefined as Body, + response: undefined as any as Response, + }; + }, + }; + } +} + +type TypeArgToZod = K extends keyof T + ? toZod + : undefined; + +interface Types { + path?: any; + query?: any; + body?: any; + response?: any; +} + +interface TypeEndpointParams< + MethodAndUrl extends HttpEndpoint, + Config extends EndpointConfig | undefined, + Path extends ZodObjectSchema | undefined, + Query extends ZodObjectSchema | undefined, + Body extends ZodObjectSchema | undefined, + Response extends z.ZodTypeAny = z.ZodVoid +> { + endpoint: MethodAndUrl; + config?: Config; + handler: Handler< + StlContext>, + Path, + Query, + Body, + Response extends z.ZodTypeAny ? z.input : undefined + >; +} + +interface TypeEndpointBuilder< + Path extends ZodObjectSchema | undefined, + Query extends ZodObjectSchema | undefined, + Body extends ZodObjectSchema | undefined, + Response extends z.ZodTypeAny = z.ZodVoid +> { + endpoint< + MethodAndUrl extends HttpEndpoint, + Config extends EndpointConfig | undefined + >( + params: TypeEndpointParams< + MethodAndUrl, + Config, + Path, + Query, + Body, + Response + > + ): Endpoint; } diff --git a/packages/stl-api-gen/src/endpointPreprocess.ts b/packages/stl-api-gen/src/endpointPreprocess.ts index 94de7847..355b83b4 100644 --- a/packages/stl-api-gen/src/endpointPreprocess.ts +++ b/packages/stl-api-gen/src/endpointPreprocess.ts @@ -92,5 +92,4 @@ function propertyToNodeType(property: tm.Symbol, location: tm.Node): NodeType { const node = getPropertyDeclaration(property); if (!node) throw new Error("internal error: invalid property encountered"); return [node, property.getTypeAtLocation(location)]; - } diff --git a/packages/stl-api-gen/src/index.ts b/packages/stl-api-gen/src/index.ts index 94960243..e56d6a98 100644 --- a/packages/stl-api-gen/src/index.ts +++ b/packages/stl-api-gen/src/index.ts @@ -20,7 +20,7 @@ import { import { generateFiles, - generateImportStatements, + generateImportStatementsESM, generatePath, } from "ts-to-zod/dist/generateFiles"; @@ -49,22 +49,6 @@ argParser.option( const NODE_MODULES_GEN_PATH = "@stl-api/gen"; -const Z_IMPORT_STATEMENT = factory.createImportDeclaration( - [], - factory.createImportClause( - false, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("z") - ), - ]) - ), - factory.createStringLiteral("stainless") -); - interface CallDiagnostics { line: number; column: number; @@ -72,6 +56,11 @@ interface CallDiagnostics { diagnostics: Map; } +interface EndpointCall { + endpointPath: string; + schemaExpression: ts.Expression; +} + async function main() { argParser.parse(); const options = argParser.opts(); @@ -170,7 +159,7 @@ async function evaluate( const generationConfig = createGenerationConfig(generationOptions); const callDiagnostics: CallDiagnostics[] = []; - const endpointCalls: Map = new Map(); + const endpointCalls: Map = new Map(); // all of the stl.types calls found @@ -198,7 +187,30 @@ async function evaluate( const call = handleEndpoint(callExpression); if (!call) continue; const fileCalls = getOrInsert(endpointCalls, file, () => []); - fileCalls.push(call); + const endpointSchema = convertEndpointCall( + ctx, + call, + file, + callDiagnostics + ); + if (!endpointSchema) { + break; + } + fileCalls.push({ + endpointPath: call.endpointPath, + schemaExpression: endpointSchema, + }); + const ctxFile = getOrInsert(ctx.files, file.getFilePath(), () => ({ + imports: new Map(), + generatedSchemas: [], + })); + ctxFile.generatedSchemas.push({ + name: mangleString(call.endpointPath), + expression: endpointSchema, + isExported: true, + /** non-user-visible type should be given any type */ + type: factory.createTypeReferenceNode("any"), + }); continue; } @@ -302,7 +314,7 @@ async function evaluate( } // add new imports necessary for schema generation - let importDeclarations = generateImportStatements( + let importDeclarations = generateImportStatementsESM( generationConfig, file.getFilePath(), "stainless", @@ -355,6 +367,7 @@ async function evaluate( const mapEntries = []; outer: for (const [file, calls] of endpointCalls) { + baseCtx.diagnostics = new Map(); const ctx = new ConvertTypeContext(baseCtx, file); for (const call of calls) { const mangledName = mangleString(call.endpointPath); @@ -363,9 +376,9 @@ async function evaluate( undefined, [ factory.createStringLiteral( - convertPathToImport( + `${convertPathToImport( generatePath(file.getFilePath(), generationConfig) - ) + )}.js` ), ] ); @@ -391,84 +404,10 @@ async function evaluate( callExpression ); mapEntries.push(entry); - const filex = generatePath(file.getFilePath(), generationConfig); - - const generatedStatements = getOrInsert( - generatedFileContents, - generatePath(file.getFilePath(), generationConfig), - () => [Z_IMPORT_STATEMENT] - ); - - const objectProperties = []; - - const requestTypes = [ - ["query", call.query], - ["path", call.path], - ["body", call.body], - ].filter(([_, type]) => type) as [string, NodeType][]; - for (const [name, nodeType] of requestTypes) { - const schemaExpression = convertEndpointType( - ctx, - call.callExpression, - callDiagnostics, - file, - nodeType[0], - nodeType[1] - ); - if (!schemaExpression) break outer; - objectProperties.push( - factory.createPropertyAssignment(name, schemaExpression) - ); - } - - let schemaExpression; - if (call.response) { - schemaExpression = convertEndpointType( - ctx, - call.callExpression, - callDiagnostics, - file, - call.response[0], - call.response[1] - ); - if (!schemaExpression) break outer; - } else { - // if no response type is provided, use the default schema z.void() to indicate no response - schemaExpression = factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("z"), - "void" - ), - [], - [] - ); - } - objectProperties.push( - factory.createPropertyAssignment("response", schemaExpression) - ); - - const variableDeclaration = factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - mangledName, - undefined, - undefined, - factory.createObjectLiteralExpression(objectProperties) - ), - ], - ts.NodeFlags.Const - ); - - generatedStatements.push( - factory.createVariableStatement( - [factory.createToken(ts.SyntaxKind.ExportKeyword)], - variableDeclaration - ) - ); } } - const mapConstant = factory.createVariableDeclarationList( + /* const mapConstant = factory.createVariableDeclarationList( [ factory.createVariableDeclaration( "someName", @@ -478,23 +417,65 @@ async function evaluate( ), ], // ts.NodeFlags.Const - ); + ); const mapStatement = factory.createVariableStatement( [factory.createToken(ts.SyntaxKind.ExportKeyword)], mapConstant ); - const mapSourceFile = factory.createSourceFile( - [mapStatement], + */ + const mapExpression = factory.createObjectLiteralExpression(mapEntries); + + const mapStatementCJS = factory.createExpressionStatement( + factory.createAssignment( + factory.createPropertyAccessExpression( + factory.createIdentifier("exports"), + "map" + ), + mapExpression + ) + ); + + const mapDeclaration = factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + "someName", + undefined, + undefined, + factory.createObjectLiteralExpression(mapEntries) + ), + ], + ts.NodeFlags.Const + ); + const mapStatementESM = factory.createVariableStatement( + [factory.createToken(ts.SyntaxKind.ExportKeyword)], + mapDeclaration + ); + + const mapSourceFileCJS = factory.createSourceFile( + [mapStatementCJS], + factory.createToken(ts.SyntaxKind.EndOfFileToken), + 0 + ); + + const mapSourceFileESM = factory.createSourceFile( + [mapStatementESM], factory.createToken(ts.SyntaxKind.EndOfFileToken), 0 ); const genPath = Path.join(rootPath, "node_modules", NODE_MODULES_GEN_PATH); - const endpointMapGenPath = Path.join(genPath, "__endpointMap.js"); await fs.promises.mkdir(genPath, { recursive: true }); + + const endpointMapGenPathCJS = Path.join(genPath, "__endpointMap.js"); + await fs.promises.writeFile( + endpointMapGenPathCJS, + printer.printFile(mapSourceFileCJS) + ); + + const endpointMapGenPathESM = Path.join(genPath, "__endpointMap.mjs"); await fs.promises.writeFile( - endpointMapGenPath, - printer.printFile(mapSourceFile) + endpointMapGenPathESM, + printer.printFile(mapSourceFileESM) ); } @@ -578,7 +559,6 @@ async function evaluate( } for (const [file, fileStatments] of generatedFileContents) { - console.log(`writing to file ${file}`); const fileDir = Path.dirname(file); // creates directory where to write file to, if it doesn't already exist await fs.promises.mkdir(fileDir, { @@ -680,6 +660,61 @@ function convertEndpointType( } } +function convertEndpointCall( + ctx: ConvertTypeContext, + call: EndpointTypeInstance, + file: tm.SourceFile, + callDiagnostics: CallDiagnostics[] +): ts.Expression | undefined { + const objectProperties = []; + const requestTypes = [ + ["query", call.query], + ["path", call.path], + ["body", call.body], + ].filter(([_, type]) => type) as [string, NodeType][]; + for (const [name, nodeType] of requestTypes) { + const schemaExpression = convertEndpointType( + ctx, + call.callExpression, + callDiagnostics, + file, + nodeType[0], + nodeType[1] + ); + if (!schemaExpression) return; + objectProperties.push( + factory.createPropertyAssignment(name, schemaExpression) + ); + } + + let schemaExpression; + if (call.response) { + schemaExpression = convertEndpointType( + ctx, + call.callExpression, + callDiagnostics, + file, + call.response[0], + call.response[1] + ); + if (!schemaExpression) return; + } else { + // if no response type is provided, use the default schema z.void() to indicate no response + schemaExpression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("z"), + "void" + ), + [], + [] + ); + } + objectProperties.push( + factory.createPropertyAssignment("response", schemaExpression) + ); + return factory.createObjectLiteralExpression(objectProperties); +} + // TODO: factor out to ts-to-zod? function mangleTypeName(type: tm.Type, name: string): string { if (type.isEnum()) { diff --git a/packages/stl-api-gen/src/utils.ts b/packages/stl-api-gen/src/utils.ts index 1315fe09..41834e4c 100644 --- a/packages/stl-api-gen/src/utils.ts +++ b/packages/stl-api-gen/src/utils.ts @@ -11,7 +11,7 @@ export function isSymbolStlMethod(symbol: tm.Symbol): boolean { export function mangleString(str: string): string { const unicodeLetterRegex = /\p{L}/u; - const escapedStringBuilder = []; + const escapedStringBuilder = ["__"]; for (const codePointString of str) { if (codePointString === "/") { escapedStringBuilder.push("$"); diff --git a/packages/stl-api-gen/src/watch.ts b/packages/stl-api-gen/src/watch.ts index 53c13145..e59b28af 100644 --- a/packages/stl-api-gen/src/watch.ts +++ b/packages/stl-api-gen/src/watch.ts @@ -129,7 +129,7 @@ function updateCtx(ctx: SchemaGenContext) { // delete a generated schema based on whether symbol is updated // need to add two new fields: generation updated, and dependencies - ctx.getFileInfo("file").generatedSchemas[0].symbol; + // ctx.getFileInfo("file").generatedSchemas[0].symbol; // how do we update these? todo: go to file and see how this is // being used. perhaps just invalidate every time the file changes? diff --git a/packages/ts-to-zod/src/convertType.ts b/packages/ts-to-zod/src/convertType.ts index 7aa1b432..2b1fcda6 100644 --- a/packages/ts-to-zod/src/convertType.ts +++ b/packages/ts-to-zod/src/convertType.ts @@ -183,7 +183,7 @@ export interface ImportInfo { sourceFile: string; } -interface FileInfo { +export interface FileInfo { imports: Map; generatedSchemas: GeneratedSchema[]; /** maps imported type id to local identifier it's imported as */ @@ -205,9 +205,16 @@ function buildImportTypeMap(sourceFile: tm.SourceFile): Map { } interface GeneratedSchema { - symbol: tm.Symbol; + name: string; expression: ts.Expression; isExported: boolean; + /** + * The type for the schema value to generate in d.ts. + * Defaults to `z.ZodTypeAny`. + */ + type?: ts.TypeNode, + /** Whether the type shouldn't be exported from the definition file. Defaults to false. */ + private?: boolean, } export function convertSymbol( @@ -256,7 +263,7 @@ export function convertSymbol( ctx.symbols.add(symbol); const generated = convertType(internalCtx, type, diagnosticItem); const generatedSchema = { - symbol, + name: symbol.getName(), expression: generated, isExported: isRoot || isDeclarationExported(declaration), }; diff --git a/packages/ts-to-zod/src/generateFiles.ts b/packages/ts-to-zod/src/generateFiles.ts index 019c51bf..e4ccd229 100644 --- a/packages/ts-to-zod/src/generateFiles.ts +++ b/packages/ts-to-zod/src/generateFiles.ts @@ -1,6 +1,6 @@ -import { groupBy } from "lodash"; -import { ImportInfo, SchemaGenContext } from "./convertType"; -import { ts, Symbol } from "ts-morph"; +import { Dictionary, groupBy } from "lodash"; +import { FileInfo, ImportInfo, SchemaGenContext } from "./convertType"; +import { ts } from "ts-morph"; const { factory } = ts; import * as Path from "path"; import { @@ -18,31 +18,127 @@ export function generateFiles( for (const [path, info] of ctx.files.entries()) { const generatedPath = generatePath(path, generationConfig); - const statements: ts.Statement[] = generateImportStatements( - generationConfig, - generatedPath, - options.zPackage, - info.imports + const esmPath = `${generatedPath}.mjs`; + const dtsPath = `${generatedPath}.d.ts`; + const cjsPath = `${generatedPath}.js`; + + outputMap.set( + esmPath, + generateStatementsESM(info, generationConfig, generatedPath, options) + ); + outputMap.set( + dtsPath, + generateStatementsDTS(info, generationConfig, generatedPath, options) + ); + outputMap.set( + cjsPath, + generateStatementsCJS(info, generationConfig, generatedPath, options) + ); + } + return outputMap; +} + +function generateStatementsESM( + info: FileInfo, + generationConfig: GenerationConfig, + generatedPath: string, + options: GenOptions +): ts.Statement[] { + const statements: ts.Statement[] = generateImportStatementsESM( + generationConfig, + generatedPath, + options.zPackage, + info.imports + ); + + for (const schema of info.generatedSchemas) { + const declaration = factory.createVariableDeclaration( + schema.name, + undefined, + undefined, + schema.expression + ); + const variableStatement = factory.createVariableStatement( + schema.isExported + ? [factory.createToken(ts.SyntaxKind.ExportKeyword)] + : [], + factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const) + ); + statements.push(variableStatement); + } + return statements; +} + +function generateStatementsDTS( + info: FileInfo, + generationConfig: GenerationConfig, + generatedPath: string, + options: GenOptions +): ts.Statement[] { + const statements: ts.Statement[] = generateImportStatementsESM( + generationConfig, + generatedPath, + options.zPackage, + info.imports + ); + + for (const schema of info.generatedSchemas) { + if (schema.private) continue; + const declaration = factory.createVariableDeclaration( + schema.name, + undefined, + schema.type || factory.createTypeReferenceNode("z.ZodTypeAny") + ); + const variableStatement = factory.createVariableStatement( + schema.isExported + ? [factory.createToken(ts.SyntaxKind.ExportKeyword)] + : [], + factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const) ); + statements.push(variableStatement); + } + return statements; +} - for (const schema of info.generatedSchemas) { +function generateStatementsCJS( + info: FileInfo, + generationConfig: GenerationConfig, + generatedPath: string, + options: GenOptions +): ts.Statement[] { + const statements: ts.Statement[] = generateImportStatementsCJS( + generationConfig, + generatedPath, + options.zPackage, + info.imports + ); + + for (const schema of info.generatedSchemas) { + if (schema.isExported) { + const exportTarget = factory.createPropertyAccessExpression( + factory.createIdentifier("exports"), + schema.name + ); + statements.push( + factory.createExpressionStatement( + factory.createAssignment(exportTarget, schema.expression) + ) + ); + } else { const declaration = factory.createVariableDeclaration( - schema.symbol.getName(), + schema.name, undefined, undefined, schema.expression ); const variableStatement = factory.createVariableStatement( - schema.isExported - ? [factory.createToken(ts.SyntaxKind.ExportKeyword)] - : [], + undefined, factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const) ); statements.push(variableStatement); } - outputMap.set(generatedPath, statements); } - return outputMap; + return statements; } function relativeImportPath( @@ -54,6 +150,7 @@ function relativeImportPath( return relativePath; } +/** Returns the path in which to generate schemas for an input path. Generates without a file extension. */ export function generatePath( path: string, { basePath, baseDependenciesPath, rootPath, suffix }: GenerationConfig @@ -73,24 +170,54 @@ export function generatePath( ); } - return Path.join(chosenBasePath, Path.relative(rootPath, path)); + const pathWithExtension = Path.join( + chosenBasePath, + Path.relative(rootPath, path) + ); + const parsed = Path.parse(pathWithExtension); + return Path.join(parsed.dir, parsed.name); } -export function generateImportStatements( - config: GenerationConfig, +function generateImportGroups( + imports: Map, filePath: string, - zPackage: string | undefined, - imports: Map -): ts.ImportDeclaration[] { - const importDeclarations = []; - const importGroups = groupBy( + config: GenerationConfig +): Dictionary<[string, ImportInfo][]> { + return groupBy( [...imports.entries()], - ([symbol, { importFromUserFile, sourceFile }]) => + ([_, { importFromUserFile, sourceFile }]) => relativeImportPath( filePath, importFromUserFile ? sourceFile : generatePath(sourceFile, config) ) ); +} + +function normalizeImport(relativePath: string): string { + // use absolute, not relative, imports for things in node_modules + const nodeModulesPos = relativePath.lastIndexOf("node_modules"); + if (nodeModulesPos >= 0) { + relativePath = relativePath.substring(nodeModulesPos + 13); + } + + // strip extension like '.ts' off file + const parsedRelativePath = Path.parse(relativePath); + let extensionlessRelativePath = Path.join( + parsedRelativePath.dir, + parsedRelativePath.name + ); + if (extensionlessRelativePath[0] !== "." && nodeModulesPos < 0) { + extensionlessRelativePath = `./${extensionlessRelativePath}`; + } + return extensionlessRelativePath; +} + +export function generateImportStatementsESM( + config: GenerationConfig, + filePath: string, + zPackage: string | undefined, + imports: Map +): ts.ImportDeclaration[] { const zImportClause = factory.createImportClause( false, undefined, @@ -108,7 +235,9 @@ export function generateImportStatements( factory.createStringLiteral(zPackage || "zod") ); - importDeclarations.push(zImportDeclaration); + const importDeclarations = [zImportDeclaration]; + + const importGroups = generateImportGroups(imports, filePath, config); for (let [relativePath, entries] of Object.entries(importGroups)) { const importSpecifiers = entries.map(([name, { as }]) => { @@ -126,29 +255,86 @@ export function generateImportStatements( factory.createNamedImports(importSpecifiers) ); - // use absolute, not relative, imports for things in node_modules - const nodeModulesPos = relativePath.lastIndexOf("node_modules"); - if (nodeModulesPos >= 0) { - relativePath = relativePath.substring(nodeModulesPos + 13); - } - - // strip extension like '.ts' off file - const parsedRelativePath = Path.parse(relativePath); - let extensionlessRelativePath = Path.join( - parsedRelativePath.dir, - parsedRelativePath.name - ); - if (extensionlessRelativePath[0] !== "." && nodeModulesPos < 0) { - extensionlessRelativePath = `./${extensionlessRelativePath}`; - } + const normalizedImport = normalizeImport(relativePath); const importDeclaration = factory.createImportDeclaration( undefined, importClause, - factory.createStringLiteral(extensionlessRelativePath), + factory.createStringLiteral(normalizedImport), undefined ); importDeclarations.push(importDeclaration); } return importDeclarations; } + +export function generateImportStatementsCJS( + config: GenerationConfig, + filePath: string, + zPackage: string | undefined, + imports: Map +): ts.Statement[] { + const zRequireExpression = factory.createCallExpression( + factory.createIdentifier("require"), + undefined, + [factory.createStringLiteral(zPackage || "zod")] + ); + const zImportStatement = factory.createVariableStatement( + [], + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createObjectBindingPattern([ + factory.createBindingElement(undefined, undefined, "z"), + ]), + undefined, + undefined, + zRequireExpression + ), + ], + ts.NodeFlags.Const + ) + ); + + const importStatements: ts.Statement[] = [zImportStatement]; + + const importGroups = generateImportGroups(imports, filePath, config); + + for (let [relativePath, entries] of Object.entries(importGroups)) { + const bindingElements = entries.map(([name, { as }]) => { + if (as === name) as = undefined; + + return factory.createBindingElement( + undefined, + as ? factory.createIdentifier(name) : undefined, + factory.createIdentifier(as || name) + ); + }); + const requireDestructure = + factory.createObjectBindingPattern(bindingElements); + + const normalizedImport = normalizeImport(relativePath); + const requireExpression = factory.createCallExpression( + factory.createIdentifier("require"), + undefined, + [factory.createStringLiteral(normalizedImport)] + ); + + const importStatement = factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + requireDestructure, + undefined, + undefined, + requireExpression + ), + ], + ts.NodeFlags.Const + ) + ); + importStatements.push(importStatement); + } + return importStatements; +}