JavaScript Feature Reference: Module Web Browser Support Test
1. Introduction
In this web page there are two web browser JavaScript feature support tests; 1.) a feature implementation test, and 2.) a feature capability test. First, the implementation test is run. The implementation test determines if the web browser recognizes JavaScript modules. The implementation test is a simple test for the presence of web browser support, and a definitive test for the absence of web browser support. If the web browser does not recognize JavaScript modules, the testing is stopped and the implementation test reports: Fail (no support): The web browser does not recognize JavaScript modules. The web browser does not support JavaScript modules.
If the web browser recognizes JavaScript modules, the capability test is run. The capability test determines if the web browser's implementation of JavaScript modules includes support for at least one JavaScript module capability. The capability test is a more definitive, albeit not an all inclusive, test for the presence of web browser support. If the web browser's implementation of JavaScript modules includes support for the tested capability, the capability test reports: Pass (at least partial/possibly full support): The web browser recognizes JavaScript modules, and supports at least one module capability. The web browser at least partially/possibly fully supports JavaScript modules. Positive determination of full web browser support is beyond the scope of this test.
If the web browser's implementation of JavaScript modules does not include support for the tested capability, the capability test reports: Pass/Fail (partial support): The web browser recognizes JavaScript modules, but does not support at least one module capability. The web browser partially supports JavaScript modules.
The web browser support test source code is shown in Section 2.1. The web browser support test source code is run in Section 2.2, which shows the web browser support test result.
1.1. Module Web Browser Support
Pass (at least partial/possibly full support):
ED16+, FF60+*, CH61+, OP48+.Fail (no support):
IE11-, SF5.1.7-.
* By default, in FF60+, the about:config | dom.moduleScripts.enabled preference is set to true. By default, in FF54 - 59, the about:config | dom.moduleScripts.enabled preference is set to false. In FF53-, the about:config | dom.moduleScripts.enabled preference does not exist.
1.2. Abbreviations
- IE = Internet Explorer.
- ED = Edge Legacy 12 - 18 (EdgeHTML based) and Edge 79+ (Chromium based).
- FF = Firefox.
- SF = Safari.
- CH = Chrome.
- OP = Opera.
2. Module Web Browser Support Test
2.1. Web Browser Support Test HTML Source Code
<p id='testId'><b>Fail</b> (no support): The web browser does not recognize JavaScript modules. The web browser does not support JavaScript modules.</p><!-- Hard code Fail because web browser no support error stops JavaScript execution. --> <script type="module"> // HTML document embedded JavaScript module. Capability test eight: DOM manipulation. For DOM manipulation, no exporting/importing of DOM manipulating code, and no special DOM manipulating code, required. Instead, load module with imports and regular DOM manipulating code into HTML document either: 1.) explicitly via script element with type="module" attribute, as in this example; 2.) explicitly via script element with type="module" and src="path_to_external_javascript_module.js" attributes; or 3.) implicitly via import {foo} from "path_to_external_javascript_module.js"; statement. import {passImplementationTest, passCapabilityTest} from "./module_external_main.js"; if (passImplementationTest){ var element = document.getElementById("testId"); if (passCapabilityTest){ element.innerHTML = "<b>Pass</b> (at least partial/possibly full support): The web browser recognizes JavaScript modules, and supports at least one module capability. The web browser at least partially/possibly fully supports JavaScript modules. Positive determination of full web browser support is beyond the scope of this test."; } else { element.innerHTML = "<b>Pass/Fail</b> (partial support): The web browser recognizes JavaScript modules, but does not support at least one module capability. The web browser partially supports JavaScript modules."; } } </script>
2.2. Web Browser Support Test JavaScript Source Code
JavaScript source code: module_external_main.js:
// Central location for all imports (ie, import module_external_1.js and module_external_2.js exports here). Resolve any identifier naming conflicts. Perform web browser support implementation test and capability tests. Export anything required by HTML document embedded JavaScript module. // Implementation test. There is no good implementation test. Cannot test if (window.export/import) (window.export/import does not exist), if (this.export/import) (TypeError FF57 and CH64) (this, which is undefined, has no properties), and if (export/import) (SyntaxError FF57 and CH64). Therefore, for implementation test, simply check for presence of objectName object from capability test two: namespace import below. var passImplementationTest = false; if (objectName){ // For objectName object, see capability test two: namespace import below. A test of import statement hoisting. passImplementationTest = true; } export {passImplementationTest}; // Imported by HTML document embedded JavaScript module. // Capability test one: named exports/imports. import {booleanLiteral} from "./module_external_1.js"; // Import single export. import {numberLiteral, stringLiteral, nullLiteral, undefinedLiteral, undefinedVariable, functionDeclaration, functionExpression, arrayLiteral, objectLiteral, ClassDeclaration} from "./module_external_1.js"; // Import multiple exports. numberLiteral and stringLiteral variable identifiers also imported below; however, to avoid web browser JavaScript console SyntaxError: FF57 redeclaration of import/CH64 identifier already declared, import statements below rename numberLiteral to numberLiteralFoo and stringLiteral to stringLiteralFoo. var objectOneViaClassDeclaration = new ClassDeclaration(); // Instantiate object via imported class definition. var passNamedExportsImportsCapabilityTest = false; if (booleanLiteral && (numberLiteral === 1) && (stringLiteral === "abc") && (nullLiteral === null) && (undefinedLiteral === undefined) && (undefinedVariable === undefined) && (functionDeclaration() === "functionDeclaration return value") && (functionExpression() === "functionExpression return value") && (arrayLiteral[0] === "a") && (objectLiteral.property === "property value") && (objectLiteral["property"] === "property value") && (objectOneViaClassDeclaration.ownProperty === "ownProperty value") && (objectOneViaClassDeclaration["ownProperty"] === "ownProperty value") && (objectOneViaClassDeclaration.prototypeProperty === "prototypeProperty value") && (objectOneViaClassDeclaration["prototypeProperty"] === "prototypeProperty value")){ passNamedExportsImportsCapabilityTest = true; } // Capability test two: namespace import. import * as objectName from "./module_external_1.js"; // Import all exports as properties of objectName object. objectName object is atypical object in that it does not inherit from Object and has no prototype. However, like typical object, objectName object supports use of object property dot notation and bracket notation. var objectTwoViaClassDeclaration = new objectName.ClassDeclaration(); // Instantiate object via imported class definition. var passNamespaceImportCapabilityTest = false; if ((typeof objectName === "object") && !(objectName instanceof Object) && !Object.prototype.isPrototypeOf(objectName) && (Object.getPrototypeOf(objectName) === null) && objectName.booleanLiteral && objectName["booleanLiteral"] && (objectName.numberLiteral === 1) && (objectName["numberLiteral"] === 1) && (objectName.stringLiteral === "abc") && (objectName["stringLiteral"] === "abc") && (objectName.nullLiteral === null) && (objectName["nullLiteral"] === null) && (objectName.undefinedLiteral === undefined) && (objectName["undefinedLiteral"] === undefined) && (objectName.undefinedVariable === undefined) && (objectName["undefinedVariable"] === undefined) && (objectName.functionDeclaration() === "functionDeclaration return value") && (objectName.functionExpression() === "functionExpression return value") && (objectName.arrayLiteral[0] === "a") && (objectName.objectLiteral.property === "property value") && (objectName.objectLiteral["property"] === "property value") && (objectTwoViaClassDeclaration.ownProperty === "ownProperty value") && (objectTwoViaClassDeclaration["ownProperty"] === "ownProperty value") && (objectTwoViaClassDeclaration.prototypeProperty === "prototypeProperty value") && (objectTwoViaClassDeclaration["prototypeProperty"] === "prototypeProperty value")){ passNamespaceImportCapabilityTest = true; } // Capability test three: rename exports/imports. var passRenameExportsImportsCapabilityTest = false; import {numberLiteralOneRenamed} from "./module_external_2.js"; // Import variable renamed in export statement. import {numberLiteralTwo as numberLiteralTwoRenamed} from "./module_external_2.js"; // Rename variable in import statement. if ((numberLiteralOneRenamed === 1) && (numberLiteralTwoRenamed === 2)){ passRenameExportsImportsCapabilityTest = true; } // Capability test four: module default export. import defaultExport from "./module_external_2.js"; // Module default export assigned to defaultExport, which can be any identifier. Implicit rename. Unlike import single/multiple exports above, identifier not in curly brackets ({}). var passModuleDefaultExportCapabilityTest = false; if (defaultExport() === "functionDeclaration return value"){ passModuleDefaultExportCapabilityTest = true; } // Capability test five: imports are live connections. import {numberLiteral as numberLiteralFoo, incrementNumberLiteral} from "./module_external_2.js"; // numberLiteral variable identifier also imported above; however, to avoid web browser JavaScript console SyntaxError: FF57 redeclaration of import/CH64 identifier already declared, import statement rename numberLiteral to numberLiteralFoo. var numberLiteralFooInitial = numberLiteralFoo; incrementNumberLiteral(); var numberLiteralFooFinal = numberLiteralFoo; var passImportsAreLiveConnectionsCapabilitTest = false; if ((numberLiteralFooInitial === 10) && (numberLiteralFooFinal === 11)){ passImportsAreLiveConnectionsCapabilitTest = true; } // Capability test six: import statements are hoisted. var passImportStatementsAreHoistedCapabilityTest = false; if (stringLiteralFoo === "stringLiteral value"){ // stringLiteral evaluated in line before import stringLiteral statement. If import statement hoisted, passImportStatementsAreHoistedCapabilityTest set to boolean true. If import statement not hoisted, passImportStatementsAreHoistedCapabilityTest remains boolean false. passImportStatementsAreHoistedCapabilityTest = true; } import {stringLiteral as stringLiteralFoo} from "./module_external_2.js"; // stringLiteral variable identifier also imported above; however, to avoid web browser JavaScript console SyntaxError: FF57 redeclaration of import/CH64 identifier already declared, import statement rename stringLiteral to stringLiteralFoo. // Capability test seven: module this value is undefined. import {externalModuleThisValue} from "./module_external_2.js"; // External JavaScript module this value inside the external JavaScript module. var passModuleThisValueIsUndefinedCapabilityTest = false; if ((this === undefined) && (externalModuleThisValue === undefined)){ // Test this module this value and external module this value. passModuleThisValueIsUndefinedCapabilityTest = true; } var passCapabilityTest = false; if (passNamedExportsImportsCapabilityTest && passNamespaceImportCapabilityTest && passRenameExportsImportsCapabilityTest && passModuleDefaultExportCapabilityTest && passImportsAreLiveConnectionsCapabilitTest && passImportStatementsAreHoistedCapabilityTest && passModuleThisValueIsUndefinedCapabilityTest){ passCapabilityTest = true; } export {passCapabilityTest}; // Imported by HTML document embedded JavaScript module.
JavaScript source code: module_external_1.js:
// Exports are imported by module_external_main.js, not by HTML document embedded JavaScript module. // Capability test one: named exports/imports and capability test two: namespace import. var booleanLiteral = true; // Variable declaration. export {booleanLiteral}; // Export statement. Variable declaration and export statement on separate lines. export var numberLiteral = 1; // Export statement and variable declaration combined in same line. var numberLiteral also in module_external_2.js. export var stringLiteral = "abc"; // var stringLiteral also in module_external_2.js. export var nullLiteral = null; export var undefinedLiteral = undefined; export var undefinedVariable; export function functionDeclaration(){ // function functionDeclaration() also in module_external_2.js. return "functionDeclaration return value"; } export var functionExpression = function(){ return "functionExpression return value"; }; export var arrayLiteral = ["a", "b", "c"]; export var objectLiteral = { property: "property value" }; export class ClassDeclaration { constructor(){ this.ownProperty = "ownProperty value"; } }; ClassDeclaration.prototype.prototypeProperty = "prototypeProperty value";
JavaScript source code: module_external_2.js:
// Exports are imported by module_external_main.js, not by HTML document embedded JavaScript module. // Capability test three: rename exports/imports. var numberLiteralOne = 1; export {numberLiteralOne as numberLiteralOneRenamed}; // Rename variable in export statement. export var numberLiteralTwo = 2; // To be renamed in import statement. // Capability test four: module default export. function functionDeclaration(){return "functionDeclaration return value";} // function functionDeclaration() also in module_external_1.js. export default functionDeclaration; // Identical to: 1.) export default function functionDeclaration(){return "functionDeclaration return value";}. Export default function declaration itself, not variable. No trailing semicolon (;).; and 2.) export default function(){return "functionDeclaration return value";}. Export default anonymous function declaration. Since only one export default per module is allowed, and export default import statement assigns name implicitly, can export default anonymous function declaration. No trailing semicolon (;). Also can export default anonymous class declarations and literals (eg, export default "stringLiteral value";). // Capability test five: imports are live connections. export var numberLiteral = 10; // var numberLiteral also in module_external_1.js. export function incrementNumberLiteral(){ numberLiteral++; } // Capability test six: import statements are hoisted. export var stringLiteral = "stringLiteral value"; // var stringLiteral also in module_external_1.js. // Capability test seven: module this value is undefined. export var externalModuleThisValue = this; // External JavaScript module this value.
2.3. Web Browser Support Test Result
Fail (no support): The web browser does not recognize JavaScript modules. The web browser does not support JavaScript modules.