From b8c9a4818c0be44d327f058fb6ba3b3181392d55 Mon Sep 17 00:00:00 2001 From: Christopher Hiller Date: Fri, 20 Sep 2024 13:34:26 -0700 Subject: [PATCH] chore: anal retention --- packages/compartment-mapper/src/map-parser.js | 114 +++++++++++++----- packages/compartment-mapper/src/types.js | 31 ++++- 2 files changed, 114 insertions(+), 31 deletions(-) diff --git a/packages/compartment-mapper/src/map-parser.js b/packages/compartment-mapper/src/map-parser.js index fffd3f1379..a7fe7b87e2 100644 --- a/packages/compartment-mapper/src/map-parser.js +++ b/packages/compartment-mapper/src/map-parser.js @@ -1,5 +1,6 @@ /** - * Exports {@link mapParsers}, which matches a module to a parser based on reasons. + * Exports {@link makeMapParsers}, which creates a function which matches a + * module to a parser based on reasons. * * @module */ @@ -12,6 +13,9 @@ import { parseExtension } from './extension.js'; /** * @import { * LanguageForExtension, + LanguageForModuleSpecifier, + * MakeMapParsersOptions, + * MapParsersFn, * ModuleTransform, * ModuleTransforms, * ParseFn, @@ -23,7 +27,7 @@ import { parseExtension } from './extension.js'; * } from './types.js'; */ -const { entries, fromEntries, keys, hasOwnProperty } = Object; +const { entries, fromEntries, keys, hasOwnProperty, values } = Object; const { apply } = Reflect; // q, as in quote, for strings in error messages. const q = JSON.stringify; @@ -184,7 +188,9 @@ const makeExtensionParser = ( ); }; - // Unfortunately, typescript was not smart enough to figure out the return type depending on a boolean in arguments, so it has to be ParseFnAsync|ParseFn + // Unfortunately, typescript was not smart enough to figure out the return + // type depending on a boolean in arguments, so it has to be + // ParseFnAsync|ParseFn if (preferSynchronous) { transforms = syncModuleTransforms; return syncParser; @@ -201,23 +207,54 @@ const makeExtensionParser = ( }; /** + * Creates a synchronous parser + * + * @overload * @param {LanguageForExtension} languageForExtension - * @param {Record} languageForModuleSpecifier - In a rare case, the type of a module - * is implied by package.json and should not be inferred from its extension. + * @param {LanguageForModuleSpecifier} languageForModuleSpecifier - In a rare case, + * the type of a module is implied by package.json and should not be inferred + * from its extension. * @param {ParserForLanguage} parserForLanguage * @param {ModuleTransforms} [moduleTransforms] * @param {SyncModuleTransforms} [syncModuleTransforms] - * @param {boolean} [preferSynchronous] + * @param {true} preferSynchronous If `true`, will create a `ParseFn` + * @returns {ParseFn} + */ + +/** + * Creates an asynchronous parser + * + * @overload + * @param {LanguageForExtension} languageForExtension + * @param {LanguageForModuleSpecifier} languageForModuleSpecifier - In a rare case, + * the type of a module is implied by package.json and should not be inferred + * from its extension. + * @param {ParserForLanguage} parserForLanguage + * @param {ModuleTransforms} [moduleTransforms] + * @param {SyncModuleTransforms} [syncModuleTransforms] + * @param {false} [preferSynchronous] + * @returns {ParseFnAsync} + */ + +/** + * @param {LanguageForExtension} languageForExtension + * @param {LanguageForModuleSpecifier} languageForModuleSpecifier - In a rare case, + * the type of a module is implied by package.json and should not be inferred + * from its extension. + * @param {ParserForLanguage} parserForLanguage + * @param {ModuleTransforms} [moduleTransforms] + * @param {SyncModuleTransforms} [syncModuleTransforms] + * @param {boolean} [preferSynchronous] If `true`, will create a `ParseFn` * @returns {ParseFnAsync|ParseFn} */ -export const mapParsers = ( +function mapParsers( languageForExtension, languageForModuleSpecifier, parserForLanguage, moduleTransforms = {}, syncModuleTransforms = {}, preferSynchronous = false, -) => { +) { const languageForExtensionEntries = []; const problems = []; for (const [extension, language] of entries(languageForExtension)) { @@ -238,35 +275,58 @@ export const mapParsers = ( moduleTransforms, syncModuleTransforms, ); -}; +} /** - * Prepares a function to map parsers after verifying whether synchronous behavior is - * preferred. Synchronous behavior is selected if all parsers are synchronous and no - * async transforms are provided. + * Prepares a function to map parsers after verifying whether synchronous + * behavior is preferred. Synchronous behavior is selected if all parsers are + * synchronous and no async transforms are provided. + * + * _Note:_ The type argument for {@link MapParsersFn} _could_ be inferred from + * the function arguments _if_ {@link ParserForLanguage} contained only values + * of type `ParserImplementation & {synchronous: true}` (and also considering + * the emptiness of `moduleTransforms`); may only be worth the effort if it was + * causing issues in practice. * - * @param {object} options - * @param {ParserForLanguage} options.parserForLanguage - * @param {ModuleTransforms} [options.moduleTransforms] - * @param {SyncModuleTransforms} [options.syncModuleTransforms] - * @returns {(languageForExtension: LanguageForExtension, languageForModuleSpecifier: Record) => ParseFnAsync|ParseFn} + * @param {MakeMapParsersOptions} options + * @returns {MapParsersFn} */ export const makeMapParsers = ({ parserForLanguage, moduleTransforms, syncModuleTransforms, }) => { - let preferSynchronous = true; + /** + * Async `mapParsers()` function; returned when a non-synchronous parser is + * present _or_ when `moduleTransforms` is non-empty. + * + * @type {MapParsersFn} + */ + const asyncParseFn = (languageForExtension, languageForModuleSpecifier) => + mapParsers( + languageForExtension, + languageForModuleSpecifier, + parserForLanguage, + moduleTransforms, + syncModuleTransforms, + ); + if (moduleTransforms && keys(moduleTransforms).length > 0) { - preferSynchronous = false; - } else { - for (const [_language, { synchronous }] of entries(parserForLanguage)) { - if (!synchronous) { - preferSynchronous = false; - break; - } - } + return asyncParseFn; } + + // all parsers must explicitly be flagged `synchronous` to return a + // `MapParsersFn` + if (values(parserForLanguage).some(({ synchronous }) => !synchronous)) { + return asyncParseFn; + } + + /** + * Synchronous `mapParsers()` function; returned when all parsers are + * synchronous and `moduleTransforms` is empty. + * + * @type {MapParsersFn} + */ return (languageForExtension, languageForModuleSpecifier) => mapParsers( languageForExtension, @@ -274,6 +334,6 @@ export const makeMapParsers = ({ parserForLanguage, moduleTransforms, syncModuleTransforms, - preferSynchronous, + true, ); }; diff --git a/packages/compartment-mapper/src/types.js b/packages/compartment-mapper/src/types.js index 03680184c7..a9dc3791fd 100644 --- a/packages/compartment-mapper/src/types.js +++ b/packages/compartment-mapper/src/types.js @@ -906,7 +906,7 @@ export {}; /** * Either synchronous or asynchronous operators for `createStaticModuleType`. * - * @typedef {AsyncCreateStaticModuleTypeOperators|SyncCreateStaticModuleTypeOperators} CreateStaticModuleTypeOperators + * @typedef {AsyncCreateStaticModuleTypeOperators | SyncCreateStaticModuleTypeOperators} CreateStaticModuleTypeOperators */ /** @@ -918,8 +918,7 @@ export {}; * {@link SyncCreateStaticModuleTypeOperators}. * * @typedef {ReturnType | - * ReturnType} - * CreateStaticModuleTypeYieldables + * ReturnType} CreateStaticModuleTypeYieldables */ /** @@ -930,5 +929,29 @@ export {}; * @property {Record} compartmentDescriptors * @property {Record} compartments * @property {string} absoluteModuleSpecifier A module specifier which is an absolute path - * @property {string} packageLocation + * @property {string} packageLocation Location of the compartment descriptor's package + */ + +/** + * Options for `makeMapParsers()` + * + * @typedef MakeMapParsersOptions + * @property {ParserForLanguage} parserForLanguage Mapping of language to + * {@link ParserImplementation} + * @property {ModuleTransforms} [moduleTransforms] Async or sync module + * transforms. If non-empty, dynamic requires are unsupported. + * @property {SyncModuleTransforms} [syncModuleTransforms] Sync module + * transforms + */ + +/** + * The value returned by `makeMapParsers()` + * + * @template {ParseFn|ParseFnAsync} [T=ParseFn|ParseFnAsync] + * @callback MapParsersFn + * @param {LanguageForExtension} languageForExtension Mapping of file extension + * to {@link Language} + * @param {LanguageForModuleSpecifier} languageForModuleSpecifier Mapping of + * module specifier to {@link Language} + * @returns {T} Parser function */