@eventra_dev/eventra-cli 0.1.1 → 0.1.3
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 +48 -73
- package/dist/commands/check.js +18 -7
- package/dist/commands/send.js +7 -1
- package/dist/commands/sync.js +5 -3
- package/dist/commands/watch.js +13 -55
- package/dist/engine/callResolver.js +15 -24
- package/dist/engine/engine.js +32 -7
- package/dist/engine/graph.js +50 -9
- package/dist/engine/hash.js +10 -0
- package/dist/engine/languageService.js +74 -0
- package/dist/engine/resolver.js +88 -24
- package/dist/engine/scanner.js +57 -67
- package/dist/utils/config.js +7 -2
- package/dist/utils/parsers/astro.js +8 -1
- package/dist/utils/parsers/router.js +1 -0
- package/dist/utils/parsers/svelte.js +4 -1
- package/dist/utils/parsers/vue.js +13 -4
- package/dist/utils/processFile.js +3 -1
- package/package.json +1 -1
- package/src/commands/check.ts +21 -7
- package/src/commands/send.ts +8 -2
- package/src/commands/sync.ts +6 -3
- package/src/commands/watch.ts +14 -60
- package/src/engine/callResolver.ts +21 -32
- package/src/engine/engine.ts +40 -7
- package/src/engine/graph.ts +58 -11
- package/src/engine/hash.ts +5 -0
- package/src/engine/languageService.ts +103 -0
- package/src/engine/resolver.ts +104 -24
- package/src/engine/scanner.ts +67 -74
- package/src/types.ts +5 -0
- package/src/utils/config.ts +15 -33
- package/src/utils/parsers/astro.ts +11 -4
- package/src/utils/parsers/router.ts +5 -10
- package/src/utils/parsers/svelte.ts +6 -4
- package/src/utils/parsers/vue.ts +15 -5
- package/src/utils/processFile.ts +6 -3
- package/dist/engine/extract.js +0 -53
- package/dist/engine/flow.js +0 -38
- package/dist/engine/project.js +0 -74
- package/dist/engine/symbol.js +0 -31
- package/src/engine/extract.ts +0 -74
- package/src/engine/flow.ts +0 -54
- package/src/engine/project.ts +0 -75
- package/src/engine/symbol.ts +0 -42
package/dist/engine/resolver.js
CHANGED
|
@@ -1,36 +1,100 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.resolveNodeValue = resolveNodeValue;
|
|
4
|
-
const
|
|
5
|
-
function resolveNodeValue(node, checker) {
|
|
6
|
-
if (
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
9
|
+
if (seen.has(node))
|
|
7
10
|
return null;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
seen.add(node);
|
|
12
|
+
// param mapping
|
|
13
|
+
if (typescript_1.default.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
14
|
+
return resolveNodeValue(paramMap.get(node.text), checker, paramMap);
|
|
15
|
+
}
|
|
16
|
+
if (typescript_1.default.isStringLiteral(node)) {
|
|
17
|
+
return { values: [node.text], dynamic: false };
|
|
18
|
+
}
|
|
19
|
+
if (typescript_1.default.isTemplateExpression(node)) {
|
|
20
|
+
let result = node.head.text;
|
|
21
|
+
for (const span of node.templateSpans) {
|
|
22
|
+
result += "*";
|
|
23
|
+
result += span.literal.text;
|
|
24
|
+
}
|
|
25
|
+
return { values: [result], dynamic: true };
|
|
26
|
+
}
|
|
27
|
+
if (typescript_1.default.isConditionalExpression(node)) {
|
|
28
|
+
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
29
|
+
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
30
|
+
return {
|
|
31
|
+
values: [...new Set([...(a?.values ?? []), ...(b?.values ?? [])])],
|
|
32
|
+
dynamic: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (typescript_1.default.isIdentifier(node)) {
|
|
36
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
37
|
+
if (!symbol)
|
|
38
|
+
return null;
|
|
39
|
+
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
40
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
41
|
+
}
|
|
42
|
+
for (const decl of symbol.getDeclarations() ?? []) {
|
|
43
|
+
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
44
|
+
return resolveNodeValue(decl.initializer, checker, paramMap, seen);
|
|
17
45
|
}
|
|
18
|
-
if (
|
|
19
|
-
|
|
46
|
+
if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
|
|
47
|
+
if (typescript_1.default.isStringLiteral(decl.initializer)) {
|
|
48
|
+
return {
|
|
49
|
+
values: [decl.initializer.text],
|
|
50
|
+
dynamic: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
20
53
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (typescript_1.default.isPropertyAccessExpression(node)) {
|
|
57
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
58
|
+
if (symbol) {
|
|
59
|
+
let s = symbol;
|
|
60
|
+
if (s.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
61
|
+
s = checker.getAliasedSymbol(s);
|
|
62
|
+
}
|
|
63
|
+
for (const decl of s.getDeclarations() ?? []) {
|
|
64
|
+
if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
|
|
65
|
+
if (typescript_1.default.isStringLiteral(decl.initializer)) {
|
|
66
|
+
return {
|
|
67
|
+
values: [decl.initializer.text],
|
|
68
|
+
dynamic: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
26
71
|
}
|
|
27
|
-
return { values: [result], dynamic: true };
|
|
28
72
|
}
|
|
29
73
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
74
|
+
}
|
|
75
|
+
if (typescript_1.default.isObjectLiteralExpression(node)) {
|
|
76
|
+
const values = [];
|
|
77
|
+
for (const prop of node.properties) {
|
|
78
|
+
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
79
|
+
continue;
|
|
80
|
+
const name = prop.name.getText();
|
|
81
|
+
if (name === "event") {
|
|
82
|
+
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
83
|
+
if (res)
|
|
84
|
+
values.push(...res.values);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { values, dynamic: true };
|
|
88
|
+
}
|
|
89
|
+
if (typescript_1.default.isBinaryExpression(node)) {
|
|
90
|
+
if (node.operatorToken.kind === typescript_1.default.SyntaxKind.PlusToken) {
|
|
91
|
+
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
92
|
+
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
93
|
+
if (left && right) {
|
|
94
|
+
return {
|
|
95
|
+
values: left.values.flatMap(l => right.values.map(r => l + r)),
|
|
96
|
+
dynamic: true,
|
|
97
|
+
};
|
|
34
98
|
}
|
|
35
99
|
}
|
|
36
100
|
}
|
package/dist/engine/scanner.js
CHANGED
|
@@ -1,78 +1,68 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.scanSource = scanSource;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
const resolver_1 = require("./resolver");
|
|
6
9
|
const callResolver_1 = require("./callResolver");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return expr.getText();
|
|
11
|
-
if (ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
12
|
-
return expr.getName();
|
|
13
|
-
return expr.getText();
|
|
14
|
-
}
|
|
15
|
-
function isTrackLike(name) {
|
|
16
|
-
return /(track|trackFeature)/i.test(name);
|
|
17
|
-
}
|
|
18
|
-
function pickArg(args) {
|
|
19
|
-
return (args.find(ts_morph_1.Node.isStringLiteral) ??
|
|
20
|
-
args.find(ts_morph_1.Node.isTemplateExpression) ??
|
|
21
|
-
args.find(ts_morph_1.Node.isObjectLiteralExpression) ??
|
|
22
|
-
args[0]);
|
|
23
|
-
}
|
|
24
|
-
function scanSource(source, checker) {
|
|
10
|
+
function scanSource(source, checker, visited = new Set(), depth = 0) {
|
|
11
|
+
if (depth > 5)
|
|
12
|
+
return new Set();
|
|
25
13
|
const events = new Set();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
14
|
+
if (visited.has(source.fileName))
|
|
15
|
+
return events;
|
|
16
|
+
visited.add(source.fileName);
|
|
17
|
+
function visit(node) {
|
|
18
|
+
if (typescript_1.default.isCallExpression(node)) {
|
|
19
|
+
const name = getCallName(node.expression);
|
|
20
|
+
if (/(track|event|capture|send)/i.test(name)) {
|
|
21
|
+
const fn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
|
|
22
|
+
// param mapping
|
|
23
|
+
let paramMap = new Map();
|
|
24
|
+
if (fn && typescript_1.default.isFunctionLike(fn)) {
|
|
25
|
+
fn.parameters.forEach((param, index) => {
|
|
26
|
+
const arg = node.arguments[index];
|
|
27
|
+
if (!arg)
|
|
28
|
+
return;
|
|
29
|
+
if (typescript_1.default.isIdentifier(param.name)) {
|
|
30
|
+
paramMap.set(param.name.text, arg);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// cross-file traversal
|
|
35
|
+
if (fn) {
|
|
36
|
+
const sf = fn.getSourceFile();
|
|
37
|
+
if (!visited.has(sf.fileName)) {
|
|
38
|
+
scanSource(sf, checker, visited, depth + 1)
|
|
39
|
+
.forEach(e => events.add(e));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const arg of node.arguments) {
|
|
43
|
+
const res = (0, resolver_1.resolveNodeValue)(arg, checker, paramMap);
|
|
44
|
+
res?.values.forEach(v => {
|
|
45
|
+
if (v &&
|
|
46
|
+
v.length > 1 &&
|
|
47
|
+
v.length < 100 &&
|
|
48
|
+
!v.includes(" ") &&
|
|
49
|
+
/^[a-zA-Z0-9._:-]+$/.test(v)) {
|
|
50
|
+
events.add(v);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
|
-
|
|
44
|
-
if (isValid(value))
|
|
45
|
-
events.add(value);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
// JSX
|
|
49
|
-
const jsx = [
|
|
50
|
-
...source.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxOpeningElement),
|
|
51
|
-
...source.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement),
|
|
52
|
-
];
|
|
53
|
-
for (const el of jsx) {
|
|
54
|
-
for (const attr of el.getAttributes()) {
|
|
55
|
-
if (!ts_morph_1.Node.isJsxAttribute(attr))
|
|
56
|
-
continue;
|
|
57
|
-
const name = attr.getNameNode().getText();
|
|
58
|
-
if (!/(event|name|action)/i.test(name))
|
|
59
|
-
continue;
|
|
60
|
-
const init = attr.getInitializer();
|
|
61
|
-
if (!init)
|
|
62
|
-
continue;
|
|
63
|
-
const expr = ts_morph_1.Node.isJsxExpression(init)
|
|
64
|
-
? init.getExpression()
|
|
65
|
-
: init;
|
|
66
|
-
if (!expr)
|
|
67
|
-
continue;
|
|
68
|
-
(0, extract_1.extractEventsFromArgs)([expr], checker).forEach(({ value }) => {
|
|
69
|
-
if (isValid(value))
|
|
70
|
-
events.add(value);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
56
|
+
typescript_1.default.forEachChild(node, visit);
|
|
73
57
|
}
|
|
58
|
+
visit(source);
|
|
74
59
|
return events;
|
|
75
60
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
61
|
+
function getCallName(expr) {
|
|
62
|
+
if (typescript_1.default.isIdentifier(expr))
|
|
63
|
+
return expr.text;
|
|
64
|
+
if (typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
65
|
+
return expr.name.text;
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
78
68
|
}
|
package/dist/utils/config.js
CHANGED
|
@@ -36,8 +36,13 @@ async function loadConfig() {
|
|
|
36
36
|
if (!(await fs_extra_1.default.pathExists(configPath))) {
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
try {
|
|
40
|
+
const config = await fs_extra_1.default.readJSON(configPath);
|
|
41
|
+
return normalizeConfig(config);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
47
|
async function saveConfig(config) {
|
|
43
48
|
const configPath = path_1.default.join(process.cwd(), exports.CONFIG_NAME);
|
|
@@ -2,5 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseAstro = parseAstro;
|
|
4
4
|
function parseAstro(content) {
|
|
5
|
-
|
|
5
|
+
const frontmatter = content.match(/---([\s\S]*?)---/);
|
|
6
|
+
const scripts = [
|
|
7
|
+
...content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/g)
|
|
8
|
+
];
|
|
9
|
+
return [
|
|
10
|
+
frontmatter?.[1] ?? "",
|
|
11
|
+
...scripts.map(s => s[1])
|
|
12
|
+
].join("\n");
|
|
6
13
|
}
|
|
@@ -2,5 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseSvelte = parseSvelte;
|
|
4
4
|
function parseSvelte(content) {
|
|
5
|
-
|
|
5
|
+
const scriptMatches = [
|
|
6
|
+
...content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/g)
|
|
7
|
+
];
|
|
8
|
+
return scriptMatches.map(m => m[1]).join("\n");
|
|
6
9
|
}
|
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseVue = parseVue;
|
|
4
4
|
function parseVue(content) {
|
|
5
|
-
const
|
|
5
|
+
const scriptMatches = [
|
|
6
|
+
...content.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/g)
|
|
7
|
+
];
|
|
8
|
+
const script = scriptMatches.map(m => m[1]).join("\n");
|
|
9
|
+
// template
|
|
6
10
|
const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
|
|
7
|
-
const script = scriptMatch?.[1] ?? "";
|
|
8
11
|
let template = templateMatch?.[1] ?? "";
|
|
9
12
|
template = template.replace(/<!--[\s\S]*?-->/g, "");
|
|
13
|
+
// Vue → JSX
|
|
10
14
|
template = template
|
|
15
|
+
.replace(/\bclass=/g, "className=")
|
|
16
|
+
.replace(/\bfor=/g, "htmlFor=")
|
|
17
|
+
.replace(/@(\w+)=/g, (_, e) => {
|
|
18
|
+
const name = e.charAt(0).toUpperCase() + e.slice(1);
|
|
19
|
+
return `on${name}=`;
|
|
20
|
+
})
|
|
11
21
|
.replace(/v-if="[^"]+"/g, "")
|
|
12
|
-
.replace(/v-for="[^"]+"/g, "")
|
|
13
|
-
.replace(/@click=/g, "onClick=");
|
|
22
|
+
.replace(/v-for="[^"]+"/g, "");
|
|
14
23
|
const jsx = `
|
|
15
24
|
const __VUE_JSX__ = (
|
|
16
25
|
<>
|
|
@@ -13,7 +13,9 @@ function processFile(file, content) {
|
|
|
13
13
|
content = (0, svelte_1.parseSvelte)(content);
|
|
14
14
|
if (parser === "astro")
|
|
15
15
|
content = (0, astro_1.parseAstro)(content);
|
|
16
|
-
const virtualFile = file.endsWith(".vue")
|
|
16
|
+
const virtualFile = file.endsWith(".vue") ||
|
|
17
|
+
file.endsWith(".svelte") ||
|
|
18
|
+
file.endsWith(".astro")
|
|
17
19
|
? file + ".tsx"
|
|
18
20
|
: file;
|
|
19
21
|
return { content, virtualFile };
|
package/package.json
CHANGED
package/src/commands/check.ts
CHANGED
|
@@ -30,8 +30,12 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
30
30
|
|
|
31
31
|
engine.scanFile(virtualFile, content);
|
|
32
32
|
|
|
33
|
-
} catch {
|
|
34
|
-
console.log(chalk.
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.log(chalk.yellow(`Skipped: ${file}`));
|
|
35
|
+
|
|
36
|
+
if (err instanceof Error) {
|
|
37
|
+
console.log(chalk.gray(err.message));
|
|
38
|
+
}
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -39,14 +43,17 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
39
43
|
const known = new Set(config.events ?? []);
|
|
40
44
|
|
|
41
45
|
const newEvents = [...found].filter(e => !known.has(e));
|
|
46
|
+
const removed = [...known].filter(e => !found.has(e));
|
|
42
47
|
|
|
43
48
|
if (fix) {
|
|
49
|
+
const merged = new Set(config.events ?? []);
|
|
50
|
+
|
|
44
51
|
newEvents.forEach(e => {
|
|
45
|
-
|
|
52
|
+
merged.add(e);
|
|
46
53
|
console.log(chalk.green(`+ ${e}`));
|
|
47
54
|
});
|
|
48
55
|
|
|
49
|
-
config.events = [...
|
|
56
|
+
config.events = [...merged].sort();
|
|
50
57
|
|
|
51
58
|
await saveConfig(config);
|
|
52
59
|
|
|
@@ -54,9 +61,16 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
if (newEvents.length) {
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
if (newEvents.length || removed.length) {
|
|
65
|
+
if (newEvents.length) {
|
|
66
|
+
console.log(chalk.red("\nNew events:"));
|
|
67
|
+
newEvents.forEach(e => console.log(chalk.red(`+ ${e}`)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (removed.length) {
|
|
71
|
+
console.log(chalk.yellow("\nRemoved events:"));
|
|
72
|
+
removed.forEach(e => console.log(chalk.yellow(`- ${e}`)));
|
|
73
|
+
}
|
|
60
74
|
|
|
61
75
|
process.exit(1);
|
|
62
76
|
}
|
package/src/commands/send.ts
CHANGED
|
@@ -81,6 +81,9 @@ export async function send() {
|
|
|
81
81
|
)
|
|
82
82
|
);
|
|
83
83
|
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
86
|
+
|
|
84
87
|
try {
|
|
85
88
|
const res = await fetch(
|
|
86
89
|
endpoint,
|
|
@@ -99,8 +102,9 @@ export async function send() {
|
|
|
99
102
|
version: CLI_VERSION,
|
|
100
103
|
runtime: "node"
|
|
101
104
|
}
|
|
102
|
-
})
|
|
103
|
-
|
|
105
|
+
}),
|
|
106
|
+
signal: controller.signal
|
|
107
|
+
},
|
|
104
108
|
);
|
|
105
109
|
|
|
106
110
|
if (!res.ok) {
|
|
@@ -203,5 +207,7 @@ export async function send() {
|
|
|
203
207
|
)
|
|
204
208
|
);
|
|
205
209
|
}
|
|
210
|
+
} finally {
|
|
211
|
+
clearTimeout(timeout);
|
|
206
212
|
}
|
|
207
213
|
}
|
package/src/commands/sync.ts
CHANGED
|
@@ -31,9 +31,12 @@ export async function sync() {
|
|
|
31
31
|
|
|
32
32
|
engine.scanFile(virtualFile, content);
|
|
33
33
|
|
|
34
|
-
} catch (
|
|
35
|
-
console.log(chalk.
|
|
36
|
-
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log(chalk.yellow(`Skipped: ${file}`));
|
|
36
|
+
|
|
37
|
+
if (err instanceof Error) {
|
|
38
|
+
console.log(chalk.gray(err.message));
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
package/src/commands/watch.ts
CHANGED
|
@@ -5,31 +5,8 @@ import fg from "fast-glob";
|
|
|
5
5
|
|
|
6
6
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
7
7
|
import { EventraEngine } from "../engine/engine";
|
|
8
|
+
import { processFile } from "../utils/processFile";
|
|
8
9
|
|
|
9
|
-
import { detectParser } from "../utils/parsers/router";
|
|
10
|
-
import { parseVue } from "../utils/parsers/vue";
|
|
11
|
-
import { parseSvelte } from "../utils/parsers/svelte";
|
|
12
|
-
import { parseAstro } from "../utils/parsers/astro";
|
|
13
|
-
|
|
14
|
-
// PREPROCESS (CONTENT)
|
|
15
|
-
function preprocess(file: string, content: string) {
|
|
16
|
-
const parser = detectParser(file);
|
|
17
|
-
|
|
18
|
-
if (parser === "vue") return parseVue(content);
|
|
19
|
-
if (parser === "svelte") return parseSvelte(content);
|
|
20
|
-
if (parser === "astro") return parseAstro(content);
|
|
21
|
-
|
|
22
|
-
return content;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// VIRTUAL FILE (PATH)
|
|
26
|
-
function toVirtualFile(file: string) {
|
|
27
|
-
return file.endsWith(".vue")
|
|
28
|
-
? file + ".tsx"
|
|
29
|
-
: file;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// WATCH
|
|
33
10
|
export async function watch() {
|
|
34
11
|
const config = await loadConfig();
|
|
35
12
|
if (!config) return;
|
|
@@ -40,20 +17,13 @@ export async function watch() {
|
|
|
40
17
|
ignore: config.sync.exclude,
|
|
41
18
|
});
|
|
42
19
|
|
|
43
|
-
if (!files.length) {
|
|
44
|
-
console.log(chalk.yellow("No files found"));
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
20
|
const engine = new EventraEngine(process.cwd());
|
|
49
21
|
|
|
50
|
-
// INITIAL SCAN
|
|
51
22
|
for (const file of files) {
|
|
52
23
|
try {
|
|
53
24
|
const raw = await fs.readFile(file, "utf-8");
|
|
54
25
|
|
|
55
|
-
const content =
|
|
56
|
-
const virtualFile = toVirtualFile(file);
|
|
26
|
+
const { content, virtualFile } = processFile(file, raw);
|
|
57
27
|
|
|
58
28
|
engine.scanFile(virtualFile, content);
|
|
59
29
|
|
|
@@ -69,9 +39,9 @@ export async function watch() {
|
|
|
69
39
|
chalk.gray(`Initial: ${config.events.length} events\n`)
|
|
70
40
|
);
|
|
71
41
|
|
|
72
|
-
// STATE
|
|
73
42
|
let locked = false;
|
|
74
43
|
let queued = new Set<string>();
|
|
44
|
+
let timer: NodeJS.Timeout | null = null;
|
|
75
45
|
|
|
76
46
|
const run = async () => {
|
|
77
47
|
if (locked) return;
|
|
@@ -84,8 +54,7 @@ export async function watch() {
|
|
|
84
54
|
try {
|
|
85
55
|
const raw = await fs.readFile(file, "utf-8");
|
|
86
56
|
|
|
87
|
-
const content =
|
|
88
|
-
const virtualFile = toVirtualFile(file);
|
|
57
|
+
const { content, virtualFile } = processFile(file, raw);
|
|
89
58
|
|
|
90
59
|
engine.updateFile(virtualFile, content);
|
|
91
60
|
|
|
@@ -116,48 +85,33 @@ export async function watch() {
|
|
|
116
85
|
|
|
117
86
|
const schedule = (file: string) => {
|
|
118
87
|
queued.add(file);
|
|
119
|
-
|
|
88
|
+
|
|
89
|
+
if (timer) clearTimeout(timer);
|
|
90
|
+
|
|
91
|
+
timer = setTimeout(run, 100);
|
|
120
92
|
};
|
|
121
93
|
|
|
122
|
-
// WATCHER
|
|
123
94
|
const watcher = chokidar.watch(config.sync.include, {
|
|
124
95
|
ignored: config.sync.exclude,
|
|
125
96
|
ignoreInitial: true,
|
|
126
|
-
awaitWriteFinish: {
|
|
127
|
-
stabilityThreshold: 50,
|
|
128
|
-
pollInterval: 10,
|
|
129
|
-
},
|
|
130
97
|
});
|
|
131
98
|
|
|
132
99
|
watcher
|
|
133
|
-
.on("add",
|
|
134
|
-
|
|
135
|
-
schedule(file);
|
|
136
|
-
})
|
|
137
|
-
.on("change", (file) => {
|
|
138
|
-
console.log(chalk.gray(`change: ${file}`));
|
|
139
|
-
schedule(file);
|
|
140
|
-
})
|
|
100
|
+
.on("add", schedule)
|
|
101
|
+
.on("change", schedule)
|
|
141
102
|
.on("unlink", async (file) => {
|
|
142
103
|
console.log(chalk.gray(`remove: ${file}`));
|
|
143
104
|
|
|
144
|
-
const virtualFile =
|
|
105
|
+
const { virtualFile } = processFile(file, "");
|
|
145
106
|
|
|
146
|
-
engine.removeFile
|
|
107
|
+
engine.removeFile(virtualFile);
|
|
147
108
|
|
|
148
109
|
const next = engine.getAllEvents();
|
|
149
110
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (removed.length) {
|
|
153
|
-
removed.forEach(e => console.log(chalk.red(`- ${e}`)));
|
|
154
|
-
|
|
155
|
-
config.events = next.sort();
|
|
156
|
-
await saveConfig(config);
|
|
157
|
-
}
|
|
111
|
+
config.events = next.sort();
|
|
112
|
+
await saveConfig(config);
|
|
158
113
|
});
|
|
159
114
|
|
|
160
|
-
// EXIT
|
|
161
115
|
process.on("SIGINT", async () => {
|
|
162
116
|
console.log(chalk.yellow("\nStopping watcher..."));
|
|
163
117
|
await watcher.close();
|
|
@@ -1,35 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export function resolveFunctionFromCall(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(Node.isArrowFunction(init) ||
|
|
23
|
-
Node.isFunctionExpression(init))
|
|
24
|
-
) {
|
|
25
|
-
return init;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (Node.isMethodDeclaration(d)) return d;
|
|
30
|
-
|
|
31
|
-
if (Node.isFunctionExpression(d) || Node.isArrowFunction(d)) {
|
|
32
|
-
return d;
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
export function resolveFunctionFromCall(
|
|
4
|
+
expr: ts.Expression,
|
|
5
|
+
checker: ts.TypeChecker
|
|
6
|
+
): ts.FunctionLikeDeclaration | null {
|
|
7
|
+
|
|
8
|
+
const type = checker.getTypeAtLocation(expr);
|
|
9
|
+
const signatures = type.getCallSignatures();
|
|
10
|
+
|
|
11
|
+
for (const sig of signatures) {
|
|
12
|
+
const decl = sig.getDeclaration();
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
decl &&
|
|
16
|
+
(ts.isFunctionDeclaration(decl) ||
|
|
17
|
+
ts.isMethodDeclaration(decl) ||
|
|
18
|
+
ts.isFunctionExpression(decl) ||
|
|
19
|
+
ts.isArrowFunction(decl))
|
|
20
|
+
) {
|
|
21
|
+
return decl;
|
|
33
22
|
}
|
|
34
23
|
}
|
|
35
24
|
|