solid-start icon indicating copy to clipboard operation
solid-start copied to clipboard

[Bug]: top-level `await` does not work with libraries when enabled (and is not supported out of the box)

Open trusktr opened this issue 1 year ago • 16 comments

Duplicates

  • [X] I have searched the existing issues

Latest version

  • [X] I have tested the latest version

Current behavior 😯

Use npm create solid to make a basic app, then put a top-level await something in a module, and you'll get an error during npm run build like so:

 ERROR  Transform failed with 1 error:                                                                               10:24:53 AM
/Users/trusktr/src/showcase/.vinxi/build/ssr/ssr.js:1:17460: ERROR: Top-level await is not available in the configured target environment ("es2019")

Expected behavior 🤔

It should just work out of the box. It works in all JS runtimes and browsers out of the box.

Steps to reproduce 🕹

Problem 1:

Steps:

  1. npm create solid
  2. put an await something() at the top level of a module
  3. run npm run build
  4. see error in terminal after loading the app in browser

The error will look something like this:

 ERROR  Transform failed with 1 error:                                                                               10:24:53 AM
/Users/trusktr/src/showcase/.vinxi/build/ssr/ssr.js:1:17460: ERROR: Top-level await is not available in the configured target environment ("es2019")

To fix the problem, the following will not work:

export default defineConfig({
	vite: {
		build: { target: 'esnext' },
	},
})

but one would think that it would, because this is what we see when we look at intellisense:

Screenshot 2024-08-24 at 10 54 43 AM

In the screenshot we can see that the auto-completion in VS Code will give us a strong hint that setting this option will work. But that's not the case (when it comes to application code).

Instead, we must set a different option of the same name at another location, which is not so obvious considering that Vite uses esbuild and one may very well assume the vite esbuild config will do the trick:

export default defineConfig({
	server: {
		esbuild: { options: { target: 'esnext' } },
	},
})

Problem 2:

If you import a library from npm that has top-level await, there's no way to make it work. Setting either of the two options above will not work!

Steps:

  1. npm create solid
  2. npm install yoga-layout
  3. npm run dev
  4. see error in terminal after loading the app in browser

The error will look something like this,

✘ [ERROR] Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides)

    node_modules/yoga-layout/dist/src/index.js:13:26:
      13 │ const Yoga = wrapAssembly(await loadYoga());
         ╵                           ~~~~~

8:19:44 PM [vite] error while updating dependencies:
Error: Build failed with 1 error:
node_modules/yoga-layout/dist/src/index.js:13:26: ERROR: Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides)
    at failureErrorWithLog (/Users/trusktr/src/showcase/node_modules/vite/node_modules/esbuild/lib/main.js:1472:15)
    at /Users/trusktr/src/showcase/node_modules/vite/node_modules/esbuild/lib/main.js:945:25
    at /Users/trusktr/src/showcase/node_modules/vite/node_modules/esbuild/lib/main.js:1353:9
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

which as you can imagine is frustrating because yoga-layout is a 3rd-party library, and the neither of the two target configs is helping.

The error says my target environment is chrome87. Why is that, if I've specified esnext, when is comes to the node_modules dependency?

Context 🔦

All JS runtimes support top-level await natively for a long time now. Solid Start should too!

Your environment 🌎

npmPackages:
❯ npm ls
showcase@ /Users/trusktr/src/showcase
├── @babel/[email protected]
├── @solidjs/[email protected]
├── @solidjs/[email protected]
├── @types/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

trusktr avatar Aug 26 '24 03:08 trusktr

Reproduction: https://github.com/lume/showcase/tree/solid-start-issue-1614

git clone [email protected]:lume/showcase.git
cd showcase
git checkout solid-start-issue-1614
npm ci
npm run dev # see error in terminal after loading the app in browser

trusktr avatar Aug 26 '24 03:08 trusktr

Workaround:

A workaround is to do a native import() inside eval() (to avoid the compiler handling it):

// If using typescript, type-only `import` will be fine
import type {Node} from 'yoga-layout'

function getYoga() {
	const [yoga, setYoga] = createSignal<typeof import('yoga-layout')>()
	const promise = eval("import('https://unpkg.com/[email protected]/dist/src/index.js')")
	promise.then(setYoga)
	return yoga
}

// ...

			const yoga = getYoga()

			createEffect(() => {
				const yogaModule = yoga()
				if (!yogaModule) return
				const {default: Yoga, Direction, FlexDirection, Gutter} = yogaModule

				// ... use yoga as before ...
			})

trusktr avatar Aug 26 '24 04:08 trusktr

This seems somehow related to the issue of effects not running. https://discord.com/channels/722131463138705510/1275456175462420614

In the above case, I was able to cause effects not to run due to the presence of await import() inside of components, and was able to workaround by setting ssr:false in app.config (only for dev mode, prod mode is not working at all), although maybe this is just a secondary effect of the real issue.

trusktr avatar Aug 26 '24 16:08 trusktr

Looks like server.esbuild.options.target is in fact passed to Nitro. So Nitro needs an update so that it will handle node_modules?

trusktr avatar Aug 31 '24 21:08 trusktr

I took Nitro 2.9.7 for a spin, looks like it supports top-level await out of the box.

trusktr avatar Sep 01 '24 00:09 trusktr

I forced nitropack version 2.9.7 in the Solid Start app, but I still get the same top-level await error for yoga-layout when running npm run dev.

trusktr avatar Sep 01 '24 00:09 trusktr

The fix in

  • https://github.com/ryansolid/dom-expressions/pull/345

got rid of the need to use await import() for the lume package, cleaning up imports in several modules.

However the issue of node_modules not being handled by the dev/build tooling still persists with yoga-layout, so now I'm down to a single file that has an await import(), currently looking like this:

// This is not working yet (https://github.com/solidjs/solid-start/issues/1614), so we import with conditional await import()
// import Yoga, {Direction, FlexDirection, Gutter, Wrap, Edge, Justify, Align} from 'yoga-layout'

let Yoga: typeof import('yoga-layout').default
let Direction: typeof import('yoga-layout').Direction
let FlexDirection: typeof import('yoga-layout').FlexDirection
let Gutter: typeof import('yoga-layout').Gutter
let Wrap: typeof import('yoga-layout').Wrap
let Edge: typeof import('yoga-layout').Edge
let Justify: typeof import('yoga-layout').Justify
let Align: typeof import('yoga-layout').Align

if (globalThis.window?.document) {
	;({
		default: Yoga,
		Direction,
		FlexDirection,
		Gutter,
		Wrap,
		Edge,
		Justify,
		Align,
	} = (await import('https://unpkg.com/[email protected]/dist/src/index.js')) as typeof import('yoga-layout'))
}

// ... 

trusktr avatar Sep 01 '24 17:09 trusktr

The await import('https://unpkg.com/[email protected]/dist/src/index.js') with an actual URL causes the build to avoid transforming the import() expression, so the app will import from unpkg.com in the browser.

trusktr avatar Sep 01 '24 17:09 trusktr

Ultimately this is a Nitro defaults issue. I'm hoping upgrading Nitro to v2 when it comes out will fix this soon.

ryansolid avatar Sep 25 '24 22:09 ryansolid

Is it confirmed this is fixed in Nitro v2? Or do we need to make an issue?

Its almost 2025, hard to believe top-level await is not supported when all JS engines do natively.

trusktr avatar Oct 10 '24 01:10 trusktr

It now is 2025, and I'm running into this issue in trying to deploy my first Solid Start site. It appears that we're using 2.10.4 of nitro as of the time of this writing, but we get the same error: Top-level await is not available in the configured target environment ("es2019").

Do we have any updates on the path forward for this?

reallyely avatar Jan 20 '25 21:01 reallyely

I'm also having this issue

chasingtheflow avatar Jan 30 '25 20:01 chasingtheflow

To those who run into this issue; As the author said in the first comment, the following workaround works:

export default defineConfig({
	server: {
		esbuild: { options: { target: 'esnext' } },
	},
})

Also, the following works too (which seems to be preferred):

export default defineConfig({
	server: {
		esbuild: {
			options: {
				supported: {
					'top-level-await': true,
				},
			},
		},
	},
})

hakatashi avatar Feb 14 '25 01:02 hakatashi

To those who run into this issue; As the author said in the first comment, the following workaround works:

export default defineConfig({
	server: {
		esbuild: { options: { target: 'esnext' } },
	},
})

Also, the following works too (which seems to be preferred):

export default defineConfig({
	server: {
		esbuild: {
			options: {
				supported: {
					'top-level-await': true,
				},
			},
		},
	},
})

why is this issue still open then?

huseeiin avatar Apr 01 '25 08:04 huseeiin

@hakatashi thanks!!!!

sittiponghaus avatar Apr 11 '25 14:04 sittiponghaus

adding @hakatashi suggestion fixed this error for me:

ERROR: Top-level await is not available in the configured target environment ("es2019")

This error displays when I add an async middleware function:

async function setContext(event: FetchEvent) {
...
}

export default createMiddleware({
  onRequest: [setContext],
});

Sleepful avatar May 18 '25 04:05 Sleepful