Skip to content

Commit

Permalink
chore: anal retention
Browse files Browse the repository at this point in the history
  • Loading branch information
boneskull committed Sep 20, 2024
1 parent 2c7ed7b commit b8c9a48
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 31 deletions.
114 changes: 87 additions & 27 deletions packages/compartment-mapper/src/map-parser.js
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -12,6 +13,9 @@ import { parseExtension } from './extension.js';
/**
* @import {
* LanguageForExtension,
LanguageForModuleSpecifier,
* MakeMapParsersOptions,
* MapParsersFn,
* ModuleTransform,
* ModuleTransforms,
* ParseFn,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -201,23 +207,54 @@ const makeExtensionParser = (
};

/**
* Creates a synchronous parser
*
* @overload
* @param {LanguageForExtension} languageForExtension
* @param {Record<string, string>} 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)) {
Expand All @@ -238,42 +275,65 @@ 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<string, string>) => 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<ParseFnAsync>}
*/
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<ParseFn>`
if (values(parserForLanguage).some(({ synchronous }) => !synchronous)) {
return asyncParseFn;
}

/**
* Synchronous `mapParsers()` function; returned when all parsers are
* synchronous and `moduleTransforms` is empty.
*
* @type {MapParsersFn<ParseFn>}
*/
return (languageForExtension, languageForModuleSpecifier) =>
mapParsers(
languageForExtension,
languageForModuleSpecifier,
parserForLanguage,
moduleTransforms,
syncModuleTransforms,
preferSynchronous,
true,
);
};
31 changes: 27 additions & 4 deletions packages/compartment-mapper/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ export {};
/**
* Either synchronous or asynchronous operators for `createStaticModuleType`.
*
* @typedef {AsyncCreateStaticModuleTypeOperators|SyncCreateStaticModuleTypeOperators} CreateStaticModuleTypeOperators
* @typedef {AsyncCreateStaticModuleTypeOperators | SyncCreateStaticModuleTypeOperators} CreateStaticModuleTypeOperators
*/

/**
Expand All @@ -918,8 +918,7 @@ export {};
* {@link SyncCreateStaticModuleTypeOperators}.
*
* @typedef {ReturnType<CreateStaticModuleTypeOperators['maybeRead']> |
* ReturnType<CreateStaticModuleTypeOperators['parse']>}
* CreateStaticModuleTypeYieldables
* ReturnType<CreateStaticModuleTypeOperators['parse']>} CreateStaticModuleTypeYieldables
*/

/**
Expand All @@ -930,5 +929,29 @@ export {};
* @property {Record<string, CompartmentDescriptor>} compartmentDescriptors
* @property {Record<string, Compartment>} 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
*/

0 comments on commit b8c9a48

Please sign in to comment.