funkophile 0.2.5 → 1.0.0

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.
package/dist/esm/index.js CHANGED
@@ -1,531 +1,657 @@
1
- import chokidar from "chokidar";
2
- import { createSelector } from "reselect";
3
- import { createStore } from "redux";
1
+ // index.ts
2
+ import Promise2 from "bluebird";
3
+ import fse2 from "fs-extra";
4
+
5
+ // utils.ts
4
6
  import fs from "fs";
5
7
  import fse from "fs-extra";
6
- import { glob } from "glob";
7
8
  import http from "http";
8
9
  import path from "path";
9
- import Promise from "bluebird";
10
+ import { createStore } from "redux";
11
+ import { createSelector } from "reselect";
10
12
  import url from "url";
11
- export default (funkophileConfig) => {
12
- Promise.config({
13
- cancellation: true,
13
+ import { glob } from "glob";
14
+ import chokidar from "chokidar";
15
+ var INITIALIZE = "INITIALIZE";
16
+ var UPSERT = "UPSERT";
17
+ var REMOVE = "REMOVE";
18
+ var previousState = {};
19
+ var logger = {
20
+ watchError: (p) => console.log("\x1B[7m ! \x1B[0m" + p),
21
+ watchReady: (p) => console.log("\x1B[7m\x1B[36m < \x1B[0m" + p),
22
+ watchAdd: (p) => console.log("\x1B[7m\x1B[34m + \x1B[0m./" + p),
23
+ watchChange: (p) => console.log("\x1B[7m\x1B[35m * \x1B[0m" + p),
24
+ watchUnlink: (p) => console.log("\x1B[7m\x1B[31m - \x1B[0m./" + p),
25
+ stateChange: () => console.log("\x1B[7m\x1B[31m --- Redux state changed --- \x1B[0m"),
26
+ cleaningEmptyfolder: (p) => console.log("\x1B[31m\x1B[7m XXX! \x1B[0m" + p),
27
+ readingFile: (p) => console.log("\x1B[31m <-- \x1B[0m" + p),
28
+ removedFile: (p) => console.log("\x1B[31m\x1B[7m ??? \x1B[0m./" + p),
29
+ writingString: (p) => console.log("\x1B[32m --> \x1B[0m" + p),
30
+ writingFunction: (p) => console.log("\x1B[33m ... \x1B[0m" + p),
31
+ writingPromise: (p) => console.log("\x1B[33m ... \x1B[0m" + p),
32
+ writingError: (p, message) => console.log("\x1B[31m !!! \x1B[0m" + p + " " + message),
33
+ waiting: () => console.log(
34
+ "\x1B[7m Funkophile is done for now but waiting on changes...\x1B[0m "
35
+ ),
36
+ done: () => console.log("\x1B[7m Funkophile is done!\x1B[0m ")
37
+ };
38
+ function cleanEmptyFoldersRecursively(folder) {
39
+ var isDir = fs.statSync(folder).isDirectory();
40
+ if (!isDir) {
41
+ return;
42
+ }
43
+ var files = fs.readdirSync(folder);
44
+ if (files.length > 0) {
45
+ files.forEach(function(file) {
46
+ var fullPath = path.join(folder, file);
14
47
  });
15
- const INITIALIZE = "INITIALIZE";
16
- const UPSERT = "UPSERT";
17
- const REMOVE = "REMOVE";
18
- const previousState = {};
19
- let outputPromise = Promise.resolve();
20
- const logger = {
21
- watchError: (p) => console.log("\u001b[7m ! \u001b[0m" + p),
22
- watchReady: (p) => console.log("\u001b[7m\u001b[36m < \u001b[0m" + p),
23
- watchAdd: (p) => console.log("\u001b[7m\u001b[34m + \u001b[0m./" + p),
24
- watchChange: (p) => console.log("\u001b[7m\u001b[35m * \u001b[0m" + p),
25
- watchUnlink: (p) => console.log("\u001b[7m\u001b[31m - \u001b[0m./" + p),
26
- stateChange: () => console.log("\u001b[7m\u001b[31m --- Redux state changed --- \u001b[0m"),
27
- cleaningEmptyfolder: (p) => console.log("\u001b[31m\u001b[7m XXX! \u001b[0m" + p),
28
- readingFile: (p) => console.log("\u001b[31m <-- \u001b[0m" + p),
29
- removedFile: (p) => console.log("\u001b[31m\u001b[7m ??? \u001b[0m./" + p),
30
- writingString: (p) => console.log("\u001b[32m --> \u001b[0m" + p),
31
- writingFunction: (p) => console.log("\u001b[33m ... \u001b[0m" + p),
32
- writingPromise: (p) => console.log("\u001b[33m ... \u001b[0m" + p),
33
- writingError: (p, message) => console.log("\u001b[31m !!! \u001b[0m" + p + " " + message),
34
- waiting: () => console.log("\u001b[7m Funkophile is done for now but waiting on changes...\u001b[0m "),
35
- done: () => console.log("\u001b[7m Funkophile is done!\u001b[0m "),
36
- };
37
- function cleanEmptyFoldersRecursively(folder) {
38
- var isDir = fs.statSync(folder).isDirectory();
39
- if (!isDir) {
40
- return;
41
- }
42
- var files = fs.readdirSync(folder);
43
- if (files.length > 0) {
44
- files.forEach(function (file) {
45
- var fullPath = path.join(folder, file);
46
- });
47
- // re-evaluate files; after deleting subfolder
48
- // we may have parent folder empty now
49
- files = fs.readdirSync(folder);
50
- }
51
- if (files.length == 0) {
52
- logger.cleaningEmptyfolder(folder);
53
- fs.rmdirSync(folder);
54
- return;
55
- }
56
- }
57
- const dispatchUpsert = (store, key, file, encodings) => {
58
- const fileType = path.basename(file).split(".")[1];
59
- let encoding = Object.keys(encodings).find((e) => {
60
- return encodings[e].includes(fileType);
61
- });
62
- logger.readingFile(file);
63
- store.dispatch({
64
- type: UPSERT,
65
- payload: {
66
- key: key,
67
- // key: path.relative(process.cwd(), key),
68
- src: file,
69
- contents: fse.readFileSync(file, encoding),
70
- },
71
- });
72
- };
73
- function omit(key, obj) {
74
- const { [key]: omitted, ...rest } = obj;
75
- return rest;
48
+ files = fs.readdirSync(folder);
49
+ }
50
+ if (files.length == 0) {
51
+ logger.cleaningEmptyfolder(folder);
52
+ fs.rmdirSync(folder);
53
+ return;
54
+ }
55
+ }
56
+ var dispatchUpsert = (store, key, file, encodings) => {
57
+ const fileType = path.basename(file).split(".")[1];
58
+ let encoding = Object.keys(encodings).find((e) => {
59
+ return encodings[e].includes(fileType);
60
+ });
61
+ logger.readingFile(file);
62
+ store.dispatch({
63
+ type: UPSERT,
64
+ payload: {
65
+ key,
66
+ // key: path.relative(process.cwd(), key),
67
+ src: file,
68
+ contents: fse.readFileSync(file, encoding)
76
69
  }
77
- const store = createStore((state = {
78
- initialLoad: true,
79
- ...funkophileConfig.initialState,
80
- timestamp: Date.now(),
70
+ });
71
+ };
72
+ function omit(key, obj) {
73
+ const { [key]: omitted, ...rest } = obj;
74
+ return rest;
75
+ }
76
+ function newStore(funkophileConfig) {
77
+ const initialInputState = Object.keys(funkophileConfig.inputs).reduce(
78
+ (state, inputKey) => {
79
+ state[inputKey] = {};
80
+ return state;
81
+ },
82
+ {}
83
+ );
84
+ return createStore(
85
+ (state = {
86
+ initialLoad: true,
87
+ ...initialInputState,
88
+ ...funkophileConfig.initialState,
89
+ timestamp: Date.now()
81
90
  }, action) => {
82
- if (state === undefined) {
83
- throw new Error("Redux state is undefined. This should never happen.");
84
- }
85
- // console.log("\u001b[7m\u001b[35m ||| Redux recieved action \u001b[0m", action.type)
86
- if (!action.type.includes("@@redux")) {
87
- if (action.type === INITIALIZE) {
88
- return {
89
- ...state,
90
- initialLoad: false,
91
- timestamp: Date.now(),
92
- };
93
- }
94
- else if (action.type === UPSERT) {
95
- return {
96
- ...state,
97
- [action["payload"].key]: {
98
- // @ts-ignore
99
- ...state[action.payload.key],
100
- ...{
101
- [action["payload"].src]: action["payload"].contents,
102
- },
103
- },
104
- timestamp: Date.now(),
105
- };
106
- }
107
- else if (action.type === REMOVE) {
108
- return {
109
- ...state,
110
- [action["payload"].key]: omit(action["payload"].file, state[action["payload"].key]),
111
- timestamp: Date.now(),
112
- };
113
- }
114
- else {
115
- console.error("Redux was asked to handle an unknown action type: " + action.type);
116
- process.exit(-1);
117
- }
118
- // return state
91
+ if (state === void 0) {
92
+ throw new Error("Redux state is undefined. This should never happen.");
93
+ }
94
+ console.log(
95
+ `\x1B[35m\x1B[1m[Funkophile]\x1B[0m Redux received action: ${action.type}`
96
+ );
97
+ if (!action.type.includes("@@redux")) {
98
+ if (action.type === INITIALIZE) {
99
+ console.log(
100
+ `\x1B[35m\x1B[1m[Funkophile]\x1B[0m INITIALIZE action - setting initialLoad to false`
101
+ );
102
+ return {
103
+ ...state,
104
+ initialLoad: false,
105
+ timestamp: Date.now()
106
+ };
107
+ } else if (action.type === UPSERT) {
108
+ console.log(
109
+ `\x1B[35m\x1B[1m[Funkophile]\x1B[0m UPSERT action for key: ${action["payload"].key}, file: ${action["payload"].src}`
110
+ );
111
+ return {
112
+ ...state,
113
+ [action["payload"].key]: {
114
+ // @ts-ignore
115
+ ...state[action.payload.key],
116
+ ...{
117
+ [action["payload"].src]: action["payload"].contents
118
+ }
119
+ },
120
+ timestamp: Date.now()
121
+ };
122
+ } else if (action.type === REMOVE) {
123
+ console.log(
124
+ `\x1B[35m\x1B[1m[Funkophile]\x1B[0m REMOVE action for key: ${action["payload"].key}, file: ${action["payload"].file}`
125
+ );
126
+ const currentKeyState = state[action["payload"].key] || {};
127
+ return {
128
+ ...state,
129
+ [action["payload"].key]: omit(
130
+ action["payload"].file,
131
+ currentKeyState
132
+ ),
133
+ timestamp: Date.now()
134
+ };
135
+ } else {
136
+ console.error(
137
+ "Redux was asked to handle an unknown action type: " + action.type
138
+ );
139
+ process.exit(-1);
119
140
  }
120
- });
121
- const finalSelector = funkophileConfig.outputs(Object.keys(funkophileConfig.inputs).reduce((mm, inputKey) => {
122
- return {
123
- ...mm,
124
- [inputKey]: createSelector([(x) => x], (root) => {
125
- const result = root[inputKey];
126
- if (result === undefined) {
127
- throw new Error(`Input key "${inputKey}" is undefined in state. ` +
128
- `This means no files were found for the pattern "${funkophileConfig.inputs[inputKey]}". ` +
129
- `Available state keys: ${Object.keys(root).join(', ')}`);
130
- }
131
- return result;
132
- }),
133
- };
134
- }, {}));
135
- // Start HTTP server in watch mode
136
- let server = null;
137
- if (funkophileConfig.mode === "watch") {
138
- const port = funkophileConfig.options.port || 8080;
139
- server = http.createServer((req, res) => {
140
- if (!req.url) {
141
- res.statusCode = 400;
142
- res.end('Bad Request');
143
- return;
144
- }
145
- const parsedUrl = url.parse(req.url);
146
- let pathname = parsedUrl.pathname;
147
- // Default to index.html if the path ends with /
148
- if (pathname && pathname.endsWith('/')) {
149
- pathname += 'index.html';
150
- }
151
- // Remove leading slash
152
- const filePath = pathname ? pathname.substring(1) : 'index.html';
153
- // Construct the full path to the file
154
- const fullPath = path.join(process.cwd(), funkophileConfig.options.outFolder, filePath);
155
- // Check if file exists
156
- fs.access(fullPath, fs.constants.F_OK, (err) => {
157
- if (err) {
158
- // Try with .html extension
159
- const htmlPath = fullPath + '.html';
160
- fs.access(htmlPath, fs.constants.F_OK, (htmlErr) => {
161
- if (htmlErr) {
162
- // File not found
163
- res.statusCode = 404;
164
- res.end('File not found');
165
- }
166
- else {
167
- // Serve the .html file
168
- fs.readFile(htmlPath, (readErr, data) => {
169
- if (readErr) {
170
- res.statusCode = 500;
171
- res.end('Internal Server Error');
172
- }
173
- else {
174
- res.setHeader('Content-Type', 'text/html');
175
- res.end(data);
176
- }
177
- });
178
- }
179
- });
180
- }
181
- else {
182
- // Serve the file
183
- fs.readFile(fullPath, (readErr, data) => {
184
- if (readErr) {
185
- res.statusCode = 500;
186
- res.end('Internal Server Error');
187
- }
188
- else {
189
- // Set appropriate content type based on file extension
190
- const ext = path.extname(fullPath).toLowerCase();
191
- const contentTypes = {
192
- '.html': 'text/html',
193
- '.css': 'text/css',
194
- '.js': 'application/javascript',
195
- '.json': 'application/json',
196
- '.png': 'image/png',
197
- '.jpg': 'image/jpeg',
198
- '.jpeg': 'image/jpeg',
199
- '.gif': 'image/gif',
200
- '.svg': 'image/svg+xml',
201
- '.ico': 'image/x-icon'
202
- };
203
- res.setHeader('Content-Type', contentTypes[ext] || 'application/octet-stream');
204
- res.end(data);
205
- }
206
- });
207
- }
141
+ }
142
+ return state;
143
+ }
144
+ );
145
+ }
146
+ function makeFinalSelector(funkophileConfig) {
147
+ return funkophileConfig.outputs(
148
+ Object.keys(funkophileConfig.inputs).reduce((mm, inputKey) => {
149
+ return {
150
+ ...mm,
151
+ [inputKey]: createSelector([(x) => x], (root) => {
152
+ const result = root[inputKey];
153
+ if (result === void 0) {
154
+ console.warn(
155
+ `\x1B[33m\x1B[1m[Funkophile]\x1B[0m Input key "${inputKey}" is undefined in state, which shouldn't happen. Using empty object.`
156
+ );
157
+ return {};
158
+ }
159
+ return result;
160
+ })
161
+ };
162
+ }, {})
163
+ );
164
+ }
165
+ function startServing(funkophileConfig) {
166
+ const port = funkophileConfig.options.port || 8080;
167
+ const server = http.createServer((req, res) => {
168
+ if (!req.url) {
169
+ res.statusCode = 400;
170
+ res.end("Bad Request");
171
+ return;
172
+ }
173
+ const parsedUrl = url.parse(req.url);
174
+ let pathname = parsedUrl.pathname;
175
+ if (pathname && pathname.endsWith("/")) {
176
+ pathname += "index.html";
177
+ }
178
+ const filePath = pathname ? pathname.substring(1) : "index.html";
179
+ const fullPath = path.join(
180
+ process.cwd(),
181
+ funkophileConfig.options.outFolder,
182
+ filePath
183
+ );
184
+ fs.access(fullPath, fs.constants.F_OK, (err) => {
185
+ if (err) {
186
+ const htmlPath = fullPath + ".html";
187
+ fs.access(htmlPath, fs.constants.F_OK, (htmlErr) => {
188
+ if (htmlErr) {
189
+ res.statusCode = 404;
190
+ res.end("File not found");
191
+ } else {
192
+ fs.readFile(htmlPath, (readErr, data) => {
193
+ if (readErr) {
194
+ res.statusCode = 500;
195
+ res.end("Internal Server Error");
196
+ } else {
197
+ res.setHeader("Content-Type", "text/html");
198
+ res.end(data);
199
+ }
208
200
  });
201
+ }
209
202
  });
210
- server.listen(port, () => {
211
- console.log(`\u001b[36m\u001b[1m[Funkophile]\u001b[0m Server running at http://localhost:${port}/`);
203
+ } else {
204
+ fs.readFile(fullPath, (readErr, data) => {
205
+ if (readErr) {
206
+ res.statusCode = 500;
207
+ res.end("Internal Server Error");
208
+ } else {
209
+ const ext = path.extname(fullPath).toLowerCase();
210
+ const contentTypes = {
211
+ ".html": "text/html",
212
+ ".css": "text/css",
213
+ ".js": "application/javascript",
214
+ ".json": "application/json",
215
+ ".png": "image/png",
216
+ ".jpg": "image/jpeg",
217
+ ".jpeg": "image/jpeg",
218
+ ".gif": "image/gif",
219
+ ".svg": "image/svg+xml",
220
+ ".ico": "image/x-icon"
221
+ };
222
+ res.setHeader(
223
+ "Content-Type",
224
+ contentTypes[ext] || "application/octet-stream"
225
+ );
226
+ res.end(data);
227
+ }
212
228
  });
229
+ }
230
+ });
231
+ });
232
+ server.listen(port, () => {
233
+ console.log(
234
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Server running at http://localhost:${port}/`
235
+ );
236
+ });
237
+ process.on("SIGINT", () => {
238
+ if (server) {
239
+ server.close();
213
240
  }
214
- // Wait for all the file watchers to check in
215
- Promise.all(Object.keys(funkophileConfig.inputs).map((inputRuleKey) => {
216
- // Ensure the pattern includes the inFolder and is relative to the current working directory
217
- // Also, make sure to handle patterns that might already include the inFolder
218
- const pattern = funkophileConfig.inputs[inputRuleKey] || "";
219
- // For glob, we want the pattern to be relative to process.cwd()
220
- // Join inFolder and pattern using forward slashes
221
- const globPattern = path.posix.join(funkophileConfig.options.inFolder, pattern);
222
- // console.log(`[Funkophile] Looking for files with glob pattern: ${globPattern}`);
223
- // console.log(`[Funkophile] Current working directory: ${process.cwd()}`);
224
- return new Promise((fulfill, reject) => {
225
- if (funkophileConfig.mode === "build") {
226
- // Use the glob pattern we constructed earlier
227
- // console.log(`[Funkophile] Searching for files matching pattern: ${globPattern}`);
228
- // console.log(`[Funkophile] Input rule key: ${inputRuleKey}`);
229
- glob(globPattern, { cwd: process.cwd() })
230
- .then((files) => {
231
- // console.log(`[Funkophile] Found ${files.length} files for ${inputRuleKey} (pattern: ${pattern}):`, files);
232
- if (files.length === 0) {
233
- console.warn(`No files found for input key "${inputRuleKey}" with pattern "${globPattern}"`);
234
- // console.log(`[Funkophile] The glob pattern used was: ${globPattern}`);
235
- }
236
- else {
237
- files.forEach((file) => {
238
- // Make sure the file path is absolute
239
- const absoluteFilePath = path.resolve(process.cwd(), file);
240
- // console.log(`[Funkophile] Adding file to state for key ${inputRuleKey}: ${absoluteFilePath}`);
241
- dispatchUpsert(store, inputRuleKey, absoluteFilePath, funkophileConfig.encodings);
242
- });
243
- }
244
- })
245
- .then(() => {
246
- fulfill();
247
- })
248
- .catch((error) => {
249
- // console.error(`[Funkophile] Error globbing for pattern ${globPattern}:`, error);
250
- reject(error);
251
- });
252
- }
253
- else if (funkophileConfig.mode === "watch") {
254
- // Use the same glob pattern for watch mode
255
- // console.log(`[Funkophile] Watching for files matching pattern: ${globPattern}`);
256
- // First, use glob to find existing files to ensure they're added to the state
257
- glob(globPattern, { cwd: process.cwd() })
258
- .then((files) => {
259
- // console.log(`[Funkophile] Found ${files.length} existing files for ${inputRuleKey} (pattern: ${pattern}):`, files);
260
- files.forEach((file) => {
261
- const absoluteFilePath = path.resolve(process.cwd(), file);
262
- // console.log(`[Funkophile] Adding existing file to state for key ${inputRuleKey}: ${absoluteFilePath}`);
263
- dispatchUpsert(store, inputRuleKey, absoluteFilePath, funkophileConfig.encodings);
264
- });
265
- // Now set up the watcher
266
- const watcher = chokidar
267
- .watch(globPattern, {
268
- cwd: process.cwd(),
269
- ignoreInitial: true // We've already handled initial files above
270
- })
271
- .on("error", (error) => {
272
- logger.watchError(globPattern);
273
- })
274
- .on("ready", () => {
275
- logger.watchReady(globPattern);
276
- fulfill();
277
- })
278
- .on("add", (filePath) => {
279
- logger.watchAdd(filePath);
280
- const absoluteFilePath = path.resolve(process.cwd(), filePath);
281
- dispatchUpsert(store, inputRuleKey, absoluteFilePath, funkophileConfig.encodings);
282
- })
283
- .on("change", (filePath) => {
284
- logger.watchChange(filePath);
285
- const absoluteFilePath = path.resolve(process.cwd(), filePath);
286
- dispatchUpsert(store, inputRuleKey, absoluteFilePath, funkophileConfig.encodings);
287
- })
288
- .on("unlink", (filePath) => {
289
- logger.watchUnlink(filePath);
290
- const absoluteFilePath = path.resolve(process.cwd(), filePath);
291
- store.dispatch({
292
- type: REMOVE,
293
- payload: {
294
- key: inputRuleKey,
295
- file: absoluteFilePath,
296
- },
297
- });
298
- })
299
- .on("unlinkDir", (filePath) => {
300
- logger.watchUnlink(filePath);
301
- });
302
- })
303
- .catch((error) => {
304
- console.error(`[Funkophile] Error globbing for pattern ${globPattern}:`, error);
305
- reject(error);
306
- });
307
- // .on('raw', (event, p, details) => { // internal
308
- // log('Raw event info:', event, p, details);
309
- // })
310
- }
311
- else {
312
- console.error(`mode should be 'watch' or 'build', not "${funkophileConfig.mode}"`);
313
- process.exit(-1);
314
- }
241
+ });
242
+ }
243
+ function logInputKeys(funkophileConfig, currentState) {
244
+ Object.keys(funkophileConfig.inputs).forEach((inputKey) => {
245
+ if (currentState[inputKey]) {
246
+ console.log(
247
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Input key "${inputKey}" found in state with ${Object.keys(currentState[inputKey]).length} files`
248
+ );
249
+ if (Object.keys(currentState[inputKey]).length > 0) {
250
+ console.log(
251
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Files for "${inputKey}":`,
252
+ Object.keys(currentState[inputKey])
253
+ );
254
+ }
255
+ } else {
256
+ console.warn(
257
+ `\x1B[33m\x1B[1m[Funkophile]\x1B[0m Input key "${inputKey}" NOT found in state`
258
+ );
259
+ }
260
+ });
261
+ }
262
+ function logDone(funkophileConfig, currentState) {
263
+ if (funkophileConfig.mode === "build") {
264
+ console.log(
265
+ "\x1B[32m\x1B[1m[Funkophile]\x1B[0m Build completed successfully!"
266
+ );
267
+ logger.done();
268
+ } else if (funkophileConfig.mode === "watch") {
269
+ console.log(
270
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m Watching for file changes..."
271
+ );
272
+ const port = funkophileConfig.options.port || 8080;
273
+ console.log(
274
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Serving at: http://localhost:${port}/`
275
+ );
276
+ logger.waiting();
277
+ } else {
278
+ throw `\x1B[31m\x1B[1m[Funkophile]\x1B[0m The mode should be 'watch' or 'build', not "${funkophileConfig.mode}"`;
279
+ }
280
+ }
281
+ function makePromissesArray(funkophileConfig, store) {
282
+ return Object.keys(funkophileConfig.inputs).map((inputRuleKey) => {
283
+ const pattern = funkophileConfig.inputs[inputRuleKey] || "";
284
+ const globPattern = path.posix.join(
285
+ funkophileConfig.options.inFolder,
286
+ pattern
287
+ );
288
+ return new Promise((fulfill, reject) => {
289
+ if (funkophileConfig.mode === "build") {
290
+ glob(globPattern, { cwd: process.cwd() }).then((files) => {
291
+ if (files.length === 0) {
292
+ console.warn(
293
+ `No files found for input key "${inputRuleKey}" with pattern "${globPattern}"`
294
+ );
295
+ } else {
296
+ files.forEach((file) => {
297
+ const absoluteFilePath = path.resolve(process.cwd(), file);
298
+ dispatchUpsert(
299
+ store,
300
+ inputRuleKey,
301
+ absoluteFilePath,
302
+ funkophileConfig.encodings
303
+ );
304
+ });
305
+ }
306
+ }).then(() => {
307
+ fulfill();
308
+ }).catch((error) => {
309
+ reject(error);
315
310
  });
316
- })).then(function () {
317
- // console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m All input files processed. Setting up store subscription...');
318
- // Debug: log the current state after all files are processed
319
- const currentState = store.getState();
320
- console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Current state keys:', Object.keys(currentState));
321
- // Log all input keys to see if they're present
322
- Object.keys(funkophileConfig.inputs).forEach(inputKey => {
323
- if (currentState[inputKey]) {
324
- // console.log(`[Funkophile] Input key "${inputKey}" found in state with ${Object.keys(currentState[inputKey]).length} files`);
325
- }
326
- else {
327
- throw (`Input key "${inputKey}" NOT found in state`);
311
+ } else if (funkophileConfig.mode === "watch") {
312
+ console.log(
313
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Setting up watcher for pattern: ${globPattern}`
314
+ );
315
+ console.log(
316
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Current working directory: ${process.cwd()}`
317
+ );
318
+ glob(globPattern, { cwd: process.cwd() }).then((files) => {
319
+ console.log(
320
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Found ${files.length} initial files for ${inputRuleKey}`
321
+ );
322
+ files.forEach((file) => {
323
+ const absoluteFilePath = path.resolve(process.cwd(), file);
324
+ console.log(
325
+ `\x1B[32m\x1B[1m[Funkophile]\x1B[0m Adding initial file: ${file}`
326
+ );
327
+ dispatchUpsert(
328
+ store,
329
+ inputRuleKey,
330
+ absoluteFilePath,
331
+ funkophileConfig.encodings
332
+ );
333
+ });
334
+ const watcher = chokidar.watch(globPattern, {
335
+ cwd: process.cwd(),
336
+ ignoreInitial: true,
337
+ // We've already processed initial files
338
+ persistent: true,
339
+ usePolling: false,
340
+ interval: 100,
341
+ binaryInterval: 300,
342
+ alwaysStat: false,
343
+ depth: 99,
344
+ awaitWriteFinish: {
345
+ stabilityThreshold: 50,
346
+ pollInterval: 10
328
347
  }
348
+ }).on("error", (error) => {
349
+ console.error(
350
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Watcher error for pattern ${globPattern}:`,
351
+ error
352
+ );
353
+ logger.watchError(globPattern);
354
+ }).on("add", (filePath) => {
355
+ console.log(
356
+ `\x1B[32m\x1B[1m[Funkophile]\x1B[0m File added: ${filePath}`
357
+ );
358
+ logger.watchAdd(filePath);
359
+ const absoluteFilePath = path.resolve(process.cwd(), filePath);
360
+ console.log(
361
+ `\x1B[32m\x1B[1m[Funkophile]\x1B[0m Dispatching UPSERT for key: ${inputRuleKey}, file: ${absoluteFilePath}`
362
+ );
363
+ dispatchUpsert(
364
+ store,
365
+ inputRuleKey,
366
+ absoluteFilePath,
367
+ funkophileConfig.encodings
368
+ );
369
+ }).on("change", (filePath) => {
370
+ console.log(
371
+ `\x1B[33m\x1B[1m[Funkophile]\x1B[0m File changed: ${filePath}`
372
+ );
373
+ logger.watchChange(filePath);
374
+ const absoluteFilePath = path.resolve(process.cwd(), filePath);
375
+ console.log(
376
+ `\x1B[33m\x1B[1m[Funkophile]\x1B[0m Dispatching UPSERT for key: ${inputRuleKey}, file: ${absoluteFilePath}`
377
+ );
378
+ dispatchUpsert(
379
+ store,
380
+ inputRuleKey,
381
+ absoluteFilePath,
382
+ funkophileConfig.encodings
383
+ );
384
+ }).on("unlink", (filePath) => {
385
+ console.log(
386
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m File removed: ${filePath}`
387
+ );
388
+ logger.watchUnlink(filePath);
389
+ const absoluteFilePath = path.resolve(process.cwd(), filePath);
390
+ console.log(
391
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Dispatching REMOVE for key: ${inputRuleKey}, file: ${absoluteFilePath}`
392
+ );
393
+ store.dispatch({
394
+ type: REMOVE,
395
+ payload: {
396
+ key: inputRuleKey,
397
+ file: absoluteFilePath
398
+ }
399
+ });
400
+ }).on("unlinkDir", (filePath) => {
401
+ console.log(
402
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Directory removed: ${filePath}`
403
+ );
404
+ logger.watchUnlink(filePath);
405
+ }).on("raw", (event, path2, details) => {
406
+ console.log(
407
+ `\x1B[90m\x1B[1m[Funkophile]\x1B[0m Raw event: ${event} for path: ${path2}`
408
+ );
409
+ });
410
+ console.log(
411
+ `\x1B[32m\x1B[1m[Funkophile]\x1B[0m Watcher is ready for pattern: ${globPattern}`
412
+ );
413
+ logger.watchReady(globPattern);
414
+ fulfill();
415
+ }).catch((error) => {
416
+ console.error(
417
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Error processing initial files for pattern ${globPattern}:`,
418
+ error
419
+ );
420
+ reject(error);
329
421
  });
330
- // listen for changes to the store
331
- store.subscribe(() => {
332
- const s = store.getState();
333
- // Skip processing during initial load
334
- if (s.initialLoad) {
335
- console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Initial load in progress, skipping processing...');
336
- console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m State keys during initial load:', Object.keys(s));
337
- return;
338
- }
339
- logger.stateChange();
340
- console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Processing state changes...');
341
- let outputs;
342
- try {
343
- outputs = finalSelector(s);
344
- console.log(`\u001b[36m\u001b[1m[Funkophile]\u001b[0m Generated ${Object.keys(outputs).length} outputs`);
345
- }
346
- catch (error) {
347
- console.error('\u001b[31m\u001b[1m[Funkophile]\u001b[0m FATAL: Error in output selector chain:');
348
- console.error(' Error:', error.message);
349
- console.error(' Stack:', error.stack);
350
- // Don't exit the process in watch mode, just log the error and continue
351
- if (funkophileConfig.mode === 'build') {
352
- process.exit(1);
353
- }
354
- else {
355
- console.log('\u001b[33m\u001b[1m[Funkophile]\u001b[0m Continuing to watch for changes despite error...');
356
- // Reset previousState to empty to ensure we try processing again on next change
357
- Object.keys(previousState).forEach(key => {
358
- delete previousState[key];
359
- });
360
- return;
361
- }
362
- }
363
- if (outputPromise.isPending()) {
364
- console.log("\u001b[33m\u001b[1m[Funkophile]\u001b[0m Cancelling previous write operation!");
365
- outputPromise.cancel();
366
- }
367
- outputPromise = Promise.all(Array.from(new Set(Object.keys(previousState).concat(Object.keys(outputs)))).map((key) => {
368
- return new Promise((fulfill, reject) => {
369
- if (!outputs[key]) {
370
- const file = funkophileConfig.options.outFolder + "/" + key;
371
- logger.removedFile(file);
372
- console.log(`\u001b[31m\u001b[1m[Funkophile]\u001b[0m Removing file: ${file}`);
373
- try {
374
- fse.unlinkSync("./" + file);
375
- cleanEmptyFoldersRecursively("./" + file.substring(0, file.lastIndexOf("/")));
376
- }
377
- catch (ex) {
378
- // Log error but don't fail the entire process
379
- console.error(`\u001b[31m\u001b[1m[Funkophile]\u001b[0m Error removing file ${file}:`, ex.message);
380
- }
381
- finally {
382
- delete previousState[key];
383
- fulfill();
422
+ } else {
423
+ console.error(
424
+ `mode should be 'watch' or 'build', not "${funkophileConfig.mode}"`
425
+ );
426
+ process.exit(-1);
427
+ }
428
+ });
429
+ });
430
+ }
431
+
432
+ // index.ts
433
+ Promise2.config({
434
+ cancellation: true
435
+ });
436
+ var index_default = (funkophileConfig) => {
437
+ let outputPromise = Promise2.resolve();
438
+ const store = newStore(funkophileConfig);
439
+ const finalSelector = makeFinalSelector(funkophileConfig);
440
+ if (funkophileConfig.mode === "watch") {
441
+ startServing(funkophileConfig);
442
+ }
443
+ Promise2.all(
444
+ makePromissesArray(funkophileConfig, store)
445
+ ).then(function() {
446
+ console.log(
447
+ "\x1B[32m\x1B[1m[Funkophile]\x1B[0m All input watchers are ready. Setting up store subscription..."
448
+ );
449
+ store.subscribe(() => {
450
+ const s = store.getState();
451
+ console.log(
452
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Store updated. initialLoad: ${s.initialLoad}, timestamp: ${s.timestamp}`
453
+ );
454
+ if (s.initialLoad) {
455
+ console.log(
456
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m Initial load in progress, skipping processing..."
457
+ );
458
+ console.log(
459
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m State keys during initial load:",
460
+ Object.keys(s)
461
+ );
462
+ return;
463
+ }
464
+ logger.stateChange();
465
+ console.log(
466
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m Processing state changes..."
467
+ );
468
+ console.log(
469
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m Current state keys:",
470
+ Object.keys(s)
471
+ );
472
+ let outputs;
473
+ try {
474
+ outputs = finalSelector(s);
475
+ console.log(
476
+ `\x1B[36m\x1B[1m[Funkophile]\x1B[0m Generated ${Object.keys(outputs).length} outputs`
477
+ );
478
+ } catch (error) {
479
+ console.error(
480
+ "\x1B[31m\x1B[1m[Funkophile]\x1B[0m FATAL: Error in output selector chain ? :"
481
+ );
482
+ console.error(" Error:", error.message);
483
+ console.error(" Stack:", error.stack);
484
+ if (funkophileConfig.mode === "build") {
485
+ process.exit(1);
486
+ } else {
487
+ console.log(
488
+ "\x1B[33m\x1B[1m[Funkophile]\x1B[0m Continuing to watch for changes despite error..."
489
+ );
490
+ Object.keys(previousState).forEach((key) => {
491
+ delete previousState[key];
492
+ });
493
+ return;
494
+ }
495
+ }
496
+ if (outputPromise.isPending()) {
497
+ console.log(
498
+ "\x1B[33m\x1B[1m[Funkophile]\x1B[0m Cancelling previous write operation!"
499
+ );
500
+ outputPromise.cancel();
501
+ }
502
+ outputPromise = Promise2.all(
503
+ Array.from(
504
+ new Set(Object.keys(previousState).concat(Object.keys(outputs)))
505
+ ).map((key) => {
506
+ return new Promise2((fulfill, reject) => {
507
+ if (!outputs[key]) {
508
+ const file = funkophileConfig.options.outFolder + "/" + key;
509
+ logger.removedFile(file);
510
+ console.log(
511
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Removing file: ${file}`
512
+ );
513
+ try {
514
+ fse2.unlinkSync("./" + file);
515
+ cleanEmptyFoldersRecursively(
516
+ "./" + file.substring(0, file.lastIndexOf("/"))
517
+ );
518
+ } catch (ex) {
519
+ console.error(
520
+ `\x1B[31m\x1B[1m[Funkophile]\x1B[0m Error removing file ${file}:`,
521
+ ex.message
522
+ );
523
+ } finally {
524
+ delete previousState[key];
525
+ fulfill();
526
+ }
527
+ } else {
528
+ if (outputs[key] !== previousState[key]) {
529
+ previousState[key] = outputs[key];
530
+ const relativeFilePath = "./" + funkophileConfig.options.outFolder + "/" + key;
531
+ const contents = outputs[key];
532
+ if (typeof contents === "function") {
533
+ logger.writingFunction(relativeFilePath);
534
+ contents((err, res) => {
535
+ if (err) {
536
+ logger.writingError(relativeFilePath, err.message);
537
+ fulfill();
538
+ } else {
539
+ fse2.outputFile(relativeFilePath, res, (err2) => {
540
+ if (err2) {
541
+ logger.writingError(relativeFilePath, err2.message);
542
+ fulfill();
543
+ } else {
544
+ logger.writingString(relativeFilePath);
545
+ fulfill();
384
546
  }
547
+ });
385
548
  }
386
- else {
387
- if (outputs[key] !== previousState[key]) {
388
- previousState[key] = outputs[key];
389
- const relativeFilePath = "./" + funkophileConfig.options.outFolder + "/" + key;
390
- const contents = outputs[key];
391
- // console.log(`\u001b[32m\u001b[1m[Funkophile]\u001b[0m Writing file: ${relativeFilePath}`);
392
- if (typeof contents === "function") {
393
- logger.writingFunction(relativeFilePath);
394
- contents((err, res) => {
395
- if (err) {
396
- logger.writingError(relativeFilePath, err.message);
397
- fulfill(); // Still fulfill to continue processing other files
398
- }
399
- else {
400
- fse.outputFile(relativeFilePath, res, (err) => {
401
- if (err) {
402
- logger.writingError(relativeFilePath, err.message);
403
- fulfill(); // Still fulfill to continue processing other files
404
- }
405
- else {
406
- logger.writingString(relativeFilePath);
407
- fulfill();
408
- }
409
- });
410
- }
411
- });
412
- }
413
- else if (typeof contents === "string") {
414
- fse.outputFile(relativeFilePath, contents, (err) => {
415
- if (err) {
416
- logger.writingError(relativeFilePath, err.message);
417
- fulfill();
418
- }
419
- else {
420
- logger.writingString(relativeFilePath);
421
- fulfill();
422
- }
423
- });
424
- }
425
- else if (Buffer.isBuffer(contents)) {
426
- fse.outputFile(relativeFilePath, contents, (err) => {
427
- if (err) {
428
- logger.writingError(relativeFilePath, err.message);
429
- fulfill();
430
- }
431
- else {
432
- logger.writingString(relativeFilePath);
433
- fulfill();
434
- }
435
- });
436
- }
437
- else if (Array.isArray(contents)) {
438
- fse.outputFile(relativeFilePath, JSON.stringify(contents), (err) => {
439
- if (err) {
440
- logger.writingError(relativeFilePath, err.message);
441
- fulfill();
442
- }
443
- else {
444
- logger.writingString(relativeFilePath);
445
- fulfill();
446
- }
447
- });
448
- }
449
- else if (typeof contents.then === "function") {
450
- logger.writingPromise(relativeFilePath);
451
- Promise.resolve(contents).then(function (value) {
452
- if (value instanceof Error) {
453
- logger.writingError(relativeFilePath, value.message);
454
- fulfill();
455
- }
456
- else {
457
- fse.outputFile(relativeFilePath, value, (err) => {
458
- if (err) {
459
- logger.writingError(relativeFilePath, err.message);
460
- fulfill();
461
- }
462
- else {
463
- logger.writingString(relativeFilePath);
464
- fulfill();
465
- }
466
- });
467
- }
468
- }, function (error) {
469
- logger.writingError(relativeFilePath, error.message);
470
- fulfill();
471
- });
472
- }
473
- else {
474
- console.log(`\u001b[33m\u001b[1m[Funkophile]\u001b[0m Unrecognized content type for ${relativeFilePath}, attempting to write:`, typeof contents);
475
- fse.outputFile(relativeFilePath, contents, (err) => {
476
- if (err) {
477
- logger.writingError(relativeFilePath, err.message);
478
- fulfill();
479
- }
480
- else {
481
- logger.writingString(relativeFilePath);
482
- fulfill();
483
- }
484
- });
485
- }
486
- }
487
- else {
488
- // console.log(`\u001b[90m\u001b[1m[Funkophile]\u001b[0m Skipping unchanged file: ${key}`);
549
+ });
550
+ } else if (typeof contents === "string") {
551
+ fse2.outputFile(relativeFilePath, contents, (err) => {
552
+ if (err) {
553
+ logger.writingError(relativeFilePath, err.message);
554
+ fulfill();
555
+ } else {
556
+ logger.writingString(relativeFilePath);
557
+ fulfill();
558
+ }
559
+ });
560
+ } else if (Buffer.isBuffer(contents)) {
561
+ fse2.outputFile(relativeFilePath, contents, (err) => {
562
+ if (err) {
563
+ logger.writingError(relativeFilePath, err.message);
564
+ fulfill();
565
+ } else {
566
+ logger.writingString(relativeFilePath);
567
+ fulfill();
568
+ }
569
+ });
570
+ } else if (Array.isArray(contents)) {
571
+ fse2.outputFile(
572
+ relativeFilePath,
573
+ JSON.stringify(contents),
574
+ (err) => {
575
+ if (err) {
576
+ logger.writingError(relativeFilePath, err.message);
577
+ fulfill();
578
+ } else {
579
+ logger.writingString(relativeFilePath);
580
+ fulfill();
581
+ }
582
+ }
583
+ );
584
+ } else if (typeof contents.then === "function") {
585
+ logger.writingPromise(relativeFilePath);
586
+ Promise2.resolve(contents).then(
587
+ function(value) {
588
+ if (value instanceof Error) {
589
+ logger.writingError(relativeFilePath, value.message);
590
+ fulfill();
591
+ } else {
592
+ fse2.outputFile(relativeFilePath, value, (err) => {
593
+ if (err) {
594
+ logger.writingError(relativeFilePath, err.message);
489
595
  fulfill();
490
- }
596
+ } else {
597
+ logger.writingString(relativeFilePath);
598
+ fulfill();
599
+ }
600
+ });
601
+ }
602
+ },
603
+ function(error) {
604
+ logger.writingError(relativeFilePath, error.message);
605
+ fulfill();
491
606
  }
492
- });
493
- })).then(() => {
494
- // console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Cleaning empty folders...');
495
- cleanEmptyFoldersRecursively(funkophileConfig.options.outFolder);
496
- if (funkophileConfig.mode === "build") {
497
- console.log('\u001b[32m\u001b[1m[Funkophile]\u001b[0m Build completed successfully!');
498
- logger.done();
499
- }
500
- else if (funkophileConfig.mode === "watch") {
501
- console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Watching for file changes...');
502
- // Log the localhost URL if port is specified
503
- const port = funkophileConfig.options.port || 8080;
504
- console.log(`\u001b[36m\u001b[1m[Funkophile]\u001b[0m Serving at: http://localhost:${port}/`);
505
- logger.waiting();
506
- }
507
- else {
508
- throw (`\u001b[31m\u001b[1m[Funkophile]\u001b[0m The mode should be 'watch' or 'build', not "${funkophileConfig.mode}"`);
607
+ );
608
+ } else {
609
+ console.log(
610
+ `\x1B[33m\x1B[1m[Funkophile]\x1B[0m Unrecognized content type for ${relativeFilePath}, attempting to write:`,
611
+ typeof contents
612
+ );
613
+ fse2.outputFile(relativeFilePath, contents, (err) => {
614
+ if (err) {
615
+ logger.writingError(relativeFilePath, err.message);
616
+ fulfill();
617
+ } else {
618
+ logger.writingString(relativeFilePath);
619
+ fulfill();
620
+ }
621
+ });
509
622
  }
510
- });
511
- // .catch((error) => {
512
- // // console.error('\u001b[31m\u001b[1m[Funkophile]\u001b[0m Error during file operations:', error);
513
- // });
514
- });
515
- // console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Initializing store...');
516
- // lastly, turn the store `on`.
517
- // This is to prevent unecessary recomputations when initialy adding files to redux
518
- store.dispatch({
519
- type: INITIALIZE,
520
- payload: true,
521
- });
522
- // console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Store initialized. Starting processing...');
523
- });
524
- // Handle process exit to close the server
525
- process.on('SIGINT', () => {
526
- if (server) {
527
- server.close();
528
- }
529
- process.exit(0);
623
+ } else {
624
+ fulfill();
625
+ }
626
+ }
627
+ });
628
+ })
629
+ ).then(() => {
630
+ cleanEmptyFoldersRecursively(funkophileConfig.options.outFolder);
631
+ logDone(funkophileConfig, currentState);
632
+ });
530
633
  });
634
+ console.log(
635
+ "\x1B[32m\x1B[1m[Funkophile]\x1B[0m Dispatching INITIALIZE action to enable processing..."
636
+ );
637
+ const currentState = store.getState();
638
+ console.log(
639
+ "\x1B[36m\x1B[1m[Funkophile]\x1B[0m Current state keys:",
640
+ Object.keys(currentState)
641
+ );
642
+ logInputKeys(funkophileConfig, currentState);
643
+ setTimeout(() => {
644
+ store.dispatch({
645
+ type: INITIALIZE,
646
+ payload: true
647
+ });
648
+ console.log(
649
+ "\x1B[32m\x1B[1m[Funkophile]\x1B[0m Store initialized. Ready to process changes!"
650
+ );
651
+ }, 100);
652
+ });
653
+ };
654
+ export {
655
+ index_default as default
531
656
  };
657
+ //# sourceMappingURL=index.js.map