Uncaught SyntaxError: Cannot use import statement outside a module

I’m currently trying to add firebase accounts to my games and keep getting this error over and over “Uncaught SyntaxError: Cannot use import statement outside a module” I’m using the code google provides in the docs (Read and Write Data on the Web  |  Firebase Documentation) and the issue is caused by an import command but have never used those before now and don’t know what I’m doing if someone knows how to use an import command please tell me.

Hi @WilliamBoersma31,

Yes you can’t use import in PlayCanvas scripts. You will have to either use a browser based build of the firebase sdk or use some trick JS to load the module on runtime.

Here is an example for the latter:

1 Like

where does the module go in the script and how do I define a section as module I read that and just got more confused

Here is an example script, where you drag your module .js asset at moduleAsset, and you need to provide a global name to reference your module.

Later in your code, after it has loaded, you can access it like this:

MyModuleName.methodName();

Here is the script:

var ModuleLoader = pc.createScript('moduleLoader');

ModuleLoader.attributes.add('moduleAsset', {
    type: 'asset',
    assetType: 'script'
});

ModuleLoader.attributes.add('className', {
    type: 'string',
    default: 'MyModuleName'
});

// initialize code called once per entity
ModuleLoader.prototype.initialize = function() {
    
    if(!this.moduleAsset) return;

    let path = this.moduleAsset.getFileUrl();

    // --- check if it's an absolute path, if not update
    if (path.indexOf('http') !== 0 && window.location.origin.indexOf('launch.playcanvas.com') === -1) {
      path = window.location.origin + window.location.pathname + path;
    }    

    import(path).then( (module) =>{
        window[this.className] = module[this.className];
    });
};
1 Like

@WilliamBoersma31 Were you able to get Firebase working? We see that the version 9 update has broken how we did it before and we aren’t sure how to make it work again.

@Leonidas I am trying to use your code example here, but am having a little trouble. I was hoping you could help. So far I have been able to bundle up Firebase using rollup and a simple index.js that so far only contains the initialize code from Firebase’s website.

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "xxxxxxxx",
  authDomain: "xxxxxxx",
  projectId: "xxxxxxxxx",
  storageBucket: "xxxxxxxx",
  messagingSenderId: "xxxxxxxx",
  appId: "xxxxxxxx",
  measurementId: "xxxxxxxxx"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);

Then I place the bundle.js file into my playcanvas project and drop it into the moduleAsset in your ModuleLoader script. This initialize code appears to run, but then I cannot seem to access it to call methods from it. I called className “firebaseModule” and if I try to call a method in a different script in this way “firebaseModule.initializeApp(config);” I get the following error:

ReferenceError: firebaseModule is not defined

Any advice, clues, steps I am missing that you can think of?

Thanks so much,
Justin

Hi @justinISO,

I think your index.js file should export a module to work with the module loader script.

I can’t write code now, I’m on my phone but if you search online on how to export an ES6 module you will find a number of examples.

Hope that helps,

Hey @Leonidas thank you so much for getting back to me!

So it looks simple enough to add a module by adding export{analytics} to the end of my index.js, but I still seem to be stuck on how to get to those modules via MyModuleName created from the ModuleLoader class.

I may just also be completely misunderstanding how this module system works.

If I add a simple function to my index.js as follows:

function QuickTest(){
console.log("Test here");
};

and add this line to the end of my index.js

export{QuickTest};

Then I should be able to call firebaseModule (the className given to my instance of the ModuleLoader) firebaseModule.QuickTest(); and it should print out “Test Here” to the console?

Again thanks for all of your help!

Yes, I think what you write is correct. That module script served me well like this, it should work.

If not, try sharing a sample project and I’ll take a look tomorrow.

I made an empty test project that has the same concepts set up in it.

https://playcanvas.com/project/897332/overview/moduletester

So, two things here:

  1. The code you have in your main.js script will execute before the module is loaded. It takes some time for the module to async load, so to access the code you need to wait for that.

  2. The bundled code (bundle.js) seems to fail to load as a module, not sure why. If I replace it with this simple code it works:

export function QuickTest() {
	console.log('hey!');
}

With this test script, check the changes I’ve made in the last method:

var ModuleLoader = pc.createScript('moduleLoader');

ModuleLoader.attributes.add('moduleAsset', {
    type: 'asset',
    assetType: 'script'
});

ModuleLoader.attributes.add('className', {
    type: 'string',
    default: 'MyModuleName'
});

// initialize code called once per entity
ModuleLoader.prototype.initialize = function () {

    if (!this.moduleAsset) return;

    let path = this.moduleAsset.getFileUrl();

    // --- check if it's an absolute path, if not update
    if (path.indexOf('http') !== 0 && window.location.origin.indexOf('launch.playcanvas.com') === -1) {
        path = window.location.origin + window.location.pathname + path;
    }

    import(path).then((module) => {
        window[this.className] = module;

        // will print hey! in the console
        TestModule.QuickTest();
    });
};

Thanks so much for your help! I can see what you are saying about waiting for the module to load, but I am not sure how to go about fixing the module itself.

The source code is pretty simple (index.js):

export function QuickTest() {
	console.log('hey!');
}

Then I run the rollup command as follows: rollup index.js --file bundle.js --format iife

And I get this bundle.js:

(function (exports) {
	'use strict';

	function QuickTest() {
		console.log('hey!');
	}

	exports.QuickTest = QuickTest;

	Object.defineProperty(exports, '__esModule', { value: true });

	return exports;

})({});

So my only remaining guess is that I am not running the correct commands/arguments in rollup?

Sorry I am not familiar with how rollup is set, but what I see here is that rollup disables ES6 modules (export keyword) and fallbacks to an earlier ES5 implementation?

Maybe you don’t need to use this module importer with this rollup system and just leave preload = true so PlayCanvas loads it as any other script.

Then you can do something like this in your original script:

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "xxxxxxxx",
  authDomain: "xxxxxxx",
  projectId: "xxxxxxxxx",
  storageBucket: "xxxxxxxx",
  messagingSenderId: "xxxxxxxx",
  appId: "xxxxxxxx",
  measurementId: "xxxxxxxxx"
};

// Initialize Firebase
windows.firebaseModule = {
   app: initializeApp(firebaseConfig),
   analytics: getAnalytics(app)
};

Now after this script has been loaded (you can move it up in the project script loading order), you can access globally that object like this:

window.firebaseModule.app;
// or even shorter
firebaseModule.app;
firebaseModule.analytics;

Ahhh that is interesting! Thank you for your insight on this I really appreciate it!

So that initialize code appears to run, which is great, but then do you have any insight on how I can call a function from within that bundle.js? What is the scope level I am missing to call a method from a different playcanvas script?

This code that I wrote above is in a global context, it will work from any other script. And even outside from scripts e.g. in the browser console.

You just need to wait for the module to load, before calling them. You can use scripts events to inform other scripts when the module is ready.

Check this manual page for more on that:

https://developer.playcanvas.com/en/user-manual/scripting/communication/

@Leonidas I cannot thank you enough for your help on this one! I still don’t have it 100% working as I would like, but your guidance has got me going in what appears to be the correct direction!

Thanks again!

1 Like

No I Never Got It Working I’ve Been Working On Other Projects But I’ll Probably Be Coming Back To It Soon

@justinISO Hi
Did you get a chance to implement that?
Thanks

No I Haven’t Yet But I Was Considering Trying Again Here Soon.

@Leonidas I have firebase module perfectly fine in working conditions and I am able to call it’s functions from within that firebase script.

Can you guide me about how to access and call those functions from outside that module, from any other script?
Thank you