diff --git a/e2e/express.e2e-spec.ts b/e2e/express.e2e-spec.ts index 7194de819..e2c6c1c16 100644 --- a/e2e/express.e2e-spec.ts +++ b/e2e/express.e2e-spec.ts @@ -7,6 +7,7 @@ import * as request from 'supertest'; import * as SwaggerParser from 'swagger-parser'; import { DocumentBuilder, SwaggerModule } from '../lib'; import { ApplicationModule } from './src/app.module'; +import * as path from 'path'; describe('Express Swagger', () => { let app: NestExpressApplication; @@ -83,7 +84,10 @@ describe('Express Swagger', () => { app, builder.build() ); - SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument); + SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument, { + // to showcase that in new implementation u can use custom swagger-ui path. Useful when using e.g. webpack + customSwaggerUiPath: path.resolve(`./node_modules/swagger-ui-dist`), + }); await app.init(); }); @@ -100,6 +104,14 @@ describe('Express Swagger', () => { expect(response.status).toEqual(200); expect(Object.keys(response.body).length).toBeGreaterThan(0); }); + + it('content type of served static should be available', async () => { + const response = await request(app.getHttpServer()).get( + `${SWAGGER_RELATIVE_URL}/swagger-ui-bundle.js` + ); + + expect(response.status).toEqual(200); + }); }); describe('custom documents endpoints', () => { diff --git a/e2e/fastify.e2e-spec.ts b/e2e/fastify.e2e-spec.ts index 432d3eb05..237eb4589 100644 --- a/e2e/fastify.e2e-spec.ts +++ b/e2e/fastify.e2e-spec.ts @@ -7,6 +7,7 @@ import * as request from 'supertest'; import * as SwaggerParser from 'swagger-parser'; import { DocumentBuilder, SwaggerModule } from '../lib'; import { ApplicationModule } from './src/app.module'; +import * as path from 'path'; describe('Fastify Swagger', () => { let app: NestFastifyApplication; @@ -83,9 +84,12 @@ describe('Fastify Swagger', () => { beforeEach(async () => { const swaggerDocument = SwaggerModule.createDocument( app, - builder.build() + builder.build(), ); - SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument); + SwaggerModule.setup(SWAGGER_RELATIVE_URL, app, swaggerDocument, { + // to showcase that in new implementation u can use custom swagger-ui path. Useful when using e.g. webpack + customSwaggerUiPath: path.resolve(`./node_modules/swagger-ui-dist`), + }); await app.init(); await app.getHttpAdapter().getInstance().ready(); @@ -103,6 +107,14 @@ describe('Fastify Swagger', () => { expect(response.status).toEqual(200); expect(Object.keys(response.body).length).toBeGreaterThan(0); }); + + it('content type of served static should be available', async () => { + const response = await request(app.getHttpServer()).get( + `${SWAGGER_RELATIVE_URL}/swagger-ui-bundle.js` + ); + + expect(response.status).toEqual(200); + }); }); describe('custom documents endpoints', () => { diff --git a/lib/interfaces/swagger-custom-options.interface.ts b/lib/interfaces/swagger-custom-options.interface.ts index 584a96bb1..a28e6d3aa 100644 --- a/lib/interfaces/swagger-custom-options.interface.ts +++ b/lib/interfaces/swagger-custom-options.interface.ts @@ -11,6 +11,7 @@ export interface SwaggerCustomOptions { customJs?: string | string[]; customJsStr?: string | string[]; customfavIcon?: string; + customSwaggerUiPath?: string; swaggerUrl?: string; customSiteTitle?: string; validatorUrl?: string; diff --git a/lib/swagger-module.ts b/lib/swagger-module.ts index a04c9bfdc..1d9b6dfb7 100644 --- a/lib/swagger-module.ts +++ b/lib/swagger-module.ts @@ -20,6 +20,7 @@ import { getGlobalPrefix } from './utils/get-global-prefix'; import { normalizeRelPath } from './utils/normalize-rel-path'; import { validateGlobalPrefix } from './utils/validate-global-prefix.util'; import { validatePath } from './utils/validate-path.util'; +import { resolvePath } from './utils/resolve-path.util'; export class SwaggerModule { private static readonly metadataLoader = new MetadataLoader(); @@ -53,19 +54,23 @@ export class SwaggerModule { return this.metadataLoader.load(metadata); } - private static serveStatic(finalPath: string, app: INestApplication) { + private static serveStatic(finalPath: string, app: INestApplication, customStaticPath?: string) { const httpAdapter = app.getHttpAdapter(); - const swaggerAssetsAbsoluteFSPath = getSwaggerAssetsAbsoluteFSPath(); + + // See + const swaggerAssetsPath = customStaticPath + ? resolvePath(customStaticPath) + : getSwaggerAssetsAbsoluteFSPath(); if (httpAdapter && httpAdapter.getType() === 'fastify') { (app as NestFastifyApplication).useStaticAssets({ - root: swaggerAssetsAbsoluteFSPath, + root: swaggerAssetsPath, prefix: finalPath, decorateReply: false }); } else { (app as NestExpressApplication).useStaticAssets( - swaggerAssetsAbsoluteFSPath, + swaggerAssetsPath, { prefix: finalPath } ); } @@ -264,7 +269,6 @@ export class SwaggerModule { : path ); const urlLastSubdirectory = finalPath.split('/').slice(-1).pop() || ''; - const validatedGlobalPrefix = options?.useGlobalPrefix && validateGlobalPrefix(globalPrefix) ? validatePath(globalPrefix) @@ -292,7 +296,7 @@ export class SwaggerModule { } ); - SwaggerModule.serveStatic(finalPath, app); + SwaggerModule.serveStatic(finalPath, app, options?.customSwaggerUiPath); /** * Covers assets fetched through a relative path when Swagger url ends with a slash '/'. * @see https://github.com/nestjs/swagger/issues/1976 diff --git a/lib/utils/resolve-path.util.ts b/lib/utils/resolve-path.util.ts new file mode 100644 index 000000000..c8815a9d5 --- /dev/null +++ b/lib/utils/resolve-path.util.ts @@ -0,0 +1,5 @@ +import * as pathLib from 'path'; + +export function resolvePath(path: string): string { + return path ? pathLib.resolve(path) : path; +} \ No newline at end of file