@eventra_dev/eventra-cli 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -32
- package/dist/analysis/cache/evaluationCache.js +21 -0
- package/dist/analysis/cache/resolvedCallCache.js +21 -0
- package/dist/analysis/cache/resolvedExportCache.js +21 -0
- package/dist/analysis/cache/semanticDependencyGraph.js +25 -0
- package/dist/analysis/cache/symbolInvalidation.js +20 -0
- package/dist/analysis/extractor/extractor.js +47 -0
- package/dist/analysis/extractor/propagationExtractor.js +52 -0
- package/dist/analysis/pipeline/semanticPass.js +2 -0
- package/dist/analysis/resolver/callResolver.js +125 -0
- package/dist/analysis/resolver/exportResolver.js +75 -0
- package/dist/analysis/resolver/resolver.js +133 -0
- package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +193 -0
- package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +105 -0
- package/dist/analysis/scanner/scanner.js +117 -0
- package/dist/analysis/scanner/sinkDetector.js +91 -0
- package/dist/analysis/shared/evaluationContext.js +9 -0
- package/dist/analysis/shared/propagation.js +3 -0
- package/dist/analysis/shared/returnPropagation.js +2 -0
- package/dist/analysis/shared/types.js +2 -0
- package/dist/analysis/shared/utils.js +62 -0
- package/dist/analysis/symbols/symbolUtils.js +68 -0
- package/dist/analysis/symbols/wrapperRegistry.js +55 -0
- package/dist/cli/check.js +73 -0
- package/dist/{commands → cli}/init.js +1 -1
- package/dist/{commands → cli}/send.js +1 -1
- package/dist/cli/sync.js +56 -0
- package/dist/{commands → cli}/watch.js +41 -25
- package/dist/compiler/compilerContext.js +204 -0
- package/dist/compiler/documentRegistry.js +78 -0
- package/dist/compiler/importGraph.js +83 -0
- package/dist/compiler/scheduler.js +29 -0
- package/dist/core/EventraEngine.js +234 -0
- package/dist/core/projectScanner.js +73 -0
- package/dist/{utils → filesystem}/getVirtualFile.js +1 -3
- package/dist/filesystem/processFile.js +38 -0
- package/dist/index.js +5 -5
- package/dist/shared/buildSyntheticTsx.js +20 -0
- package/dist/shared/templateExpressionExtractor.js +22 -0
- package/package.json +32 -9
- package/.github/workflows/release.yml +0 -43
- package/ARCHITECTURE.md +0 -88
- package/assets/eventra-icon-animated.svg +0 -19
- package/dist/commands/check.js +0 -78
- package/dist/commands/sync.js +0 -79
- package/dist/engine/boundary.js +0 -14
- package/dist/engine/callResolver.js +0 -58
- package/dist/engine/engine.js +0 -154
- package/dist/engine/exportResolver.js +0 -46
- package/dist/engine/functionAnalyzer.js +0 -71
- package/dist/engine/graph.js +0 -168
- package/dist/engine/languageService.js +0 -162
- package/dist/engine/resolver.js +0 -149
- package/dist/engine/scanner.js +0 -230
- package/dist/utils/processFile.js +0 -28
- package/dist/utils/universal.js +0 -190
- package/src/commands/check.ts +0 -91
- package/src/commands/init.ts +0 -174
- package/src/commands/send.ts +0 -213
- package/src/commands/sync.ts +0 -106
- package/src/commands/watch.ts +0 -144
- package/src/engine/boundary.ts +0 -12
- package/src/engine/callResolver.ts +0 -65
- package/src/engine/engine.ts +0 -227
- package/src/engine/exportResolver.ts +0 -56
- package/src/engine/functionAnalyzer.ts +0 -81
- package/src/engine/graph.ts +0 -193
- package/src/engine/hash.ts +0 -5
- package/src/engine/languageService.ts +0 -209
- package/src/engine/resolver.ts +0 -177
- package/src/engine/scanner.ts +0 -298
- package/src/index.ts +0 -49
- package/src/types.ts +0 -35
- package/src/utils/config.ts +0 -53
- package/src/utils/getVirtualFile.ts +0 -5
- package/src/utils/processFile.ts +0 -29
- package/src/utils/universal.ts +0 -249
- package/tests/fixtures/backend/express/app.ts +0 -233
- package/tests/fixtures/backend/nest/service.ts +0 -70
- package/tests/fixtures/backend/node/index.ts +0 -63
- package/tests/fixtures/frontend/next/page.tsx +0 -101
- package/tests/fixtures/frontend/react/App.tsx +0 -104
- package/tests/fixtures/frontend/vue/App.vue +0 -83
- package/tests/fixtures/wrappers/component/test.tsx +0 -62
- package/tests/fixtures/wrappers/function/test.ts +0 -60
- package/tests/run.ts +0 -106
- package/tsconfig.json +0 -14
- /package/dist/{utils → config}/config.js +0 -0
- /package/dist/{engine → shared}/hash.js +0 -0
package/src/commands/init.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
|
-
|
|
4
|
-
import { saveConfig } from "../utils/config";
|
|
5
|
-
import {ComponentWrapper, FunctionWrapper} from "../types";
|
|
6
|
-
|
|
7
|
-
export async function init() {
|
|
8
|
-
console.log(
|
|
9
|
-
chalk.blue("Initializing Eventra...")
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
// API KEY ENDPOINT
|
|
13
|
-
const { apiKey, endpoint } =
|
|
14
|
-
await inquirer.prompt([
|
|
15
|
-
{
|
|
16
|
-
type: "input",
|
|
17
|
-
name: "apiKey",
|
|
18
|
-
message:
|
|
19
|
-
"API Key (optional):"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
type: "input",
|
|
23
|
-
name: "endpoint",
|
|
24
|
-
message: "Custom endpoint (optional):",
|
|
25
|
-
default: "https://api.eventra.dev/api/v1/cli/events"
|
|
26
|
-
}
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
console.log(
|
|
30
|
-
chalk.gray(
|
|
31
|
-
"\nEventra automatically detects:"
|
|
32
|
-
)
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
console.log(
|
|
36
|
-
chalk.gray("• track('event')")
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
40
|
-
chalk.gray(
|
|
41
|
-
"• tracker.track('event')"
|
|
42
|
-
)
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const wrappers: ComponentWrapper[] = [];
|
|
46
|
-
const functionWrappers: FunctionWrapper[] = [];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// COMPONENT WRAPPERS
|
|
50
|
-
console.log(
|
|
51
|
-
chalk.blue(
|
|
52
|
-
"\nComponent wrappers"
|
|
53
|
-
)
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
let addComponent = true;
|
|
57
|
-
|
|
58
|
-
while (addComponent) {
|
|
59
|
-
const { useWrapper } =
|
|
60
|
-
await inquirer.prompt([
|
|
61
|
-
{
|
|
62
|
-
type: "confirm",
|
|
63
|
-
name: "useWrapper",
|
|
64
|
-
message:
|
|
65
|
-
"Add component wrapper?",
|
|
66
|
-
default: false
|
|
67
|
-
}
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
if (!useWrapper) break;
|
|
71
|
-
|
|
72
|
-
const { name, prop } =
|
|
73
|
-
await inquirer.prompt([
|
|
74
|
-
{
|
|
75
|
-
type: "input",
|
|
76
|
-
name: "name",
|
|
77
|
-
message: "Component name:",
|
|
78
|
-
validate: (v) =>
|
|
79
|
-
v ? true : "Required"
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
type: "input",
|
|
83
|
-
name: "prop",
|
|
84
|
-
message:
|
|
85
|
-
"Event prop:",
|
|
86
|
-
default: "event"
|
|
87
|
-
}
|
|
88
|
-
]);
|
|
89
|
-
|
|
90
|
-
wrappers.push({
|
|
91
|
-
name,
|
|
92
|
-
prop
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// FUNCTION WRAPPERS
|
|
97
|
-
console.log(
|
|
98
|
-
chalk.blue(
|
|
99
|
-
"\nFunction wrappers"
|
|
100
|
-
)
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
let addFunction = true;
|
|
104
|
-
|
|
105
|
-
while (addFunction) {
|
|
106
|
-
const { useWrapper } =
|
|
107
|
-
await inquirer.prompt([
|
|
108
|
-
{
|
|
109
|
-
type: "confirm",
|
|
110
|
-
name: "useWrapper",
|
|
111
|
-
message:
|
|
112
|
-
"Add function wrapper?",
|
|
113
|
-
default: false
|
|
114
|
-
}
|
|
115
|
-
]);
|
|
116
|
-
|
|
117
|
-
if (!useWrapper) break;
|
|
118
|
-
|
|
119
|
-
const { name, event } =
|
|
120
|
-
await inquirer.prompt([
|
|
121
|
-
{
|
|
122
|
-
type: "input",
|
|
123
|
-
name: "name",
|
|
124
|
-
message:
|
|
125
|
-
"Function name:"
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
type: "input",
|
|
129
|
-
name: "event",
|
|
130
|
-
message:
|
|
131
|
-
"Event field (leave empty if string argument):",
|
|
132
|
-
default: ""
|
|
133
|
-
}
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
functionWrappers.push({
|
|
137
|
-
name,
|
|
138
|
-
event: event || undefined
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const config = {
|
|
143
|
-
apiKey,
|
|
144
|
-
endpoint,
|
|
145
|
-
events: [],
|
|
146
|
-
wrappers,
|
|
147
|
-
functionWrappers,
|
|
148
|
-
sync: {
|
|
149
|
-
include: [
|
|
150
|
-
"**/*.{ts,tsx,js,jsx,vue,svelte,astro}"
|
|
151
|
-
],
|
|
152
|
-
exclude: [
|
|
153
|
-
"node_modules",
|
|
154
|
-
"dist",
|
|
155
|
-
".next",
|
|
156
|
-
".git"
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
await saveConfig(config);
|
|
162
|
-
|
|
163
|
-
console.log(
|
|
164
|
-
chalk.green(
|
|
165
|
-
"\neventra.json created"
|
|
166
|
-
)
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
console.log(
|
|
170
|
-
chalk.gray(
|
|
171
|
-
"\nRun `eventra sync`"
|
|
172
|
-
)
|
|
173
|
-
);
|
|
174
|
-
}
|
package/src/commands/send.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
|
-
import {
|
|
4
|
-
loadConfig,
|
|
5
|
-
saveConfig
|
|
6
|
-
} from "../utils/config";
|
|
7
|
-
|
|
8
|
-
import pkg from "../../package.json";
|
|
9
|
-
|
|
10
|
-
const CLI_VERSION = pkg.version;
|
|
11
|
-
|
|
12
|
-
export async function send() {
|
|
13
|
-
const config = await loadConfig();
|
|
14
|
-
|
|
15
|
-
const endpoint =
|
|
16
|
-
config?.endpoint ||
|
|
17
|
-
process.env.EVENTRA_ENDPOINT;
|
|
18
|
-
|
|
19
|
-
if (!config) {
|
|
20
|
-
console.log(
|
|
21
|
-
chalk.red(
|
|
22
|
-
"eventra.json not found. Run 'eventra init'"
|
|
23
|
-
)
|
|
24
|
-
);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let apiKey = config.apiKey;
|
|
29
|
-
|
|
30
|
-
// ask api key
|
|
31
|
-
if (!apiKey) {
|
|
32
|
-
const answers =
|
|
33
|
-
await inquirer.prompt([
|
|
34
|
-
{
|
|
35
|
-
type: "input",
|
|
36
|
-
name: "apiKey",
|
|
37
|
-
message:
|
|
38
|
-
"Enter your API key:"
|
|
39
|
-
}
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
apiKey = answers.apiKey;
|
|
43
|
-
|
|
44
|
-
config.apiKey = apiKey;
|
|
45
|
-
|
|
46
|
-
await saveConfig(config);
|
|
47
|
-
|
|
48
|
-
console.log(
|
|
49
|
-
chalk.green("API key saved")
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// no events
|
|
54
|
-
if (!config.events?.length) {
|
|
55
|
-
console.log(
|
|
56
|
-
chalk.yellow(
|
|
57
|
-
"No events found. Run 'eventra sync'"
|
|
58
|
-
)
|
|
59
|
-
);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!apiKey) {
|
|
64
|
-
console.log(
|
|
65
|
-
chalk.red("API key required")
|
|
66
|
-
);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!endpoint) {
|
|
71
|
-
console.log(
|
|
72
|
-
chalk.red("Endpoint not configured")
|
|
73
|
-
);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
console.log("");
|
|
78
|
-
console.log(
|
|
79
|
-
chalk.blue(
|
|
80
|
-
`Sending ${config.events.length} events...`
|
|
81
|
-
)
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const controller = new AbortController();
|
|
85
|
-
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const res = await fetch(
|
|
89
|
-
endpoint,
|
|
90
|
-
{
|
|
91
|
-
method: "POST",
|
|
92
|
-
headers: {
|
|
93
|
-
"Content-Type":
|
|
94
|
-
"application/json",
|
|
95
|
-
"x-api-key": apiKey
|
|
96
|
-
},
|
|
97
|
-
body: JSON.stringify({
|
|
98
|
-
events: config.events,
|
|
99
|
-
cli: {
|
|
100
|
-
name:
|
|
101
|
-
"@eventra_dev/eventra-cli",
|
|
102
|
-
version: CLI_VERSION,
|
|
103
|
-
runtime: "node"
|
|
104
|
-
}
|
|
105
|
-
}),
|
|
106
|
-
signal: controller.signal
|
|
107
|
-
},
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
if (!res.ok) {
|
|
111
|
-
console.log(
|
|
112
|
-
chalk.red(
|
|
113
|
-
`Request failed (${res.status})`
|
|
114
|
-
)
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const text =
|
|
119
|
-
await res.text();
|
|
120
|
-
|
|
121
|
-
console.log(
|
|
122
|
-
chalk.gray(text)
|
|
123
|
-
);
|
|
124
|
-
} catch {}
|
|
125
|
-
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const data =
|
|
130
|
-
await res.json();
|
|
131
|
-
|
|
132
|
-
console.log(
|
|
133
|
-
chalk.green(
|
|
134
|
-
"Events registered successfully"
|
|
135
|
-
)
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// created
|
|
139
|
-
if (data.created?.length) {
|
|
140
|
-
console.log(
|
|
141
|
-
chalk.green("\nNew events:")
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
data.created.forEach(
|
|
145
|
-
(e: string) =>
|
|
146
|
-
console.log(
|
|
147
|
-
chalk.green(
|
|
148
|
-
`+ ${e}`
|
|
149
|
-
)
|
|
150
|
-
)
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// existing
|
|
155
|
-
if (data.existing?.length) {
|
|
156
|
-
console.log(
|
|
157
|
-
chalk.gray(
|
|
158
|
-
"\nExisting events:"
|
|
159
|
-
)
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
data.existing.forEach(
|
|
163
|
-
(e: string) =>
|
|
164
|
-
console.log(
|
|
165
|
-
chalk.gray(
|
|
166
|
-
`• ${e}`
|
|
167
|
-
)
|
|
168
|
-
)
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// processing notice
|
|
173
|
-
if (data.created?.length) {
|
|
174
|
-
console.log("");
|
|
175
|
-
console.log(
|
|
176
|
-
chalk.yellow(
|
|
177
|
-
"Events queued for processing (~2 min)"
|
|
178
|
-
)
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
console.log(
|
|
182
|
-
chalk.gray(
|
|
183
|
-
"They will appear in dashboard shortly"
|
|
184
|
-
)
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
console.log("");
|
|
189
|
-
|
|
190
|
-
console.log(
|
|
191
|
-
chalk.gray(
|
|
192
|
-
`Sent ${config.events.length} events`
|
|
193
|
-
)
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
} catch (err) {
|
|
197
|
-
console.log(
|
|
198
|
-
chalk.red(
|
|
199
|
-
"Network error"
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
if (err instanceof Error) {
|
|
204
|
-
console.log(
|
|
205
|
-
chalk.gray(
|
|
206
|
-
err.message
|
|
207
|
-
)
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
} finally {
|
|
211
|
-
clearTimeout(timeout);
|
|
212
|
-
}
|
|
213
|
-
}
|
package/src/commands/sync.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import fg from "fast-glob";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import path from "path";
|
|
5
|
-
|
|
6
|
-
import { loadConfig, saveConfig } from "../utils/config";
|
|
7
|
-
import { EventraEngine } from "../engine/engine";
|
|
8
|
-
import { processFile } from "../utils/processFile";
|
|
9
|
-
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
10
|
-
|
|
11
|
-
export async function sync() {
|
|
12
|
-
const config = await loadConfig();
|
|
13
|
-
if (!config) return;
|
|
14
|
-
|
|
15
|
-
console.log(chalk.blue("Scanning..."));
|
|
16
|
-
|
|
17
|
-
const files = await fg(config.sync.include, {
|
|
18
|
-
ignore: config.sync.exclude,
|
|
19
|
-
absolute: true,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const engine = new EventraEngine(process.cwd());
|
|
23
|
-
|
|
24
|
-
const cache = new Map<string, { content: string; deps: string[] }>();
|
|
25
|
-
const toScan = new Set<string>();
|
|
26
|
-
|
|
27
|
-
// COLLECT
|
|
28
|
-
for (const file of files) {
|
|
29
|
-
try {
|
|
30
|
-
const raw = await fs.readFile(file, "utf-8");
|
|
31
|
-
if (raw.length > 2_000_000) continue;
|
|
32
|
-
|
|
33
|
-
const parsed = processFile(file, raw);
|
|
34
|
-
|
|
35
|
-
cache.set(file, parsed);
|
|
36
|
-
toScan.add(file);
|
|
37
|
-
|
|
38
|
-
parsed.deps.forEach(dep => toScan.add(path.resolve(dep)));
|
|
39
|
-
|
|
40
|
-
} catch {}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// PRELOAD
|
|
44
|
-
for (const file of toScan) {
|
|
45
|
-
try {
|
|
46
|
-
let parsed = cache.get(file);
|
|
47
|
-
|
|
48
|
-
if (!parsed) {
|
|
49
|
-
const raw = await fs.readFile(file, "utf-8");
|
|
50
|
-
parsed = processFile(file, raw);
|
|
51
|
-
cache.set(file, parsed);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
engine.preloadFile(getVirtualFile(file), parsed.content);
|
|
55
|
-
|
|
56
|
-
} catch {}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// SCAN
|
|
60
|
-
const detectedFn = new Set<string>();
|
|
61
|
-
const detectedCmp = new Map<string, string>();
|
|
62
|
-
|
|
63
|
-
for (const file of toScan) {
|
|
64
|
-
try {
|
|
65
|
-
const parsed = cache.get(file);
|
|
66
|
-
if (!parsed) continue;
|
|
67
|
-
|
|
68
|
-
const res = engine.updateFile(
|
|
69
|
-
getVirtualFile(file),
|
|
70
|
-
parsed.content,
|
|
71
|
-
config
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
res.detectedFunctionWrappers.forEach(w => detectedFn.add(w));
|
|
75
|
-
res.detectedComponentWrappers.forEach((v, k) => detectedCmp.set(k, v));
|
|
76
|
-
|
|
77
|
-
} catch {}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// RESULT
|
|
81
|
-
const events = engine.getAllEvents().sort();
|
|
82
|
-
|
|
83
|
-
config.events = events;
|
|
84
|
-
|
|
85
|
-
config.functionWrappers = mergeUnique(
|
|
86
|
-
config.functionWrappers ?? [],
|
|
87
|
-
[...detectedFn].map(name => ({ name })),
|
|
88
|
-
w => w.name
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
config.wrappers = mergeUnique(
|
|
92
|
-
config.wrappers ?? [],
|
|
93
|
-
[...detectedCmp.entries()].map(([name, prop]) => ({ name, prop })),
|
|
94
|
-
w => `${w.name}:${w.prop}`
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
await saveConfig(config);
|
|
98
|
-
|
|
99
|
-
console.log(chalk.green(`Found ${events.length} events`));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function mergeUnique<T>(a: T[], b: T[], key: (v: T) => string): T[] {
|
|
103
|
-
const map = new Map<string, T>();
|
|
104
|
-
[...b, ...a].forEach(item => map.set(key(item), item));
|
|
105
|
-
return [...map.values()];
|
|
106
|
-
}
|
package/src/commands/watch.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import chokidar from "chokidar";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import fs from "fs/promises";
|
|
4
|
-
import fg from "fast-glob";
|
|
5
|
-
import path from "path";
|
|
6
|
-
|
|
7
|
-
import { loadConfig, saveConfig } from "../utils/config";
|
|
8
|
-
import { EventraEngine } from "../engine/engine";
|
|
9
|
-
import { processFile } from "../utils/processFile";
|
|
10
|
-
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
11
|
-
|
|
12
|
-
export async function watch() {
|
|
13
|
-
const config = await loadConfig();
|
|
14
|
-
if (!config) return;
|
|
15
|
-
|
|
16
|
-
console.log(chalk.blue("Watching...\n"));
|
|
17
|
-
|
|
18
|
-
const engine = new EventraEngine(process.cwd());
|
|
19
|
-
const fileDeps = new Map<string, Set<string>>();
|
|
20
|
-
const cache = new Map<string, { content: string; deps: string[] }>();
|
|
21
|
-
const watchedDeps = new Set<string>();
|
|
22
|
-
|
|
23
|
-
const normalize = (f: string) =>
|
|
24
|
-
path.resolve(f).replace(/\\/g, "/");
|
|
25
|
-
|
|
26
|
-
// INITIAL LOAD
|
|
27
|
-
const files = await fg(config.sync.include, {
|
|
28
|
-
ignore: config.sync.exclude,
|
|
29
|
-
absolute: true,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
for (const file of files) {
|
|
33
|
-
try {
|
|
34
|
-
const abs = normalize(file);
|
|
35
|
-
const raw = await fs.readFile(abs, "utf-8");
|
|
36
|
-
|
|
37
|
-
const parsed = processFile(abs, raw);
|
|
38
|
-
|
|
39
|
-
cache.set(abs, parsed);
|
|
40
|
-
|
|
41
|
-
engine.preloadFile(getVirtualFile(abs), parsed.content);
|
|
42
|
-
engine.updateFile(getVirtualFile(abs), parsed.content, config);
|
|
43
|
-
|
|
44
|
-
fileDeps.set(abs, new Set(parsed.deps.map(d => normalize(d))));
|
|
45
|
-
|
|
46
|
-
} catch {}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
config.events = engine.getAllEvents().sort();
|
|
50
|
-
await saveConfig(config);
|
|
51
|
-
|
|
52
|
-
console.log(chalk.gray(`Initial: ${config.events.length}\n`));
|
|
53
|
-
|
|
54
|
-
// WATCHER
|
|
55
|
-
const watcher = chokidar.watch(
|
|
56
|
-
[...files.map(normalize), ...watchedDeps],
|
|
57
|
-
{ ignoreInitial: true }
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const queue = new Map<string, boolean>();
|
|
61
|
-
let timer: NodeJS.Timeout | null = null;
|
|
62
|
-
|
|
63
|
-
const run = async () => {
|
|
64
|
-
const batch = [...queue.keys()];
|
|
65
|
-
queue.clear();
|
|
66
|
-
|
|
67
|
-
for (const file of batch) {
|
|
68
|
-
try {
|
|
69
|
-
const raw = await fs.readFile(file, "utf-8");
|
|
70
|
-
const parsed = processFile(file, raw);
|
|
71
|
-
|
|
72
|
-
cache.set(file, parsed);
|
|
73
|
-
|
|
74
|
-
engine.updateFile(
|
|
75
|
-
getVirtualFile(file),
|
|
76
|
-
parsed.content,
|
|
77
|
-
config
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
//dependency diff
|
|
81
|
-
const abs = normalize(file);
|
|
82
|
-
|
|
83
|
-
const prevDeps = fileDeps.get(abs) ?? new Set();
|
|
84
|
-
const nextDeps = new Set(parsed.deps.map(d => normalize(d)));
|
|
85
|
-
|
|
86
|
-
// add new deps
|
|
87
|
-
for (const dep of nextDeps) {
|
|
88
|
-
if (!prevDeps.has(dep)) {
|
|
89
|
-
watcher.add(dep);
|
|
90
|
-
watchedDeps.add(dep);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// remove old deps
|
|
95
|
-
for (const dep of prevDeps) {
|
|
96
|
-
if (!nextDeps.has(dep)) {
|
|
97
|
-
watcher.unwatch(dep);
|
|
98
|
-
watchedDeps.delete(dep);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
fileDeps.set(abs, nextDeps);
|
|
103
|
-
|
|
104
|
-
} catch {
|
|
105
|
-
console.log(chalk.gray(`skip: ${file}`));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const next = engine.getAllEvents().sort();
|
|
110
|
-
config.events = next;
|
|
111
|
-
|
|
112
|
-
await saveConfig(config);
|
|
113
|
-
|
|
114
|
-
console.log(chalk.green(`Updated: ${next.length}`));
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const schedule = (file: string) => {
|
|
118
|
-
const abs = normalize(file);
|
|
119
|
-
queue.set(abs, true);
|
|
120
|
-
|
|
121
|
-
if (timer) clearTimeout(timer);
|
|
122
|
-
timer = setTimeout(run, 200);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
watcher
|
|
126
|
-
.on("change", schedule)
|
|
127
|
-
.on("add", schedule)
|
|
128
|
-
.on("unlink", async (file) => {
|
|
129
|
-
const abs = normalize(file);
|
|
130
|
-
|
|
131
|
-
engine.removeFile(getVirtualFile(abs), config);
|
|
132
|
-
|
|
133
|
-
const next = engine.getAllEvents().sort();
|
|
134
|
-
config.events = next;
|
|
135
|
-
await saveConfig(config);
|
|
136
|
-
|
|
137
|
-
console.log(chalk.red(`Removed → ${next.length}`));
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
process.on("SIGINT", async () => {
|
|
141
|
-
await watcher.close();
|
|
142
|
-
process.exit(0);
|
|
143
|
-
});
|
|
144
|
-
}
|
package/src/engine/boundary.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
|
|
3
|
-
export function isExternalFile(file: string) {
|
|
4
|
-
const normalized = path.resolve(file);
|
|
5
|
-
|
|
6
|
-
return (
|
|
7
|
-
normalized.includes(`${path.sep}node_modules${path.sep}`) ||
|
|
8
|
-
normalized.includes(`${path.sep}dist${path.sep}`) ||
|
|
9
|
-
normalized.includes(`${path.sep}build${path.sep}`) ||
|
|
10
|
-
normalized.endsWith(".d.ts")
|
|
11
|
-
);
|
|
12
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
|
|
3
|
-
function isFn(node: ts.Node): node is ts.FunctionLikeDeclaration {
|
|
4
|
-
return (
|
|
5
|
-
ts.isFunctionDeclaration(node) ||
|
|
6
|
-
ts.isMethodDeclaration(node) ||
|
|
7
|
-
ts.isFunctionExpression(node) ||
|
|
8
|
-
ts.isArrowFunction(node)
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function resolveFromSymbol(
|
|
13
|
-
symbol: ts.Symbol,
|
|
14
|
-
checker: ts.TypeChecker,
|
|
15
|
-
seen = new Set<ts.Symbol>()
|
|
16
|
-
): ts.FunctionLikeDeclaration | null {
|
|
17
|
-
if (seen.has(symbol)) return null;
|
|
18
|
-
seen.add(symbol);
|
|
19
|
-
|
|
20
|
-
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
21
|
-
return resolveFromSymbol(checker.getAliasedSymbol(symbol), checker, seen);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
for (const d of symbol.getDeclarations() ?? []) {
|
|
25
|
-
if (isFn(d)) return d;
|
|
26
|
-
|
|
27
|
-
if (ts.isVariableDeclaration(d) && d.initializer) {
|
|
28
|
-
if (isFn(d.initializer)) return d.initializer;
|
|
29
|
-
|
|
30
|
-
const s = checker.getSymbolAtLocation(d.initializer);
|
|
31
|
-
if (s) {
|
|
32
|
-
const r = resolveFromSymbol(s, checker, seen);
|
|
33
|
-
if (r) return r;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function resolveFunctionFromCall(
|
|
42
|
-
expr: ts.Expression,
|
|
43
|
-
checker: ts.TypeChecker
|
|
44
|
-
): ts.FunctionLikeDeclaration | null {
|
|
45
|
-
// foo.bar()
|
|
46
|
-
if (ts.isPropertyAccessExpression(expr)) {
|
|
47
|
-
const s = checker.getSymbolAtLocation(expr.name);
|
|
48
|
-
if (s) {
|
|
49
|
-
const r = resolveFromSymbol(s, checker);
|
|
50
|
-
if (r) return r;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// direct call
|
|
55
|
-
const type = checker.getTypeAtLocation(expr);
|
|
56
|
-
for (const sig of type.getCallSignatures()) {
|
|
57
|
-
const d = sig.getDeclaration();
|
|
58
|
-
if (d && isFn(d)) return d;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const s = checker.getSymbolAtLocation(expr);
|
|
62
|
-
if (s) return resolveFromSymbol(s, checker);
|
|
63
|
-
|
|
64
|
-
return null;
|
|
65
|
-
}
|