diff --git a/docs/core/globals.md b/docs/core/globals.md index c9b271be9de..3ef9155d81a 100644 --- a/docs/core/globals.md +++ b/docs/core/globals.md @@ -52,13 +52,14 @@ global. This same interface is also exposed if requiring with CommonJS ## Requiring `AFRAME` in a Node.js Environment -It is possible to run A-Frame in [Node.js](https://nodejs.org/en/about) to get access to its globals. The only catch is we need to supply a browser `window` mock since Node.js lacks a `window` object. A-Frame is tested with [jsdom](https://github.com/jsdom/jsdom), although any JavaScript-based browser implementation should work. +It is possible to run A-Frame in [Node.js](https://nodejs.org/en/about) to get access to its globals. The only catch is we need to supply a browser `window` mock since Node.js lacks a `window` object. You can do that with [jsdom-global](https://www.npmjs.com/package/jsdom-global), and you also need to mock `customElements`. ```js -const jsdom = require("jsdom"); -global.window = new jsdom.JSDOM().window; +const cleanup = require('jsdom-global')(); +global.customElements = { define: function () {} }; var aframe = require('aframe/src'); console.log(aframe.version); +cleanup(); ``` -Although A-Frame can load in Node.js, A-Frame isn't (yet) able to run any simulations at run time. +You can't use jsdom to run tests with aframe components because `customElements` api is missing. A-Frame is using karma to open a real browser to run the tests. diff --git a/package.json b/package.json index 57044ab2492..27ded603cc0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "test:firefox": "npm test -- --browsers Firefox", "test:chrome": "npm test -- --browsers Chrome", "test:nobrowser": "NO_BROWSER=true npm test", - "test:node": "mocha --ui tdd tests/node" + "test:node": "node --experimental-require-module ./node_modules/mocha/bin/mocha --ui tdd tests/node" }, "repository": "aframevr/aframe", "license": "MIT", @@ -40,7 +40,7 @@ "deep-assign": "^2.0.0", "load-bmfont": "^1.2.3", "super-animejs": "^3.1.0", - "super-three": "0.167.0", + "three": "npm:super-three@0.167.0", "three-bmfont-text": "dmarcos/three-bmfont-text#eed4878795be9b3e38cf6aec6b903f56acd1f695", "webvr-polyfill": "^0.10.12" }, @@ -60,7 +60,8 @@ "git-rev": "^0.2.1", "glob": "^8.0.3", "husky": "^0.11.7", - "jsdom": "^20.0.0", + "jsdom": "^24.0.0", + "jsdom-global": "^3.0.2", "karma": "^6.4.0", "karma-chai-shallow-deep-equal": "0.0.4", "karma-chrome-launcher": "^3.1.1", diff --git a/src/components/scene/inspector.js b/src/components/scene/inspector.js index 4256e27781d..66bce97e9d3 100644 --- a/src/components/scene/inspector.js +++ b/src/components/scene/inspector.js @@ -1,4 +1,4 @@ -/* global AFRAME */ +/* global AFRAME, INSPECTOR_VERSION */ var AFRAME_INJECTED = require('../../constants').AFRAME_INJECTED; var pkg = require('../../../package'); var registerComponent = require('../../core/component').registerComponent; @@ -16,7 +16,7 @@ function getFuzzyPatchVersion (version) { var INSPECTOR_DEV_URL = 'https://aframe.io/aframe-inspector/dist/aframe-inspector.js'; var INSPECTOR_RELEASE_URL = 'https://unpkg.com/aframe-inspector@' + getFuzzyPatchVersion(pkg.version) + '/dist/aframe-inspector.min.js'; -var INSPECTOR_URL = process.env.INSPECTOR_VERSION === 'dev' ? INSPECTOR_DEV_URL : INSPECTOR_RELEASE_URL; +var INSPECTOR_URL = typeof INSPECTOR_VERSION !== 'undefined' && INSPECTOR_VERSION === 'dev' ? INSPECTOR_DEV_URL : INSPECTOR_RELEASE_URL; var LOADING_MESSAGE = 'Loading Inspector'; var LOADING_ERROR_MESSAGE = 'Error loading Inspector'; diff --git a/src/core/scene/a-scene.js b/src/core/scene/a-scene.js index d123a55d0af..4ee5932db33 100644 --- a/src/core/scene/a-scene.js +++ b/src/core/scene/a-scene.js @@ -1,4 +1,4 @@ -/* global Promise, screen, CustomEvent */ +/* global Promise, customElements, screen, CustomEvent */ var initMetaTags = require('./metaTags').inject; var initWakelock = require('./wakelock'); var loadingScreen = require('./loadingScreen'); @@ -920,7 +920,7 @@ function constrainSizeTo (size, maxSize) { return size; } -window.customElements.define('a-scene', AScene); +customElements.define('a-scene', AScene); /** * Return the canvas size where the scene will be rendered. diff --git a/src/index.js b/src/index.js index 5e6a9ae6d72..a0c67f8b581 100644 --- a/src/index.js +++ b/src/index.js @@ -80,7 +80,7 @@ require('./extras/primitives/'); console.log('A-Frame Version: 1.6.0 (Date 2024-08-29, Commit #8c8d0e19)'); console.log('THREE Version (https://github.com/supermedium/three.js):', - pkg.dependencies['super-three']); + THREE.REVISION); console.log('WebVR Polyfill Version:', pkg.dependencies['webvr-polyfill']); // Wait for ready state, unless user asynchronously initializes A-Frame. diff --git a/src/lib/three.js b/src/lib/three.js index 1cd02bf22a9..d4b87cc28e6 100644 --- a/src/lib/three.js +++ b/src/lib/three.js @@ -1,4 +1,7 @@ -var THREE = require('./three.module.js').default; +var THREE = require('./three.mjs').default; +// TODO: Eventually include these only if they are needed by a component. +global.THREE = THREE; +require('../../vendor/DeviceOrientationControls'); // In-memory caching for XHRs (for images, audio files, textures, etc.). if (THREE.Cache) { diff --git a/src/lib/three.mjs b/src/lib/three.mjs new file mode 100644 index 00000000000..22a78bee7eb --- /dev/null +++ b/src/lib/three.mjs @@ -0,0 +1,21 @@ +import * as SUPER_THREE from 'three'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; +import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js'; +import { OBB } from 'three/addons/math/OBB.js'; +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; +import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'; +import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'; +import { LightProbeGenerator } from 'three/examples/jsm/lights/LightProbeGenerator.js'; + +var THREE = { ...SUPER_THREE }; +THREE.DRACOLoader = DRACOLoader; +THREE.GLTFLoader = GLTFLoader; +THREE.KTX2Loader = KTX2Loader; +THREE.OBJLoader = OBJLoader; +THREE.MTLLoader = MTLLoader; +THREE.OBB = OBB; +THREE.BufferGeometryUtils = BufferGeometryUtils; +THREE.LightProbeGenerator = LightProbeGenerator; + +export default THREE; diff --git a/src/lib/three.module.js b/src/lib/three.module.js deleted file mode 100644 index 2b495b39cc4..00000000000 --- a/src/lib/three.module.js +++ /dev/null @@ -1,24 +0,0 @@ -import * as SUPER_THREE from 'super-three'; -import { DRACOLoader } from 'super-three/examples/jsm/loaders/DRACOLoader'; -import { GLTFLoader } from 'super-three/examples/jsm/loaders/GLTFLoader'; -import { KTX2Loader } from 'super-three/examples/jsm/loaders/KTX2Loader'; -import { OBB } from 'super-three/addons/math/OBB.js'; -import { OBJLoader } from 'super-three/examples/jsm/loaders/OBJLoader'; -import { MTLLoader } from 'super-three/examples/jsm/loaders/MTLLoader'; -import * as BufferGeometryUtils from 'super-three/examples/jsm/utils/BufferGeometryUtils'; -import { LightProbeGenerator } from 'super-three/examples/jsm/lights/LightProbeGenerator'; - -var THREE = window.THREE = SUPER_THREE; - -// TODO: Eventually include these only if they are needed by a component. -require('../../vendor/DeviceOrientationControls'); // THREE.DeviceOrientationControls -THREE.DRACOLoader = DRACOLoader; -THREE.GLTFLoader = GLTFLoader; -THREE.KTX2Loader = KTX2Loader; -THREE.OBJLoader = OBJLoader; -THREE.MTLLoader = MTLLoader; -THREE.OBB = OBB; -THREE.BufferGeometryUtils = BufferGeometryUtils; -THREE.LightProbeGenerator = LightProbeGenerator; - -export default THREE; diff --git a/src/utils/debug.js b/src/utils/debug.js index acec07dad28..2304ff0a5f1 100644 --- a/src/utils/debug.js +++ b/src/utils/debug.js @@ -1,4 +1,5 @@ var debug = require('debug'); +var isBrowserEnvironment = require('./device').isBrowserEnvironment; var settings = { colors: { @@ -114,6 +115,6 @@ if (ls && (parseInt(ls.logs, 10) || ls.logs === 'true')) { debug.enable('*:error,*:info,*:warn'); } -if (process.browser) { window.logs = debug; } +if (isBrowserEnvironment) { window.logs = debug; } module.exports = debug; diff --git a/src/utils/device.js b/src/utils/device.js index f81e419dfbe..dc48aeb1356 100644 --- a/src/utils/device.js +++ b/src/utils/device.js @@ -213,9 +213,9 @@ module.exports.isLandscape = function () { * We need to check a node api that isn't mocked on either side. * `require` and `module.exports` are mocked in browser by bundlers. * `window` is mocked in node. - * `process` is also mocked by browserify, but has custom properties. + * `process` is also mocked by webpack running with karma, but has custom properties like process.browser. */ -module.exports.isBrowserEnvironment = !!(!process || process.browser); +module.exports.isBrowserEnvironment = typeof process === 'undefined' || process.browser === true; /** * Check if running in node on the server. diff --git a/tests/karma.conf.js b/tests/karma.conf.js index 6b77d549417..cb10d4cc9d7 100644 --- a/tests/karma.conf.js +++ b/tests/karma.conf.js @@ -1,7 +1,8 @@ // Karma configuration. var path = require('path'); var glob = require('glob'); -var webpackConfiguration = require("../webpack.config.js"); +var webpack = require('webpack'); +var webpackConfiguration = require('../webpack.config.js'); // Define test files. var FILES = [ @@ -29,7 +30,13 @@ if (process.env.TEST_FILE) { // add 'src' to be able to resolve require('utils/tracked-controls') for // example in the tests +if (!webpackConfiguration.resolve) { + webpackConfiguration.resolve = {}; +} webpackConfiguration.resolve.modules = ['src', 'node_modules']; +webpackConfiguration.plugins.push(new webpack.ProvidePlugin({ + process: 'process/browser' +})); // webpack will create a lot of files, use build directory instead of dist webpackConfiguration.output.path = path.resolve(__dirname, '../build'); diff --git a/tests/node/test.js b/tests/node/test.js index a2b95ed779a..37e5aabc94e 100644 --- a/tests/node/test.js +++ b/tests/node/test.js @@ -2,28 +2,21 @@ 'use strict'; const path = require('path'); -const assert = require('chai').assert; -const jsdom = require('jsdom'); +const assert = require('assert'); suite('node acceptance tests', function () { setup(function () { - let _window = global.window = new jsdom.JSDOM().window; - global.navigator = _window.navigator; - global.document = _window.document; - global.screen = {}; - global.HTMLElement = _window.Element; - global.window.customElements = {define: function () {}}; + this.jsdomCleanup = require('jsdom-global')(); + global.customElements = { define: function () {} }; }); teardown(function () { - delete global.window; - delete global.navigator; - delete global.document; - delete global.screen; + delete global.customElements; + this.jsdomCleanup(); }); test('can run in node', function () { - let aframe = require(path.join(process.cwd(), 'src')); + const aframe = require(path.join(process.cwd(), 'src')); assert.ok(aframe.version); }); diff --git a/webpack.config.js b/webpack.config.js index 5ade0f60e56..1c098c7a778 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,20 +22,14 @@ module.exports = { }, plugins: [ new webpack.DefinePlugin({ - 'process.env.INSPECTOR_VERSION': JSON.stringify( + INSPECTOR_VERSION: JSON.stringify( process.env.INSPECTOR_VERSION ) }), new webpack.ProvidePlugin({ - process: 'process/browser', Buffer: ['buffer', 'Buffer'] }) ], - resolve: { - alias: { - three: 'super-three' - } - }, module: { rules: [ {