Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swagger UI + Webpack: no such file or directory, open 'C:\indexTemplate.html' #90

Closed
azizj1 opened this issue Sep 11, 2018 · 18 comments
Closed

Comments

@azizj1
Copy link

azizj1 commented Sep 11, 2018

I have an Express app built via webpack 4.17.2. It works great until I add swagger-ui-express (v4.0.1). I added swagger to my express app by doing:

import * as swaggerUi from 'swagger-ui-express';
import * as swaggerDoc from './swagger.json';
// ...
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc));

As soon as I do that, my webpack build crashes with the following error:

None
I've uploaded a gist with more details on the configuration: webpack.config.js, app.ts, and relevant info from package.json. And another gist with just the swagger.json.

Node: 8.9.4

Any help would be appreciated.

@shockey
Copy link
Contributor

shockey commented Sep 11, 2018

Looks like this error is coming from here:

var html = fs.readFileSync(__dirname + '/indexTemplate.html');

which is strange, it would appear that __dirname is empty.

@azizj1, can you edit node_modules/swagger-ui-express/index.js and add a console.log(__dirname) before the line I linked to, and share the output?

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

I added console.log('__dirname=' + __dirname); and got the output of __dirname=/

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

I got it to work. I updated my webpack.config.js and added the following:

const config = {
    node: {
        __dirname: true
    },

And now, __dirname=node_modules\swagger-ui-express

Thank you!

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

One more issue unfortunately, along the same vein. When I build with a libraryTarget of commonjs2, i.e.,:

const config = {
    mode: DEBUG ? 'development' : 'production',
    target: 'node',
    node: {
        __dirname: true
    },
    entry: DEBUG ?
        { 'local': './src/bin/www.ts' } :
        { 'lambda': './src/bin/lambda.ts' }
    ,
    output: {
        path: BUILD_DIR,
        filename: '[name].js',
        publicPath: '/',
        ... DEBUG ? {} : {
            libraryTarget: 'commonjs2'
        }
    },

I get a runtime error:

module initialization error: Error
at Object.fs.openSync (fs.js:646:18)
at Object.fs.readFileSync (fs.js:551:33)
at s (/var/task/lambda.js:536:1323)
at Object.setup (/var/task/lambda.js:536:2433)
at Object.<anonymous> (/var/task/lambda.js:296:57091)
at n (/var/task/lambda.js:1:186)
at Object.module.exports.Object.defineProperty.value (/var/task/lambda.js:296:56347)
at n (/var/task/lambda.js:1:186)
at /var/task/lambda.js:1:986
at Object.<anonymous> (/var/task/lambda.js:1:997)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)

Any thoughts on that?

I was able to console.log and get that __dirname=node_modules\swagger-ui-express, but I'm still crashing unfortunately.

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

Update: I wrapped the var html = fs.readFileSync(__dirname + '/indexTemplate.html'); in a try/catch to get more details on the error:

  var html = null;
  try {
    html = fs.readFileSync(__dirname + '/indexTemplate.html');
    console.log('done reading html!');
    console.log(html);
  } catch (e) {
    console.log('error reading sync of indexTemplate.html: ' + JSON.stringify(e));
  }

And I got the following error:

2018-09-11T19:40:35.247Z	8cb71427-b5fa-11e8-bfad-196a00a45e9d	error reading sync of indexTemplate.html:
{
    "errno": -2,
    "code": "ENOENT",
    "syscall": "open",
    "path": "node_modules\\swagger-ui-express/indexTemplate.html"
}

Now I'm a little unsure of where to go from here.

@scottie1984
Copy link
Owner

I am no webpack expert and can only point you to this thread which may or may not help webpack/webpack#1599

@shockey
Copy link
Contributor

shockey commented Sep 11, 2018

@azizj1, try webpack: false instead of webpack: true in your config.

Per the docs:

true: The dirname of the input file relative to the context option.

false: The regular Node.js __dirname behavior. The dirname of the output file when run in a Node.js environment.

I believe we need the latter here in order for the indexTemplate behavior to work.

@shockey
Copy link
Contributor

shockey commented Sep 11, 2018

That said... I'm not 100% sure that swagger-ui-express will work with Webpack at all, since swagger-ui-express uses the filesystem at runtime.

Is there any particular reason you're using webpack to compile a Node.js application? Generally, you'd use webpack to produce bundles for single-page applications - curious what your use case here is.

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

Yea, I'm ready to concede that it's not going to work with webpack. :( I was able to get it running locally just fine, but on my dev environment, hitting the /docs endpoint gave the following console error:

image

The server is returning the files, just the content of the files is all messed up:

image

azizj1 added a commit to azizj1/google-calendar-analytics that referenced this issue Sep 11, 2018
@shockey
Copy link
Contributor

shockey commented Sep 11, 2018

If I was to hazard a guess I'd say that express.static isn't playing nice with the bundling:

return express.static(swaggerUi.getAbsoluteFSPath(), opts)

I see you have a lot of other things going on in your app (TypeScript, Babel, etc)... consider setting up a test repo with only webpack, express, and Swagger UI Express, and try to reproduce your issue there. With so many parts, it could be anything.

@azizj1
Copy link
Author

azizj1 commented Sep 11, 2018

So locally, it only works when I have node.__dirname = true in my webpack.config.js. On AWS lambda (which has a libraryTarget of commonjs2, it doesn't work with node.__dirname = true, =false, or mock, which is ultimately what I need it for.

@scottie1984
Copy link
Owner

scottie1984 commented Jan 19, 2019

Does the library target not need to be umd as described here: https://blog.atj.me/2017/10/bundle-lambda-functions-using-webpack/

@elitsi
Copy link

elitsi commented Jul 31, 2019

I have managed to make swagger-ui work with AWS Lambda.
Using 'CopyWebpackPlugin' you could copy swagger static files into the root directory of the lambda function.

Also, __dirname in the webpack configurations should be false.
This way, when swagger will get the absolute path for serving the files, it will server them from the function dir.

In the webpack.config.js file, inside "plugins" , insert the following -
plugins: [ new CopyWebpackPlugin([ '../../../node_modules/swagger-ui-express/indexTemplate.html.tpl', '../../../node_modules/swagger-ui-express/swagger-ui-init.js.tpl', '../../../node_modules/swagger-ui-dist/swagger-ui.css', '../../../node_modules/swagger-ui-dist/swagger-ui-bundle.js', '../../../node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js', '../../../node_modules/swagger-ui-dist/favicon-16x16.png', '../../../node_modules/swagger-ui-dist/favicon-32x32.png' ]) ]

@WeslyG
Copy link

WeslyG commented Nov 20, 2019

So locally, it only works when I have node.__dirname = true in my webpack.config.js. On AWS lambda (which has a libraryTarget of commonjs2, it doesn't work with node.__dirname = true, =false, or mock, which is ultimately what I need it for.

I try to test node.__dirname = true, but did not get the expected result, apparently this is due to the features of my build

module.exports = {
  mode: 'development',
  watch: true,
  watchOptions: {
    aggregateTimeout: 100
  },
  node: {
    global: true,
    __dirname: true,
  },
  devtool: 'eval',
  entry: ['./app.js'],
  target: 'node',
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader'
      }
    }]
  },
  plugins: [
    new NodemonPlugin(),
    new Dotenv(),
    new CleanWebpackPlugin(['build/*'])
  ],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.dev.js'
  },
};

@azizj1 - How did you manage to make it work on the local machine?
"webpack": "^4.39.1",
node 12 (latest)

@Berger92
Copy link

Berger92 commented Jan 24, 2020

I can confirm that @elitsi 's solution worked for me.

So I added this to the webpack config:

   node: {
        __dirname: false
    },
    plugins: [
        new CopyWebpackPlugin([
            'node_modules/swagger-ui-dist/swagger-ui.css',
            'node_modules/swagger-ui-dist/swagger-ui-bundle.js',
            'node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js',
            'node_modules/swagger-ui-dist/favicon-16x16.png',
            'node_modules/swagger-ui-dist/favicon-32x32.png'
        ])
    ]

The swagger-dist files have to be next to the lambda handler file.

@scottie1984
Copy link
Owner

I can confirm that @elitsi 's solution worked for me.

So I added this to the webpack config:

   node: {
        __dirname: false
    },
    plugins: [
        new CopyWebpackPlugin([
            'node_modules/swagger-ui-dist/swagger-ui.css',
            'node_modules/swagger-ui-dist/swagger-ui-bundle.js',
            'node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js',
            'node_modules/swagger-ui-dist/favicon-16x16.png',
            'node_modules/swagger-ui-dist/favicon-32x32.png'
        ])
    ]

The swagger-dist files have to be next to the lambda handler file.

@Berger92 - was thinking off adding this to the Readme for future webpack users.

@JoshuaAlzate
Copy link

JoshuaAlzate commented Jul 23, 2020

I can confirm that @elitsi 's solution worked for me.

So I added this to the webpack config:

   node: {
        __dirname: false
    },
    plugins: [
        new CopyWebpackPlugin([
            'node_modules/swagger-ui-dist/swagger-ui.css',
            'node_modules/swagger-ui-dist/swagger-ui-bundle.js',
            'node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js',
            'node_modules/swagger-ui-dist/favicon-16x16.png',
            'node_modules/swagger-ui-dist/favicon-32x32.png'
        ])
    ]

The swagger-dist files have to be next to the lambda handler file.

Whenever I try this, I got this error

Argument of type 'string[]' is not assignable to parameter of type 'CopyPluginOptions'. Property 'patterns' is missing in type 'string[]' but required in type 'CopyPluginOptions'.ts(2345)

Most likely the CopyWebpackPlugin changed their implementation. I got it working by replacing it with this

plugins: [
    new CopyWebpackPlugin({
        patterns: [
            './node_modules/swagger-ui-dist/swagger-ui.css',
            './node_modules/swagger-ui-dist/swagger-ui-bundle.js',
            './node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js',
            './node_modules/swagger-ui-dist/favicon-16x16.png',
            './node_modules/swagger-ui-dist/favicon-32x32.png'
        ]
    })
]

@scottie1984
Copy link
Owner

Closing with solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants