nextcloud-libraries / nextcloud-vue

🍱 Vue.js components for Nextcloud app development ✌ https://npmjs.org/@nextcloud/vue
https://nextcloud-vue-components.netlify.app/
Other
216 stars 87 forks source link

Issues when importing some functions from NcRichText.js #3864

Closed julien-nc closed 1 year ago

julien-nc commented 1 year ago

When importing registerWidget, registerCustomPickerElement or NcCustomPickerRenderResult from @nextcloud/vue/dist/Components/NcRichText.js the script fails and we get Uncaught TypeError: inspect is undefined in the browser console:

image

But this error goes away as soon as we import something from @nextcloud/vue-richtext, even an empty import like import {} from '@nextcloud/vue-richtext'.

This issue cannot be reproduced when linking with @nextcloud/vue (same with master or v7.8.0, it works fine).

So it seems there are some missing dependencies in the @nextcloud/vue package. Or can it be related with node-polyfill? I'm lost there.

To reproduce this bug, one can use this integration_tmdb branch: https://github.com/julien-nc/integration_tmdb/tree/enh/noid/replace-vue-richtext-by-vue , compile and enable the app in NC 26 or master and browse Talk or the Files app (with Text enabled). The problematic import is in https://github.com/julien-nc/integration_tmdb/blob/enh/noid/replace-vue-richtext-by-vue/src/reference.js#L23-L24

cc @raimund-schluessler @juliushaertl

raimund-schluessler commented 1 year ago

I went through the (dev)dependencies list of vue-richtext again, and found that these packages

"mdast-util-from-markdown": "^1.2.0",
"mdast-util-gfm-autolink-literal": "^1.0.2",
"mdast-util-to-string": "^3.1.0",
"micromark-extension-gfm-autolink-literal": "^1.0.3",
"remark-disable-tokenizers": "^1.1.0",

are not present in nextcloud/vue anymore. I didn't install them in #3841 because I thought that they were unused (I didn't find where they were imported/used). Maybe it helps adding them?

It would probably help to know which package includes inspect. Would it work to compare the bundles produced with and w/o the import from vue-richtext?

mejo- commented 1 year ago

@julien-nc I'm unable to reproduce this issue with either your branch of integration_tmdb and with collectives. I did the following:

Anything I'm missing? :thinking:

julien-nc commented 1 year ago

I've tried it with server stable26 and master in Talk and Text: not working. I'm using npm v8.15.0 and node v16.17.0. Weird indeed. What could be different between our contexts?

juliusknorr commented 1 year ago

Just sharing the screenshot I took when checking this a bit with @julien-nc

It was util -> console-browserify -> assert trying to call this

Screenshot 2023-03-06 at 21 50 26

My first thought was that https://www.npmjs.com/package/node-polyfill-webpack-plugin was missing but adding didn't make any difference.

juliusknorr commented 1 year ago

Also worth to mention that this doesn't happen in server with https://github.com/nextcloud/server/pull/33755

juliusknorr commented 1 year ago

I could reproduce this locally with using npm pack instead of npm link, however have not really figured out what the difference is and why this doesn't work without the separate import.

Failing upstream lines in the trace https://github.com/browserify/commonjs-assert/blob/master/internal/assert/assertion_error.js#L6 https://github.com/browserify/commonjs-assert/blob/master/internal/assert/assertion_error.js#L456

Steps to use npm pack

julien-nc commented 1 year ago

@ShGKme Hey, do you have any clue about that? Could it be caused by the webpack config in nextcloud/vue and how the package is built?

ShGKme commented 1 year ago

@ShGKme Hey, do you have any clue about that? Could it be caused by the webpack config in nextcloud/vue and how the package is built?

better late than never

however have not really figured out what the difference is

Short answer:

  1. npm pack + update package.json + npm ci = Installing the library
  2. npm link = Link to a folder with the library

The difference between "The library" and "The folder with the library" is the dependency resolving:

  1. With npm pack you have a library and then dependencies are resolved by npm ci (or npm install):
    • NPM installs only lib's "production" dependencies to your app node_modules and resolves version conflicts
  2. With npm link in the linked library's folder you most likely already have node_modules folder:
    • For the library dependencies, this folder has a higher priority than your app's node_modules
    • This node_modules most likely also has devDependencies (because you built the lib earlier)
# With npm pack + npm ci
app
  node_modules/
    ...<app and app libs' production required dependencies>
    ...<production required @nextcloud/vue dependencies>
    @nextcloud/vue/
      node_modules/
        ...<production required @nextcloud/vue dependencies with version conflicts>

# With npm link
app
  node_modules/
    ...<app and app libs' production required dependencies>
    [linked] @nextcloud/vue
      node_modules/
        ...<all @nextcloud/vue dependencies>
        ...<all @nextcloud/vue devDependencies>

So, if some lib is a dependency of your app and your app's other libs and also a devDependency of the lib.

Right now I cannot say what exactly caused the problem. But I'm sure for 75%, that the problem is around wrong dependencies and devDependencies. The understanding of devDependencies for frontend libs is often not correct. It is not "development-tools deps". It is "not required to be installed with the lib dependencies". Or about "what is bundled with the lib".

I will look into the reproduction repo.

ShGKme commented 1 year ago

Sooo. I could reproduce it and found the problem after some funny hours.

Preface

  1. Some libs are isomorph (for example, axios) and uses native Node modules (such as util, process)
  2. In @nextcloud/webpack-vue-config we polyfill all native "Node" modules with node-polyfill-webpack-plugin
  3. All means ALL. Including, for example, console

Why it fails

  1. NcRichText depends on util native Node module polyfill
  2. util polyfill depends on console polyfill (link)
  3. console polyfill depends on util and assert native node modules
  4. 🎉 Congratulations: you have circular dependencies util -> console -> util 🔁
  5. But the circular dependencies is not the problem. While module is in process of executing it already exists as an (empty) object. Dependencies can save the reference to this object.
  6. assert polyfill depends on util (more circular dependencies)
  7. assert also REQUIRES util polyfill execution to be completed before. Because it uses not use a reference to the whole module object but takes a property from the module during evaluation
// 📁 browserify/commonjs-assert/assert.js
const { inspect } = require('util')
// While `util` is in process of evaluating, util = {}, inspect === undefined
// Later `util` will be evaluated. But `inspect` will remain undefined.

Why it works

It is enough to evaluate console before util 🏆

  1. @nextcloud/vue-richtext requires @nextcloud/axois -> axios -> buffer polyfill -> console polyfill
  2. console -> util -> console. On this moment util is successfully polyfilled (having a reference to an empty console polyfill module).
  3. console -> assert -> util now works, because util is already complete and cached 🥳

How to solve the problem

Console should not be polyfilling. To archive that we need to exclude it from node-polyfill-webpack-plugin:

// 📁 webpack.config.js
const webpackConfig = {
  plugins: [
    // ... other plugins ...
-   new NodePolyfillPlugin()
+   new NodePolyfillPlugin({
+     excludeAliases: ['console']
+   })
    // ... other plugins ... 
  ]
}

Workaround for integration_tmdb:

// 📁 webpack.js
// eslint-disable-next-line n/no-extraneous-require
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
webpackConfig.plugins[1] = new NodePolyfillPlugin({
    excludeAliases: ['console'],
})

Note

Buffer is also available in the browser and should not be polyfelt.

julien-nc commented 1 year ago

@ShGKme Thank you so much for the effort! You :guitar:. So in you opinion there is nothing to adjust in @nextcloud/vue but rather in apps that use it (via a fix in @nextcloud/webpack-vue-config), right? In other words: Could this be fixed by adjusting something in @nextcloud/vue or do you think fixing @nextcloud/webpack-vue-config is the only way to go?

ShGKme commented 1 year ago

In other words: Could this be fixed by adjusting something in @nextcloud/vue or do you think fixing @nextcloud/webpack-vue-config is the only way to go?

It is not related to @nextcloud/vue because it is a problem with application bundling. It can be fixed either in a certain app or in @nextcloud/webpack-vue-config

ShGKme commented 1 year ago

I'm closing the issue as it is not @nextcloud/vue issue