diff options
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/data/thing.js | 2 | ||||
-rw-r--r-- | src/find-reverse.js | 100 | ||||
-rw-r--r-- | src/find.js | 80 | ||||
-rw-r--r-- | src/reverse.js | 32 | ||||
-rw-r--r-- | src/write/build-modes/repl.js | 3 |
6 files changed, 158 insertions, 60 deletions
diff --git a/package.json b/package.json index c4b74c26..1bfb4f34 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "#node-utils": "./src/util/node-utils.js", "#repl": "./src/write/build-modes/repl.js", "#replacer": "./src/util/replacer.js", + "#reverse": "./src/reverse.js", "#search": "./src/search.js", "#search-spec": "./src/util/search-spec.js", "#serialize": "./src/data/serialize.js", diff --git a/src/data/thing.js b/src/data/thing.js index 78ad3642..4c3ba3e4 100644 --- a/src/data/thing.js +++ b/src/data/thing.js @@ -16,6 +16,8 @@ export default class Thing extends CacheableObject { static findSpecs = Symbol.for('Thing.findSpecs'); static findThisThingOnly = Symbol.for('Thing.findThisThingOnly'); + static reverseSpecs = Symbol.for('Thing.reverseSpecs'); + static yamlDocumentSpec = Symbol.for('Thing.yamlDocumentSpec'); static getYamlLoadingSpec = Symbol.for('Thing.getYamlLoadingSpec'); diff --git a/src/find-reverse.js b/src/find-reverse.js new file mode 100644 index 00000000..47e0e268 --- /dev/null +++ b/src/find-reverse.js @@ -0,0 +1,100 @@ +// Helpers common to #find and #reverse logic. + +import thingConstructors from '#things'; + +export function getAllSpecs({ + word, + constructorKey, + + hardcodedSpecs, + postprocessSpec, +}) { + try { + thingConstructors; + } catch (error) { + throw new Error(`Thing constructors aren't ready yet, can't get all ${word} specs`); + } + + const specs = {...hardcodedSpecs}; + + for (const thingConstructor of Object.values(thingConstructors)) { + const thingSpecs = thingConstructor[constructorKey]; + if (!thingSpecs) continue; + + for (const [key, spec] of Object.entries(thingSpecs)) { + specs[key] = + postprocessSpec(spec, { + thingConstructor, + }); + } + } + + return specs; +} + +export function findSpec(key, { + word, + constructorKey, + + hardcodedSpecs, + postprocessSpec, +}) { + if (Object.hasOwn(hardcodedSpecs, key)) { + return hardcodedSpecs[key]; + } + + try { + thingConstructors; + } catch (error) { + throw new Error(`Thing constructors aren't ready yet, can't check if "${word}.${key}" available`); + } + + for (const thingConstructor of Object.values(thingConstructors)) { + const thingSpecs = thingConstructor[constructorKey]; + if (!thingSpecs) continue; + + if (Object.hasOwn(thingSpecs, key)) { + return postprocessSpec(thingSpecs[key], { + thingConstructor, + }); + } + } + + throw new Error(`"${word}.${key}" isn't available`); +} + +export function tokenProxy({ + findSpec, + prepareBehavior, + + handle: customHandle = + (_key) => undefined, + + decorate = + (_token, _key) => {}, +}) { + return new Proxy({}, { + get: (store, key) => { + const custom = customHandle(key); + if (custom !== undefined) { + return custom; + } + + if (!Object.hasOwn(store, key)) { + let behavior = (...args) => { + // This will error if the spec isn't available... + const spec = findSpec(key); + + // ...or, if it is available, replace this function with the + // ready-for-use find function made out of that spec. + return (behavior = prepareBehavior(spec))(...args); + }; + + store[key] = (...args) => behavior(...args); + decorate(store[key], key); + } + + return store[key]; + }, + }); +} diff --git a/src/find.js b/src/find.js index d647419a..29f32dad 100644 --- a/src/find.js +++ b/src/find.js @@ -5,6 +5,8 @@ import {compareObjects, stitchArrays, typeAppearance} from '#sugar'; import thingConstructors from '#things'; import {isFunction, validateArrayItems} from '#validators'; +import {findSpec, getAllSpecs, tokenProxy} from './find-reverse.js'; + function warnOrThrow(mode, message) { if (mode === 'error') { throw new Error(message); @@ -240,6 +242,14 @@ const hardcodedFindSpecs = { }, }; +const findReverseHelperConfig = { + word: `find`, + constructorKey: Symbol.for('Thing.findSpecs'), + + hardcodedSpecs: hardcodedFindSpecs, + postprocessSpec: postprocessFindSpec, +}; + export function postprocessFindSpec(spec, {thingConstructor}) { const newSpec = {...spec}; @@ -261,52 +271,11 @@ export function postprocessFindSpec(spec, {thingConstructor}) { } export function getAllFindSpecs() { - try { - thingConstructors; - } catch (error) { - throw new Error(`Thing constructors aren't ready yet, can't get all find specs`); - } - - const findSpecs = {...hardcodedFindSpecs}; - - for (const thingConstructor of Object.values(thingConstructors)) { - const thingFindSpecs = thingConstructor[Symbol.for('Thing.findSpecs')]; - if (!thingFindSpecs) continue; - - for (const [key, spec] of Object.entries(thingFindSpecs)) { - findSpecs[key] = - postprocessFindSpec(spec, { - thingConstructor, - }); - } - } - - return findSpecs; + return getAllSpecs(findReverseHelperConfig); } export function findFindSpec(key) { - if (Object.hasOwn(hardcodedFindSpecs, key)) { - return hardcodedFindSpecs[key]; - } - - try { - thingConstructors; - } catch (error) { - throw new Error(`Thing constructors aren't ready yet, can't check if "find.${key}" available`); - } - - for (const thingConstructor of Object.values(thingConstructors)) { - const thingFindSpecs = thingConstructor[Symbol.for('Thing.findSpecs')]; - if (!thingFindSpecs) continue; - - if (Object.hasOwn(thingFindSpecs, key)) { - return postprocessFindSpec(thingFindSpecs[key], { - thingConstructor, - }); - } - } - - throw new Error(`"find.${key}" isn't available`); + return findSpec(key, findReverseHelperConfig); } export const findTokenKey = Symbol.for('find.findTokenKey'); @@ -425,27 +394,18 @@ export function findMixed(config) { return findMixedStore.get(config); } -export default new Proxy({}, { - get: (store, key) => { +export default tokenProxy({ + findSpec: findFindSpec, + prepareBehavior: findHelper, + + handle(key) { if (key === 'mixed') { return findMixed; } + }, - if (!Object.hasOwn(store, key)) { - let behavior = (...args) => { - // This will error if the find spec isn't available... - const findSpec = findFindSpec(key); - - // ...or, if it is available, replace this function with the - // ready-for-use find function made out of that find spec. - return (behavior = findHelper(findSpec))(...args); - }; - - store[key] = (...args) => behavior(...args); - store[key][findTokenKey] = key; - } - - return store[key]; + decorate(token, key) { + token[findTokenKey] = key; }, }); diff --git a/src/reverse.js b/src/reverse.js new file mode 100644 index 00000000..b5a4bf4c --- /dev/null +++ b/src/reverse.js @@ -0,0 +1,32 @@ +import {findSpec, getAllSpecs, tokenProxy} from './find-reverse.js'; + +const hardcodedReverseSpecs = {}; + +const findReverseHelperConfig = { + word: `reverse`, + constructorKey: Symbol.for('Thing.reverseSpecs'), + + hardcodedSpecs: hardcodedReverseSpecs, + postprocessSpec: postprocessReverseSpec, +}; + +export function postprocessReverseSpec(spec, {thingConstructor}) { + const newSpec = {...spec}; + + void thingConstructor; + + return newSpec; +} + +export function getAllReverseSpecs() { + return getAllSpecs(findReverseHelperConfig); +} + +export function findReverseSpec(key) { + return findSpec(key, findReverseHelperConfig); +} + +export default tokenProxy({ + findSpec: findReverseSpec, + prepareBehavior: spec => from => ({spec, from}), +}); diff --git a/src/write/build-modes/repl.js b/src/write/build-modes/repl.js index 957d2c2d..ff64ee82 100644 --- a/src/write/build-modes/repl.js +++ b/src/write/build-modes/repl.js @@ -36,6 +36,7 @@ import * as path from 'node:path'; import * as repl from 'node:repl'; import _find, {bindFind} from '#find'; +import _reverse from '#reverse'; import CacheableObject from '#cacheable-object'; import {logWarn} from '#cli'; import {debugComposite} from '#composite'; @@ -95,6 +96,8 @@ export async function getContextAssignments({ find, bindFind, + _reverse, + showAggregate, }; |