@eventra_dev/eventra-cli 0.0.19 → 0.1.2
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 +17 -117
- package/dist/commands/sync.js +20 -115
- package/dist/commands/watch.js +107 -69
- package/dist/engine/callResolver.js +34 -0
- package/dist/engine/engine.js +47 -0
- package/dist/engine/graph.js +25 -0
- package/dist/engine/hash.js +10 -0
- package/dist/engine/languageService.js +67 -0
- package/dist/engine/resolver.js +69 -0
- package/dist/engine/scanner.js +50 -0
- package/dist/utils/parsers/vue.js +16 -12
- package/dist/utils/processFile.js +20 -0
- package/package.json +1 -1
- package/src/commands/check.ts +19 -151
- package/src/commands/sync.ts +23 -194
- package/src/commands/watch.ts +119 -66
- package/src/engine/callResolver.ts +43 -0
- package/src/engine/engine.ts +60 -0
- package/src/engine/graph.ts +23 -0
- package/src/engine/hash.ts +5 -0
- package/src/engine/languageService.ts +73 -0
- package/src/engine/resolver.ts +86 -0
- package/src/engine/scanner.ts +61 -0
- package/src/types.ts +5 -6
- package/src/utils/parsers/vue.ts +19 -20
- package/src/utils/processFile.ts +18 -0
- package/tests/fixtures/backend/express/app.ts +167 -12
- package/tests/fixtures/wrappers/component/test.tsx +1 -1
- package/tests/run.ts +4 -17
- package/dist/utils/detect-aliases.js +0 -39
- package/dist/utils/detect-wrappers.js +0 -84
- package/dist/utils/extract.js +0 -56
- package/dist/utils/scanners/component-wrappers.js +0 -73
- package/dist/utils/scanners/function-wrappers.js +0 -83
- package/dist/utils/scanners/track.js +0 -64
- package/src/utils/detect-aliases.ts +0 -46
- package/src/utils/detect-wrappers.ts +0 -119
- package/src/utils/extract.ts +0 -72
- package/src/utils/scanners/component-wrappers.ts +0 -109
- package/src/utils/scanners/function-wrappers.ts +0 -137
- package/src/utils/scanners/track.ts +0 -99
package/dist/commands/check.js
CHANGED
|
@@ -6,17 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.check = check;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
|
-
const ts_morph_1 = require("ts-morph");
|
|
10
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
10
|
const config_1 = require("../utils/config");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const svelte_1 = require("../utils/parsers/svelte");
|
|
16
|
-
const astro_1 = require("../utils/parsers/astro");
|
|
17
|
-
const track_1 = require("../utils/scanners/track");
|
|
18
|
-
const function_wrappers_1 = require("../utils/scanners/function-wrappers");
|
|
19
|
-
const component_wrappers_1 = require("../utils/scanners/component-wrappers");
|
|
11
|
+
const engine_1 = require("../engine/engine");
|
|
12
|
+
const processFile_1 = require("../utils/processFile");
|
|
20
13
|
async function check({ fix = false }) {
|
|
21
14
|
const config = await (0, config_1.loadConfig)();
|
|
22
15
|
if (!config) {
|
|
@@ -24,129 +17,36 @@ async function check({ fix = false }) {
|
|
|
24
17
|
process.exit(1);
|
|
25
18
|
}
|
|
26
19
|
console.log(chalk_1.default.blue("Checking events..."));
|
|
27
|
-
const project = new ts_morph_1.Project();
|
|
28
|
-
const knownEvents = new Set(config.events ?? []);
|
|
29
|
-
const aliases = { ...(config.aliases ?? {}) };
|
|
30
|
-
const foundEvents = new Map();
|
|
31
|
-
const newEvents = new Map();
|
|
32
|
-
const unresolvedDynamic = new Map();
|
|
33
20
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
34
|
-
ignore: config.sync.exclude
|
|
21
|
+
ignore: config.sync.exclude,
|
|
35
22
|
});
|
|
23
|
+
const engine = new engine_1.EventraEngine(process.cwd());
|
|
36
24
|
for (const file of files) {
|
|
37
25
|
try {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const parser = (0, router_1.detectParser)(file);
|
|
43
|
-
let content = await promises_1.default.readFile(file, "utf-8");
|
|
44
|
-
if (parser === "vue")
|
|
45
|
-
content = (0, vue_1.parseVue)(content);
|
|
46
|
-
if (parser === "svelte")
|
|
47
|
-
content = (0, svelte_1.parseSvelte)(content);
|
|
48
|
-
if (parser === "astro")
|
|
49
|
-
content = (0, astro_1.parseAstro)(content);
|
|
50
|
-
const source = project.createSourceFile(file, content, {
|
|
51
|
-
overwrite: true
|
|
52
|
-
});
|
|
53
|
-
const found = [
|
|
54
|
-
...(0, track_1.scanTrack)(source, config, aliases),
|
|
55
|
-
...(0, function_wrappers_1.scanFunctionWrappers)(source, config.functionWrappers ?? [], aliases),
|
|
56
|
-
...(0, component_wrappers_1.scanComponentWrappers)(source, config.wrappers ?? [], aliases)
|
|
57
|
-
];
|
|
58
|
-
for (const event of found) {
|
|
59
|
-
const { value, dynamic } = event;
|
|
60
|
-
if (!value)
|
|
61
|
-
continue;
|
|
62
|
-
// skip
|
|
63
|
-
if (aliases[value] === "__skip__")
|
|
64
|
-
continue;
|
|
65
|
-
// alias resolve
|
|
66
|
-
if (value in aliases) {
|
|
67
|
-
foundEvents.set(aliases[value], {
|
|
68
|
-
...event,
|
|
69
|
-
value: aliases[value]
|
|
70
|
-
});
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
if (dynamic) {
|
|
74
|
-
unresolvedDynamic.set(value, event);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
foundEvents.set(value, event);
|
|
78
|
-
}
|
|
79
|
-
project.removeSourceFile(source);
|
|
26
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
27
|
+
const { content, virtualFile } = (0, processFile_1.processFile)(file, raw);
|
|
28
|
+
engine.scanFile(virtualFile, content);
|
|
80
29
|
}
|
|
81
30
|
catch {
|
|
82
31
|
console.log(chalk_1.default.gray(`Skipped: ${file}`));
|
|
83
32
|
}
|
|
84
33
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
newEvents.set(key, event);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// FIX MODE
|
|
34
|
+
const found = new Set(engine.getAllEvents());
|
|
35
|
+
const known = new Set(config.events ?? []);
|
|
36
|
+
const newEvents = [...found].filter(e => !known.has(e));
|
|
92
37
|
if (fix) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.log(chalk_1.default.green(`+ ${name}`));
|
|
98
|
-
}
|
|
99
|
-
// resolve dynamic
|
|
100
|
-
for (const [value] of unresolvedDynamic) {
|
|
101
|
-
const { action } = await inquirer_1.default.prompt([
|
|
102
|
-
{
|
|
103
|
-
type: "list",
|
|
104
|
-
name: "action",
|
|
105
|
-
message: `Dynamic: ${value}`,
|
|
106
|
-
choices: [
|
|
107
|
-
{ name: "Alias", value: "alias" },
|
|
108
|
-
{ name: "Skip", value: "skip" }
|
|
109
|
-
]
|
|
110
|
-
}
|
|
111
|
-
]);
|
|
112
|
-
if (action === "skip") {
|
|
113
|
-
aliases[value] = "__skip__";
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
const { name } = await inquirer_1.default.prompt([
|
|
117
|
-
{
|
|
118
|
-
type: "input",
|
|
119
|
-
name: "name",
|
|
120
|
-
message: "Event name:"
|
|
121
|
-
}
|
|
122
|
-
]);
|
|
123
|
-
const normalized = name.trim().toLowerCase();
|
|
124
|
-
if (!normalized)
|
|
125
|
-
continue;
|
|
126
|
-
aliases[value] = normalized;
|
|
127
|
-
config.events.push(normalized);
|
|
128
|
-
console.log(chalk_1.default.green(`${value} → ${normalized}`));
|
|
129
|
-
}
|
|
130
|
-
config.aliases = Object.fromEntries(Object.entries(aliases).sort());
|
|
38
|
+
newEvents.forEach(e => {
|
|
39
|
+
config.events.push(e);
|
|
40
|
+
console.log(chalk_1.default.green(`+ ${e}`));
|
|
41
|
+
});
|
|
131
42
|
config.events = [...new Set(config.events)].sort();
|
|
132
43
|
await (0, config_1.saveConfig)(config);
|
|
133
|
-
console.log(chalk_1.default.green("\
|
|
44
|
+
console.log(chalk_1.default.green("\nFixed"));
|
|
134
45
|
return;
|
|
135
46
|
}
|
|
136
|
-
|
|
137
|
-
let hasError = false;
|
|
138
|
-
if (unresolvedDynamic.size) {
|
|
139
|
-
hasError = true;
|
|
140
|
-
console.log(chalk_1.default.red("\nDynamic events:"));
|
|
141
|
-
unresolvedDynamic.forEach((e) => console.log(chalk_1.default.red(`- ${e.value} (${e.file}:${e.line})`)));
|
|
142
|
-
}
|
|
143
|
-
if (newEvents.size) {
|
|
144
|
-
hasError = true;
|
|
47
|
+
if (newEvents.length) {
|
|
145
48
|
console.log(chalk_1.default.red("\nNew events:"));
|
|
146
|
-
newEvents.forEach(
|
|
147
|
-
}
|
|
148
|
-
if (hasError) {
|
|
149
|
-
console.log(chalk_1.default.red("\nEvent check failed"));
|
|
49
|
+
newEvents.forEach(e => console.log(chalk_1.default.red(`+ ${e}`)));
|
|
150
50
|
process.exit(1);
|
|
151
51
|
}
|
|
152
52
|
console.log(chalk_1.default.green("\nAll good"));
|
package/dist/commands/sync.js
CHANGED
|
@@ -4,133 +4,38 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.sync = sync;
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
7
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
|
-
const ts_morph_1 = require("ts-morph");
|
|
10
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
-
const
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
10
|
const config_1 = require("../utils/config");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const router_1 = require("../utils/parsers/router");
|
|
16
|
-
const vue_1 = require("../utils/parsers/vue");
|
|
17
|
-
const svelte_1 = require("../utils/parsers/svelte");
|
|
18
|
-
const astro_1 = require("../utils/parsers/astro");
|
|
19
|
-
const track_1 = require("../utils/scanners/track");
|
|
20
|
-
const function_wrappers_1 = require("../utils/scanners/function-wrappers");
|
|
21
|
-
const component_wrappers_1 = require("../utils/scanners/component-wrappers");
|
|
11
|
+
const engine_1 = require("../engine/engine");
|
|
12
|
+
const processFile_1 = require("../utils/processFile");
|
|
22
13
|
async function sync() {
|
|
23
14
|
const config = await (0, config_1.loadConfig)();
|
|
24
|
-
if (!config)
|
|
25
|
-
console.log(chalk_1.default.red("Run 'eventra init'"));
|
|
15
|
+
if (!config)
|
|
26
16
|
return;
|
|
27
|
-
|
|
28
|
-
console.log(chalk_1.default.blue("Scanning project..."));
|
|
29
|
-
const project = new ts_morph_1.Project();
|
|
30
|
-
const events = new Set();
|
|
31
|
-
const aliases = config.aliases ?? {};
|
|
32
|
-
const asked = new Set();
|
|
17
|
+
console.log(chalk_1.default.blue("Scanning..."));
|
|
33
18
|
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
34
|
-
ignore: config.sync.exclude
|
|
19
|
+
ignore: config.sync.exclude,
|
|
35
20
|
});
|
|
21
|
+
if (!files.length) {
|
|
22
|
+
console.log(chalk_1.default.yellow("No files found"));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const engine = new engine_1.EventraEngine(process.cwd());
|
|
36
26
|
for (const file of files) {
|
|
37
27
|
try {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
content = (0, vue_1.parseVue)(content);
|
|
42
|
-
if (parser === "svelte")
|
|
43
|
-
content = (0, svelte_1.parseSvelte)(content);
|
|
44
|
-
if (parser === "astro")
|
|
45
|
-
content = (0, astro_1.parseAstro)(content);
|
|
46
|
-
const virtualFile = parser === "ts" ? file : file + ".tsx";
|
|
47
|
-
const source = project.createSourceFile(virtualFile, content, { overwrite: true });
|
|
48
|
-
// AUTO ALIASES
|
|
49
|
-
const detectedAliases = (0, detect_aliases_1.detectAliases)(source);
|
|
50
|
-
for (const key in detectedAliases) {
|
|
51
|
-
if (!(key in aliases)) {
|
|
52
|
-
aliases[key] = detectedAliases[key];
|
|
53
|
-
console.log(chalk_1.default.green(`Detected alias: ${key} → ${detectedAliases[key]}`));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// AUTO WRAPPERS
|
|
57
|
-
const detected = (0, detect_wrappers_1.detectWrappers)(source);
|
|
58
|
-
// function wrappers
|
|
59
|
-
for (const fn of detected.functions) {
|
|
60
|
-
if (!config.functionWrappers?.some((w) => w.name === fn.name)) {
|
|
61
|
-
config.functionWrappers.push(fn);
|
|
62
|
-
console.log(chalk_1.default.green(`Detected function wrapper: ${fn.name}`));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// component wrappers
|
|
66
|
-
for (const comp of detected.components) {
|
|
67
|
-
if (!config.wrappers?.some((w) => w.name === comp.name)) {
|
|
68
|
-
config.wrappers.push(comp);
|
|
69
|
-
console.log(chalk_1.default.green(`Detected component wrapper: ${comp.name} (prop: ${comp.prop})`));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// SCAN EVENTS
|
|
73
|
-
const found = [
|
|
74
|
-
...(0, track_1.scanTrack)(source, config, aliases),
|
|
75
|
-
...(0, function_wrappers_1.scanFunctionWrappers)(source, config.functionWrappers ?? [], aliases),
|
|
76
|
-
...(0, component_wrappers_1.scanComponentWrappers)(source, config.wrappers ?? [], aliases)
|
|
77
|
-
];
|
|
78
|
-
for (const event of found) {
|
|
79
|
-
const { value, dynamic } = event;
|
|
80
|
-
if (!value)
|
|
81
|
-
continue;
|
|
82
|
-
// skip explicit ignore
|
|
83
|
-
if (value in aliases && aliases[value] === "__skip__") {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
// resolve alias
|
|
87
|
-
if (value in aliases) {
|
|
88
|
-
events.add(aliases[value]);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
// static
|
|
92
|
-
if (!dynamic) {
|
|
93
|
-
events.add(value);
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
// already handled
|
|
97
|
-
if (asked.has(value))
|
|
98
|
-
continue;
|
|
99
|
-
// ASK ONLY IF NEEDED
|
|
100
|
-
console.log(chalk_1.default.yellow("\nDynamic event detected:"));
|
|
101
|
-
console.log(chalk_1.default.gray(value));
|
|
102
|
-
const { name } = await inquirer_1.default.prompt([
|
|
103
|
-
{
|
|
104
|
-
type: "input",
|
|
105
|
-
name: "name",
|
|
106
|
-
message: "Enter event name (or type 'skip'):"
|
|
107
|
-
}
|
|
108
|
-
]);
|
|
109
|
-
const normalized = name?.trim();
|
|
110
|
-
asked.add(value);
|
|
111
|
-
if (!normalized || normalized === "skip") {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
// save alias
|
|
115
|
-
aliases[value] = normalized;
|
|
116
|
-
console.log(chalk_1.default.green(`Saved alias: ${value} → ${normalized}`));
|
|
117
|
-
events.add(normalized);
|
|
118
|
-
}
|
|
119
|
-
// free memory
|
|
120
|
-
project.removeSourceFile(source);
|
|
28
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
29
|
+
const { content, virtualFile } = (0, processFile_1.processFile)(file, raw);
|
|
30
|
+
engine.scanFile(virtualFile, content);
|
|
121
31
|
}
|
|
122
|
-
catch {
|
|
123
|
-
console.log(chalk_1.default.
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.log(chalk_1.default.red(`ERROR: ${file}`));
|
|
34
|
+
console.error(e);
|
|
124
35
|
}
|
|
125
36
|
}
|
|
126
|
-
|
|
127
|
-
config.
|
|
128
|
-
// MERGE
|
|
129
|
-
const merged = new Set([
|
|
130
|
-
...config.events,
|
|
131
|
-
...events
|
|
132
|
-
]);
|
|
133
|
-
config.events = [...merged].sort();
|
|
37
|
+
const events = engine.getAllEvents().sort();
|
|
38
|
+
config.events = events;
|
|
134
39
|
await (0, config_1.saveConfig)(config);
|
|
135
|
-
console.log(chalk_1.default.green(
|
|
40
|
+
console.log(chalk_1.default.green(`Found ${events.length} events`));
|
|
136
41
|
}
|
package/dist/commands/watch.js
CHANGED
|
@@ -6,92 +6,130 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.watch = watch;
|
|
7
7
|
const chokidar_1 = __importDefault(require("chokidar"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const ts_morph_1 = require("ts-morph");
|
|
10
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
11
11
|
const config_1 = require("../utils/config");
|
|
12
|
+
const engine_1 = require("../engine/engine");
|
|
12
13
|
const router_1 = require("../utils/parsers/router");
|
|
13
14
|
const vue_1 = require("../utils/parsers/vue");
|
|
14
15
|
const svelte_1 = require("../utils/parsers/svelte");
|
|
15
16
|
const astro_1 = require("../utils/parsers/astro");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
17
|
+
// PREPROCESS (CONTENT)
|
|
18
|
+
function preprocess(file, content) {
|
|
19
|
+
const parser = (0, router_1.detectParser)(file);
|
|
20
|
+
if (parser === "vue")
|
|
21
|
+
return (0, vue_1.parseVue)(content);
|
|
22
|
+
if (parser === "svelte")
|
|
23
|
+
return (0, svelte_1.parseSvelte)(content);
|
|
24
|
+
if (parser === "astro")
|
|
25
|
+
return (0, astro_1.parseAstro)(content);
|
|
26
|
+
return content;
|
|
27
|
+
}
|
|
28
|
+
// VIRTUAL FILE (PATH)
|
|
29
|
+
function toVirtualFile(file) {
|
|
30
|
+
return file.endsWith(".vue")
|
|
31
|
+
? file + ".tsx"
|
|
32
|
+
: file;
|
|
33
|
+
}
|
|
34
|
+
// WATCH
|
|
19
35
|
async function watch() {
|
|
20
36
|
const config = await (0, config_1.loadConfig)();
|
|
21
|
-
if (!config)
|
|
22
|
-
console.log(chalk_1.default.red("Run init first"));
|
|
37
|
+
if (!config)
|
|
23
38
|
return;
|
|
24
|
-
}
|
|
25
39
|
console.log(chalk_1.default.blue("Watching...\n"));
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
await handle(file);
|
|
37
|
-
}
|
|
38
|
-
config.events = [...new Set(config.events)].sort();
|
|
39
|
-
await (0, config_1.saveConfig)(config);
|
|
40
|
-
};
|
|
41
|
-
const handle = async (file) => {
|
|
40
|
+
const files = await (0, fast_glob_1.default)(config.sync.include, {
|
|
41
|
+
ignore: config.sync.exclude,
|
|
42
|
+
});
|
|
43
|
+
if (!files.length) {
|
|
44
|
+
console.log(chalk_1.default.yellow("No files found"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const engine = new engine_1.EventraEngine(process.cwd());
|
|
48
|
+
// INITIAL SCAN
|
|
49
|
+
for (const file of files) {
|
|
42
50
|
try {
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (parser === "svelte")
|
|
48
|
-
content = (0, svelte_1.parseSvelte)(content);
|
|
49
|
-
if (parser === "astro")
|
|
50
|
-
content = (0, astro_1.parseAstro)(content);
|
|
51
|
-
const source = project.createSourceFile(file, content, {
|
|
52
|
-
overwrite: true
|
|
53
|
-
});
|
|
54
|
-
const found = [
|
|
55
|
-
...(0, track_1.scanTrack)(source, config, aliases),
|
|
56
|
-
...(0, function_wrappers_1.scanFunctionWrappers)(source, config.functionWrappers ?? [], aliases),
|
|
57
|
-
...(0, component_wrappers_1.scanComponentWrappers)(source, config.wrappers ?? [], aliases)
|
|
58
|
-
];
|
|
59
|
-
for (const e of found) {
|
|
60
|
-
if (!e.value)
|
|
61
|
-
continue;
|
|
62
|
-
if (aliases[e.value] === "__skip__")
|
|
63
|
-
continue;
|
|
64
|
-
if (e.value in aliases)
|
|
65
|
-
continue;
|
|
66
|
-
if (seen.has(e.value))
|
|
67
|
-
continue;
|
|
68
|
-
seen.add(e.value);
|
|
69
|
-
if (!e.dynamic) {
|
|
70
|
-
config.events.push(e.value);
|
|
71
|
-
console.log(chalk_1.default.green(`+ ${e.value}`));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
console.log(chalk_1.default.yellow(`Dynamic: ${e.value}`));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
project.removeSourceFile(source);
|
|
51
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
52
|
+
const content = preprocess(file, raw);
|
|
53
|
+
const virtualFile = toVirtualFile(file);
|
|
54
|
+
engine.scanFile(virtualFile, content);
|
|
78
55
|
}
|
|
79
56
|
catch {
|
|
80
57
|
console.log(chalk_1.default.gray(`skip: ${file}`));
|
|
81
58
|
}
|
|
59
|
+
}
|
|
60
|
+
config.events = engine.getAllEvents().sort();
|
|
61
|
+
await (0, config_1.saveConfig)(config);
|
|
62
|
+
console.log(chalk_1.default.gray(`Initial: ${config.events.length} events\n`));
|
|
63
|
+
// STATE
|
|
64
|
+
let locked = false;
|
|
65
|
+
let queued = new Set();
|
|
66
|
+
const run = async () => {
|
|
67
|
+
if (locked)
|
|
68
|
+
return;
|
|
69
|
+
locked = true;
|
|
70
|
+
const batch = [...queued];
|
|
71
|
+
queued.clear();
|
|
72
|
+
for (const file of batch) {
|
|
73
|
+
try {
|
|
74
|
+
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
75
|
+
const content = preprocess(file, raw);
|
|
76
|
+
const virtualFile = toVirtualFile(file);
|
|
77
|
+
engine.updateFile(virtualFile, content);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
console.log(chalk_1.default.gray(`skip: ${file}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const next = engine.getAllEvents();
|
|
84
|
+
const added = next.filter(e => !config.events.includes(e));
|
|
85
|
+
const removed = config.events.filter(e => !next.includes(e));
|
|
86
|
+
if (added.length || removed.length) {
|
|
87
|
+
added.forEach(e => console.log(chalk_1.default.green(`+ ${e}`)));
|
|
88
|
+
removed.forEach(e => console.log(chalk_1.default.red(`- ${e}`)));
|
|
89
|
+
config.events = next.sort();
|
|
90
|
+
await (0, config_1.saveConfig)(config);
|
|
91
|
+
console.log(chalk_1.default.gray(`Total: ${config.events.length} events\n`));
|
|
92
|
+
}
|
|
93
|
+
locked = false;
|
|
94
|
+
};
|
|
95
|
+
const schedule = (file) => {
|
|
96
|
+
queued.add(file);
|
|
97
|
+
setTimeout(run, 50);
|
|
82
98
|
};
|
|
99
|
+
// WATCHER
|
|
83
100
|
const watcher = chokidar_1.default.watch(config.sync.include, {
|
|
84
|
-
ignored:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
ignored: config.sync.exclude,
|
|
102
|
+
ignoreInitial: true,
|
|
103
|
+
awaitWriteFinish: {
|
|
104
|
+
stabilityThreshold: 50,
|
|
105
|
+
pollInterval: 10,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
watcher
|
|
109
|
+
.on("add", (file) => {
|
|
110
|
+
console.log(chalk_1.default.gray(`add: ${file}`));
|
|
111
|
+
schedule(file);
|
|
112
|
+
})
|
|
113
|
+
.on("change", (file) => {
|
|
114
|
+
console.log(chalk_1.default.gray(`change: ${file}`));
|
|
115
|
+
schedule(file);
|
|
116
|
+
})
|
|
117
|
+
.on("unlink", async (file) => {
|
|
118
|
+
console.log(chalk_1.default.gray(`remove: ${file}`));
|
|
119
|
+
const virtualFile = toVirtualFile(file);
|
|
120
|
+
engine.removeFile?.(virtualFile);
|
|
121
|
+
const next = engine.getAllEvents();
|
|
122
|
+
const removed = config.events.filter(e => !next.includes(e));
|
|
123
|
+
if (removed.length) {
|
|
124
|
+
removed.forEach(e => console.log(chalk_1.default.red(`- ${e}`)));
|
|
125
|
+
config.events = next.sort();
|
|
126
|
+
await (0, config_1.saveConfig)(config);
|
|
127
|
+
}
|
|
91
128
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
129
|
+
// EXIT
|
|
130
|
+
process.on("SIGINT", async () => {
|
|
131
|
+
console.log(chalk_1.default.yellow("\nStopping watcher..."));
|
|
132
|
+
await watcher.close();
|
|
133
|
+
process.exit(0);
|
|
96
134
|
});
|
|
97
135
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveFunctionFromCall = resolveFunctionFromCall;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
function resolveFunctionFromCall(expr, checker) {
|
|
9
|
+
let symbol = checker.getSymbolAtLocation(expr);
|
|
10
|
+
if (!symbol && typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
11
|
+
symbol = checker.getSymbolAtLocation(expr.name);
|
|
12
|
+
}
|
|
13
|
+
if (!symbol)
|
|
14
|
+
return null;
|
|
15
|
+
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
16
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
17
|
+
}
|
|
18
|
+
for (const decl of symbol.getDeclarations() ?? []) {
|
|
19
|
+
if (typescript_1.default.isFunctionDeclaration(decl) ||
|
|
20
|
+
typescript_1.default.isMethodDeclaration(decl) ||
|
|
21
|
+
typescript_1.default.isFunctionExpression(decl) ||
|
|
22
|
+
typescript_1.default.isArrowFunction(decl)) {
|
|
23
|
+
return decl;
|
|
24
|
+
}
|
|
25
|
+
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
26
|
+
const init = decl.initializer;
|
|
27
|
+
if (typescript_1.default.isArrowFunction(init) ||
|
|
28
|
+
typescript_1.default.isFunctionExpression(init)) {
|
|
29
|
+
return init;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventraEngine = void 0;
|
|
4
|
+
const languageService_1 = require("./languageService");
|
|
5
|
+
const scanner_1 = require("./scanner");
|
|
6
|
+
const graph_1 = require("./graph");
|
|
7
|
+
const hash_1 = require("./hash");
|
|
8
|
+
class EventraEngine {
|
|
9
|
+
ts;
|
|
10
|
+
graph = new graph_1.DependencyGraph();
|
|
11
|
+
fileEvents = new Map();
|
|
12
|
+
fileHash = new Map();
|
|
13
|
+
constructor(root) {
|
|
14
|
+
this.ts = new languageService_1.TSService(root);
|
|
15
|
+
}
|
|
16
|
+
scanFile(file, content) {
|
|
17
|
+
const h = (0, hash_1.hash)(content);
|
|
18
|
+
if (this.fileHash.get(file) === h) {
|
|
19
|
+
return this.fileEvents.get(file) ?? new Set();
|
|
20
|
+
}
|
|
21
|
+
this.fileHash.set(file, h);
|
|
22
|
+
this.ts.updateFile(file, content);
|
|
23
|
+
const source = this.ts.getSourceFile(file);
|
|
24
|
+
const checker = this.ts.getChecker();
|
|
25
|
+
if (!source || !checker)
|
|
26
|
+
return new Set();
|
|
27
|
+
this.graph.update(file, source);
|
|
28
|
+
const events = (0, scanner_1.scanSource)(source, checker);
|
|
29
|
+
this.fileEvents.set(file, events);
|
|
30
|
+
return events;
|
|
31
|
+
}
|
|
32
|
+
updateFile(file, content) {
|
|
33
|
+
return this.scanFile(file, content);
|
|
34
|
+
}
|
|
35
|
+
getAllEvents() {
|
|
36
|
+
const all = new Set();
|
|
37
|
+
for (const set of this.fileEvents.values()) {
|
|
38
|
+
set.forEach(e => all.add(e));
|
|
39
|
+
}
|
|
40
|
+
return [...all];
|
|
41
|
+
}
|
|
42
|
+
removeFile(file) {
|
|
43
|
+
this.fileEvents.delete(file);
|
|
44
|
+
this.fileHash.delete(file);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.EventraEngine = EventraEngine;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DependencyGraph = void 0;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
class DependencyGraph {
|
|
9
|
+
reverse = new Map();
|
|
10
|
+
update(file, source) {
|
|
11
|
+
for (const stmt of source.statements) {
|
|
12
|
+
if (!typescript_1.default.isImportDeclaration(stmt))
|
|
13
|
+
continue;
|
|
14
|
+
const module = stmt.moduleSpecifier.getText().replace(/['"]/g, "");
|
|
15
|
+
if (!this.reverse.has(module)) {
|
|
16
|
+
this.reverse.set(module, new Set());
|
|
17
|
+
}
|
|
18
|
+
this.reverse.get(module).add(file);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
getDependents(file) {
|
|
22
|
+
return [...(this.reverse.get(file) ?? [])];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.DependencyGraph = DependencyGraph;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.hash = hash;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
function hash(content) {
|
|
9
|
+
return crypto_1.default.createHash("md5").update(content).digest("hex");
|
|
10
|
+
}
|