webdiscus / pug-loader

Pug loader for Webpack renders pug to HTML or template function
https://webdiscus.github.io/pug-loader/pug-filters
ISC License
72 stars 5 forks source link

Errors don't show the exact location of the error #9

Closed Rush closed 2 years ago

Rush commented 2 years ago
[2] Template file: /code/nexus/portal/server/views/unsupported-browser.pug
[2] Possible reason: in the template may be used undefined variable.
[2] Solution in this case pass a variable into a pug file via the query parameter.
[2] For example, if you use in pug the external variable, like title= customData.options.title,
[2] then pass it into pug 'template.pug?customData=' + JSON.stringify({options:{title:'My title'}})
[2] TypeError: Cannot read properties of undefined (reading 'undefined')
[2]     at processResult (/code/nexus/node_modules/webpack/lib/NormalModule.js:750:12)
[2]     at /code/nexus/node_modules/webpack/lib/NormalModule.js:855:5
[2]     at /code/nexus/node_modules/loader-runner/lib/LoaderRunner.js:399:11
[2]     at /code/nexus/node_modules/loader-runner/lib/LoaderRunner.js:251:18
[2]     at runSyncOrAsync (/code/nexus/node_modules/loader-runner/lib/LoaderRunner.js:156:3)
[2]     at iterateNormalLoaders (/code/nexus/node_modules/loader-runner/lib/LoaderRunner.js:250:2)
[2]     at /code/nexus/node_modules/loader-runner/lib/LoaderRunner.js:223:4
[2]     at /code/nexus/node_modules/webpack/lib/NormalModule.js:829:15
[2]     at Array.eval (eval at create (/code/nexus/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:12:1)
[2]     at runCallbacks (/code/nexus/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:27:15)
[2]  @ ./server/views/index.ts 84:19-55
[2]  @ ./server/main.ts 29:16-34
[2]  @ ./server/mainPublic.ts 8:15-32 9:13-30

It only shows the main file where the error is, but it uses a layout which then uses a layout .. the error is hard to find.

webdiscus commented 2 years ago

1) Are you using the HtmlWebpackPlugin in your project? If yes, whether using in PUG external variables, e.g.:

div= htmlWebpackPlugin.options.title

2) Did this bug appear after update to v1.6.3 or is it a new case?

Could you please create a simple repo with the file unsupported-browser.pug and all other dependencies in this template. I need the source of unsupported-browser.pug and webpack.config.js.

Rush commented 2 years ago

In this case I am using the default function output. I discovered the problematic code is:

link(rel="preload" href=require('assets/fonts/ibm-plex-sans-v7-latin-regular.woff2') as="font" type="font/woff2" crossorigin)

which then goes to the webpack file-loader

This code worked with webdiscus 1.4 but broke with 1.6. I think perhaps the version should have been bumped to 2.x?

Perhaps 1.6 is doing something funky with require.

webdiscus commented 2 years ago

the error Cannot read properties of undefined (reading 'undefined') has not a relation with it the require(). If the required resource is not found (e.g. wrong resolved path), it will display some like Can't resolve the file ....

To locate the problem I need more information:

  1. Are you using the HtmlWebpackPlugin in your project? If yes, are external variables used in the pug template? e.g.:
    div= htmlWebpackPlugin.options.title
  2. Is used Webpack 4 or 5?
  3. What version of pug-loader was used? 1.6.0, 1.6.1, 1.6.2, 1.6.3?

I have created the test case for require fonts.

With Webpack 5 it works

index.pug, where the 'assets/fonts/ is relative path by the the template

html
  head
    link(rel="preload" href=require('assets/fonts/ibm-plex-sans-v7-latin-regular.woff2') as="font" type="font/woff2" crossorigin)
  body
    p Hello World!
    img(src=require('image.jpeg'))

webpack.config.js

const path = require('path');
const PugPlugin = require('pug-plugin');

module.exports = {
  output: {
    path: path.join(__dirname, 'public/'),
    publicPath: '/',
  },

  entry: {
    index: 'src/index.pug', // the `pug-plugin` is required 
  },

  plugins: [
    new PugPlugin() //  extract HTML from pug defined in webpack entry and save to separate file
  ],

  module: {
    rules: [
      {
        test: /\.pug$/,
        loader: PugPlugin.loader, // `@webdiscus/pug-loader` is already included in PugPlugin
        options: {
          method: 'render', // export the HTML string, rendered at compile time
        },
      },

      {
        test: /\.(png|jpg|jpeg)/,
        type: 'asset/resource', // <= must be exact this to handles an image
        generator: {
          filename: 'assets/images/[name].[hash:8][ext]', // save required image under this filename
        },
      },

      {
        test: /\.(eot|ttf|woff|woff2)/,
        type: 'asset/resource',  // <= must be exact this to handles an font
        generator: {
          filename: 'assets/fonts/[name][ext]', // save required font under this filename
        },
      },
    ],
  },
};

What is difference by usage require() to compare with your case? I think the problem is elsewhere.

The file-loader is deprecated for Webpack 5, link to file-loader doc. For Webpack 5 should be used assets module.

From ver 1.6 I have "improved" the resolving of required resources. The "interpolation" of paths was replaced with "evaluation" of once. Now is possible use a variable in require('path/to/images/${myImage}') at compile time. Added resolving of paths from tsconfig.js.

Rush commented 2 years ago

Thank you for the investigation. Unfortunately I can't use the assets modules as it's not as flexible when it comes to the output paths.

I am trying to make a reproducible small test case, which is not that easy. :)

Rush commented 2 years ago

I suspect method: "compile" is for some reason not being used.

https://github.com/Rush/webdiscus-issue-9-repro

I also suspect file-loader has nothing to do with it. It's an issue purely in pug-loader. Please check out my repo and follow README.

Rush commented 2 years ago

In regards to file-loader I need more granularity as for where to output the files. I believe webpack doesn't expose such granular options for assets.

  const fileLoader = {
    loader: 'file-loader',
    options: {
      esModule: false,
      outputPath: '../public-built',
      context: path.resolve(__dirname),
      publicPath: '/',
      name: '[hash].[ext][query]'
    },
  };
Rush commented 2 years ago

Just wondering if you were able to run my repo and reproduce?

webdiscus commented 2 years ago

Just wondering if you were able to run my repo and reproduce?

Sorry, I am just very busy, I try your repo tomorrow. Thank you for the repo!

Rush commented 2 years ago

Sorry, I am just very busy,

Good luck with whatever keeps you occupied! :)

I try your repo tomorrow.

Thanks!

webdiscus commented 2 years ago

yes, I can reproduce the issue and I found the place in the code. I will fix it. Thank you for very important issue report.

webdiscus commented 2 years ago

The problem is by usage variables in template. At pre compile time the usage of variables like opts.prop1.prop2 or opts[prop1] has issue because they are not defined. Only in runtime will be passed variables available. This is the BUG :-/

Temporary solution

Anywhere in PUG use ?. notation (node >= 14) by each variable with a property. Use this only in JS context, not in PUG context, e.g.:

you use already the correct chained properties with ?.:

 - const osName = parsedUA?.os?.name; // RIGHT!

In your template unsupported-browser.pug you should change:

this

span.browser-icon
  +svgIcon(browserIcons[recommendedBrowser])

to

span.browser-icon
  - const icons = browserIcons?.[recommendedBrowser];
  +svgIcon(icons)

and in mixin self use properties with ?.:

mixin svgIcon(iconData, currentColor = 'currentColor')
  - const [width, height, _, __, paths] = iconData?.icon || [];
  ...

I understand, this is not comfortable and tricked, sorry, but currently this works with this limitation.

P.S. I try to fix it, but it is not easy and not quick.

Rush commented 2 years ago

The workarounds seem really messy. I will stick with the old version.

Probably variable existence check should only be applied to html render mode.

webdiscus commented 2 years ago

Hello @Rush,

in the new version 1.7.0 I have fixed the issue undefined variable by usage the compile method.

This was a compromise for compile method:

But the same limitation has original pug-loader. The problem is with webpack require. My solution in v1.6. was a mega useful "killer-feature", no other pug-loader does this.

Do you can please try to test the version 1.7.0?

Rush commented 2 years ago

Yes thank you. This is now solved. There is another issue for which I'll open a different ticket.

Rush commented 2 years ago

https://github.com/webdiscus/pug-loader/issues/10