@eventra_dev/eventra-cli 0.1.23 → 0.1.25
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/commands/check.js +40 -16
- package/dist/commands/sync.js +40 -38
- package/dist/commands/watch.js +87 -24
- package/dist/engine/engine.js +3 -1
- package/dist/utils/getVirtualFile.js +8 -0
- package/package.json +1 -1
- package/src/commands/check.ts +49 -29
- package/src/commands/sync.ts +48 -47
- package/src/commands/watch.ts +114 -27
- package/src/engine/engine.ts +3 -1
- package/src/utils/getVirtualFile.ts +5 -0
package/dist/commands/check.js
CHANGED
|
@@ -7,9 +7,11 @@ exports.check = check;
|
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const config_1 = require("../utils/config");
|
|
11
12
|
const engine_1 = require("../engine/engine");
|
|
12
13
|
const processFile_1 = require("../utils/processFile");
|
|
14
|
+
const getVirtualFile_1 = require("../utils/getVirtualFile");
|
|
13
15
|
async function check({ fix = false }) {
|
|
14
16
|
const config = await (0, config_1.loadConfig)();
|
|
15
17
|
if (!config) {
|
|
@@ -19,37 +21,59 @@ async function check({ fix = false }) {
|
|
|
19
21
|
console.log(chalk_1.default.blue("Checking events..."));
|
|
20
22
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
21
23
|
ignore: config.sync.exclude,
|
|
24
|
+
absolute: true,
|
|
22
25
|
});
|
|
23
26
|
const engine = new engine_1.EventraEngine(process.cwd());
|
|
27
|
+
const toScan = new Set();
|
|
28
|
+
const cache = new Map();
|
|
29
|
+
// COLLECT + CACHE
|
|
24
30
|
for (const file of files) {
|
|
25
31
|
try {
|
|
26
32
|
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
catch { }
|
|
33
|
+
if (raw.length > 2_000_000)
|
|
34
|
+
continue;
|
|
35
|
+
const parsed = (0, processFile_1.processFile)(file, raw);
|
|
36
|
+
cache.set(file, parsed);
|
|
37
|
+
toScan.add(file);
|
|
38
|
+
for (const dep of parsed.deps) {
|
|
39
|
+
toScan.add(path_1.default.resolve(dep));
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
catch
|
|
39
|
-
|
|
42
|
+
catch { }
|
|
43
|
+
}
|
|
44
|
+
// PRELOAD
|
|
45
|
+
for (const file of toScan) {
|
|
46
|
+
try {
|
|
47
|
+
let parsed = cache.get(file);
|
|
48
|
+
if (!parsed) {
|
|
49
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
50
|
+
parsed = (0, processFile_1.processFile)(file, raw);
|
|
51
|
+
cache.set(file, parsed);
|
|
52
|
+
}
|
|
53
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
54
|
+
engine.preloadFile(virtual, parsed.content);
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
}
|
|
58
|
+
// SCAN
|
|
59
|
+
for (const file of toScan) {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = cache.get(file);
|
|
62
|
+
if (!parsed)
|
|
63
|
+
continue;
|
|
64
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
65
|
+
engine.scanFile(virtual, parsed.content, config);
|
|
40
66
|
}
|
|
67
|
+
catch { }
|
|
41
68
|
}
|
|
69
|
+
// DIFF
|
|
42
70
|
const found = new Set(engine.getAllEvents());
|
|
43
71
|
const known = new Set(config.events ?? []);
|
|
44
72
|
const newEvents = [...found].filter(e => !known.has(e));
|
|
45
73
|
const removed = [...known].filter(e => !found.has(e));
|
|
46
74
|
// FIX MODE
|
|
47
75
|
if (fix) {
|
|
48
|
-
|
|
49
|
-
found.forEach(e => next.add(e));
|
|
50
|
-
newEvents.forEach(e => console.log(chalk_1.default.green(`+ ${e}`)));
|
|
51
|
-
removed.forEach(e => console.log(chalk_1.default.red(`- ${e}`)));
|
|
52
|
-
config.events = [...next].sort();
|
|
76
|
+
config.events = [...found].sort();
|
|
53
77
|
await (0, config_1.saveConfig)(config);
|
|
54
78
|
console.log(chalk_1.default.green(`\nSynced (${config.events.length} events)`));
|
|
55
79
|
return;
|
package/dist/commands/sync.js
CHANGED
|
@@ -7,9 +7,11 @@ exports.sync = sync;
|
|
|
7
7
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const config_1 = require("../utils/config");
|
|
11
12
|
const engine_1 = require("../engine/engine");
|
|
12
13
|
const processFile_1 = require("../utils/processFile");
|
|
14
|
+
const getVirtualFile_1 = require("../utils/getVirtualFile");
|
|
13
15
|
async function sync() {
|
|
14
16
|
const config = await (0, config_1.loadConfig)();
|
|
15
17
|
if (!config)
|
|
@@ -17,73 +19,73 @@ async function sync() {
|
|
|
17
19
|
console.log(chalk_1.default.blue("Scanning..."));
|
|
18
20
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
19
21
|
ignore: config.sync.exclude,
|
|
22
|
+
absolute: true,
|
|
20
23
|
});
|
|
21
24
|
if (!files.length) {
|
|
22
25
|
console.log(chalk_1.default.yellow("No files found"));
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
28
|
const engine = new engine_1.EventraEngine(process.cwd());
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
+
const toScan = new Set();
|
|
30
|
+
const cache = new Map();
|
|
31
|
+
// COLLECT + CACHE
|
|
29
32
|
for (const file of files) {
|
|
30
33
|
try {
|
|
31
34
|
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const { content: depContent, virtualFile: depVirtual, } = (0, processFile_1.processFile)(dep, depRaw);
|
|
40
|
-
engine["ts"].updateFile(depVirtual, depContent);
|
|
41
|
-
fileCache.push({ file: depVirtual, content: depContent });
|
|
42
|
-
}
|
|
43
|
-
catch { }
|
|
35
|
+
if (raw.length > 2_000_000)
|
|
36
|
+
continue;
|
|
37
|
+
const parsed = (0, processFile_1.processFile)(file, raw);
|
|
38
|
+
cache.set(file, parsed);
|
|
39
|
+
toScan.add(file);
|
|
40
|
+
for (const dep of parsed.deps) {
|
|
41
|
+
toScan.add(path_1.default.resolve(dep));
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
catch { }
|
|
47
45
|
}
|
|
48
|
-
|
|
46
|
+
// PRELOAD
|
|
47
|
+
for (const file of toScan) {
|
|
49
48
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
let parsed = cache.get(file);
|
|
50
|
+
if (!parsed) {
|
|
51
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
52
|
+
parsed = (0, processFile_1.processFile)(file, raw);
|
|
53
|
+
cache.set(file, parsed);
|
|
53
54
|
}
|
|
55
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
56
|
+
engine.preloadFile(virtual, parsed.content);
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
}
|
|
60
|
+
// SCAN
|
|
61
|
+
const detectedFn = new Set();
|
|
62
|
+
const detectedCmp = new Map();
|
|
63
|
+
for (const file of toScan) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = cache.get(file);
|
|
66
|
+
if (!parsed)
|
|
67
|
+
continue;
|
|
68
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
69
|
+
const res = engine.scanFile(virtual, parsed.content, config);
|
|
70
|
+
res.detectedFunctionWrappers.forEach(w => detectedFn.add(w));
|
|
54
71
|
for (const [k, v] of res.detectedComponentWrappers) {
|
|
55
72
|
detectedCmp.set(k, v);
|
|
56
73
|
}
|
|
57
74
|
}
|
|
58
|
-
catch
|
|
59
|
-
console.log(chalk_1.default.
|
|
60
|
-
if (err instanceof Error) {
|
|
61
|
-
console.log(chalk_1.default.gray(err.message));
|
|
62
|
-
}
|
|
75
|
+
catch {
|
|
76
|
+
console.log(chalk_1.default.gray(`skip: ${file}`));
|
|
63
77
|
}
|
|
64
78
|
}
|
|
65
|
-
//
|
|
79
|
+
// RESULT
|
|
66
80
|
const events = engine.getAllEvents().sort();
|
|
67
|
-
|
|
68
|
-
events.forEach(e => mergedEvents.add(e));
|
|
69
|
-
config.events = [...mergedEvents].sort();
|
|
70
|
-
// FUNCTION WRAPPERS
|
|
81
|
+
config.events = events;
|
|
71
82
|
config.functionWrappers = mergeUnique(config.functionWrappers ?? [], [...detectedFn].map(name => ({ name })), w => w.name);
|
|
72
|
-
// COMPONENT WRAPPERS
|
|
73
83
|
config.wrappers = mergeUnique(config.wrappers ?? [], [...detectedCmp.entries()].map(([name, prop]) => ({
|
|
74
84
|
name,
|
|
75
85
|
prop,
|
|
76
86
|
})), w => `${w.name}:${w.prop}`);
|
|
77
87
|
await (0, config_1.saveConfig)(config);
|
|
78
88
|
console.log(chalk_1.default.green(`Found ${events.length} events`));
|
|
79
|
-
if (detectedFn.size) {
|
|
80
|
-
console.log(chalk_1.default.blue("\nDetected function wrappers:"));
|
|
81
|
-
detectedFn.forEach(w => console.log(chalk_1.default.gray(`• ${w}`)));
|
|
82
|
-
}
|
|
83
|
-
if (detectedCmp.size) {
|
|
84
|
-
console.log(chalk_1.default.blue("\nDetected component wrappers:"));
|
|
85
|
-
detectedCmp.forEach((prop, name) => console.log(chalk_1.default.gray(`• ${name} (${prop})`)));
|
|
86
|
-
}
|
|
87
89
|
}
|
|
88
90
|
// HELPERS
|
|
89
91
|
function mergeUnique(a, b, key) {
|
package/dist/commands/watch.js
CHANGED
|
@@ -12,6 +12,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
12
12
|
const config_1 = require("../utils/config");
|
|
13
13
|
const engine_1 = require("../engine/engine");
|
|
14
14
|
const processFile_1 = require("../utils/processFile");
|
|
15
|
+
const getVirtualFile_1 = require("../utils/getVirtualFile");
|
|
15
16
|
async function watch() {
|
|
16
17
|
const config = await (0, config_1.loadConfig)();
|
|
17
18
|
if (!config)
|
|
@@ -19,56 +20,102 @@ async function watch() {
|
|
|
19
20
|
console.log(chalk_1.default.blue("Watching...\n"));
|
|
20
21
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
21
22
|
ignore: config.sync.exclude,
|
|
23
|
+
absolute: true,
|
|
22
24
|
});
|
|
23
25
|
const engine = new engine_1.EventraEngine(process.cwd());
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
+
const watchedDeps = new Set();
|
|
27
|
+
const cache = new Map();
|
|
28
|
+
const toScan = new Set();
|
|
29
|
+
// COLLECT + CACHE
|
|
26
30
|
for (const file of files) {
|
|
27
31
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
33
|
+
if (raw.length > 2_000_000)
|
|
34
|
+
continue; // safeguard
|
|
35
|
+
const parsed = (0, processFile_1.processFile)(file, raw);
|
|
36
|
+
cache.set(file, parsed);
|
|
37
|
+
toScan.add(file);
|
|
38
|
+
for (const dep of parsed.deps) {
|
|
39
|
+
const absDep = path_1.default.resolve(dep);
|
|
40
|
+
toScan.add(absDep);
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
catch {
|
|
34
44
|
console.log(chalk_1.default.gray(`skip: ${file}`));
|
|
35
45
|
}
|
|
36
46
|
}
|
|
47
|
+
// PRELOAD (TS PROGRAM)
|
|
48
|
+
for (const file of toScan) {
|
|
49
|
+
try {
|
|
50
|
+
let parsed = cache.get(file);
|
|
51
|
+
if (!parsed) {
|
|
52
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
53
|
+
parsed = (0, processFile_1.processFile)(file, raw);
|
|
54
|
+
}
|
|
55
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
56
|
+
engine.preloadFile?.(virtual, parsed.content);
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
}
|
|
37
60
|
// INITIAL SCAN
|
|
38
|
-
for (const
|
|
39
|
-
|
|
61
|
+
for (const file of toScan) {
|
|
62
|
+
try {
|
|
63
|
+
let parsed = cache.get(file);
|
|
64
|
+
if (!parsed) {
|
|
65
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
66
|
+
parsed = (0, processFile_1.processFile)(file, raw);
|
|
67
|
+
}
|
|
68
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
|
|
69
|
+
engine.scanFile(virtual, parsed.content, config);
|
|
70
|
+
for (const dep of parsed.deps) {
|
|
71
|
+
watchedDeps.add(path_1.default.resolve(dep));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
40
75
|
}
|
|
41
76
|
config.events = engine.getAllEvents().sort();
|
|
42
77
|
await (0, config_1.saveConfig)(config);
|
|
43
78
|
console.log(chalk_1.default.gray(`Initial: ${config.events.length} events\n`));
|
|
79
|
+
// QUEUE
|
|
44
80
|
let locked = false;
|
|
45
|
-
|
|
81
|
+
const queued = new Map();
|
|
46
82
|
let timer = null;
|
|
47
83
|
const run = async () => {
|
|
48
84
|
if (locked)
|
|
49
85
|
return;
|
|
50
86
|
locked = true;
|
|
51
|
-
const batch = [...queued];
|
|
87
|
+
const batch = [...queued.values()];
|
|
52
88
|
queued.clear();
|
|
53
|
-
for (const
|
|
89
|
+
for (const item of batch) {
|
|
54
90
|
try {
|
|
55
|
-
const raw = await promises_1.default.readFile(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
91
|
+
const raw = await promises_1.default.readFile(item.real, "utf-8");
|
|
92
|
+
if (raw.length > 2_000_000)
|
|
93
|
+
continue;
|
|
94
|
+
const parsed = (0, processFile_1.processFile)(item.real, raw);
|
|
95
|
+
cache.set(item.real, parsed);
|
|
96
|
+
engine.updateFile(item.virtual, parsed.content, config);
|
|
97
|
+
// DEPS
|
|
98
|
+
for (const dep of parsed.deps) {
|
|
99
|
+
const absDep = path_1.default.resolve(dep);
|
|
100
|
+
if (!watchedDeps.has(absDep)) {
|
|
101
|
+
watcher.add(absDep);
|
|
102
|
+
watchedDeps.add(absDep);
|
|
103
|
+
}
|
|
60
104
|
try {
|
|
61
|
-
const depRaw = await promises_1.default.readFile(
|
|
62
|
-
const
|
|
63
|
-
|
|
105
|
+
const depRaw = await promises_1.default.readFile(absDep, "utf-8");
|
|
106
|
+
const depParsed = (0, processFile_1.processFile)(absDep, depRaw);
|
|
107
|
+
cache.set(absDep, depParsed);
|
|
108
|
+
const depVirtual = (0, getVirtualFile_1.getVirtualFile)(absDep);
|
|
109
|
+
engine.updateFile(depVirtual, depParsed.content, config);
|
|
64
110
|
}
|
|
65
111
|
catch { }
|
|
66
112
|
}
|
|
67
113
|
}
|
|
68
114
|
catch {
|
|
69
|
-
console.log(chalk_1.default.gray(`skip: ${
|
|
115
|
+
console.log(chalk_1.default.gray(`skip: ${item.real}`));
|
|
70
116
|
}
|
|
71
117
|
}
|
|
118
|
+
// DIFF EVENTS
|
|
72
119
|
const next = engine.getAllEvents();
|
|
73
120
|
const added = next.filter(e => !config.events.includes(e));
|
|
74
121
|
const removed = config.events.filter(e => !next.includes(e));
|
|
@@ -81,15 +128,21 @@ async function watch() {
|
|
|
81
128
|
}
|
|
82
129
|
locked = false;
|
|
83
130
|
};
|
|
131
|
+
// SCHEDULER
|
|
84
132
|
const schedule = (file) => {
|
|
85
|
-
|
|
133
|
+
const abs = path_1.default.resolve(file);
|
|
134
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(abs);
|
|
135
|
+
console.log(chalk_1.default.gray(`change: ${file}`));
|
|
136
|
+
queued.set(abs, { real: abs, virtual });
|
|
86
137
|
if (timer)
|
|
87
138
|
clearTimeout(timer);
|
|
88
|
-
timer = setTimeout(run,
|
|
139
|
+
timer = setTimeout(run, 250);
|
|
89
140
|
};
|
|
141
|
+
// WATCHER
|
|
90
142
|
const watcher = chokidar_1.default.watch(config.sync.include, {
|
|
91
143
|
ignored: config.sync.exclude,
|
|
92
144
|
ignoreInitial: true,
|
|
145
|
+
persistent: true,
|
|
93
146
|
});
|
|
94
147
|
watcher
|
|
95
148
|
.on("add", schedule)
|
|
@@ -97,13 +150,23 @@ async function watch() {
|
|
|
97
150
|
.on("unlink", async (file) => {
|
|
98
151
|
const abs = path_1.default.resolve(file);
|
|
99
152
|
console.log(chalk_1.default.gray(`remove: ${file}`));
|
|
100
|
-
const
|
|
101
|
-
engine.removeFile(
|
|
153
|
+
const virtual = (0, getVirtualFile_1.getVirtualFile)(abs);
|
|
154
|
+
engine.removeFile(virtual, config);
|
|
155
|
+
watcher.unwatch(abs);
|
|
156
|
+
watchedDeps.delete(abs);
|
|
102
157
|
const next = engine.getAllEvents();
|
|
103
158
|
config.events = next.sort();
|
|
104
159
|
await (0, config_1.saveConfig)(config);
|
|
105
160
|
console.log(chalk_1.default.gray(`Total: ${config.events.length} events\n`));
|
|
161
|
+
})
|
|
162
|
+
.on("error", (err) => {
|
|
163
|
+
console.error(chalk_1.default.red("Watcher error:"), err);
|
|
106
164
|
});
|
|
165
|
+
// WATCH DEPS
|
|
166
|
+
for (const dep of watchedDeps) {
|
|
167
|
+
watcher.add(dep);
|
|
168
|
+
}
|
|
169
|
+
// EXIT
|
|
107
170
|
process.on("SIGINT", async () => {
|
|
108
171
|
console.log(chalk_1.default.yellow("\nStopping watcher..."));
|
|
109
172
|
await watcher.close();
|
package/dist/engine/engine.js
CHANGED
|
@@ -16,6 +16,9 @@ class EventraEngine {
|
|
|
16
16
|
const { baseUrl, paths } = this.ts.getModuleResolutionConfig();
|
|
17
17
|
this.graph = new graph_1.DependencyGraph(baseUrl, paths);
|
|
18
18
|
}
|
|
19
|
+
preloadFile(file, content) {
|
|
20
|
+
this.ts.updateFile(file, content);
|
|
21
|
+
}
|
|
19
22
|
// SCAN FILE
|
|
20
23
|
scanFile(file, content, config) {
|
|
21
24
|
const h = (0, hash_1.hash)(content);
|
|
@@ -45,7 +48,6 @@ class EventraEngine {
|
|
|
45
48
|
}
|
|
46
49
|
// UPDATE FILE (incremental)
|
|
47
50
|
updateFile(file, content, config) {
|
|
48
|
-
// сначала обновляем текущий файл
|
|
49
51
|
const res = this.scanFile(file, content, config);
|
|
50
52
|
const dependents = this.graph.getAllDependentsDeep(file);
|
|
51
53
|
for (const dep of dependents) {
|
package/package.json
CHANGED
package/src/commands/check.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import fg from "fast-glob";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
4
5
|
|
|
5
6
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
6
7
|
import { EventraEngine } from "../engine/engine";
|
|
7
8
|
import { processFile } from "../utils/processFile";
|
|
9
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
8
10
|
|
|
9
11
|
export async function check({ fix = false }: { fix?: boolean }) {
|
|
10
12
|
const config = await loadConfig();
|
|
@@ -18,36 +20,68 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
18
20
|
|
|
19
21
|
const files = await fg(config.sync.include, {
|
|
20
22
|
ignore: config.sync.exclude,
|
|
23
|
+
absolute: true,
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
const engine = new EventraEngine(process.cwd());
|
|
24
27
|
|
|
28
|
+
const toScan = new Set<string>();
|
|
29
|
+
|
|
30
|
+
const cache = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{ content: string; deps: string[] }
|
|
33
|
+
>();
|
|
34
|
+
|
|
35
|
+
// COLLECT + CACHE
|
|
25
36
|
for (const file of files) {
|
|
26
37
|
try {
|
|
27
38
|
const raw = await fs.readFile(file, "utf-8");
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
if (raw.length > 2_000_000) continue;
|
|
41
|
+
|
|
42
|
+
const parsed = processFile(file, raw);
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
cache.set(file, parsed);
|
|
45
|
+
toScan.add(file);
|
|
32
46
|
|
|
33
|
-
for (const dep of deps) {
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
for (const dep of parsed.deps) {
|
|
48
|
+
toScan.add(path.resolve(dep));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
// PRELOAD
|
|
55
|
+
for (const file of toScan) {
|
|
56
|
+
try {
|
|
57
|
+
let parsed = cache.get(file);
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
if (!parsed) {
|
|
60
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
61
|
+
parsed = processFile(file, raw);
|
|
62
|
+
cache.set(file, parsed);
|
|
44
63
|
}
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
const virtual = getVirtualFile(file);
|
|
66
|
+
engine.preloadFile(virtual, parsed.content);
|
|
67
|
+
|
|
68
|
+
} catch {}
|
|
49
69
|
}
|
|
50
70
|
|
|
71
|
+
// SCAN
|
|
72
|
+
for (const file of toScan) {
|
|
73
|
+
try {
|
|
74
|
+
const parsed = cache.get(file);
|
|
75
|
+
if (!parsed) continue;
|
|
76
|
+
|
|
77
|
+
const virtual = getVirtualFile(file);
|
|
78
|
+
|
|
79
|
+
engine.scanFile(virtual, parsed.content, config);
|
|
80
|
+
|
|
81
|
+
} catch {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// DIFF
|
|
51
85
|
const found = new Set(engine.getAllEvents());
|
|
52
86
|
const known = new Set(config.events ?? []);
|
|
53
87
|
|
|
@@ -56,26 +90,12 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
56
90
|
|
|
57
91
|
// FIX MODE
|
|
58
92
|
if (fix) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
found.forEach(e => next.add(e));
|
|
62
|
-
|
|
63
|
-
newEvents.forEach(e =>
|
|
64
|
-
console.log(chalk.green(`+ ${e}`))
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
removed.forEach(e =>
|
|
68
|
-
console.log(chalk.red(`- ${e}`))
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
config.events = [...next].sort();
|
|
72
|
-
|
|
93
|
+
config.events = [...found].sort();
|
|
73
94
|
await saveConfig(config);
|
|
74
95
|
|
|
75
96
|
console.log(
|
|
76
97
|
chalk.green(`\nSynced (${config.events.length} events)`)
|
|
77
98
|
);
|
|
78
|
-
|
|
79
99
|
return;
|
|
80
100
|
}
|
|
81
101
|
|
package/src/commands/sync.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fg from "fast-glob";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import path from "path";
|
|
4
5
|
|
|
5
6
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
6
7
|
import { EventraEngine } from "../engine/engine";
|
|
7
8
|
import { processFile } from "../utils/processFile";
|
|
9
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
8
10
|
|
|
9
11
|
export async function sync() {
|
|
10
12
|
const config = await loadConfig();
|
|
@@ -14,6 +16,7 @@ export async function sync() {
|
|
|
14
16
|
|
|
15
17
|
const files = await fg(config.sync.include, {
|
|
16
18
|
ignore: config.sync.exclude,
|
|
19
|
+
absolute: true,
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
if (!files.length) {
|
|
@@ -23,76 +26,86 @@ export async function sync() {
|
|
|
23
26
|
|
|
24
27
|
const engine = new EventraEngine(process.cwd());
|
|
25
28
|
|
|
26
|
-
const
|
|
27
|
-
const detectedCmp = new Map<string, string>();
|
|
29
|
+
const toScan = new Set<string>();
|
|
28
30
|
|
|
29
|
-
const
|
|
31
|
+
const cache = new Map<
|
|
32
|
+
string,
|
|
33
|
+
{ content: string; deps: string[] }
|
|
34
|
+
>();
|
|
30
35
|
|
|
36
|
+
// COLLECT + CACHE
|
|
31
37
|
for (const file of files) {
|
|
32
38
|
try {
|
|
33
39
|
const raw = await fs.readFile(file, "utf-8");
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
if (raw.length > 2_000_000) continue;
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
fileCache.push({ file: virtualFile, content });
|
|
43
|
+
const parsed = processFile(file, raw);
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const depRaw = await fs.readFile(dep, "utf-8");
|
|
45
|
+
cache.set(file, parsed);
|
|
46
|
+
toScan.add(file);
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} = processFile(dep, depRaw);
|
|
48
|
+
for (const dep of parsed.deps) {
|
|
49
|
+
toScan.add(path.resolve(dep));
|
|
50
|
+
}
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
// PRELOAD
|
|
56
|
+
for (const file of toScan) {
|
|
57
|
+
try {
|
|
58
|
+
let parsed = cache.get(file);
|
|
59
|
+
|
|
60
|
+
if (!parsed) {
|
|
61
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
62
|
+
parsed = processFile(file, raw);
|
|
63
|
+
cache.set(file, parsed);
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
const virtual = getVirtualFile(file);
|
|
67
|
+
engine.preloadFile(virtual, parsed.content);
|
|
68
|
+
|
|
56
69
|
} catch {}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
|
|
72
|
+
// SCAN
|
|
73
|
+
const detectedFn = new Set<string>();
|
|
74
|
+
const detectedCmp = new Map<string, string>();
|
|
75
|
+
|
|
76
|
+
for (const file of toScan) {
|
|
60
77
|
try {
|
|
61
|
-
const
|
|
78
|
+
const parsed = cache.get(file);
|
|
79
|
+
if (!parsed) continue;
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
const virtual = getVirtualFile(file);
|
|
82
|
+
|
|
83
|
+
const res = engine.scanFile(virtual, parsed.content, config);
|
|
84
|
+
|
|
85
|
+
res.detectedFunctionWrappers.forEach(w =>
|
|
86
|
+
detectedFn.add(w)
|
|
87
|
+
);
|
|
66
88
|
|
|
67
89
|
for (const [k, v] of res.detectedComponentWrappers) {
|
|
68
90
|
detectedCmp.set(k, v);
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
} catch
|
|
72
|
-
console.log(chalk.
|
|
73
|
-
|
|
74
|
-
if (err instanceof Error) {
|
|
75
|
-
console.log(chalk.gray(err.message));
|
|
76
|
-
}
|
|
93
|
+
} catch {
|
|
94
|
+
console.log(chalk.gray(`skip: ${file}`));
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
//
|
|
98
|
+
// RESULT
|
|
81
99
|
const events = engine.getAllEvents().sort();
|
|
82
100
|
|
|
83
|
-
|
|
84
|
-
events.forEach(e => mergedEvents.add(e));
|
|
101
|
+
config.events = events;
|
|
85
102
|
|
|
86
|
-
config.events = [...mergedEvents].sort();
|
|
87
|
-
|
|
88
|
-
// FUNCTION WRAPPERS
|
|
89
103
|
config.functionWrappers = mergeUnique(
|
|
90
104
|
config.functionWrappers ?? [],
|
|
91
105
|
[...detectedFn].map(name => ({ name })),
|
|
92
106
|
w => w.name
|
|
93
107
|
);
|
|
94
108
|
|
|
95
|
-
// COMPONENT WRAPPERS
|
|
96
109
|
config.wrappers = mergeUnique(
|
|
97
110
|
config.wrappers ?? [],
|
|
98
111
|
[...detectedCmp.entries()].map(([name, prop]) => ({
|
|
@@ -105,18 +118,6 @@ export async function sync() {
|
|
|
105
118
|
await saveConfig(config);
|
|
106
119
|
|
|
107
120
|
console.log(chalk.green(`Found ${events.length} events`));
|
|
108
|
-
|
|
109
|
-
if (detectedFn.size) {
|
|
110
|
-
console.log(chalk.blue("\nDetected function wrappers:"));
|
|
111
|
-
detectedFn.forEach(w => console.log(chalk.gray(`• ${w}`)));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (detectedCmp.size) {
|
|
115
|
-
console.log(chalk.blue("\nDetected component wrappers:"));
|
|
116
|
-
detectedCmp.forEach((prop, name) =>
|
|
117
|
-
console.log(chalk.gray(`• ${name} (${prop})`))
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// HELPERS
|
package/src/commands/watch.ts
CHANGED
|
@@ -7,6 +7,12 @@ import path from "path";
|
|
|
7
7
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
8
8
|
import { EventraEngine } from "../engine/engine";
|
|
9
9
|
import { processFile } from "../utils/processFile";
|
|
10
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
11
|
+
|
|
12
|
+
type QueueItem = {
|
|
13
|
+
real: string;
|
|
14
|
+
virtual: string;
|
|
15
|
+
};
|
|
10
16
|
|
|
11
17
|
export async function watch() {
|
|
12
18
|
const config = await loadConfig();
|
|
@@ -16,27 +22,76 @@ export async function watch() {
|
|
|
16
22
|
|
|
17
23
|
const files = await fg(config.sync.include, {
|
|
18
24
|
ignore: config.sync.exclude,
|
|
25
|
+
absolute: true,
|
|
19
26
|
});
|
|
20
27
|
|
|
21
28
|
const engine = new EventraEngine(process.cwd());
|
|
29
|
+
const watchedDeps = new Set<string>();
|
|
30
|
+
const cache = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{ content: string; deps: string[] }
|
|
33
|
+
>();
|
|
22
34
|
|
|
23
|
-
const
|
|
35
|
+
const toScan = new Set<string>();
|
|
24
36
|
|
|
25
|
-
//
|
|
37
|
+
// COLLECT + CACHE
|
|
26
38
|
for (const file of files) {
|
|
27
39
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
41
|
+
|
|
42
|
+
if (raw.length > 2_000_000) continue; // safeguard
|
|
43
|
+
|
|
44
|
+
const parsed = processFile(file, raw);
|
|
45
|
+
|
|
46
|
+
cache.set(file, parsed);
|
|
47
|
+
toScan.add(file);
|
|
48
|
+
|
|
49
|
+
for (const dep of parsed.deps) {
|
|
50
|
+
const absDep = path.resolve(dep);
|
|
51
|
+
toScan.add(absDep);
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
} catch {
|
|
33
55
|
console.log(chalk.gray(`skip: ${file}`));
|
|
34
56
|
}
|
|
35
57
|
}
|
|
36
58
|
|
|
59
|
+
// PRELOAD (TS PROGRAM)
|
|
60
|
+
for (const file of toScan) {
|
|
61
|
+
try {
|
|
62
|
+
let parsed = cache.get(file);
|
|
63
|
+
|
|
64
|
+
if (!parsed) {
|
|
65
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
66
|
+
parsed = processFile(file, raw);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const virtual = getVirtualFile(file);
|
|
70
|
+
|
|
71
|
+
engine.preloadFile?.(virtual, parsed.content);
|
|
72
|
+
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
|
|
37
76
|
// INITIAL SCAN
|
|
38
|
-
for (const
|
|
39
|
-
|
|
77
|
+
for (const file of toScan) {
|
|
78
|
+
try {
|
|
79
|
+
let parsed = cache.get(file);
|
|
80
|
+
|
|
81
|
+
if (!parsed) {
|
|
82
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
83
|
+
parsed = processFile(file, raw);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const virtual = getVirtualFile(file);
|
|
87
|
+
|
|
88
|
+
engine.scanFile(virtual, parsed.content, config);
|
|
89
|
+
|
|
90
|
+
for (const dep of parsed.deps) {
|
|
91
|
+
watchedDeps.add(path.resolve(dep));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
} catch {}
|
|
40
95
|
}
|
|
41
96
|
|
|
42
97
|
config.events = engine.getAllEvents().sort();
|
|
@@ -46,45 +101,57 @@ export async function watch() {
|
|
|
46
101
|
chalk.gray(`Initial: ${config.events.length} events\n`)
|
|
47
102
|
);
|
|
48
103
|
|
|
104
|
+
// QUEUE
|
|
49
105
|
let locked = false;
|
|
50
|
-
|
|
106
|
+
const queued = new Map<string, QueueItem>();
|
|
51
107
|
let timer: NodeJS.Timeout | null = null;
|
|
52
108
|
|
|
53
109
|
const run = async () => {
|
|
54
110
|
if (locked) return;
|
|
55
111
|
locked = true;
|
|
56
112
|
|
|
57
|
-
const batch = [...queued];
|
|
113
|
+
const batch = [...queued.values()];
|
|
58
114
|
queued.clear();
|
|
59
115
|
|
|
60
|
-
for (const
|
|
116
|
+
for (const item of batch) {
|
|
61
117
|
try {
|
|
62
|
-
const raw = await fs.readFile(
|
|
118
|
+
const raw = await fs.readFile(item.real, "utf-8");
|
|
63
119
|
|
|
64
|
-
|
|
120
|
+
if (raw.length > 2_000_000) continue;
|
|
65
121
|
|
|
66
|
-
|
|
122
|
+
const parsed = processFile(item.real, raw);
|
|
123
|
+
|
|
124
|
+
cache.set(item.real, parsed);
|
|
125
|
+
|
|
126
|
+
engine.updateFile(item.virtual, parsed.content, config);
|
|
127
|
+
|
|
128
|
+
// DEPS
|
|
129
|
+
for (const dep of parsed.deps) {
|
|
130
|
+
const absDep = path.resolve(dep);
|
|
131
|
+
|
|
132
|
+
if (!watchedDeps.has(absDep)) {
|
|
133
|
+
watcher.add(absDep);
|
|
134
|
+
watchedDeps.add(absDep);
|
|
135
|
+
}
|
|
67
136
|
|
|
68
|
-
// deps
|
|
69
|
-
for (const dep of deps) {
|
|
70
137
|
try {
|
|
71
|
-
const depRaw = await fs.readFile(
|
|
138
|
+
const depRaw = await fs.readFile(absDep, "utf-8");
|
|
139
|
+
const depParsed = processFile(absDep, depRaw);
|
|
72
140
|
|
|
73
|
-
|
|
74
|
-
content: depContent,
|
|
75
|
-
virtualFile: depVirtual,
|
|
76
|
-
} = processFile(dep, depRaw);
|
|
141
|
+
cache.set(absDep, depParsed);
|
|
77
142
|
|
|
78
|
-
|
|
143
|
+
const depVirtual = getVirtualFile(absDep);
|
|
79
144
|
|
|
145
|
+
engine.updateFile(depVirtual, depParsed.content, config);
|
|
80
146
|
} catch {}
|
|
81
147
|
}
|
|
82
148
|
|
|
83
149
|
} catch {
|
|
84
|
-
console.log(chalk.gray(`skip: ${
|
|
150
|
+
console.log(chalk.gray(`skip: ${item.real}`));
|
|
85
151
|
}
|
|
86
152
|
}
|
|
87
153
|
|
|
154
|
+
// DIFF EVENTS
|
|
88
155
|
const next = engine.getAllEvents();
|
|
89
156
|
|
|
90
157
|
const added = next.filter(e => !config.events.includes(e));
|
|
@@ -105,16 +172,24 @@ export async function watch() {
|
|
|
105
172
|
locked = false;
|
|
106
173
|
};
|
|
107
174
|
|
|
175
|
+
// SCHEDULER
|
|
108
176
|
const schedule = (file: string) => {
|
|
109
|
-
|
|
177
|
+
const abs = path.resolve(file);
|
|
178
|
+
const virtual = getVirtualFile(abs);
|
|
179
|
+
|
|
180
|
+
console.log(chalk.gray(`change: ${file}`));
|
|
181
|
+
|
|
182
|
+
queued.set(abs, { real: abs, virtual });
|
|
110
183
|
|
|
111
184
|
if (timer) clearTimeout(timer);
|
|
112
|
-
timer = setTimeout(run,
|
|
185
|
+
timer = setTimeout(run, 250);
|
|
113
186
|
};
|
|
114
187
|
|
|
188
|
+
// WATCHER
|
|
115
189
|
const watcher = chokidar.watch(config.sync.include, {
|
|
116
190
|
ignored: config.sync.exclude,
|
|
117
191
|
ignoreInitial: true,
|
|
192
|
+
persistent: true,
|
|
118
193
|
});
|
|
119
194
|
|
|
120
195
|
watcher
|
|
@@ -125,9 +200,12 @@ export async function watch() {
|
|
|
125
200
|
|
|
126
201
|
console.log(chalk.gray(`remove: ${file}`));
|
|
127
202
|
|
|
128
|
-
const
|
|
203
|
+
const virtual = getVirtualFile(abs);
|
|
204
|
+
|
|
205
|
+
engine.removeFile(virtual, config);
|
|
129
206
|
|
|
130
|
-
|
|
207
|
+
watcher.unwatch(abs);
|
|
208
|
+
watchedDeps.delete(abs);
|
|
131
209
|
|
|
132
210
|
const next = engine.getAllEvents();
|
|
133
211
|
|
|
@@ -137,8 +215,17 @@ export async function watch() {
|
|
|
137
215
|
console.log(
|
|
138
216
|
chalk.gray(`Total: ${config.events.length} events\n`)
|
|
139
217
|
);
|
|
218
|
+
})
|
|
219
|
+
.on("error", (err) => {
|
|
220
|
+
console.error(chalk.red("Watcher error:"), err);
|
|
140
221
|
});
|
|
141
222
|
|
|
223
|
+
// WATCH DEPS
|
|
224
|
+
for (const dep of watchedDeps) {
|
|
225
|
+
watcher.add(dep);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// EXIT
|
|
142
229
|
process.on("SIGINT", async () => {
|
|
143
230
|
console.log(chalk.yellow("\nStopping watcher..."));
|
|
144
231
|
await watcher.close();
|
package/src/engine/engine.ts
CHANGED
|
@@ -18,6 +18,9 @@ export class EventraEngine {
|
|
|
18
18
|
const { baseUrl, paths } = this.ts.getModuleResolutionConfig();
|
|
19
19
|
this.graph = new DependencyGraph(baseUrl, paths);
|
|
20
20
|
}
|
|
21
|
+
preloadFile(file: string, content: string) {
|
|
22
|
+
this.ts.updateFile(file, content);
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
// SCAN FILE
|
|
23
26
|
scanFile(
|
|
@@ -66,7 +69,6 @@ export class EventraEngine {
|
|
|
66
69
|
content: string,
|
|
67
70
|
config: EventraConfig
|
|
68
71
|
) {
|
|
69
|
-
// сначала обновляем текущий файл
|
|
70
72
|
const res = this.scanFile(file, content, config);
|
|
71
73
|
|
|
72
74
|
const dependents = this.graph.getAllDependentsDeep(file);
|