1'use strict'; 2 3const { 4 ArrayPrototypeFilter, 5 ArrayPrototypeIncludes, 6 ObjectKeys, 7 ObjectValues, 8 ObjectPrototypeHasOwnProperty, 9} = primordials; 10const { validateString } = require('internal/validators'); 11 12const { 13 ERR_IMPORT_ASSERTION_TYPE_FAILED, 14 ERR_IMPORT_ASSERTION_TYPE_MISSING, 15 ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED, 16 ERR_IMPORT_ATTRIBUTE_UNSUPPORTED, 17} = require('internal/errors').codes; 18 19// The HTML spec has an implied default type of `'javascript'`. 20const kImplicitAssertType = 'javascript'; 21 22/** 23 * Define a map of module formats to import attributes types (the value of 24 * `type` in `with { type: 'json' }`). 25 * @type {Map<string, string>} 26 */ 27const formatTypeMap = { 28 '__proto__': null, 29 'builtin': kImplicitAssertType, 30 'commonjs': kImplicitAssertType, 31 'json': 'json', 32 'module': kImplicitAssertType, 33 'wasm': kImplicitAssertType, // It's unclear whether the HTML spec will require an attribute type or not for Wasm; see https://github.com/WebAssembly/esm-integration/issues/42 34}; 35 36/** 37 * The HTML spec disallows the default type to be explicitly specified 38 * (for now); so `import './file.js'` is okay but 39 * `import './file.js' with { type: 'javascript' }` throws. 40 * @type {Array<string, string>} 41 */ 42const supportedAssertionTypes = ArrayPrototypeFilter( 43 ObjectValues(formatTypeMap), 44 (type) => type !== kImplicitAssertType); 45 46 47/** 48 * Test a module's import attributes. 49 * @param {string} url The URL of the imported module, for error reporting. 50 * @param {string} format One of Node's supported translators 51 * @param {Record<string, string>} importAttributes Validations for the 52 * module import. 53 * @returns {true} 54 * @throws {TypeError} If the format and assertion type are incompatible. 55 */ 56function validateAttributes(url, format, 57 importAttributes = { __proto__: null }) { 58 const keys = ObjectKeys(importAttributes); 59 for (let i = 0; i < keys.length; i++) { 60 if (keys[i] !== 'type') { 61 throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]]); 62 } 63 } 64 const validType = formatTypeMap[format]; 65 66 switch (validType) { 67 case undefined: 68 // Ignore attributes for module formats we don't recognize, to allow new 69 // formats in the future. 70 return true; 71 72 case kImplicitAssertType: 73 // This format doesn't allow an import assertion type, so the property 74 // must not be set on the import attributes object. 75 if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) { 76 return true; 77 } 78 return handleInvalidType(url, importAttributes.type); 79 80 case importAttributes.type: 81 // The asserted type is the valid type for this format. 82 return true; 83 84 default: 85 // There is an expected type for this format, but the value of 86 // `importAttributes.type` might not have been it. 87 if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) { 88 // `type` wasn't specified at all. 89 throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType); 90 } 91 return handleInvalidType(url, importAttributes.type); 92 } 93} 94 95/** 96 * Throw the correct error depending on what's wrong with the type assertion. 97 * @param {string} url The resolved URL for the module to be imported 98 * @param {string} type The value of the import assertion `type` property 99 */ 100function handleInvalidType(url, type) { 101 // `type` might have not been a string. 102 validateString(type, 'type'); 103 104 // `type` might not have been one of the types we understand. 105 if (!ArrayPrototypeIncludes(supportedAssertionTypes, type)) { 106 throw new ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED(type); 107 } 108 109 // `type` was the wrong value for this format. 110 throw new ERR_IMPORT_ASSERTION_TYPE_FAILED(url, type); 111} 112 113 114module.exports = { 115 kImplicitAssertType, 116 validateAttributes, 117}; 118