diff --git a/shadowdog.json b/shadowdog.json index f719e89..6194d73 100644 --- a/shadowdog.json +++ b/shadowdog.json @@ -1,7 +1,6 @@ { "$schema": "./src/config/schema.json", "plugins": [ - "shadowdog-rake", { "name": "shadowdog-local-cache", "options": { diff --git a/src/config.ts b/src/config.ts index 55f1161..a6c74cd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,7 +3,9 @@ import { logMessage } from './utils' import { z } from 'zod' import chalk from 'chalk' -const PluginTypeEnum = z.enum(['shadowdog-rake', 'shadowdog-local-cache']) +// TODO: rake plugin +// const PluginTypeEnum = z.enum(['shadowdog-rake', 'shadowdog-local-cache']) +const PluginTypeEnum = z.enum(['shadowdog-local-cache']) export const configSchema = z .object({ @@ -23,7 +25,7 @@ export const configSchema = z .array( z.union([ PluginTypeEnum.describe('Plugin name'), - z.object({ name: PluginTypeEnum, options: z.object({}) }).strict(), + z.object({ name: PluginTypeEnum, options: z.any() }).strict(), ]), ) .optional() diff --git a/src/config/schema.json b/src/config/schema.json index 0151637..8f3d6fe 100644 --- a/src/config/schema.json +++ b/src/config/schema.json @@ -25,7 +25,7 @@ "anyOf": [ { "type": "string", - "enum": ["shadowdog-rake", "shadowdog-local-cache"], + "enum": ["shadowdog-local-cache"], "description": "Plugin name" }, { @@ -33,15 +33,11 @@ "properties": { "name": { "type": "string", - "enum": ["shadowdog-rake", "shadowdog-local-cache"] + "enum": ["shadowdog-local-cache"] }, - "options": { - "type": "object", - "properties": {}, - "additionalProperties": false - } + "options": {} }, - "required": ["name", "options"], + "required": ["name"], "additionalProperties": false } ] diff --git a/src/daemon.ts b/src/daemon.ts index d628819..bd93243 100644 --- a/src/daemon.ts +++ b/src/daemon.ts @@ -14,14 +14,52 @@ import { logMessage } from './utils' import chalk from 'chalk' import { Client } from 'minio' +// TODO: Add the rest of the plugins import shadowdogRake from './plugins/shadowdog-rake' import shadowdogLocalCache from './plugins/shadowdog-local-cache' const PLUGINS_MAP = { - 'shadowdog-rake': shadowdogRake, 'shadowdog-local-cache': shadowdogLocalCache, } as const +type Result = any + +export type Middleware<Context = any> = ( + ctx: Context, + next: () => Promise<Result>, + abort: () => void, +) => Promise<Result> + +class TaskManager { + middlewares: Array<{ middleware: Middleware; options: any }> + + constructor() { + this.middlewares = [] + } + + use(middleware: Middleware, options: any = {}) { + this.middlewares.push({ middleware, options }) + } + + async execute(context: any) { + let index = -1 + let isAborted = false + + const abort = () => { + isAborted = true + } + + const next = async () => { + if (isAborted || ++index >= this.middlewares.length) return context.result + const current = this.middlewares[index] + return current.middleware({ ...context, options: current.options }, next, abort) + } + + context.result = await next() + return context.result + } +} + const setupWatchers = (config: ConfigFile, socketPath: string, client: Client | null) => { return Promise.all<chokidar.FSWatcher>( config.watchers @@ -112,22 +150,32 @@ const setupWatchers = (config: ConfigFile, socketPath: string, client: Client | await Promise.all( watcherConfig.commands.map(async (commandConfig) => { - await Promise.all( - config.plugins.map((pluginName) => { - if (typeof pluginName === 'object') { - return PLUGINS_MAP[pluginName.name].preProcessing( - watcherConfig, - commandConfig, - pluginName.options as any, // TODO: check this - ) - } - return PLUGINS_MAP[pluginName].preProcessing( - watcherConfig, - commandConfig, - {} as any, - ) // TODO: check this - }), - ) + const taskManager = new TaskManager() + + config.plugins.forEach((pluginName) => { + if (typeof pluginName === 'object') { + return taskManager.use(PLUGINS_MAP[pluginName.name], pluginName.options) + } + taskManager.use(PLUGINS_MAP[pluginName]) + }) + + taskManager.use(() => { + return runTask({ + command: commandConfig.command, + workingDirectory: process.cwd(), + filePath, + onSpawn: (task) => { + tasks.push(task) + }, + }) + }) + + return taskManager.execute({ + watcherConfig, + commandConfig, + }) + + ///////////////////////////// REVIEW THIS CODE ///////////////////////////// // At this point we need to execute the command because cache couldn't be reused @@ -146,46 +194,46 @@ const setupWatchers = (config: ConfigFile, socketPath: string, client: Client | // }) // } - try { - await runTask({ - command: commandConfig.command, - workingDirectory: process.cwd(), - filePath, - onSpawn: (task) => { - tasks.push(task) - }, - }) + // try { + // await runTask({ + // command: commandConfig.command, + // workingDirectory: process.cwd(), + // filePath, + // onSpawn: (task) => { + // tasks.push(task) + // }, + // }) - // TODO: notifications - // await notifyState(socketPath, { - // type: 'CHANGED_FILE', - // payload: { - // file: files, - // ready: true, - // }, - // }) - - return Promise.all( - config.plugins.map((pluginName) => { - if (typeof pluginName === 'object') { - return PLUGINS_MAP[pluginName.name].postProcessing( - commandConfig, - pluginName.options as any, // TODO: check this - ) - } - return PLUGINS_MAP[pluginName].postProcessing(commandConfig, {} as any) // TODO: check this - }), - ) - } catch (error) { - // TODO: notifications - // return notifyState(socketPath, { - // type: 'ERROR', - // payload: { - // file: filePath, - // errorMessage: (error as Error).message, - // }, - // }) - } + // TODO: notifications + // await notifyState(socketPath, { + // type: 'CHANGED_FILE', + // payload: { + // file: files, + // ready: true, + // }, + // }) + + // return Promise.all( + // config.plugins.map((pluginName) => { + // if (typeof pluginName === 'object') { + // return PLUGINS_MAP[pluginName.name].postProcessing( + // commandConfig, + // pluginName.options as any, // TODO: check this + // ) + // } + // return PLUGINS_MAP[pluginName].postProcessing(commandConfig, {} as any) // TODO: check this + // }), + // ) + // } catch (error) { + // TODO: notifications + // return notifyState(socketPath, { + // type: 'ERROR', + // payload: { + // file: filePath, + // errorMessage: (error as Error).message, + // }, + // }) + // } }), ) } diff --git a/src/plugins/shadowdog-local-cache.ts b/src/plugins/shadowdog-local-cache.ts index d14796f..c8ff746 100644 --- a/src/plugins/shadowdog-local-cache.ts +++ b/src/plugins/shadowdog-local-cache.ts @@ -6,10 +6,7 @@ import chalk from 'chalk' import path from 'path' import { CommandConfig, WatcherConfig } from '../config' import { compressFolder, decompressFile, logMessage } from '../utils' - -interface PluginOptions { - path: string -} +import { Middleware } from '../daemon' const restoreCache = async ( commandConfig: CommandConfig, @@ -125,26 +122,33 @@ const filterFn = (ignore: string[] | undefined, outputPath: string, filePath: st return keep } -const preProcessing = async ( - watcherConfig: WatcherConfig, - commandConfig: CommandConfig, - options: PluginOptions, -) => { +interface PluginOptions { + path: string +} + +interface Context { + commandConfig: CommandConfig + watcherConfig: WatcherConfig + options: PluginOptions +} + +const middleware: Middleware<Context> = async (ctx, next, abort) => { const currentCache = computeCache( - [...watcherConfig.files, ...(watcherConfig.invalidators?.files ?? [])], - watcherConfig.invalidators?.environment ?? [], + [...ctx.watcherConfig.files, ...(ctx.watcherConfig.invalidators?.files ?? [])], + ctx.watcherConfig.invalidators?.environment ?? [], ) - const hasBeenRestored = await restoreCache(commandConfig, currentCache, options) + const hasBeenRestored = await restoreCache(ctx.commandConfig, currentCache, ctx.options) if (hasBeenRestored) { - return + abort() + return 'Cache restored' } -} -const postProcessing = (commandConfig: CommandConfig, { path: cachePath }: PluginOptions) => { + await next() + return Promise.all( - commandConfig.artifacts.map(async (artifact) => { + ctx.commandConfig.artifacts.map(async (artifact) => { if (!fs.existsSync(path.join(process.cwd(), artifact.output))) { logMessage( `📦 Not able to store artifact '${chalk.blue( @@ -167,7 +171,7 @@ const postProcessing = (commandConfig: CommandConfig, { path: cachePath }: Plugi const cacheFileName = computeFileCacheName(currentCache, artifact.output) const cacheFilePath = path.join( - cachePath, + ctx.options.path, `${cacheFileName}${artifact.compressed ? '.tar.gz' : ''}`, ) @@ -198,7 +202,4 @@ const postProcessing = (commandConfig: CommandConfig, { path: cachePath }: Plugi ) } -export default { - preProcessing, - postProcessing, -} +export default middleware