Skip to content

Commit

Permalink
feat(type-safe-api): add metadata option to typescript codegen for es…
Browse files Browse the repository at this point in the history
…m compatible code (#888)

* feat(type-safe-api): add metadata option to typescript codegen for esm compatible code

Adds an option to the typescript generators to emit esm compatible code (eg imports ending with
.js).

* fix(type-safe-api): fix overwrite when output dir is not working directory
  • Loading branch information
cogwirrel authored Nov 27, 2024
1 parent 86f3b72 commit ad383ad
Show file tree
Hide file tree
Showing 27 changed files with 4,795 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ const splitAndWriteFiles = (renderedFileContents: string[], outputPath: string)
splitFiles.push({
contents: newFileContents,
pathRelativeToOutputPath: newFilePath,
shouldWrite: !fs.existsSync(newFilePath) || config.overwrite,
shouldWrite: !fs.existsSync(path.join(outputPath, newFilePath)) || config.overwrite,
config,
});
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
###/TSAPI_WRITE_FILE###import { TypeSafeWebsocketApi, TypeSafeWebsocketApiProps, TypeSafeWebsocketApiIntegration } from "@aws/pdk/type-safe-api";
import { Construct } from "constructs";
import { OperationConfig, OperationLookup } from "<%- metadata.runtimePackageName %>";
<%_ if (metadata.esm) { _%>
import * as url from 'url';
<%_ } else { _%>
import * as path from "path";
<%_ } _%>

export type WebSocketApiIntegrations = OperationConfig<TypeSafeWebsocketApiIntegration>;

Expand All @@ -27,7 +31,11 @@ export class WebSocketApi extends TypeSafeWebsocketApi {
...props,
integrations: props.integrations as any,
operationLookup: OperationLookup,
<%_ if (metadata.esm) { _%>
specPath: url.fileURLToPath(new URL("<%- metadata.relativeSpecPath %>", import.meta.url)),
<%_ } else { _%>
specPath: path.resolve(__dirname, "<%- metadata.relativeSpecPath %>"),
<%_ } _%>
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { Duration } from "aws-cdk-lib";
import { SnapStartFunction, SnapStartFunctionProps } from "@aws/pdk/type-safe-api";
import { Code, Function, Runtime, Tracing, FunctionProps } from "aws-cdk-lib/aws-lambda";
import * as path from "path";
<%_ if (metadata.esm) { _%>
import * as url from 'url';
<%_ } _%>

<%_ if (vendorExtensions['x-connect-handler']) { _%>
<%_ const language = vendorExtensions['x-connect-handler'].language; _%>
Expand Down Expand Up @@ -41,7 +44,7 @@ export class $ConnectFunction extends <% if (isJava) { %>SnapStart<% } %>Functio
<%_ } else if (isJava) { _%>
handler: "<%- metadata['x-handlers-java-package'] %>.$ConnectHandler",
<%_ } _%>
code: Code.fromAsset(path.resolve(__dirname, "..",
code: Code.fromAsset(<%_ if (metadata.esm) { _%>url.fileURLToPath(new URL(path.join("..",<%_ } else { _%>path.resolve(__dirname, "..",<%_ } %>
<%_ if (isTypeScript) { _%>
"<%- metadata['x-handlers-typescript-asset-path'] %>",
"$connect",
Expand All @@ -50,7 +53,7 @@ export class $ConnectFunction extends <% if (isJava) { %>SnapStart<% } %>Functio
<%_ } else if (isJava) { _%>
"<%- metadata['x-handlers-java-asset-path'] %>",
<%_ } _%>
)),
)<%_ if (metadata.esm) { _%>, import.meta.url))<%_ } _%>),
tracing: Tracing.ACTIVE,
timeout: Duration.seconds(30),
...props,
Expand Down Expand Up @@ -89,7 +92,7 @@ export class $DisconnectFunction extends <% if (isJava) { %>SnapStart<% } %>Func
<%_ } else if (isJava) { _%>
handler: "<%- metadata['x-handlers-java-package'] %>.$DisconnectHandler",
<%_ } _%>
code: Code.fromAsset(path.resolve(__dirname, "..",
code: Code.fromAsset(<%_ if (metadata.esm) { _%>url.fileURLToPath(new URL(path.join("..",<%_ } else { _%>path.resolve(__dirname, "..",<%_ } %>
<%_ if (isTypeScript) { _%>
"<%- metadata['x-handlers-typescript-asset-path'] %>",
"$disconnect",
Expand All @@ -98,7 +101,7 @@ export class $DisconnectFunction extends <% if (isJava) { %>SnapStart<% } %>Func
<%_ } else if (isJava) { _%>
"<%- metadata['x-handlers-java-asset-path'] %>",
<%_ } _%>
)),
)<%_ if (metadata.esm) { _%>, import.meta.url))<%_ } _%>),
tracing: Tracing.ACTIVE,
timeout: Duration.seconds(30),
...props,
Expand Down Expand Up @@ -138,7 +141,7 @@ export class <%- operation.operationIdPascalCase %>Function extends <% if (isJav
<%_ } else if (isJava) { _%>
handler: "<%- metadata['x-handlers-java-package'] %>.<%- operation.operationIdPascalCase %>Handler",
<%_ } _%>
code: Code.fromAsset(path.resolve(__dirname, "..",
code: Code.fromAsset(<%_ if (metadata.esm) { _%>url.fileURLToPath(new URL(path.join("..",<%_ } else { _%>path.resolve(__dirname, "..",<%_ } %>
<%_ if (isTypeScript) { _%>
"<%- metadata['x-handlers-typescript-asset-path'] %>",
"<%- operation.operationIdKebabCase %>",
Expand All @@ -147,7 +150,7 @@ export class <%- operation.operationIdPascalCase %>Function extends <% if (isJav
<%_ } else if (isJava) { _%>
"<%- metadata['x-handlers-java-asset-path'] %>",
<%_ } _%>
)),
)<%_ if (metadata.esm) { _%>, import.meta.url))<%_ } _%>),
tracing: Tracing.ACTIVE,
timeout: Duration.seconds(30),
...props,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"ext": ".ts",
"overwrite": true
}
###/TSAPI_WRITE_FILE###export * from "./api";
export * from "./functions";
export * from "./mock-integrations";
###/TSAPI_WRITE_FILE###export * from "./api<%_ if (metadata.esm) { _%>.js<%_ } _%>";
export * from "./functions<%_ if (metadata.esm) { _%>.js<%_ } _%>";
export * from "./mock-integrations<%_ if (metadata.esm) { _%>.js<%_ } _%>";
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
} from "<%= metadata.runtimePackageName %>";
import {
<%- operation.name %>
} from "../src/<%- operation.operationIdKebabCase %>";
} from "../src/<%- operation.operationIdKebabCase %><%_ if (metadata.esm) { _%>.js<%_ } _%>";
// Common request arguments
const requestArguments = {
Expand All @@ -24,7 +24,7 @@ const requestArguments = {
context: {} as any,
interceptorContext: {
logger: {
info: jest.fn(),
info: <% if (metadata.vitest) { %>vi<% } else { %>jest<% } %>.fn(),
},
},
} satisfies Omit<<%- operation.operationIdPascalCase %>ChainedRequestInput, 'input'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ext": ".ts",
"overwrite": true
}
###/TSAPI_WRITE_FILE###export * from './models';
export * from './server/operation-config';
export * from './server/server-sdk';
export * from './interceptors'
###/TSAPI_WRITE_FILE###export * from './models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';
export * from './server/operation-config<%_ if (metadata.esm) { _%>.js<%_ } _%>';
export * from './server/server-sdk<%_ if (metadata.esm) { _%>.js<%_ } _%>';
export * from './interceptors<%_ if (metadata.esm) { _%>/index.js<%_ } _%>'
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
###/TSAPI_WRITE_FILE###import {
PayloadlessChainedRequestInput,
} from '..';
} from '..<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';

/**
* Create an interceptor which catches any unhandled exceptions
Expand Down Expand Up @@ -39,7 +39,7 @@ export const tryCatchInterceptor = buildTryCatchInterceptor();
"overwrite": true
}
###/TSAPI_WRITE_FILE###import { Logger } from '@aws-lambda-powertools/logger';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';

const logger = new Logger();

Expand Down Expand Up @@ -78,7 +78,7 @@ export class LoggingInterceptor {
"overwrite": true
}
###/TSAPI_WRITE_FILE###import { Tracer } from '@aws-lambda-powertools/tracer';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';

const tracer = new Tracer();

Expand Down Expand Up @@ -149,7 +149,7 @@ export class TracingInterceptor {
"overwrite": true
}
###/TSAPI_WRITE_FILE###import { Metrics } from '@aws-lambda-powertools/metrics';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..';
import { PayloadlessChainedRequestInput, ChainedRequestInput } from '../..<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';

const metrics = new Metrics();

Expand Down Expand Up @@ -191,15 +191,15 @@ export class MetricsInterceptor {
"ext": ".ts",
"overwrite": true
}
###/TSAPI_WRITE_FILE###import { LoggingInterceptor } from './powertools/logger';
import { MetricsInterceptor } from './powertools/metrics';
import { TracingInterceptor } from './powertools/tracer';
import { tryCatchInterceptor } from './try-catch';

export * from './try-catch';
export * from './powertools/tracer';
export * from './powertools/metrics';
export * from './powertools/logger';
###/TSAPI_WRITE_FILE###import { LoggingInterceptor } from './powertools/logger<%_ if (metadata.esm) { _%>.js<%_ } _%>';
import { MetricsInterceptor } from './powertools/metrics<%_ if (metadata.esm) { _%>.js<%_ } _%>';
import { TracingInterceptor } from './powertools/tracer<%_ if (metadata.esm) { _%>.js<%_ } _%>';
import { tryCatchInterceptor } from './try-catch<%_ if (metadata.esm) { _%>.js<%_ } _%>';

export * from './try-catch<%_ if (metadata.esm) { _%>.js<%_ } _%>';
export * from './powertools/tracer<%_ if (metadata.esm) { _%>.js<%_ } _%>';
export * from './powertools/metrics<%_ if (metadata.esm) { _%>.js<%_ } _%>';
export * from './powertools/logger<%_ if (metadata.esm) { _%>.js<%_ } _%>';

/**
* All default interceptors, for logging, tracing, metrics, and error handling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
<%- model.name %>FromJSON,
<%- model.name %>ToJSON,
<%_ }); _%>
} from '../models';
} from '../models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';

<%_ const serviceClassName = services[0] ? services[0].className : "DefaultApi"; _%>
// API Gateway Types
import { APIGatewayProxyWebsocketEventV2, APIGatewayProxyResultV2, Context } from "aws-lambda";
import { <%- serviceClassName %>ServerSdk } from "./server-sdk";
import { <%- serviceClassName %>ServerSdk } from "./server-sdk<%_ if (metadata.esm) { _%>.js<%_ } _%>";

<%_ const toServerOperations = allOperations.filter(op => op.vendorExtensions && op.vendorExtensions['x-async'] && ['client_to_server', 'bidirectional'].includes(op.vendorExtensions['x-async'].direction)); _%>
// Generic type for object keyed by operation names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
<%- model.name %>FromJSON,
<%- model.name %>ToJSON,
<%_ }); _%>
} from "../models";
} from "../models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>";
import {
ApiGatewayManagementApiClient,
PostToConnectionCommand,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
###/TSAPI_WRITE_FILE###import { TypeSafeRestApi, TypeSafeRestApiProps, TypeSafeApiIntegration } from "@aws/pdk/type-safe-api";
import { Construct } from "constructs";
import { OperationLookup, OperationConfig } from "<%- metadata.runtimePackageName %>";
<%_ if (metadata.esm) { _%>
import * as url from 'url';
<%_ } else { _%>
import * as path from "path";
<%_ } _%>

export type ApiIntegrations = OperationConfig<TypeSafeApiIntegration>;

Expand All @@ -26,7 +30,11 @@ export class Api extends TypeSafeRestApi {
super(scope, id, {
...props,
integrations: props.integrations as any,
<%_ if (metadata.esm) { _%>
specPath: url.fileURLToPath(new URL("<%- metadata.relativeSpecPath %>", import.meta.url)),
<%_ } else { _%>
specPath: path.resolve(__dirname, "<%- metadata.relativeSpecPath %>"),
<%_ } _%>
operationLookup: OperationLookup as any,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { Duration } from "aws-cdk-lib";
import { SnapStartFunction, SnapStartFunctionProps } from "@aws/pdk/type-safe-api";
import { Code, Function, Runtime, Tracing, FunctionProps } from "aws-cdk-lib/aws-lambda";
import * as path from "path";
<%_ if (metadata.esm) { _%>
import * as url from 'url';
<%_ } _%>

<%_ allOperations.forEach((operation) => { _%>
<%_ if (operation.vendorExtensions && operation.vendorExtensions['x-handler']) { _%>
Expand Down Expand Up @@ -42,7 +45,7 @@ export class <%- operation.operationIdPascalCase %>Function extends <% if (isJav
<%_ } else if (isJava) { _%>
handler: "<%- metadata['x-handlers-java-package'] %>.<%- operation.operationIdPascalCase %>Handler",
<%_ } _%>
code: Code.fromAsset(path.resolve(__dirname, "..",
code: Code.fromAsset(<%_ if (metadata.esm) { _%>url.fileURLToPath(new URL(path.join("..",<%_ } else { _%>path.resolve(__dirname, "..",<%_ } %>
<%_ if (isTypeScript) { _%>
"<%- metadata['x-handlers-typescript-asset-path'] %>",
"<%- operation.operationIdKebabCase %>",
Expand All @@ -51,7 +54,7 @@ export class <%- operation.operationIdPascalCase %>Function extends <% if (isJav
<%_ } else if (isJava) { _%>
"<%- metadata['x-handlers-java-asset-path'] %>",
<%_ } _%>
)),
)<%_ if (metadata.esm) { _%>, import.meta.url))<%_ } _%>),
tracing: Tracing.ACTIVE,
timeout: Duration.seconds(30),
...props,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"ext": ".ts",
"overwrite": true
}
###/TSAPI_WRITE_FILE###export * from "./api";
export * from "./mock-integrations";
export * from "./functions";
###/TSAPI_WRITE_FILE###export * from "./api<%_ if (metadata.esm) { _%>.js<%_ } _%>";
export * from "./mock-integrations<%_ if (metadata.esm) { _%>.js<%_ } _%>";
export * from "./functions<%_ if (metadata.esm) { _%>.js<%_ } _%>";
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import { Integrations, MockIntegration } from "@aws/pdk/type-safe-api";
import * as fs from "fs";
import * as path from "path";
<%_ if (metadata.esm) { _%>
import * as url from 'url';
<%_ } _%>

/**
* Type-safe mock integrations for API operations
Expand All @@ -23,7 +26,15 @@ export class MockIntegrations {
* Read a mock data file for the given operation
*/
private static readMockDataFile(method: string, urlPath: string, statusCode: number): string {
return fs.readFileSync(path.join(__dirname, "..", "mocks", `${method.toLowerCase()}${urlPath.replace(/\//g, "-")}-${statusCode}.json`), "utf-8");
const mockPath = path.join("..", "mocks", `${method.toLowerCase()}${urlPath.replace(/\//g, "-")}-${statusCode}.json`);
return fs.readFileSync(
<%_ if (metadata.esm) { _%>
url.fileURLToPath(new URL(mockPath, import.meta.url)),
<%_ } else { _%>
path.join(__dirname, mockPath),
<%_ } _%>
"utf-8",
);
}
<%_ if (metadata.enableMockIntegrations) { _%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
} from "<%= metadata.runtimePackageName %>";
import {
<%= operation.name %>
} from "../<%= metadata.srcDir || 'src' %>/<%= operation.operationIdKebabCase %>";
} from "../<%= metadata.srcDir || 'src' %>/<%= operation.operationIdKebabCase %><%_ if (metadata.esm) { _%>.js<%_ } _%>";
// Common request arguments
const requestArguments = {
Expand All @@ -25,7 +25,7 @@ const requestArguments = {
context: {} as any,
interceptorContext: {
logger: {
info: jest.fn(),
info: <% if (metadata.vitest) { %>vi<% } else { %>jest<% } %>.fn(),
},
},
} satisfies Omit<<%= operation.operationIdPascalCase %>ChainedRequestInput, 'input'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
<%- model.name %>FromJSON,
<%- model.name %>ToJSON,
<%_ }); _%>
} from "../models";
} from "../models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>";
import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { HttpRequest } from "@aws-sdk/protocol-http";
import { SignatureV4 } from "@aws-sdk/signature-v4";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
}
###/TSAPI_WRITE_FILE###/* tslint:disable */
/* eslint-disable */
export * from './models';
export * from './client/client';
export * from './models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';
export * from './client/client<%_ if (metadata.esm) { _%>.js<%_ } _%>';
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@
* Do not edit the class manually.
*/
import * as runtime from '../runtime';
import * as runtime from '../runtime<%_ if (metadata.esm) { _%>.js<%_ } _%>';
<%_ if (service.modelImports.length > 0) { _%>
import type {
<%_ service.modelImports.forEach((modelImport) => { _%>
<%- modelImport %>,
<%_ }); _%>
} from '../models';
} from '../models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';
import {
<%_ service.modelImports.forEach((modelImport) => { _%>
<%- modelImport %>FromJSON,
<%- modelImport %>ToJSON,
<%_ }); _%>
} from '../models';
} from '../models<%_ if (metadata.esm) { _%>/index.js<%_ } _%>';
<%_ } _%>
<%_ service.operations.forEach((operation) => { _%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
###/TSAPI_WRITE_FILE###/* tslint:disable */
/* eslint-disable */
<%_ services.forEach((service) => { _%>
export * from './<%- service.name %>Api';
export * from './<%- service.name %>Api<%_ if (metadata.esm) { _%>.js<%_ } _%>';
<%_ }); _%>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
###/TSAPI_WRITE_FILE###/* tslint:disable */
/* eslint-disable */
<%_ models.forEach((model) => { _%>
export * from './<%= model.name %>';
export * from './<%= model.name %><%_ if (metadata.esm) { _%>.js<%_ } _%>';
<%_ }); _%>
<%_ } _%>
Loading

0 comments on commit ad383ad

Please sign in to comment.