diff options
Diffstat (limited to 'src/data/things/index.js')
| -rw-r--r-- | src/data/things/index.js | 295 |
1 files changed, 20 insertions, 275 deletions
diff --git a/src/data/things/index.js b/src/data/things/index.js index 35cd8cf2..bf3df9a7 100644 --- a/src/data/things/index.js +++ b/src/data/things/index.js @@ -1,275 +1,20 @@ -import * as path from 'node:path'; -import {fileURLToPath} from 'node:url'; - -import {openAggregate, showAggregate} from '#aggregate'; -import CacheableObject from '#cacheable-object'; -import {logError} from '#cli'; -import {compositeFrom} from '#composite'; -import * as serialize from '#serialize'; -import {empty} from '#sugar'; -import Thing from '#thing'; - -import * as additionalFileClasses from './additional-file.js'; -import * as additionalNameClasses from './additional-name.js'; -import * as albumClasses from './album.js'; -import * as artTagClasses from './art-tag.js'; -import * as artistClasses from './artist.js'; -import * as artworkClasses from './artwork.js'; -import * as contentClasses from './content.js'; -import * as contributionClasses from './contribution.js'; -import * as flashClasses from './flash.js'; -import * as groupClasses from './group.js'; -import * as homepageLayoutClasses from './homepage-layout.js'; -import * as languageClasses from './language.js'; -import * as musicVideoClasses from './music-video.js'; -import * as newsEntryClasses from './news-entry.js'; -import * as sortingRuleClasses from './sorting-rule.js'; -import * as staticPageClasses from './static-page.js'; -import * as trackClasses from './track.js'; -import * as wikiInfoClasses from './wiki-info.js'; - -const allClassLists = { - 'additional-file.js': additionalFileClasses, - 'additional-name.js': additionalNameClasses, - 'album.js': albumClasses, - 'art-tag.js': artTagClasses, - 'artist.js': artistClasses, - 'artwork.js': artworkClasses, - 'content.js': contentClasses, - 'contribution.js': contributionClasses, - 'flash.js': flashClasses, - 'group.js': groupClasses, - 'homepage-layout.js': homepageLayoutClasses, - 'language.js': languageClasses, - 'music-video.js': musicVideoClasses, - 'news-entry.js': newsEntryClasses, - 'sorting-rule.js': sortingRuleClasses, - 'static-page.js': staticPageClasses, - 'track.js': trackClasses, - 'wiki-info.js': wikiInfoClasses, -}; - -let allClasses = Object.create(null); - -// src/data/things/index.js -> src/ -const __dirname = path.dirname( - path.resolve( - fileURLToPath(import.meta.url), - '../..')); - -function niceShowAggregate(error, ...opts) { - showAggregate(error, { - pathToFileURL: (f) => path.relative(__dirname, fileURLToPath(f)), - showClasses: false, - ...opts, - }); -} - -function errorDuplicateClassNames() { - const locationDict = Object.create(null); - - for (const [location, classes] of Object.entries(allClassLists)) { - for (const className of Object.keys(classes)) { - if (className in locationDict) { - locationDict[className].push(location); - } else { - locationDict[className] = [location]; - } - } - } - - let success = true; - - for (const [className, locations] of Object.entries(locationDict)) { - if (locations.length === 1) { - continue; - } - - logError`Thing class name ${`"${className}"`} is defined more than once: ${locations.join(', ')}`; - success = false; - } - - return success; -} - -function flattenClassLists() { - let remaining = []; - for (const classes of Object.values(allClassLists)) { - for (const constructor of Object.values(classes)) { - if (typeof constructor !== 'function') continue; - if (!(constructor.prototype instanceof Thing)) continue; - remaining.push(constructor); - } - } - - let sorted = []; - while (true) { - if (sorted[0]) { - const superclass = Object.getPrototypeOf(sorted[0]); - if (superclass !== Thing) { - if (sorted.includes(superclass)) { - sorted.unshift(...sorted.splice(sorted.indexOf(superclass), 1)); - } else { - sorted.unshift(superclass); - } - continue; - } - } - - if (!empty(remaining)) { - sorted.unshift(remaining.shift()); - } else { - break; - } - } - - for (const constructor of sorted) { - allClasses[constructor.name] = constructor; - } -} - -function descriptorAggregateHelper({ - showFailedClasses, - message, - op, -}) { - const failureSymbol = Symbol(); - const aggregate = openAggregate({ - message, - returnOnFail: failureSymbol, - }); - - const failedClasses = []; - - for (const [name, constructor] of Object.entries(allClasses)) { - const result = aggregate.call(op, constructor); - - if (result === failureSymbol) { - failedClasses.push(name); - } - } - - try { - aggregate.close(); - return true; - } catch (error) { - niceShowAggregate(error); - showFailedClasses(failedClasses); - - /* - if (error.errors) { - for (const sub of error.errors) { - console.error(sub); - } - } - */ - - return false; - } -} - -function evaluatePropertyDescriptors() { - const opts = {...allClasses}; - - return descriptorAggregateHelper({ - message: `Errors evaluating Thing class property descriptors`, - - op(constructor) { - if (!constructor[Thing.getPropertyDescriptors]) { - throw new Error(`Missing [Thing.getPropertyDescriptors] function`); - } - - const results = constructor[Thing.getPropertyDescriptors](opts); - - for (const [key, value] of Object.entries(results)) { - if (Array.isArray(value)) { - results[key] = compositeFrom({ - annotation: `${constructor.name}.${key}`, - compose: false, - steps: value, - }); - } else if (value.toResolvedComposition) { - results[key] = compositeFrom(value.toResolvedComposition()); - } - } - - constructor[CacheableObject.propertyDescriptors] = - Object.create(constructor[CacheableObject.propertyDescriptors] ?? null); - - Object.assign(constructor[CacheableObject.propertyDescriptors], results); - }, - - showFailedClasses(failedClasses) { - logError`Failed to evaluate property descriptors for classes: ${failedClasses.join(', ')}`; - }, - }); -} - -function evaluateSerializeDescriptors() { - const opts = {...allClasses, serialize}; - - return descriptorAggregateHelper({ - message: `Errors evaluating Thing class serialize descriptors`, - - op(constructor) { - if (!constructor[Thing.getSerializeDescriptors]) { - return; - } - - constructor[serialize.serializeDescriptors] = - constructor[Thing.getSerializeDescriptors](opts); - }, - - showFailedClasses(failedClasses) { - logError`Failed to evaluate serialize descriptors for classes: ${failedClasses.join(', ')}`; - }, - }); -} - -function finalizeYamlDocumentSpecs() { - return descriptorAggregateHelper({ - message: `Errors finalizing Thing YAML document specs`, - - op(constructor) { - const superclass = Object.getPrototypeOf(constructor); - if ( - constructor[Thing.yamlDocumentSpec] && - superclass[Thing.yamlDocumentSpec] - ) { - constructor[Thing.yamlDocumentSpec] = - Thing.extendDocumentSpec(superclass, constructor[Thing.yamlDocumentSpec]); - } - }, - - showFailedClasses(failedClasses) { - logError`Failed to finalize YAML document specs for classes: ${failedClasses.join(', ')}`; - }, - }); -} - -function finalizeCacheableObjectPrototypes() { - return descriptorAggregateHelper({ - message: `Errors finalizing Thing class prototypes`, - - op(constructor) { - constructor.finalizeCacheableObjectPrototype(); - }, - - showFailedClasses(failedClasses) { - logError`Failed to finalize cacheable object prototypes for classes: ${failedClasses.join(', ')}`; - }, - }); -} - -if (!errorDuplicateClassNames()) process.exit(1); - -flattenClassLists(); - -if (!evaluatePropertyDescriptors()) process.exit(1); -if (!evaluateSerializeDescriptors()) process.exit(1); -if (!finalizeYamlDocumentSpecs()) process.exit(1); -if (!finalizeCacheableObjectPrototypes()) process.exit(1); - -Object.assign(allClasses, {Thing}); - -export default allClasses; +// Not actually the entry point for #things - that's init.js in this folder. + +export * from './additional-file.js'; +export * from './additional-name.js'; +export * from './album.js'; +export * from './art-tag.js'; +export * from './artist.js'; +export * from './artwork.js'; +export * from './content.js'; +export * from './contribution.js'; +export * from './flash.js'; +export * from './group.js'; +export * from './homepage-layout.js'; +export * from './language.js'; +export * from './music-video.js'; +export * from './news-entry.js'; +export * from './sorting-rule.js'; +export * from './static-page.js'; +export * from './track.js'; +export * from './wiki-info.js'; |