From 2fdee4395056ba9629bfdd1984b6a40cc71c3e44 Mon Sep 17 00:00:00 2001 From: cha0s Date: Wed, 20 Nov 2019 00:53:35 -0600 Subject: [PATCH] feat: hooks! --- packages/core/array.js | 8 ++++ packages/core/hook/entry.js | 1 + packages/core/hook/loader.js | 72 ++++++++++++++++++++++++++++++++++ packages/core/hook/registry.js | 27 +++++++++++++ packages/core/index.js | 3 +- 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 packages/core/hook/entry.js create mode 100644 packages/core/hook/loader.js create mode 100644 packages/core/hook/registry.js diff --git a/packages/core/array.js b/packages/core/array.js index b567689..fcacdca 100644 --- a/packages/core/array.js +++ b/packages/core/array.js @@ -5,3 +5,11 @@ export function arrayUnique(array) { export function flatten(array) { return array.reduce((flattened, elm) => flattened.concat(elm), []); } + +export function fromObject(object) { + const array = []; + for (const key in object) { + array.push(object[key]); + } + return array; +} diff --git a/packages/core/hook/entry.js b/packages/core/hook/entry.js new file mode 100644 index 0000000..22488e5 --- /dev/null +++ b/packages/core/hook/entry.js @@ -0,0 +1 @@ +// I don't know how to create assets out of thin air with webpack. diff --git a/packages/core/hook/loader.js b/packages/core/hook/loader.js new file mode 100644 index 0000000..739bcc7 --- /dev/null +++ b/packages/core/hook/loader.js @@ -0,0 +1,72 @@ +const glob = require('glob'); +const {getOptions} = require('loader-utils'); +const path = require('path'); +const validateOptions = require('schema-utils'); +const schema = { + type: 'object', + properties: { + paths: { + items: { + type: 'string', + }, + type: 'array', + }, + }, + required: [ + 'paths', + ], +}; + +// Dynamically require all traits. +module.exports = function(source) { + const options = getOptions(this); + // Validate schema. + validateOptions(schema, options, 'Avocado hooks'); + // Extract options. + const {paths} = options; + // Search avocado. + const avocadoPath = path.normalize( + path.join(__dirname, '..', '..'), + ); + paths.push(avocadoPath); + // Get source paths. + const sourcePaths = []; + paths.forEach((path) => { + const files = glob.sync(`${path}/**/hooks.js`, { + follow: true, + }); + sourcePaths.push(...files); + }); + // Build registers. + const registers = sourcePaths.map((sourcePath) => { + const relativePath = path.relative(__dirname, sourcePath); + const basename = path.basename(relativePath, '.js'); + const parts = relativePath.split('/'); + // Chop off basename. + parts.pop(); + // Module or local? + let importDirectory; + if ('..' === parts[0]) { + const avocadoPathParts = ['@avocado', 'core', 'hook'].concat(parts); + importDirectory = path.normalize(avocadoPathParts.join('/')); + } + else if ('node_modules' === parts[0]) { + importDirectory = parts.slice(1).join('/'); + } + else { + importDirectory = `./${parts.join('/')}`; + } + // Register hooks. + const importPath = `${importDirectory}/hooks`; + return [ + `registerHooks('${importDirectory}', require('${importPath}'));`, + ].join('\n'); + }); + // Import trait registry first. + const output = [ + `import {registerHooks} from '@avocado/core/hook/registry'`, + '', + ...registers + ].join('\n'); + return output; +} diff --git a/packages/core/hook/registry.js b/packages/core/hook/registry.js new file mode 100644 index 0000000..3fb6e09 --- /dev/null +++ b/packages/core/hook/registry.js @@ -0,0 +1,27 @@ +import {fromObject} from '../array'; + +const impl = {}; +export function registerHooks(name, implementations) { + for (const hook in implementations) { + if (!impl[hook]) { + impl[hook] = {}; + } + impl[hook][name] = implementations[hook]; + } +} + +export function invoke(hook, ...args) { + const hookImpl = impl[hook]; + if (!hookImpl) { + return {}; + } + const result = {}; + for (const name in hookImpl) { + result[name] = hookImpl[name](...args); + } + return result; +} + +export function invokeFlat(hook, ...args) { + return fromObject(invoke(hook, ...args)); +} diff --git a/packages/core/index.js b/packages/core/index.js index d454277..47ebf61 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,5 +1,6 @@ -export {arrayUnique, flatten} from './array'; +export {arrayUnique, flatten, fromObject as arrayFromObject} from './array'; export {EventEmitterMixin as EventEmitter} from './event-emitter'; +export {invoke as invokeHook, invokeFlat as invokeHookFlat} from './hook/registry'; export {merge} from './merge'; export { mergeDiff,