funkophile 0.2.4 → 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/.mocharc.json +3 -0
- package/.vscode/settings.json +6 -0
- package/README.md +72 -40
- package/build.ts +23 -0
- package/coverage/clover.xml +6 -0
- package/coverage/coverage-final.json +1 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +101 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +0 -0
- package/dev.ts +26 -0
- package/dist/esm/funkophileHelpers.js +100 -43
- package/dist/esm/funkophileHelpers.js.map +7 -0
- package/dist/esm/index.js +639 -269
- package/dist/esm/index.js.map +7 -0
- package/funkophileHelpers.ts +74 -5
- package/index.test.ts +210 -0
- package/index.ts +187 -284
- package/mocha.log +59 -0
- package/package.json +29 -6
- package/test-utils.ts +40 -0
- package/tsconfig.json +17 -4
- package/utils.ts +539 -0
- package/dist/esm/funkophileHelpers.d.ts +0 -108
- package/dist/esm/index.d.ts +0 -12
- package/yarn-error.log +0 -107
package/index.ts
CHANGED
|
@@ -1,267 +1,103 @@
|
|
|
1
|
-
import chokidar from "chokidar";
|
|
2
|
-
import { createSelector } from "reselect";
|
|
3
|
-
import { Action, createStore, Store } from "redux";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import fse from "fs-extra";
|
|
6
|
-
import { glob } from "glob";
|
|
7
|
-
import path from "path";
|
|
8
1
|
import Promise from "bluebird";
|
|
2
|
+
import fse from "fs-extra";
|
|
9
3
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
import { Action, Store } from "redux";
|
|
5
|
+
import {
|
|
6
|
+
IConfig,
|
|
7
|
+
INITIALIZE,
|
|
8
|
+
cleanEmptyFoldersRecursively,
|
|
9
|
+
logDone,
|
|
10
|
+
logInputKeys,
|
|
11
|
+
logger,
|
|
12
|
+
makeFinalSelector,
|
|
13
|
+
makePromissesArray,
|
|
14
|
+
newStore,
|
|
15
|
+
previousState,
|
|
16
|
+
startServing,
|
|
17
|
+
} from "./utils";
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
Promise.config({
|
|
20
|
+
cancellation: true,
|
|
21
|
+
});
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
const UPSERT = "UPSERT";
|
|
28
|
-
const REMOVE = "REMOVE";
|
|
29
|
-
|
|
30
|
-
const previousState: any = {};
|
|
23
|
+
export default (funkophileConfig: IConfig) => {
|
|
31
24
|
let outputPromise = Promise.resolve();
|
|
25
|
+
const store: Store<any, Action<string>, any> = newStore(funkophileConfig);
|
|
26
|
+
const finalSelector = makeFinalSelector(funkophileConfig);
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
watchReady: (p: string) =>
|
|
36
|
-
console.log("\u001b[7m\u001b[36m < \u001b[0m" + p),
|
|
37
|
-
watchAdd: (p: string) =>
|
|
38
|
-
console.log("\u001b[7m\u001b[34m + \u001b[0m./" + p),
|
|
39
|
-
watchChange: (p: string) =>
|
|
40
|
-
console.log("\u001b[7m\u001b[35m * \u001b[0m" + p),
|
|
41
|
-
watchUnlink: (p: string) =>
|
|
42
|
-
console.log("\u001b[7m\u001b[31m - \u001b[0m./" + p),
|
|
43
|
-
stateChange: () =>
|
|
44
|
-
console.log("\u001b[7m\u001b[31m --- Redux state changed --- \u001b[0m"),
|
|
45
|
-
cleaningEmptyfolder: (p: string) =>
|
|
46
|
-
console.log("\u001b[31m\u001b[7m XXX! \u001b[0m" + p),
|
|
47
|
-
readingFile: (p: string) => console.log("\u001b[31m <-- \u001b[0m" + p),
|
|
48
|
-
removedFile: (p: string) =>
|
|
49
|
-
console.log("\u001b[31m\u001b[7m ??? \u001b[0m./" + p),
|
|
50
|
-
writingString: (p: string) => console.log("\u001b[32m --> \u001b[0m" + p),
|
|
51
|
-
writingFunction: (p: string) => console.log("\u001b[33m ... \u001b[0m" + p),
|
|
52
|
-
writingPromise: (p: string) => console.log("\u001b[33m ... \u001b[0m" + p),
|
|
53
|
-
writingError: (p: string, message: string) =>
|
|
54
|
-
console.log("\u001b[31m !!! \u001b[0m" + p + " " + message),
|
|
55
|
-
|
|
56
|
-
waiting: () =>
|
|
57
|
-
console.log(
|
|
58
|
-
"\u001b[7m Funkophile is done for now but waiting on changes...\u001b[0m "
|
|
59
|
-
),
|
|
60
|
-
done: () => console.log("\u001b[7m Funkophile is done!\u001b[0m "),
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
function cleanEmptyFoldersRecursively(folder: string) {
|
|
64
|
-
var isDir = fs.statSync(folder).isDirectory();
|
|
65
|
-
if (!isDir) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
var files = fs.readdirSync(folder);
|
|
69
|
-
if (files.length > 0) {
|
|
70
|
-
files.forEach(function (file) {
|
|
71
|
-
var fullPath = path.join(folder, file);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// re-evaluate files; after deleting subfolder
|
|
75
|
-
// we may have parent folder empty now
|
|
76
|
-
files = fs.readdirSync(folder);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (files.length == 0) {
|
|
80
|
-
logger.cleaningEmptyfolder(folder);
|
|
81
|
-
|
|
82
|
-
fs.rmdirSync(folder);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
28
|
+
if (funkophileConfig.mode === "watch") {
|
|
29
|
+
startServing(funkophileConfig);
|
|
85
30
|
}
|
|
86
31
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const fileType: string = path.basename(file).split(".")[1];
|
|
95
|
-
|
|
96
|
-
let encoding: BufferEncoding = Object.keys(encodings).find((e) => {
|
|
97
|
-
return encodings[e].includes(fileType);
|
|
98
|
-
}) as BufferEncoding;
|
|
99
|
-
|
|
100
|
-
logger.readingFile(file);
|
|
101
|
-
store.dispatch({
|
|
102
|
-
type: UPSERT,
|
|
103
|
-
payload: {
|
|
104
|
-
key: key,
|
|
105
|
-
// key: path.relative(process.cwd(), key),
|
|
106
|
-
src: file,
|
|
107
|
-
contents: fse.readFileSync(file, encoding),
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
};
|
|
32
|
+
// Wait for all the file watchers to check in
|
|
33
|
+
Promise.all(
|
|
34
|
+
makePromissesArray(funkophileConfig, store)
|
|
35
|
+
).then(function () {
|
|
36
|
+
console.log(
|
|
37
|
+
"\u001b[32m\u001b[1m[Funkophile]\u001b[0m All input watchers are ready. Setting up store subscription..."
|
|
38
|
+
);
|
|
111
39
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
40
|
+
// Set up the store subscription BEFORE initializing to catch all changes
|
|
41
|
+
store.subscribe(() => {
|
|
42
|
+
const s = store.getState();
|
|
43
|
+
console.log(
|
|
44
|
+
`\u001b[36m\u001b[1m[Funkophile]\u001b[0m Store updated. initialLoad: ${s.initialLoad}, timestamp: ${s.timestamp}`
|
|
45
|
+
);
|
|
116
46
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
throw new Error("Redux state is undefined. This should never happen.");
|
|
128
|
-
}
|
|
129
|
-
// console.log("\u001b[7m\u001b[35m ||| Redux recieved action \u001b[0m", action.type)
|
|
130
|
-
if (!action.type.includes("@@redux")) {
|
|
131
|
-
if (action.type === INITIALIZE) {
|
|
132
|
-
return {
|
|
133
|
-
...state,
|
|
134
|
-
initialLoad: false,
|
|
135
|
-
timestamp: Date.now(),
|
|
136
|
-
};
|
|
137
|
-
} else if (action.type === UPSERT) {
|
|
138
|
-
return {
|
|
139
|
-
...state,
|
|
140
|
-
[action["payload"].key]: {
|
|
141
|
-
// @ts-ignore
|
|
142
|
-
...state[action.payload.key],
|
|
143
|
-
...{
|
|
144
|
-
[action["payload"].src]: action["payload"].contents,
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
timestamp: Date.now(),
|
|
148
|
-
};
|
|
149
|
-
} else if (action.type === REMOVE) {
|
|
150
|
-
return {
|
|
151
|
-
...state,
|
|
152
|
-
[action["payload"].key]: omit(
|
|
153
|
-
action["payload"].file,
|
|
154
|
-
state[action["payload"].key]
|
|
155
|
-
),
|
|
156
|
-
timestamp: Date.now(),
|
|
157
|
-
};
|
|
158
|
-
} else {
|
|
159
|
-
console.error(
|
|
160
|
-
"Redux was asked to handle an unknown action type: " + action.type
|
|
161
|
-
);
|
|
162
|
-
process.exit(-1);
|
|
163
|
-
}
|
|
164
|
-
// return state
|
|
47
|
+
// Skip processing during initial load
|
|
48
|
+
if (s.initialLoad) {
|
|
49
|
+
console.log(
|
|
50
|
+
"\u001b[36m\u001b[1m[Funkophile]\u001b[0m Initial load in progress, skipping processing..."
|
|
51
|
+
);
|
|
52
|
+
console.log(
|
|
53
|
+
"\u001b[36m\u001b[1m[Funkophile]\u001b[0m State keys during initial load:",
|
|
54
|
+
Object.keys(s)
|
|
55
|
+
);
|
|
56
|
+
return;
|
|
165
57
|
}
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
58
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// Wait for all the file watchers to check in
|
|
179
|
-
Promise.all(
|
|
180
|
-
Object.keys(funkophileConfig.inputs).map((inputRuleKey) => {
|
|
181
|
-
const p = path.resolve(
|
|
182
|
-
`./${funkophileConfig.options.inFolder}/${
|
|
183
|
-
funkophileConfig.inputs[inputRuleKey] || ""
|
|
184
|
-
}`
|
|
59
|
+
logger.stateChange();
|
|
60
|
+
console.log(
|
|
61
|
+
"\u001b[36m\u001b[1m[Funkophile]\u001b[0m Processing state changes..."
|
|
62
|
+
);
|
|
63
|
+
console.log(
|
|
64
|
+
"\u001b[36m\u001b[1m[Funkophile]\u001b[0m Current state keys:",
|
|
65
|
+
Object.keys(s)
|
|
185
66
|
);
|
|
186
67
|
|
|
187
|
-
|
|
68
|
+
let outputs;
|
|
69
|
+
try {
|
|
70
|
+
outputs = finalSelector(s);
|
|
71
|
+
console.log(
|
|
72
|
+
`\u001b[36m\u001b[1m[Funkophile]\u001b[0m Generated ${
|
|
73
|
+
Object.keys(outputs).length
|
|
74
|
+
} outputs`
|
|
75
|
+
);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(
|
|
78
|
+
"\u001b[31m\u001b[1m[Funkophile]\u001b[0m FATAL: Error in output selector chain ? :"
|
|
79
|
+
);
|
|
80
|
+
console.error(" Error:", error.message);
|
|
81
|
+
console.error(" Stack:", error.stack);
|
|
82
|
+
// Don't exit the process in watch mode, just log the error and continue
|
|
188
83
|
if (funkophileConfig.mode === "build") {
|
|
189
|
-
|
|
190
|
-
.then((files: string[]) => {
|
|
191
|
-
files.forEach((file) => {
|
|
192
|
-
dispatchUpsert(
|
|
193
|
-
store,
|
|
194
|
-
inputRuleKey,
|
|
195
|
-
file,
|
|
196
|
-
funkophileConfig.encodings
|
|
197
|
-
);
|
|
198
|
-
});
|
|
199
|
-
})
|
|
200
|
-
.then(() => {
|
|
201
|
-
fulfill();
|
|
202
|
-
});
|
|
203
|
-
} else if (funkophileConfig.mode === "watch") {
|
|
204
|
-
chokidar
|
|
205
|
-
.watch(p, {})
|
|
206
|
-
.on("error", (error) => {
|
|
207
|
-
logger.watchError(p);
|
|
208
|
-
})
|
|
209
|
-
.on("ready", () => {
|
|
210
|
-
logger.watchReady(p);
|
|
211
|
-
fulfill();
|
|
212
|
-
})
|
|
213
|
-
.on("add", (p) => {
|
|
214
|
-
logger.watchAdd(p);
|
|
215
|
-
dispatchUpsert(
|
|
216
|
-
store,
|
|
217
|
-
inputRuleKey,
|
|
218
|
-
p,
|
|
219
|
-
funkophileConfig.encodings
|
|
220
|
-
);
|
|
221
|
-
})
|
|
222
|
-
.on("change", (p) => {
|
|
223
|
-
logger.watchChange(p);
|
|
224
|
-
dispatchUpsert(
|
|
225
|
-
store,
|
|
226
|
-
inputRuleKey,
|
|
227
|
-
p,
|
|
228
|
-
funkophileConfig.encodings
|
|
229
|
-
);
|
|
230
|
-
})
|
|
231
|
-
.on("unlink", (p) => {
|
|
232
|
-
logger.watchUnlink(p);
|
|
233
|
-
store.dispatch({
|
|
234
|
-
type: REMOVE,
|
|
235
|
-
payload: {
|
|
236
|
-
key: inputRuleKey,
|
|
237
|
-
file: p,
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
})
|
|
241
|
-
.on("unlinkDir", (p) => {
|
|
242
|
-
logger.watchUnlink(p);
|
|
243
|
-
});
|
|
244
|
-
// .on('raw', (event, p, details) => { // internal
|
|
245
|
-
// log('Raw event info:', event, p, details);
|
|
246
|
-
// })
|
|
84
|
+
process.exit(1);
|
|
247
85
|
} else {
|
|
248
|
-
console.
|
|
249
|
-
|
|
86
|
+
console.log(
|
|
87
|
+
"\u001b[33m\u001b[1m[Funkophile]\u001b[0m Continuing to watch for changes despite error..."
|
|
250
88
|
);
|
|
251
|
-
|
|
89
|
+
// Reset previousState to empty to ensure we try processing again on next change
|
|
90
|
+
Object.keys(previousState).forEach((key) => {
|
|
91
|
+
delete previousState[key];
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
252
94
|
}
|
|
253
|
-
}
|
|
254
|
-
})
|
|
255
|
-
).then(function () {
|
|
256
|
-
// listen for changes to the store
|
|
257
|
-
store.subscribe(() => {
|
|
258
|
-
const s = store.getState();
|
|
259
|
-
|
|
260
|
-
logger.stateChange();
|
|
261
|
-
const outputs = finalSelector(s);
|
|
95
|
+
}
|
|
262
96
|
|
|
263
97
|
if (outputPromise.isPending()) {
|
|
264
|
-
console.log(
|
|
98
|
+
console.log(
|
|
99
|
+
"\u001b[33m\u001b[1m[Funkophile]\u001b[0m Cancelling previous write operation!"
|
|
100
|
+
);
|
|
265
101
|
outputPromise.cancel();
|
|
266
102
|
}
|
|
267
103
|
|
|
@@ -273,6 +109,9 @@ export default (funkophileConfig: {
|
|
|
273
109
|
if (!outputs[key]) {
|
|
274
110
|
const file = funkophileConfig.options.outFolder + "/" + key;
|
|
275
111
|
logger.removedFile(file);
|
|
112
|
+
console.log(
|
|
113
|
+
`\u001b[31m\u001b[1m[Funkophile]\u001b[0m Removing file: ${file}`
|
|
114
|
+
);
|
|
276
115
|
|
|
277
116
|
try {
|
|
278
117
|
fse.unlinkSync("./" + file);
|
|
@@ -280,14 +119,15 @@ export default (funkophileConfig: {
|
|
|
280
119
|
"./" + file.substring(0, file.lastIndexOf("/"))
|
|
281
120
|
);
|
|
282
121
|
} catch (ex) {
|
|
283
|
-
//
|
|
284
|
-
|
|
122
|
+
// Log error but don't fail the entire process
|
|
123
|
+
console.error(
|
|
124
|
+
`\u001b[31m\u001b[1m[Funkophile]\u001b[0m Error removing file ${file}:`,
|
|
125
|
+
ex.message
|
|
126
|
+
);
|
|
285
127
|
} finally {
|
|
286
|
-
|
|
287
|
-
|
|
128
|
+
delete previousState[key];
|
|
129
|
+
fulfill();
|
|
288
130
|
}
|
|
289
|
-
// delete previousState[key]
|
|
290
|
-
// fulfill()
|
|
291
131
|
} else {
|
|
292
132
|
if (outputs[key] !== previousState[key]) {
|
|
293
133
|
previousState[key] = outputs[key];
|
|
@@ -296,77 +136,140 @@ export default (funkophileConfig: {
|
|
|
296
136
|
"./" + funkophileConfig.options.outFolder + "/" + key;
|
|
297
137
|
const contents = outputs[key];
|
|
298
138
|
|
|
139
|
+
// console.log(`\u001b[32m\u001b[1m[Funkophile]\u001b[0m Writing file: ${relativeFilePath}`);
|
|
140
|
+
|
|
299
141
|
if (typeof contents === "function") {
|
|
300
142
|
logger.writingFunction(relativeFilePath);
|
|
301
143
|
contents((err, res) => {
|
|
302
|
-
|
|
303
|
-
|
|
144
|
+
if (err) {
|
|
145
|
+
logger.writingError(relativeFilePath, err.message);
|
|
146
|
+
fulfill(); // Still fulfill to continue processing other files
|
|
147
|
+
} else {
|
|
148
|
+
fse.outputFile(relativeFilePath, res, (err) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
logger.writingError(relativeFilePath, err.message);
|
|
151
|
+
fulfill(); // Still fulfill to continue processing other files
|
|
152
|
+
} else {
|
|
153
|
+
logger.writingString(relativeFilePath);
|
|
154
|
+
fulfill();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
304
158
|
});
|
|
305
159
|
} else if (typeof contents === "string") {
|
|
306
|
-
fse.outputFile(relativeFilePath, contents,
|
|
307
|
-
|
|
160
|
+
fse.outputFile(relativeFilePath, contents, (err) => {
|
|
161
|
+
if (err) {
|
|
162
|
+
logger.writingError(relativeFilePath, err.message);
|
|
163
|
+
fulfill();
|
|
164
|
+
} else {
|
|
165
|
+
logger.writingString(relativeFilePath);
|
|
166
|
+
fulfill();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
308
169
|
} else if (Buffer.isBuffer(contents)) {
|
|
309
|
-
fse.outputFile(relativeFilePath, contents,
|
|
310
|
-
|
|
170
|
+
fse.outputFile(relativeFilePath, contents, (err) => {
|
|
171
|
+
if (err) {
|
|
172
|
+
logger.writingError(relativeFilePath, err.message);
|
|
173
|
+
fulfill();
|
|
174
|
+
} else {
|
|
175
|
+
logger.writingString(relativeFilePath);
|
|
176
|
+
fulfill();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
311
179
|
} else if (Array.isArray(contents)) {
|
|
312
180
|
fse.outputFile(
|
|
313
181
|
relativeFilePath,
|
|
314
182
|
JSON.stringify(contents),
|
|
315
|
-
|
|
183
|
+
(err) => {
|
|
184
|
+
if (err) {
|
|
185
|
+
logger.writingError(relativeFilePath, err.message);
|
|
186
|
+
fulfill();
|
|
187
|
+
} else {
|
|
188
|
+
logger.writingString(relativeFilePath);
|
|
189
|
+
fulfill();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
316
192
|
);
|
|
317
|
-
logger.writingString(relativeFilePath);
|
|
318
193
|
} else if (typeof contents.then === "function") {
|
|
319
194
|
logger.writingPromise(relativeFilePath);
|
|
320
195
|
Promise.resolve(contents).then(
|
|
321
196
|
function (value) {
|
|
322
197
|
if (value instanceof Error) {
|
|
323
198
|
logger.writingError(relativeFilePath, value.message);
|
|
199
|
+
fulfill();
|
|
324
200
|
} else {
|
|
325
|
-
fse.outputFile(relativeFilePath, value,
|
|
326
|
-
|
|
201
|
+
fse.outputFile(relativeFilePath, value, (err) => {
|
|
202
|
+
if (err) {
|
|
203
|
+
logger.writingError(relativeFilePath, err.message);
|
|
204
|
+
fulfill();
|
|
205
|
+
} else {
|
|
206
|
+
logger.writingString(relativeFilePath);
|
|
207
|
+
fulfill();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
327
210
|
}
|
|
328
211
|
},
|
|
329
|
-
function (
|
|
330
|
-
|
|
212
|
+
function (error) {
|
|
213
|
+
logger.writingError(relativeFilePath, error.message);
|
|
214
|
+
fulfill();
|
|
331
215
|
}
|
|
332
216
|
);
|
|
333
217
|
} else {
|
|
334
218
|
console.log(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
typeof contents,
|
|
338
|
-
contents
|
|
219
|
+
`\u001b[33m\u001b[1m[Funkophile]\u001b[0m Unrecognized content type for ${relativeFilePath}, attempting to write:`,
|
|
220
|
+
typeof contents
|
|
339
221
|
);
|
|
340
|
-
fse.outputFile(relativeFilePath, contents,
|
|
341
|
-
|
|
222
|
+
fse.outputFile(relativeFilePath, contents, (err) => {
|
|
223
|
+
if (err) {
|
|
224
|
+
logger.writingError(relativeFilePath, err.message);
|
|
225
|
+
fulfill();
|
|
226
|
+
} else {
|
|
227
|
+
logger.writingString(relativeFilePath);
|
|
228
|
+
fulfill();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
342
231
|
}
|
|
343
232
|
} else {
|
|
233
|
+
// console.log(`\u001b[90m\u001b[1m[Funkophile]\u001b[0m Skipping unchanged file: ${key}`);
|
|
344
234
|
fulfill();
|
|
345
235
|
}
|
|
346
236
|
}
|
|
347
237
|
});
|
|
348
238
|
})
|
|
349
239
|
).then(() => {
|
|
240
|
+
// console.log('\u001b[36m\u001b[1m[Funkophile]\u001b[0m Cleaning empty folders...');
|
|
350
241
|
cleanEmptyFoldersRecursively(funkophileConfig.options.outFolder);
|
|
351
242
|
|
|
352
|
-
|
|
353
|
-
logger.done();
|
|
354
|
-
} else if (funkophileConfig.mode === "watch") {
|
|
355
|
-
logger.waiting();
|
|
356
|
-
} else {
|
|
357
|
-
console.error(
|
|
358
|
-
`The mode should be 'watch' or 'build', not "${funkophileConfig.mode}"`
|
|
359
|
-
);
|
|
360
|
-
process.exit(-1);
|
|
361
|
-
}
|
|
243
|
+
logDone(funkophileConfig, currentState)
|
|
362
244
|
});
|
|
245
|
+
// .catch((error) => {
|
|
246
|
+
// // console.error('\u001b[31m\u001b[1m[Funkophile]\u001b[0m Error during file operations:', error);
|
|
247
|
+
// });
|
|
363
248
|
});
|
|
364
249
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
250
|
+
console.log(
|
|
251
|
+
"\u001b[32m\u001b[1m[Funkophile]\u001b[0m Dispatching INITIALIZE action to enable processing..."
|
|
252
|
+
);
|
|
253
|
+
// Debug: log the current state after all files are processed
|
|
254
|
+
const currentState = store.getState();
|
|
255
|
+
console.log(
|
|
256
|
+
"\u001b[36m\u001b[1m[Funkophile]\u001b[0m Current state keys:",
|
|
257
|
+
Object.keys(currentState)
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
logInputKeys(funkophileConfig, currentState);
|
|
261
|
+
|
|
262
|
+
// Add a small delay to ensure all file operations are complete before initializing
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
// lastly, turn the store `on`.
|
|
265
|
+
// This is to prevent unecessary recomputations when initialy adding files to redux
|
|
266
|
+
store.dispatch({
|
|
267
|
+
type: INITIALIZE,
|
|
268
|
+
payload: true,
|
|
269
|
+
});
|
|
270
|
+
console.log(
|
|
271
|
+
"\u001b[32m\u001b[1m[Funkophile]\u001b[0m Store initialized. Ready to process changes!"
|
|
272
|
+
);
|
|
273
|
+
}, 100);
|
|
371
274
|
});
|
|
372
275
|
};
|
package/mocha.log
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
warning ../../package.json: No license field
|
|
2
|
+
$ mocha --loader=ts '**/*.test.ts'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Funkophile Core
|
|
6
|
+
makeStore
|
|
7
|
+
✔ should create a store with empty state until it is initialized
|
|
8
|
+
omit
|
|
9
|
+
✔ should remove the specified key from object
|
|
10
|
+
selectors
|
|
11
|
+
for the pages
|
|
12
|
+
✔ contentsOfFiles should concatenate all file contents
|
|
13
|
+
✔ contentOfFile should return first file content
|
|
14
|
+
✔ srcAndContentOfFile should return src and content
|
|
15
|
+
✔ srcAndContentOfFiles should return array of src/content pairs
|
|
16
|
+
for the posts
|
|
17
|
+
✔ contentsOfFiles should concatenate all file contents
|
|
18
|
+
✔ contentOfFile should return first file content
|
|
19
|
+
✔ srcAndContentOfFile should return src and content
|
|
20
|
+
✔ srcAndContentOfFiles should return array of src/content pairs
|
|
21
|
+
createInputSelectors
|
|
22
|
+
✔ should create a selector for each input key
|
|
23
|
+
✔ selectors should return the corresponding state slice
|
|
24
|
+
store actions
|
|
25
|
+
✔ should handle INITIALIZE action
|
|
26
|
+
✔ should handle UPSERT action
|
|
27
|
+
✔ should handle REMOVE action
|
|
28
|
+
edge cases
|
|
29
|
+
An input selector returned a different result when passed same arguments.
|
|
30
|
+
This means your output selector will likely run more frequently than intended.
|
|
31
|
+
Avoid returning a new reference inside your input selector, e.g.
|
|
32
|
+
`createSelector([state => state.todos.map(todo => todo.id)], todoIds => todoIds.length)` {
|
|
33
|
+
arguments: [Arguments] {
|
|
34
|
+
'0': { timestamp: 1752731765346, posts: [Object], pages: [Object] }
|
|
35
|
+
},
|
|
36
|
+
firstInputs: [ {} ],
|
|
37
|
+
secondInputs: [ {} ],
|
|
38
|
+
stack: 'Error\n' +
|
|
39
|
+
' at Object.runInputStabilityCheck (/Users/adam/Code/funkophile/node_modules/reselect/src/devModeChecks/inputStabilityCheck.ts:41:13)\n' +
|
|
40
|
+
' at dependenciesChecker (/Users/adam/Code/funkophile/node_modules/reselect/src/createSelectorCreator.ts:432:31)\n' +
|
|
41
|
+
' at memoized (/Users/adam/Code/funkophile/node_modules/reselect/src/weakMapMemoize.ts:228:21)\n' +
|
|
42
|
+
' at Context.<anonymous> (/Users/adam/Code/funkophile/index.test.ts:193:14)\n' +
|
|
43
|
+
' at callFn (/Users/adam/Code/funkophile/node_modules/mocha/lib/runnable.js:366:21)\n' +
|
|
44
|
+
' at Runnable.run (/Users/adam/Code/funkophile/node_modules/mocha/lib/runnable.js:354:5)\n' +
|
|
45
|
+
' at Runner.runTest (/Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:715:10)\n' +
|
|
46
|
+
' at /Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:838:12\n' +
|
|
47
|
+
' at next (/Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:630:14)\n' +
|
|
48
|
+
' at /Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:640:7\n' +
|
|
49
|
+
' at next (/Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:523:14)\n' +
|
|
50
|
+
' at Immediate._onImmediate (/Users/adam/Code/funkophile/node_modules/mocha/lib/runner.js:608:5)\n' +
|
|
51
|
+
' at process.processImmediate (node:internal/timers:478:21)'
|
|
52
|
+
}
|
|
53
|
+
✔ should handle empty file contents
|
|
54
|
+
✔ should handle Buffer content
|
|
55
|
+
✔ should throw on invalid selector
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
18 passing (10ms)
|
|
59
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "funkophile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"repository": "git@github.com:adamwong246/funkophile.git",
|
|
5
5
|
"author": "adam wong <adamwong246@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,33 +9,56 @@
|
|
|
9
9
|
"static site generator"
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
|
-
"
|
|
12
|
+
"test": "mocha",
|
|
13
|
+
"module": "./dist/esm/index.js",
|
|
13
14
|
"types": "./dist/esm/index.d.ts",
|
|
15
|
+
"mocha": {
|
|
16
|
+
"node-option": [
|
|
17
|
+
"import=tsx"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
14
20
|
"scripts": {
|
|
15
|
-
"
|
|
21
|
+
"build": "tsx build.ts",
|
|
22
|
+
"dev": "tsx --watch dev.ts",
|
|
23
|
+
"test": "mocha index.test.ts",
|
|
24
|
+
"typecheck": "tsc --noEmit"
|
|
16
25
|
},
|
|
17
26
|
"dependencies": {
|
|
18
27
|
"@tsconfig/node-lts-strictest-esm": "^18.12.1",
|
|
19
28
|
"@types/bluebird": "^3.5.42",
|
|
29
|
+
"@types/chai": "^5.2.2",
|
|
20
30
|
"@types/chokidar": "^2.1.7",
|
|
21
31
|
"@types/fs-extra": "^11.0.4",
|
|
32
|
+
"@types/mocha": "^10.0.10",
|
|
22
33
|
"@types/redux": "^3.6.0",
|
|
23
34
|
"@types/reselect": "^2.2.0",
|
|
24
35
|
"bluebird": "3.7.2",
|
|
36
|
+
"chai": "^5.2.1",
|
|
25
37
|
"chokidar": "^4.0.3",
|
|
38
|
+
"esbuild": "^0.25.0",
|
|
26
39
|
"fs-extra": "^11.3.0",
|
|
27
40
|
"glob": "11.0.1",
|
|
41
|
+
"mocha": "^11.7.1",
|
|
42
|
+
"mocha-junit-reporter": "^2.2.0",
|
|
43
|
+
"ts-node": "^10.9.1",
|
|
28
44
|
"tsc": "^2.0.4",
|
|
45
|
+
"tsx": "^4.20.6",
|
|
29
46
|
"typescript": "^5.8.2"
|
|
30
47
|
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"chokidar-cli": "^3.0.0"
|
|
50
|
+
},
|
|
31
51
|
"exports": {
|
|
32
52
|
".": {
|
|
33
|
-
"
|
|
34
|
-
|
|
53
|
+
"import": {
|
|
54
|
+
"default": "./dist/esm/index.js"
|
|
55
|
+
},
|
|
35
56
|
"default": "./dist/esm/index.js"
|
|
36
57
|
},
|
|
37
58
|
"./funkophileHelpers": {
|
|
38
|
-
"
|
|
59
|
+
"types": "./dist/esm/funkophileHelpers.d.ts",
|
|
60
|
+
"import": "./dist/esm/funkophileHelpers.js",
|
|
61
|
+
"default": "./dist/esm/funkophileHelpers.js"
|
|
39
62
|
}
|
|
40
63
|
}
|
|
41
64
|
}
|