Meteor 3.0.4 seems to break my app's imports

I’ve got a file called api.js that looks like this:

import { Meteor } from 'meteor/meteor'

if (Meteor.isServer) {
	export * from './server/api.js'
} else {
	export * from './client/api.js'
}

I believe I borrowed this sweet pattern from the Todos apps.

Anyway, since updating from 3.0.3 to 3.0.4, I get an error which says:

Error: Nested imports can not import an async module

To upgrade to 3.0.3 I had to restructure some stuff after getting this error, and I thought I’d moved beyond this.

I looked at the docs to fix the issue before but can’t find the relevant info now, or what the error means exactly in my case, or what changed between 3.0.3 and 3.0.4 that would cause this to break suddenly.

The error goes away if I reverting back to 3.0.3.

Any suggestions on what to do here?

The import subtree looks something like:

main.js:

import '/imports/api/account/cart/methods.js'

// NB: relevant since I believe having nested imports in main.js makes it a an 'async module'
if (Meteor.isAppTest) {
	import '/imports/api/test/server/methods.js'
	import '/imports/api/test/server/publications.js'
}

imports/api/account/cart/server/methods.js:

import { foo } from '/imports/api/account/cart/api.js'

/imports/api/account/cart/api.js source is at the top of this post.

Any help would be appreciated. Thanks.

1 Like

Try with 3.1, all known problems with top level await and Async modules were fixed there.

1 Like

Thanks, but sadly upgrading to 3.1 did not fix the error for me.

Async modules are any module that uses top level await, or any module that uses import ... syntax to directly or indirectly import an async module.

It doesn’t seem possible to make nested imports work with async modules. You can use require instead, since it will return a promise you can await, or the recommended solution is to use a dynamic import since it handles async and sync modules the same way.

The docs for this are here.

edit: some of the Meteor 3 versions have bugs with deciding when a module is async. I haven’t followed it closely, but it is possible that is why 3.0.3 worked, and 3.04 does not. It’s also possible there is a module that started using top-level await with the update. Generally Meteor tries to prevent Meteor packages from making app modules become async, but there are unavoidable exceptions.

I took a look at the docs again and changed api.js to look like this:

import { Meteor } from 'meteor/meteor'

let lib
if (Meteor.isServer) {
	lib = await require('./server/api.js')
} else {
	lib = await require('./client/api.js')
}

export { lib }

and things are working again.

Thanks for the info & taking the time, @zodern.

I wonder if it works as a 1 liner

import { Meteor } from 'meteor/meteor'
export const lib = Meteor.isServer ? await require('./server/api.js') : await require('./client/api.js')

// or
export const lib = await require(`./${Meteor.isServer ? 'server' : 'client'}/api.js`)

Hey, thanks, but it seems things are not as simple as I’d hoped.

Now when I do

import * as CombinedApi from '...api.js'

…CombinedApi = { lib } which is obviously not what I want – I want the exported contents of client/api.js or server/api.js

But now, after having experimented in api.js then gone back to the exact same state as my previous post, I now get:

 Errors prevented startup:

   While building for web.browser:
   ..../api.js:10:10: Unexpected reserved word 'await'.
   (10:10)

=> Your application has errors. Waiting for file change.

This error is truly bizarre because nothing else has changed, and the results seem to be nondeterministic. For example, I just restarted my server and now I get the error twice:

   While building for web.browser:
   .../api.js:13:29: Unexpected reserved word 'await'.
   (13:29)

   While building for web.browser.legacy:
   .../api.js:15:29: Unexpected reserved word 'await'.
   (15:29)

Lines 10, 13, and 15 are not even where the ‘await’ keywords sit…

Wait wait wait… I found a Meteor bug.

//module.exports = lib

Having this line – yes, commented out – as in here:

import { Meteor } from 'meteor/meteor'

let lib

if (Meteor.isServer) {
	lib = await require('./server/api.js')
} else {
	lib = await require('./client/api.js')
}

export { lib }

//module.exports = lib

causes this error: ..../api.js:10:10: Unexpected reserved word 'await'.

Removing that line removes the error.

I reported the bug on github.

The solution is to not do this at all, and to directly import the client or server file.

I think the natural way to do it is to make it into a Meteor package and use:

  api.mainModule('./server/api.js', 'server');
  api.mainModule('./client/api.js', 'client');

The Accounts package is a good example for this.

1 Like

That’s not a practical solution for me.

In my case that would mean making about 12 meteor packages.

The reason why I can’t if (!Meteor.isServer) await require('./client/api.js) is because on the client, this causes an exception.

I really miss when export * from '...' worked. It was really elegant.