[JavaScript] Importing Modules and Async Exports...

I’m trying to write some resources in JavaScript that rely on others and I can’t figure out a good way to communicate between the two…

  • It doesn’t seem like I can pass a class instance through events like ESX does in Lua…
  • It doesn’t seem like I can use the client_script '@resource/file.js' syntax to access an exported module from file.js similar how mysql-async does (again, with Lua). It seems like it would be possible, but I don’t know how to access the modules. Even if I were to use the LoadResourceFile() native to load the file, I still don’t know how I’d go about accessing the exported module from that file.
  • It doesn’t seem like I can do async events unless I wrap all of my event calls with util.promisify() which isn’t available on the client since node is not included.

Am I missing something, or is making scripts that depend on others in JavaScript more limited? The docs don’t have much information on these drawbacks and how to work around them… I’ve asked on Discord, but despite a few people attempting to help, nobody actually knew. I tried looking through FiveM’s source code, but I’ll be honest… that was over my head and didn’t help me understand these obstacles I’m facing at all.

You can. You’ll just have to make sure this is bound as ref calls will invoke directly so JS’s magical this doesn’t do anything relevant.

Exported modules don’t exist at that level, however anything defined in a top-level script ends up in globalThis scope.

eval or similar with a predefined wrapper similar to how any CommonJS implementation works.

You can still manually handle promises using new Promise.

Nope.

Anytime I’ve tried, it only seems to create a clone of the instance’s properties as an object… the methods don’t exist and it’s certainly not an instance of the class, much less a pointer to the same instance.

Just spent some time playing with this and I couldn’t find any reference to it. I created a resource with a basic class, function, and a few variables… all top-level (with the exception of an additional method I put in the class). I then created a second resource, added server_script '@test-a/server.js' to the manifest and then console logged globalThis with util.inspect()… didn’t see any additions from the referenced resource. If you know of a working example of this somewhere that I can view, I’d love to see it in practice.

Yea, that’s a bit of a nuisance though… having to include the same utility functions in every resource you make with JS. I tried to create a single utility resource that I could consume in all my other resources. It would house my wait() and createThread(), but I just continued running into issue after issue trying to get things working (due to the above issues with @ and async/await). I wrapped my export calls in promises and managed to get it working for the wait() but I couldn’t get createThread() to respect the waits and gave up.

I won’t pretend to be an expert in JS, I’m definitely not. But Lua seemed pretty straight forward with very few gotchas while JS has been one headache after another, unable to get things to work and having to rethink what should be simple tasks.

I know someone is going to say “then use Lua”, and I know that’s a valid option… but I’d much prefer working with JavaScript, if I could work through these issues and find a good structure for things.

That’s the same that happens in Lua. If you want to keep the class, close on it as a closure.

And if you just drop it into globalThis.X?

load order maybe?

ok, but that’s again not JS-exclusive.

What? You mean to assign them manually to the global scope? That goes against everything I’ve ever practiced in web development… cluttering the global scope. But yea, I suppose that’d work… I didn’t know how the engine is implemented or how things are encapsulated though… Since you’re suggesting it, I’m assuming it wouldn’t cause a potential memory leak or anything? :thinking:

Loaded them manually using the console, so…

True… but currently my only motive for creating it was to throw the wait() function in there so I could easily throw an @ reference in the manifest, and be able to use utils.wait() without having to define a function for it all the time. Which that, specifically, is due to there not being a JS equiv native of Citizen.Wait().

That’s literally the only thing that different client/server_script instances share. The same goes for both JS and Lua, again.

v8::Context/global scope is tied to the resource using a thing.

… in the fxmanifest.

… and that’s because there’s no coroutines in JS either so there literally can’t be.

Apologies, as I’m very new to js. Does this mean I can’t define globalThis.test in resource A, and access test in resource B? cause so far, I cannot, and just want to make sure I’m understanding correctly.

yes, every resource is and always has been using an isolated scope

if you want to share a scope across resources for whatever reason, make one resource into a dedicated host and implement a custom equivalent of client_script etc. like myjsframework_script and use the resource metadata natives + start/stop events (see what e.g. mapmanager does)

this will not give you automatic game data cleanup, but that’s a future todo entry:

What about the export variable in javascript files created by webpack?
The exports variable does not contain any exported function.

I get this: exports is not a function

global.exports should work instead

1 Like

Hey!, thanks bro, it worked