lucacasonato / esbuild_deno_loader

Deno module resolution for `esbuild`
https://deno.land/x/esbuild_deno_loader
MIT License
160 stars 43 forks source link

Unable to build with `npm:*` dependencies #127

Open lukeed opened 4 months ago

lukeed commented 4 months ago

I'm probably doing something wrong, but I was only able to move passed this by adding npm:* to my esbuild externals options.

Error

error: Uncaught (in promise) Error: Build failed with 5 errors:
lib/database.ts:1:21: ERROR: [plugin: deno-loader] Unreachable: 'npm:postgres@3.4.4' loaded but not reachable
lib/utils.ts:1:23: ERROR: [plugin: deno-loader] Unreachable: 'npm:/dequal@2.0.3/lite' loaded but not reachable
lib/utils/parse/xml.ts:1:26: ERROR: [plugin: deno-loader] Unreachable: 'npm:fast-xml-parser@4.2.6' loaded but not reachable
lib/utils/z.ts:1:24: ERROR: [plugin: deno-loader] Unreachable: 'npm:flattie@1.1.1' loaded but not reachable
services/.../index.ts:1:19: ERROR: [plugin: deno-loader] Unreachable: 'npm:prexit@2.2.0' loaded but not reachable

This happens both with npm:* aliases in my deno.imports map and also with the npm: specifier in the source file directly.

Config

let config: BuildOptions = {
    write: true,
    bundle: true,
    minify: options.isMinify,
    treeShaking: true,
    entryPoints: [entry],
    outfile: join(outdir, 'index.js'),
    sourcemap: (options.isMinify || options.isDev) ? 'inline' : false,
    logLevel: 'info',
    format: 'esm',
    // external: ['npm:*'],
    plugins: denoPlugins({
      configPath: toAbsolute('../deno.jsonc'),
      lockPath: toAbsolute('../deno.lock'),
    }),
    define: {
      __DEV__: options.isDev ? 'true' : 'false',
    },
  };

Import Map

{
  "imports": {
     // ...
    "dequal": "npm:dequal@2.0.3",
    "esbuild": "npm:esbuild@0.20.2",
    "postgres": "npm:postgres@3.4.4",
    "prexit": "npm:prexit@2.2.0"
  }
}

Also, likely unrelated, but I noticed that the error message rewrote my dequal/lite import as npm:/dequal@... (leading slash)

KyleJune commented 4 months ago

I'm having a similar issue with npm specifiers, specifically with react-router-dom and react-helmet-async.

Building app
✘ [ERROR] Could not resolve "react-helmet-async" [plugin deno-loader]

    ../mod.tsx:8:34:
      8 │ import * as reactHelmetAsync from "react-helmet-async";
        ╵                                   ~~~~~~~~~~~~~~~~~~~~

  You can mark the path "react-helmet-async" as external to exclude it from the bundle, which will
  remove this error and leave the unresolved path in the bundle.

✘ [ERROR] Could not resolve "react-router-dom" [plugin deno-loader]

    routes/blog/index.tsx:1:21:
      1 │ import { Link } from "react-router-dom";
        ╵                      ~~~~~~~~~~~~~~~~~~

  You can mark the path "react-router-dom" as external to exclude it from the bundle, which will
  remove this error and leave the unresolved path in the bundle.

Here are the relevant entries in my deno.jsonc's imports option.

    "esbuild": "npm:esbuild@0.20",
    "@luca/esbuild-deno-loader": "jsr:@luca/esbuild-deno-loader@^0.10.3",
    "react-router-dom": "npm:react-router-dom@6",
    "react-helmet-async": "npm:react-helmet-async@2",

Here is what my esbuild call looks like.

await esbuild.build({
  plugins: [
    ...denoPlugins({ configPath }),
  ],
  absWorkingDir: workingDirectory,
  entryPoints: [entryPoint],
  outdir,
  bundle: true,
  splitting: true,
  treeShaking: true,
  platform: "neutral",
  format: "esm",
  jsx: "automatic",
  jsxImportSource: "react",
});
esbuild.stop();
KyleJune commented 4 months ago

I just figured out the issue for me, it appears that with the platform being neutral, it fails. I found a PR on the fresh repo where they made a similar change from "neutral" to "browser". I just had this issue because I was trying to switch from esm.sh to npm specifiers.

https://github.com/denoland/fresh/commit/bb48f7b8ebe2ccec60f3f148a9613a9a55307a67

Maybe this could fix the issue for you too, by explicitly setting platform to "browser".

nandorojo commented 2 months ago

browser didn't work for me. I'm getting that issue trying to render react:

import { createRoot } from 'npm:react-dom'
import * as React from 'npm:react'

function App() {
  return <div>Hello World</div>
}

createRoot(document.getElementById('root')!).render(<App />)

My ESBuild file is as follows:

import * as esbuild from 'npm:esbuild@0.20.2'
// Import the WASM build on platforms where running subprocesses is not
// permitted, such as Deno Deploy, or when running without `--allow-run`.
// import * as esbuild from "https://deno.land/x/esbuild@0.20.2/wasm.js";

import { denoPlugins } from 'jsr:@luca/esbuild-deno-loader@^0.10.3'

const result = await esbuild.build({
  plugins: [...denoPlugins()],
  //   entryPoints: ['https://deno.land/std@0.185.0/bytes/mod.ts'],
  entryPoints: ['./app.tsx'],
  outfile: './dist/bytes.esm.js',
  bundle: true,
  format: 'esm',
  jsx: 'automatic',
  treeShaking: true,
  minify: true,
  platform: 'browser',
})

console.log(result.outputFiles)

esbuild.stop()

And I get this error:

ERROR: [plugin: deno-loader] Module not found "file:///Users/.../ui/app/react/jsx-runtime".
nandorojo commented 2 months ago
Screenshot 2024-06-18 at 4 05 20 PM

I just found this in the source code.

Tried this but no luck:

  plugins: [
    ...denoPlugins({
      loader: 'native',
      nodeModulesDir: true,
    }),
  ],
twosaturdayscode commented 2 months ago

Hey guys I've found this issue while trying to use esbuild to compile react tsx to js and after a while I've got it working.

My esbuild configuration is like this:

import {
  denoResolverPlugin,
  denoLoaderPlugin,
} from 'jsr:@luca/esbuild-deno-loader@0.10.3'

const ctx = await esbuild.context({
    plugins: [
      denoResolverPlugin({
        configPath: `${root}/deno.json`,
      }),
      denoLoaderPlugin({ configPath: `${root}/deno.json` }),
    ],
    entryPoints: [`${root}/main.tsx`],
    outdir: dest,
    bundle: true,
    format: 'esm',
    sourcemap: true,
    jsx: 'automatic',
    platform: 'browser',
    define: {
      globalThis: 'window',
      ...envs,
    },
  })

Where root is the absolute path of the project folder. Maybe you don't need to specify the configs because I'm trying to create a "monorepo" like experience in my project by building some sort of toolchain but this should give you the gist of it.

While the imports inside deno.json are these:

{
  "imports": {
    "react": "https://esm.sh/react@18.3.1",
    "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
    "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime"
  }
}

I've also a root deno.json with the compilerOptions since I couldn't get the language server understand them from the den.json inside the package, probably you can merge these in one config file:

// This root config to let the Deno Language Server understand compiler options.
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"],
    "jsx": "react-jsx",
    "jsxImportSource": "https://esm.sh/react@18.3.1"
  }
}
gabeidx commented 2 months ago

@twosaturdayscode solution has worked for me as well.