Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use import/export instead of require()/module.exports #5632

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mrxz
Copy link
Contributor

@mrxz mrxz commented Jan 17, 2025

Description:
As discussed in #5363 replacing CommonJS (require()/module.exports) with ES Modules (import/export) is the first step. This PR is a rebase of an earlier experiment I did, which replaces most usages.

A couple of notes:

  • No build-step would still require additional work, as some dependencies aren't ESM
  • ESLint requires the ecmaVersion to be 6 or higher, but this also allows all of the ES6 (2015) syntax and constructs. There doesn't seem to be a way to only allow import/export. Though it might be time to allow ES6 in the code base(?)
  • Some import statements could be refined, e.g. import * as X from 'x.js'; followed by var y = X.y can be simplified to import { y } from 'x.js';. This is done in some places, but not all. Generally the utils is being imported whole in a lot of places, where importing specific utilities would suffice. Generally preferable to only import what is needed to aid three-shaking (down the line).

I'll place some comments on this PR to highlight some significant changes.

Changes proposed:

  • Use import/export instead of require()/module.exports

@@ -4,6 +4,9 @@
"ecmaVersion": 12
},
"rules": {
/* To work towards no-build. */
"import/extensions": ["warn", "always", { "js": "always" }],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Webpack doesn't need the .js suffix, but if the goal is to work towards a no-build setup it will be required. Thought it would be a good idea to start enforcing it regardless.

{
"files": ["./src/utils/index.js"],
"parserOptions": {
"ecmaVersion": 11
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently needed due to export as usage. The utils where the hardest part to migrate as they have very inconsistent usages of default exports and re-exports. Preferably these are streamlined and simplified, along with having consumers import only the utils they use.

Comment on lines +24 to +27
import './meta-touch-controls.js';
import './obb-collider.js';
import './obj-model.js';
import './oculus-go-controls.js';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diff looks a tad strange, as I moved meta-touch-controls.js up to maintain alphabetical order.

var utils = require('../../utils/');
import { AFRAME_INJECTED } from '../../constants/index.js';
import { registerComponent } from '../../core/component.js';
import pkg from '../../../package.json';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth pointing out that importing JSON files would also not work in a no-build setup. Perhaps once JSON modules are readily available in browsers.

Comment on lines +34 to +38

/** Resets the canInitializeElements flag, used for testing */
export function reset () {
canInitializeElements = false;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything imported through import is not mutable. The readyState.test.js used to override this value to simulate various scenarios. As a workaround this method is introduced.

An alternative would be to create a holder and mutate its contents, e.g.:

export var canInitializeElements = {
  value: false
}

But I'd rather add this method to accommodate tests than to change all consumers because of the tests.

import { ANode } from '../a-node.js';
import { initPostMessageAPI } from './postMessage.js';

import '../../utils/ios-orientationchange-blank-bug.js';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditional logic is moved into this class. So while it's always "loaded", it's only applied for iOS. This is simply due to the fact that require() is synchronous, but import() isn't. Which would then either require a top-level await, or an awkwardly placed .then()

@@ -21,41 +54,11 @@ if (!window.cordova && window.location.protocol === 'file:') {

// CSS.
if (utils.device.isBrowserEnvironment) {
window.logs = debug;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be part of debug.js, but would be executed only when isBrowserEnvironment which is defined in device.js, which in turn depends on debug.js. This circular dependency caused issues.

Comment on lines +22 to +32
var loadPromise = Promise.allSettled([
import('index.js'),
import('core/scene/a-scene.js')
]).then(function (results) {
AFRAME = results[0].value.default;
AScene = results[1].value.AScene;

// Make sure WebGL context is not created since CI runs headless.
// Stubs below failed once in a while due to asynchronous test setup / teardown.
AScene.prototype.setupRenderer = function () {};
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asynchronous loading of AFRAME and a-scene, as otherwise the above WebXR stubbing would happen too late.

});

teardown(function () {
var el = this.sceneEl;
el.parentNode.removeChild(el);

utils.getUrlParameter = originalGetUrlParameter;
window.history.replaceState(null, '', originalLocationUrl);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of trying to overwrite the getUrlParameter util (which is now read-only) the URL is actually rewritten using the history API.


var UI_CLASSES = ['.a-orientation-modal', '.a-enter-vr-button'];
var UI_CLASSES = ['.a-orientation-modal', '.a-enter-vr'];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The a-hidden class is applied to the wrapper around the button. The test was incorrect before.

@@ -407,7 +407,7 @@ suite('rotation controls on camera with touch drag (integration unit test)', fun

canvas = sceneEl.canvas;
sceneEl.isMobile = true;
canvas.clientWidth = 1000;
assert.isAtLeast(canvas.clientWidth, 1000);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again an instance where one can't set values that are read-only. From my testing the line of code wasn't actually needed, but I've added an assertion to at least make sure the desired state is there.

Comment on lines +7 to +11
library: {
name: 'AFRAME',
type: 'var',
export: 'default'
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This instructs WebPack to basically ensure that when loading the UMD bundle, the default export will be available under the name AFRAME. This matches the current behaviour of the bundle AFAICT.

@mrxz mrxz mentioned this pull request Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant