funkophile 0.2.5 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../index.ts", "../../utils.ts"],
4
+ "sourcesContent": ["import Promise from \"bluebird\";\nimport fse from \"fs-extra\";\n\nimport { Action, Store } from \"redux\";\nimport {\n IConfig,\n INITIALIZE,\n cleanEmptyFoldersRecursively,\n logDone,\n logInputKeys,\n logger,\n makeFinalSelector,\n makePromissesArray,\n newStore,\n previousState,\n startServing,\n} from \"./utils\";\n\nPromise.config({\n cancellation: true,\n});\n\nexport default (funkophileConfig: IConfig) => {\n let outputPromise = Promise.resolve();\n const store: Store<any, Action<string>, any> = newStore(funkophileConfig);\n const finalSelector = makeFinalSelector(funkophileConfig);\n\n if (funkophileConfig.mode === \"watch\") {\n startServing(funkophileConfig);\n }\n\n // Wait for all the file watchers to check in\n Promise.all(\n makePromissesArray(funkophileConfig, store)\n ).then(function () {\n console.log(\n \"\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m All input watchers are ready. Setting up store subscription...\"\n );\n\n // Set up the store subscription BEFORE initializing to catch all changes\n store.subscribe(() => {\n const s = store.getState();\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Store updated. initialLoad: ${s.initialLoad}, timestamp: ${s.timestamp}`\n );\n\n // Skip processing during initial load\n if (s.initialLoad) {\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Initial load in progress, skipping processing...\"\n );\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m State keys during initial load:\",\n Object.keys(s)\n );\n return;\n }\n\n logger.stateChange();\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Processing state changes...\"\n );\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Current state keys:\",\n Object.keys(s)\n );\n\n let outputs;\n try {\n outputs = finalSelector(s);\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Generated ${\n Object.keys(outputs).length\n } outputs`\n );\n } catch (error) {\n console.error(\n \"\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m FATAL: Error in output selector chain ? :\"\n );\n console.error(\" Error:\", error.message);\n console.error(\" Stack:\", error.stack);\n // Don't exit the process in watch mode, just log the error and continue\n if (funkophileConfig.mode === \"build\") {\n process.exit(1);\n } else {\n console.log(\n \"\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Continuing to watch for changes despite error...\"\n );\n // Reset previousState to empty to ensure we try processing again on next change\n Object.keys(previousState).forEach((key) => {\n delete previousState[key];\n });\n return;\n }\n }\n\n if (outputPromise.isPending()) {\n console.log(\n \"\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Cancelling previous write operation!\"\n );\n outputPromise.cancel();\n }\n\n outputPromise = Promise.all(\n Array.from(\n new Set(Object.keys(previousState).concat(Object.keys(outputs)))\n ).map((key) => {\n return new Promise((fulfill, reject) => {\n if (!outputs[key]) {\n const file = funkophileConfig.options.outFolder + \"/\" + key;\n logger.removedFile(file);\n console.log(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Removing file: ${file}`\n );\n\n try {\n fse.unlinkSync(\"./\" + file);\n cleanEmptyFoldersRecursively(\n \"./\" + file.substring(0, file.lastIndexOf(\"/\"))\n );\n } catch (ex) {\n // Log error but don't fail the entire process\n console.error(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Error removing file ${file}:`,\n ex.message\n );\n } finally {\n delete previousState[key];\n fulfill();\n }\n } else {\n if (outputs[key] !== previousState[key]) {\n previousState[key] = outputs[key];\n\n const relativeFilePath =\n \"./\" + funkophileConfig.options.outFolder + \"/\" + key;\n const contents = outputs[key];\n\n // console.log(`\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Writing file: ${relativeFilePath}`);\n\n if (typeof contents === \"function\") {\n logger.writingFunction(relativeFilePath);\n contents((err, res) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill(); // Still fulfill to continue processing other files\n } else {\n fse.outputFile(relativeFilePath, res, (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill(); // Still fulfill to continue processing other files\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n });\n }\n });\n } else if (typeof contents === \"string\") {\n fse.outputFile(relativeFilePath, contents, (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill();\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n });\n } else if (Buffer.isBuffer(contents)) {\n fse.outputFile(relativeFilePath, contents, (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill();\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n });\n } else if (Array.isArray(contents)) {\n fse.outputFile(\n relativeFilePath,\n JSON.stringify(contents),\n (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill();\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n }\n );\n } else if (typeof contents.then === \"function\") {\n logger.writingPromise(relativeFilePath);\n Promise.resolve(contents).then(\n function (value) {\n if (value instanceof Error) {\n logger.writingError(relativeFilePath, value.message);\n fulfill();\n } else {\n fse.outputFile(relativeFilePath, value, (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill();\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n });\n }\n },\n function (error) {\n logger.writingError(relativeFilePath, error.message);\n fulfill();\n }\n );\n } else {\n console.log(\n `\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Unrecognized content type for ${relativeFilePath}, attempting to write:`,\n typeof contents\n );\n fse.outputFile(relativeFilePath, contents, (err) => {\n if (err) {\n logger.writingError(relativeFilePath, err.message);\n fulfill();\n } else {\n logger.writingString(relativeFilePath);\n fulfill();\n }\n });\n }\n } else {\n // console.log(`\\u001b[90m\\u001b[1m[Funkophile]\\u001b[0m Skipping unchanged file: ${key}`);\n fulfill();\n }\n }\n });\n })\n ).then(() => {\n // console.log('\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Cleaning empty folders...');\n cleanEmptyFoldersRecursively(funkophileConfig.options.outFolder);\n\n logDone(funkophileConfig, currentState) \n });\n // .catch((error) => {\n // // console.error('\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Error during file operations:', error);\n // });\n });\n\n console.log(\n \"\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Dispatching INITIALIZE action to enable processing...\"\n );\n // Debug: log the current state after all files are processed\n const currentState = store.getState();\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Current state keys:\",\n Object.keys(currentState)\n );\n\n logInputKeys(funkophileConfig, currentState);\n\n // Add a small delay to ensure all file operations are complete before initializing\n setTimeout(() => {\n // lastly, turn the store `on`.\n // This is to prevent unecessary recomputations when initialy adding files to redux\n store.dispatch({\n type: INITIALIZE,\n payload: true,\n });\n console.log(\n \"\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Store initialized. Ready to process changes!\"\n );\n }, 100);\n });\n};\n", "import fs from \"fs\";\nimport fse from \"fs-extra\";\nimport http from \"http\";\nimport path from \"path\";\nimport { Action, createStore, Store } from \"redux\";\nimport { createSelector } from \"reselect\";\nimport url from \"url\";\nimport { glob } from \"glob\";\nimport chokidar from \"chokidar\";\n\nexport type IConfig = {\n mode: \"build\" | \"watch\";\n initialState: any;\n options: {\n inFolder: string;\n outFolder: string;\n port?: number;\n };\n encodings: Record<string, string[]>;\n inputs: Record<string, string>;\n outputs: (x: any) => any;\n};\n\nexport const INITIALIZE = \"INITIALIZE\";\nexport const UPSERT = \"UPSERT\";\nexport const REMOVE = \"REMOVE\";\n\nexport const previousState: any = {};\n\nexport const logger = {\n watchError: (p: string) => console.log(\"\\u001b[7m ! \\u001b[0m\" + p),\n watchReady: (p: string) =>\n console.log(\"\\u001b[7m\\u001b[36m < \\u001b[0m\" + p),\n watchAdd: (p: string) =>\n console.log(\"\\u001b[7m\\u001b[34m + \\u001b[0m./\" + p),\n watchChange: (p: string) =>\n console.log(\"\\u001b[7m\\u001b[35m * \\u001b[0m\" + p),\n watchUnlink: (p: string) =>\n console.log(\"\\u001b[7m\\u001b[31m - \\u001b[0m./\" + p),\n stateChange: () =>\n console.log(\"\\u001b[7m\\u001b[31m --- Redux state changed --- \\u001b[0m\"),\n cleaningEmptyfolder: (p: string) =>\n console.log(\"\\u001b[31m\\u001b[7m XXX! \\u001b[0m\" + p),\n readingFile: (p: string) => console.log(\"\\u001b[31m <-- \\u001b[0m\" + p),\n removedFile: (p: string) =>\n console.log(\"\\u001b[31m\\u001b[7m ??? \\u001b[0m./\" + p),\n writingString: (p: string) => console.log(\"\\u001b[32m --> \\u001b[0m\" + p),\n writingFunction: (p: string) => console.log(\"\\u001b[33m ... \\u001b[0m\" + p),\n writingPromise: (p: string) => console.log(\"\\u001b[33m ... \\u001b[0m\" + p),\n writingError: (p: string, message: string) =>\n console.log(\"\\u001b[31m !!! \\u001b[0m\" + p + \" \" + message),\n\n waiting: () =>\n console.log(\n \"\\u001b[7m Funkophile is done for now but waiting on changes...\\u001b[0m \"\n ),\n done: () => console.log(\"\\u001b[7m Funkophile is done!\\u001b[0m \"),\n};\n\nexport function cleanEmptyFoldersRecursively(folder: string) {\n var isDir = fs.statSync(folder).isDirectory();\n if (!isDir) {\n return;\n }\n var files = fs.readdirSync(folder);\n if (files.length > 0) {\n files.forEach(function (file) {\n var fullPath = path.join(folder, file);\n });\n\n // re-evaluate files; after deleting subfolder\n // we may have parent folder empty now\n files = fs.readdirSync(folder);\n }\n\n if (files.length == 0) {\n logger.cleaningEmptyfolder(folder);\n\n fs.rmdirSync(folder);\n return;\n }\n}\n\nexport const dispatchUpsert = (\n store: Store,\n key: string,\n file: string,\n encodings: Record<string, string[]>\n) => {\n const fileType: string = path.basename(file).split(\".\")[1];\n\n let encoding: BufferEncoding = Object.keys(encodings).find((e) => {\n return encodings[e].includes(fileType);\n }) as BufferEncoding;\n\n logger.readingFile(file);\n store.dispatch({\n type: UPSERT,\n payload: {\n key: key,\n // key: path.relative(process.cwd(), key),\n src: file,\n contents: fse.readFileSync(file, encoding),\n },\n });\n};\n\nexport function omit(key: string, obj: any) {\n const { [key]: omitted, ...rest } = obj;\n return rest;\n}\n\nexport function newStore(funkophileConfig): Store<any, Action<string>, any> {\n const initialInputState = Object.keys(funkophileConfig.inputs).reduce(\n (state, inputKey) => {\n state[inputKey] = {};\n return state;\n },\n {} as Record<string, any>\n );\n\n return createStore(\n (\n state = {\n initialLoad: true,\n ...initialInputState,\n ...funkophileConfig.initialState,\n timestamp: Date.now(),\n },\n action\n ) => {\n if (state === undefined) {\n throw new Error(\"Redux state is undefined. This should never happen.\");\n }\n console.log(\n `\\u001b[35m\\u001b[1m[Funkophile]\\u001b[0m Redux received action: ${action.type}`\n );\n if (!action.type.includes(\"@@redux\")) {\n if (action.type === INITIALIZE) {\n console.log(\n `\\u001b[35m\\u001b[1m[Funkophile]\\u001b[0m INITIALIZE action - setting initialLoad to false`\n );\n return {\n ...state,\n initialLoad: false,\n timestamp: Date.now(),\n };\n } else if (action.type === UPSERT) {\n console.log(\n `\\u001b[35m\\u001b[1m[Funkophile]\\u001b[0m UPSERT action for key: ${action[\"payload\"].key}, file: ${action[\"payload\"].src}`\n );\n return {\n ...state,\n [action[\"payload\"].key]: {\n // @ts-ignore\n ...state[action.payload.key],\n ...{\n [action[\"payload\"].src]: action[\"payload\"].contents,\n },\n },\n timestamp: Date.now(),\n };\n } else if (action.type === REMOVE) {\n console.log(\n `\\u001b[35m\\u001b[1m[Funkophile]\\u001b[0m REMOVE action for key: ${action[\"payload\"].key}, file: ${action[\"payload\"].file}`\n );\n // Ensure the key exists before trying to omit from it\n const currentKeyState = state[action[\"payload\"].key] || {};\n return {\n ...state,\n [action[\"payload\"].key]: omit(\n action[\"payload\"].file,\n currentKeyState\n ),\n timestamp: Date.now(),\n };\n } else {\n console.error(\n \"Redux was asked to handle an unknown action type: \" + action.type\n );\n process.exit(-1);\n }\n // return state\n }\n return state;\n }\n );\n}\n\nexport function makeFinalSelector(funkophileConfig) {\n return funkophileConfig.outputs(\n Object.keys(funkophileConfig.inputs).reduce((mm, inputKey) => {\n return {\n ...mm,\n [inputKey]: createSelector([(x) => x], (root) => {\n // The input key should always be present now, even if it's an empty object\n const result = root[inputKey];\n // If result is undefined, it's a programming error since we initialize all input keys\n if (result === undefined) {\n console.warn(\n `\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Input key \"${inputKey}\" is undefined in state, which shouldn't happen. Using empty object.`\n );\n return {};\n }\n return result;\n }),\n };\n }, {})\n );\n}\n\nexport function startServing(funkophileConfig): void {\n const port = funkophileConfig.options.port || 8080;\n const server = http.createServer((req, res) => {\n if (!req.url) {\n res.statusCode = 400;\n res.end(\"Bad Request\");\n return;\n }\n\n const parsedUrl = url.parse(req.url);\n let pathname = parsedUrl.pathname;\n\n // Default to index.html if the path ends with /\n if (pathname && pathname.endsWith(\"/\")) {\n pathname += \"index.html\";\n }\n\n // Remove leading slash\n const filePath = pathname ? pathname.substring(1) : \"index.html\";\n\n // Construct the full path to the file\n const fullPath = path.join(\n process.cwd(),\n funkophileConfig.options.outFolder,\n filePath\n );\n\n // Check if file exists\n fs.access(fullPath, fs.constants.F_OK, (err) => {\n if (err) {\n // Try with .html extension\n const htmlPath = fullPath + \".html\";\n fs.access(htmlPath, fs.constants.F_OK, (htmlErr) => {\n if (htmlErr) {\n // File not found\n res.statusCode = 404;\n res.end(\"File not found\");\n } else {\n // Serve the .html file\n fs.readFile(htmlPath, (readErr, data) => {\n if (readErr) {\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n } else {\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(data);\n }\n });\n }\n });\n } else {\n // Serve the file\n fs.readFile(fullPath, (readErr, data) => {\n if (readErr) {\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n } else {\n // Set appropriate content type based on file extension\n const ext = path.extname(fullPath).toLowerCase();\n const contentTypes: Record<string, string> = {\n \".html\": \"text/html\",\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n };\n res.setHeader(\n \"Content-Type\",\n contentTypes[ext] || \"application/octet-stream\"\n );\n res.end(data);\n }\n });\n }\n });\n });\n\n server.listen(port, () => {\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Server running at http://localhost:${port}/`\n );\n });\n\n // Handle process exit to close the server\n process.on(\"SIGINT\", () => {\n if (server) {\n server.close();\n }\n // process.exit(0);\n });\n}\n\n// Log all input keys to see if they're present\nexport function logInputKeys(funkophileConfig, currentState) {\n Object.keys(funkophileConfig.inputs).forEach((inputKey) => {\n if (currentState[inputKey]) {\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Input key \"${inputKey}\" found in state with ${\n Object.keys(currentState[inputKey]).length\n } files`\n );\n // Only log file names if there are files to avoid cluttering the output\n if (Object.keys(currentState[inputKey]).length > 0) {\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Files for \"${inputKey}\":`,\n Object.keys(currentState[inputKey])\n );\n }\n } else {\n console.warn(\n `\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Input key \"${inputKey}\" NOT found in state`\n );\n }\n });\n}\n\nexport function logDone(funkophileConfig, currentState) {\n if (funkophileConfig.mode === \"build\") {\n console.log(\n \"\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Build completed successfully!\"\n );\n logger.done();\n } else if (funkophileConfig.mode === \"watch\") {\n console.log(\n \"\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Watching for file changes...\"\n );\n // Log the localhost URL if port is specified\n const port = funkophileConfig.options.port || 8080;\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Serving at: http://localhost:${port}/`\n );\n logger.waiting();\n } else {\n throw `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m The mode should be 'watch' or 'build', not \"${funkophileConfig.mode}\"`;\n }\n}\n\nexport function makePromissesArray(funkophileConfig, store) {\n return Object.keys(funkophileConfig.inputs).map((inputRuleKey) => {\n // Ensure the pattern includes the inFolder and is relative to the current working directory\n // Also, make sure to handle patterns that might already include the inFolder\n const pattern = funkophileConfig.inputs[inputRuleKey] || \"\";\n // For glob, we want the pattern to be relative to process.cwd()\n // Join inFolder and pattern using forward slashes\n const globPattern = path.posix.join(\n funkophileConfig.options.inFolder,\n pattern\n );\n // console.log(`[Funkophile] Looking for files with glob pattern: ${globPattern}`);\n // console.log(`[Funkophile] Current working directory: ${process.cwd()}`);\n\n return new Promise<void>((fulfill, reject) => {\n if (funkophileConfig.mode === \"build\") {\n // Use the glob pattern we constructed earlier\n // console.log(`[Funkophile] Searching for files matching pattern: ${globPattern}`);\n // console.log(`[Funkophile] Input rule key: ${inputRuleKey}`);\n\n glob(globPattern, { cwd: process.cwd() })\n .then((files: string[]) => {\n // console.log(`[Funkophile] Found ${files.length} files for ${inputRuleKey} (pattern: ${pattern}):`, files);\n if (files.length === 0) {\n console.warn(\n `No files found for input key \"${inputRuleKey}\" with pattern \"${globPattern}\"`\n );\n // Even if no files are found, the key is already initialized in the state\n // No need to dispatch anything\n } else {\n files.forEach((file) => {\n // Make sure the file path is absolute\n const absoluteFilePath = path.resolve(process.cwd(), file);\n // console.log(`[Funkophile] Adding file to state for key ${inputRuleKey}: ${absoluteFilePath}`);\n dispatchUpsert(\n store,\n inputRuleKey,\n absoluteFilePath,\n funkophileConfig.encodings\n );\n });\n }\n })\n .then(() => {\n fulfill();\n })\n .catch((error) => {\n // console.error(`[Funkophile] Error globbing for pattern ${globPattern}:`, error);\n reject(error);\n });\n } else if (funkophileConfig.mode === \"watch\") {\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Setting up watcher for pattern: ${globPattern}`\n );\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Current working directory: ${process.cwd()}`\n );\n\n // First, process initial files using glob to ensure all files are loaded\n glob(globPattern, { cwd: process.cwd() })\n .then((files: string[]) => {\n console.log(\n `\\u001b[36m\\u001b[1m[Funkophile]\\u001b[0m Found ${files.length} initial files for ${inputRuleKey}`\n );\n files.forEach((file) => {\n const absoluteFilePath = path.resolve(process.cwd(), file);\n console.log(\n `\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Adding initial file: ${file}`\n );\n dispatchUpsert(\n store,\n inputRuleKey,\n absoluteFilePath,\n funkophileConfig.encodings\n );\n });\n\n // Now set up the watcher\n const watcher = chokidar\n .watch(globPattern, {\n cwd: process.cwd(),\n ignoreInitial: true, // We've already processed initial files\n persistent: true,\n usePolling: false,\n interval: 100,\n binaryInterval: 300,\n alwaysStat: false,\n depth: 99,\n awaitWriteFinish: {\n stabilityThreshold: 50,\n pollInterval: 10,\n },\n })\n .on(\"error\", (error) => {\n console.error(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Watcher error for pattern ${globPattern}:`,\n error\n );\n logger.watchError(globPattern);\n })\n .on(\"add\", (filePath) => {\n console.log(\n `\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m File added: ${filePath}`\n );\n logger.watchAdd(filePath);\n const absoluteFilePath = path.resolve(process.cwd(), filePath);\n console.log(\n `\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Dispatching UPSERT for key: ${inputRuleKey}, file: ${absoluteFilePath}`\n );\n dispatchUpsert(\n store,\n inputRuleKey,\n absoluteFilePath,\n funkophileConfig.encodings\n );\n })\n .on(\"change\", (filePath) => {\n console.log(\n `\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m File changed: ${filePath}`\n );\n logger.watchChange(filePath);\n const absoluteFilePath = path.resolve(process.cwd(), filePath);\n console.log(\n `\\u001b[33m\\u001b[1m[Funkophile]\\u001b[0m Dispatching UPSERT for key: ${inputRuleKey}, file: ${absoluteFilePath}`\n );\n dispatchUpsert(\n store,\n inputRuleKey,\n absoluteFilePath,\n funkophileConfig.encodings\n );\n })\n .on(\"unlink\", (filePath) => {\n console.log(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m File removed: ${filePath}`\n );\n logger.watchUnlink(filePath);\n const absoluteFilePath = path.resolve(process.cwd(), filePath);\n console.log(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Dispatching REMOVE for key: ${inputRuleKey}, file: ${absoluteFilePath}`\n );\n store.dispatch({\n type: REMOVE,\n payload: {\n key: inputRuleKey,\n file: absoluteFilePath,\n },\n });\n })\n .on(\"unlinkDir\", (filePath) => {\n console.log(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Directory removed: ${filePath}`\n );\n logger.watchUnlink(filePath);\n })\n .on(\"raw\", (event, path, details) => {\n console.log(\n `\\u001b[90m\\u001b[1m[Funkophile]\\u001b[0m Raw event: ${event} for path: ${path}`\n );\n });\n\n console.log(\n `\\u001b[32m\\u001b[1m[Funkophile]\\u001b[0m Watcher is ready for pattern: ${globPattern}`\n );\n logger.watchReady(globPattern);\n fulfill();\n })\n .catch((error) => {\n console.error(\n `\\u001b[31m\\u001b[1m[Funkophile]\\u001b[0m Error processing initial files for pattern ${globPattern}:`,\n error\n );\n reject(error);\n });\n // .on('raw', (event, p, details) => { // internal\n // log('Raw event info:', event, p, details);\n // })\n } else {\n console.error(\n `mode should be 'watch' or 'build', not \"${funkophileConfig.mode}\"`\n );\n process.exit(-1);\n }\n });\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["Promise", "fse", "path", "Promise", "fse", "err"]
7
+ }
@@ -6,6 +6,11 @@ export const contentsOfFiles = (selector) => {
6
6
  if (selected === undefined || selected === null) {
7
7
  throw new Error(`contentsOfFiles: selected is ${selected}. Make sure the selector is pointing to valid state.`);
8
8
  }
9
+ const keys = Object.keys(selected);
10
+ if (keys.length === 0) {
11
+ console.warn(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m contentsOfFiles: selected object is empty. No files found. This may be because the input pattern didn't match any files.`);
12
+ return "";
13
+ }
9
14
  return Object.keys(selected).reduce((mm, k) => mm + (selected[k] || ""), "");
10
15
  });
11
16
  };
@@ -17,7 +22,8 @@ export const contentOfFile = (selector) => {
17
22
  }
18
23
  const keys = Object.keys(selected);
19
24
  if (keys.length === 0) {
20
- throw new Error(`contentOfFile: selected object is empty. No files found. This may be because the input pattern didn't match any files.`);
25
+ console.warn(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m contentOfFile: selected object is empty. No files found. This may be because the input pattern didn't match any files.`);
26
+ return "";
21
27
  }
22
28
  return selected[keys[0]] || "";
23
29
  });
@@ -31,7 +37,12 @@ export const srcAndContentOfFile = (selector, key: string) => {
31
37
 
32
38
  const keys = Object.keys(selected);
33
39
  if (keys.length === 0) {
34
- throw new Error(`srcAndContentOfFile: selected object is empty. No files found. This may be because the input pattern didn't match any files.`);
40
+ // Return a default object with empty content instead of throwing
41
+ console.warn(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m srcAndContentOfFile: selected object is empty for key "${key}". No files found. This may be because the input pattern didn't match any files.`);
42
+ return {
43
+ src: key,
44
+ content: '',
45
+ };
35
46
  }
36
47
 
37
48
  // Try exact match first
@@ -77,7 +88,11 @@ export const srcAndContentOfFile = (selector, key: string) => {
77
88
  }
78
89
 
79
90
  if (!matchingKey) {
80
- throw new Error(`srcAndContentOfFile: key "${key}" not found in selected object. Available keys: ${keys.join(', ')}`);
91
+ console.warn(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m srcAndContentOfFile: key "${key}" not found in selected object. Available keys: ${keys.join(', ')}`);
92
+ return {
93
+ src: key,
94
+ content: '',
95
+ };
81
96
  }
82
97
 
83
98
  return {
@@ -93,6 +108,10 @@ export const srcAndContentOfFiles = (selector) => {
93
108
  throw new Error(`srcAndContentOfFiles: selected is ${selected}. Make sure the selector is pointing to valid state.`);
94
109
  }
95
110
  const keys = Object.keys(selected);
111
+ if (keys.length === 0) {
112
+ console.warn(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m srcAndContentOfFiles: selected object is empty. No files found. This may be because the input pattern didn't match any files.`);
113
+ return [];
114
+ }
96
115
  return keys.map((key) => {
97
116
  return {
98
117
  src: key,
package/index.test.ts ADDED
@@ -0,0 +1,210 @@
1
+ import { expect } from "chai";
2
+ import { describe, it } from "mocha";
3
+ import {
4
+ makeStore,
5
+ // makeFinalSelector,
6
+ omit,
7
+ contentsOfFiles,
8
+ contentOfFile,
9
+ srcAndContentOfFile,
10
+ srcAndContentOfFiles,
11
+ INITIALIZE,
12
+ UPSERT,
13
+ REMOVE,
14
+ AppState,
15
+ createInputSelectors,
16
+ } from "./index";
17
+
18
+ import { createMockState, createMockConfig } from "./test-utils";
19
+
20
+ describe("Funkophile Core", () => {
21
+ const mockConfig = createMockConfig();
22
+
23
+ describe("makeStore", () => {
24
+ it("should create a store with empty state until it is initialized", () => {
25
+ const store = makeStore(mockConfig);
26
+
27
+ expect(store.getState()).to.be.undefined;
28
+
29
+ store.dispatch({ payload: true, type: INITIALIZE });
30
+
31
+ expect(store.getState()).to.not.be.undefined;
32
+ expect(store.getState().timestamp).to.be.greaterThan(0);
33
+ expect(store.getState()).deep.include(mockConfig.initialState);
34
+ });
35
+ });
36
+
37
+ describe('omit', () => {
38
+ it('should remove the specified key from object', () => {
39
+ const obj = {
40
+ 'file1.md': 'content1',
41
+ 'file2.md': 'content2'
42
+ };
43
+
44
+ const result = omit('file1.md', obj);
45
+ expect(result).to.deep.equal({ 'file2.md': 'content2' });
46
+ });
47
+ });
48
+
49
+ describe("selectors", () => {
50
+ const mockState = createMockState();
51
+
52
+ const pagesAndPosts = (subject: string) => {
53
+ it("contentsOfFiles should concatenate all file contents", () => {
54
+ expect(contentsOfFiles((state) => state[subject])(mockState)).to.equal(
55
+ Object.entries(mockState[subject]).reduce((mm, [k, v]) => {
56
+ return `${mm}${v}`;
57
+ }, "")
58
+ );
59
+ });
60
+
61
+ it("contentOfFile should return first file content", () => {
62
+ expect(contentOfFile((state) => state[subject])(mockState)).to.equal(
63
+ mockState[subject][Object.keys(mockState[subject]).sort()[0]]
64
+ );
65
+ });
66
+
67
+ it("srcAndContentOfFile should return src and content", () => {
68
+ // const filepath = "post2.md";
69
+ const filepath = Object.keys(mockState[subject])[0]
70
+
71
+ expect(
72
+ srcAndContentOfFile((state) => state[subject], filepath)(mockState)
73
+ ).to.deep.equal({
74
+ src: filepath,
75
+ content: mockState[subject][filepath],
76
+ });
77
+ });
78
+
79
+ it("srcAndContentOfFiles should return array of src/content pairs", () => {
80
+ const selector = srcAndContentOfFiles((state) => state[subject]);
81
+
82
+ expect(selector(mockState)).to.deep.equal(
83
+ Object.entries(mockState[subject]).reduce((mm, [k, v]) => {
84
+ mm.push({
85
+ src: k,
86
+ content: v,
87
+ });
88
+ return mm;
89
+ }, [])
90
+ );
91
+ });
92
+ }
93
+
94
+ describe("for the pages", () => {
95
+ pagesAndPosts("pages");
96
+ });
97
+
98
+ describe("for the posts", () => {
99
+ pagesAndPosts("posts");
100
+ });
101
+ });
102
+
103
+ describe('createInputSelectors', () => {
104
+ it('should create a selector for each input key', () => {
105
+ const selectors = createInputSelectors(mockConfig);
106
+ expect(Object.keys(selectors)).to.deep.equal(['posts', 'pages']);
107
+ expect(typeof selectors.posts).to.equal('function');
108
+ expect(typeof selectors.pages).to.equal('function');
109
+ });
110
+
111
+ it('selectors should return the corresponding state slice', () => {
112
+ const mockState = createMockState();
113
+ const selectors = createInputSelectors(mockConfig);
114
+ expect(selectors.posts(mockState)).to.equal(mockState.posts);
115
+ expect(selectors.pages(mockState)).to.equal(mockState.pages);
116
+ });
117
+ });
118
+
119
+ // describe('makeFinalSelector', () => {
120
+ // it('should create a selector that transforms inputs through output functions', () => {
121
+ // const mockConfigWithOutputs = {
122
+ // ...mockConfig,
123
+ // outputs: (selectors: any) => ({
124
+ // posts: contentsOfFiles(selectors.posts),
125
+ // pages: contentOfFile(selectors.pages)
126
+ // })
127
+ // };
128
+
129
+ // const selector = makeFinalSelector(mockConfigWithOutputs);
130
+ // const mockState = createMockState();
131
+ // const outputs = selector(mockState);
132
+
133
+ // // Verify posts output is concatenated
134
+ // expect(outputs.posts).to.equal(
135
+ // '# Post 1# Post 2hello world'
136
+ // );
137
+
138
+ // // Verify pages output is first page
139
+ // expect(outputs.pages).to.equal(
140
+ // '# About'
141
+ // );
142
+ // });
143
+
144
+ // it('should memoize results properly', () => {
145
+ // const selector = makeFinalSelector(mockConfig);
146
+ // const mockState = createMockState();
147
+ // const firstOutput = selector(mockState);
148
+ // const secondOutput = selector(mockState);
149
+
150
+ // expect(firstOutput).to.deep.equal(secondOutput);
151
+ // });
152
+ // });
153
+
154
+ describe('store actions', () => {
155
+ it('should handle INITIALIZE action', () => {
156
+ const store = makeStore(mockConfig);
157
+ store.dispatch({ type: INITIALIZE, payload: true });
158
+ const state = store.getState();
159
+ expect(state.timestamp).to.be.greaterThan(0);
160
+ });
161
+
162
+ it('should handle UPSERT action', () => {
163
+ const store = makeStore(mockConfig);
164
+ store.dispatch({
165
+ type: UPSERT,
166
+ payload: {
167
+ key: 'posts',
168
+ src: 'post3.md',
169
+ contents: '# Post 3'
170
+ }
171
+ });
172
+ const state = store.getState();
173
+ expect(state.posts['post3.md']).to.equal('# Post 3');
174
+ });
175
+
176
+ it('should handle REMOVE action', () => {
177
+ const store = makeStore(mockConfig);
178
+ store.dispatch({
179
+ type: REMOVE,
180
+ payload: {
181
+ key: 'posts',
182
+ file: 'post1.md'
183
+ }
184
+ });
185
+ const state = store.getState();
186
+ expect(state.posts['post1.md']).to.be.undefined;
187
+ });
188
+ });
189
+
190
+ describe('edge cases', () => {
191
+ it('should handle empty file contents', () => {
192
+ const selector = contentsOfFiles(() => ({}));
193
+ expect(selector(createMockState())).to.equal('');
194
+ });
195
+
196
+ it('should handle Buffer content', () => {
197
+ const buffer = Buffer.from('buffer content');
198
+ const state = createMockState({
199
+ posts: { 'file.bin': buffer }
200
+ });
201
+ const selector = contentOfFile(() => state.posts);
202
+ expect(selector(state)).to.equal(buffer);
203
+ });
204
+
205
+ it('should throw on invalid selector', () => {
206
+ const selector = contentOfFile(() => ({}));
207
+ expect(() => selector({} as AppState)).to.throw();
208
+ });
209
+ });
210
+ });