vite icon indicating copy to clipboard operation
vite copied to clipboard

The `proxyGuardOnlyEsm` does not work consistently

Open stenin-nikita opened this issue 1 year ago • 2 comments

Describe the bug

When I use the "merge-options" module in the Node environment, I get the error message "The requested module 'merge-options' does not provide an export named 'concatArrays'". As I understand it, this occurs because "vite" wraps the module with a "new Proxy", altering the context of "this" within the module functions.

For example, the "merge-options" library (https://github.com/schnittstabil/merge-options/blob/master/index.js#L155C51-L155C86) uses the following construction in its code: (this !== globalThis && this) || {}, and as a result, when accessing concatArrays here (https://github.com/schnittstabil/merge-options/blob/master/index.js#L143), the module's proxy object is accessed and an error occurs at runtime.

To resolve this issue, I propose the following solution:

export function analyzeImportedModDifference(
  mod: any,
  rawId: string,
  moduleType: string | undefined,
  metadata?: SSRImportBaseMetadata,
): void {
  // No normalization needed if the user already dynamic imports this module
  if (metadata?.isDynamicImport) return
  // If file path is ESM, everything should be fine
-  if (moduleType === 'module') return

  // For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js.
  // If the user named imports a specifier that can't be analyzed, error.
  if (metadata?.importedNames?.length) {
    const missingBindings = metadata.importedNames.filter((s) => !(s in mod))
    if (missingBindings.length) {
      const lastBinding = missingBindings[missingBindings.length - 1]

+     if (moduleType !== 'module') {
        // Copied from Node.js
        throw new SyntaxError(`\
[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '${rawId}';
const {${missingBindings.join(', ')}} = pkg;
`)
+     } else {
+       throw new SyntaxError(
+          `[vite] The requested module '${rawId}' does not provide an export named '${lastBinding}'`,
+       )
+     } 
    }
  }
}

and remove the function proxyGuardOnlyEsm.

Reproduction

https://stackblitz.com/edit/vitejs-vite-x1mh89?file=runtime.js

Steps to reproduce

npm install
npm run dev

System Info

System:
    OS: macOS 13.6.4
    CPU: (8) arm64 Apple M1
    Memory: 74.47 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.12.1 - ~/.nvm/versions/node/v18.12.1/bin/node
    npm: 8.19.2 - ~/.nvm/versions/node/v18.12.1/bin/npm
    pnpm: 8.15.4 - ~/.nvm/versions/node/v18.12.1/bin/pnpm
    bun: 1.0.29 - /opt/homebrew/bin/bun
  Browsers:
    Safari: 16.6
  npmPackages:
    vite: 5.2.11 => 5.2.11

Used Package Manager

npm

Logs

Click to expand!
Error: [vite] The requested module 'merge-options' does not provide an export named 'concatArrays'
    at _0x5084b5._evaluate (https://vitejsvitex1mh89-hklt-yd06spgk.w-corp-staticblitz.com/blitz.1d4c3cdd.js:40:793193)
    at async ModuleJob.run (https://vitejsvitex1mh89-hklt-yd06spgk.w-corp-staticblitz.com/builtins.e7ffca03.js:155:2441)

Validations

stenin-nikita avatar May 18 '24 15:05 stenin-nikita

Thanks for the investigation and proposing a solution. This makes sense to me and it would be nice to remove the proxy altogether. I'll run the PR on ecosystem-ci to see if it breaks downstream.

bluwy avatar May 28 '24 14:05 bluwy