vercel / ncc

Compile a Node.js project into a single file. Supports TypeScript, binary addons, dynamic requires.
https://npmjs.com/@vercel/ncc
MIT License
9.27k stars 291 forks source link

cant use with swagger #406

Open yeli19950109 opened 5 years ago

yeli19950109 commented 5 years ago

cant use with swagger for nestjs

styfle commented 5 years ago

Hello @yeli19950109

Can you create an example that fails with ncc build?

dominique-mueller commented 5 years ago

I'm experiencing the same issue, the path to the Swagger UI assets is no longer correct after building the app with ncc.

After some playing around, however, I've seem to found a workaround: Serving the static folder manually, after the Swagger setup. Here's a code snippet, I'm using /swagger as the public URL:

// ...

SwaggerModule.setup('/swagger', app, document);

app.useStaticAssets(join(__dirname, '/static'), {
  prefix: '/swagger',
});

// ...

Plus, I had to install fastify-swagger although I'm just using express in NestJS ... just wtf?

This all works for now, but surely is not a very pretty solution ...

dominique-mueller commented 5 years ago

The real issue is probably how swagger-ui itself gets built and served, and not necessarily how ncc tries to bundle it up. Related issue: https://github.com/scottie1984/swagger-ui-express/issues/114.

mohammadzainabbas commented 4 years ago

Any updates on this one ??

p.s: @dominique-mueller I have tried your proposed solution and it seems that I might be missing something out. Could you be kind enough to help me out with this issue ??

dominique-mueller commented 4 years ago

@mohammadzainabbas Sure, I took the time to prepare a repository here: https://github.com/dominique-mueller/ncc-nestjs-swagger-experiment. It's a working example, so you can clone it and play around with it. I also wrote a quick explanation in the README about what has to be done to get Swagger working in an existing ncc-NestJS project. Hope it helps!

eMarek commented 4 years ago

I have issues with swagger-ui-express as well after using ncc. @dominique-mueller, thank you for your example. I am wondering if it would be possible to achieve similar fix with express only? I am not using nestjs in my project.

tmtron commented 3 years ago

So my understanding of the issue is that swagger-ui works like this:
When a browser requests the swagger documentation URL (e.g. https://example.com/swagger/):

"normal" build (i.e. without ncc)

installed packages

dependencies

Runtime

When the server is running and the browser visits the swagger URL (e.g. https://example.com/swagger/):

"ncc" build:

installed packages

None: in this case we don't have access to node_modules/swagger-ui-dist
i.e. the reason to use ncc is that we don't need the large node_modules folder.

Runtime

So when we don't do anything special the swagger-html page will not work, because the requests for the relative links in the dynamic HTML cannot be resolved (express will return a HTTP 404 - Not Found error)

Fix

To fix this we must somehow provide these files. This requires 2 steps

  1. copy the relevant files to a folder which will be accessible (by node) at runtime, when we start the ncc output
  2. tell express to send those files to the client-browser when the correct URL is requested

copy files

This depends on your build tools: e.g. we use angular-cli, thus we can simply use assets to copy the relevant files, e.g.

"assets": [
   {
   "glob": "**/*.{js,css,html,png}",
   "input": "./node_modules/swagger-ui-dist/",
   "output": "./dist/assets/swagger-ui-dist/"
   },
   ....
]

Where dist is the ncc output dir (i.e. where the index.js or main.js file will be created)

serve files

When we use NestJs we can use this:

app.useStaticAssets(path.join(__dirname, 'assets/swagger-ui-dist/'), {
  prefix: '/swagger'
});

where app is our NestExpressApplication.
So basically this just configures express to return the files in the assets/swagger-ui-dist/ folder when the URL starts with /swagger: e.g. https://example.com/swagger/swagger-ui-bundle.js

Why installing "fastify-swagger" seems to work

This refers to the explanation in the ncc-nestjs-swagger-setup test project

I guess the reason is this:

When ncc analyses our code, it finds require('fastify-swagger') in the SwaggerModule.
When the package is installed (i.e. exists in node_modules), ncc will process it.
When the webpack-asset-relocator-loader (used by ncc) will notice that the file prepare-swagger-ui.js needs the files in the static folder:
image

And thus it will copy these files to the static folder in our dist dir.
Now we could serve these files and the swagger URL may work.

I think this may not work reliably, because there is no guarantee that the swagger-ui-dist version which is copied from the fastify-swagger package is compatible to the version used by swagger-ui.

So I think it's better to

Onihani commented 1 year ago

I was experiencing the same issue. Fortunately I got it to work without ncc. My solution is a bit hacky. Check out my solution on stack overflow: https://stackoverflow.com/a/74708365/13701992

ramazansancar commented 1 year ago

This is how I solved the problem. Deploying Platform: Vercel

image

kangfenmao commented 1 year ago

I wrote a patch to solve this problem. use https://unpkg.com/swagger-ui-dist@5.6.2/ as publicUrl

diff --git a/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js b/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
index 6f47648..8776139 100644
--- a/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
+++ b/node_modules/@nestjs/swagger/dist/swagger-ui/constants.js
@@ -1,8 +1,8 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.jsTemplateString = exports.htmlTemplateString = exports.favIconHtml = void 0;
-exports.favIconHtml = '<link rel="icon" type="image/png" href="<% baseUrl %>favicon-32x32.png" sizes="32x32" />' +
-    '<link rel="icon" type="image/png" href="<% baseUrl %>favicon-16x16.png" sizes="16x16" />';
+exports.favIconHtml = '<link rel="icon" type="image/png" href="<% publicUrl %>favicon-32x32.png" sizes="32x32" />' +
+    '<link rel="icon" type="image/png" href="<% publicUrl %>favicon-16x16.png" sizes="16x16" />';
 exports.htmlTemplateString = `
 <!-- HTML for static distribution bundle build -->
 <!DOCTYPE html>
@@ -10,7 +10,7 @@ exports.htmlTemplateString = `
 <head>
   <meta charset="UTF-8">
   <title><% title %></title>
-  <link rel="stylesheet" type="text/css" href="<% baseUrl %>swagger-ui.css" >
+  <link rel="stylesheet" type="text/css" href="<% publicUrl %>swagger-ui.css" >
   <% favIconString %>
   <style>
     html
@@ -71,8 +71,8 @@ exports.htmlTemplateString = `

 <div id="swagger-ui"></div>

-<script src="<% baseUrl %>swagger-ui-bundle.js"> </script>
-<script src="<% baseUrl %>swagger-ui-standalone-preset.js"> </script>
+<script src="<% publicUrl %>swagger-ui-bundle.js"> </script>
+<script src="<% publicUrl %>swagger-ui-standalone-preset.js"> </script>
 <script src="<% baseUrl %>swagger-ui-init.js"> </script>
 <% customJs %>
 <% customJsStr %>
diff --git a/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js b/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
index c067725..d60f616 100644
--- a/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
+++ b/node_modules/@nestjs/swagger/dist/swagger-ui/swagger-ui.js
@@ -55,6 +55,7 @@ function buildSwaggerHTML(baseUrl, swaggerDoc, customOptions = {}) {
         .replace('<% explorerCss %>', explorerCss)
         .replace('<% favIconString %>', favIconString)
         .replace(/<% baseUrl %>/g, baseUrl)
+        .replace(/<% publicUrl %>/g, 'https://unpkg.com/swagger-ui-dist@5.6.2/')
         .replace('<% customJs %>', toTags(customJs, toExternalScriptTag))
         .replace('<% customJsStr %>', toTags(customJsStr, toInlineScriptTag))
         .replace('<% customCssUrl %>', toTags(customCssUrl, toExternalStylesheetTag))

Patch file

@nestjs+swagger+7.1.10.patch

gaffarmalik commented 2 months ago

My swagger UI now shows with the correct CSS/JS after relocating it. But it still shows up as with the example "Petstore". Is there some kind of document (json) that's missing?