@eventra_dev/eventra-cli 0.0.18 → 0.1.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 +74 -166
- package/dist/commands/check.js +25 -88
- package/dist/commands/sync.js +22 -77
- package/dist/commands/watch.js +135 -0
- package/dist/engine/callResolver.js +31 -0
- package/dist/engine/engine.js +33 -0
- package/dist/engine/extract.js +53 -0
- package/dist/engine/flow.js +38 -0
- package/dist/engine/graph.js +22 -0
- package/dist/engine/project.js +74 -0
- package/dist/engine/resolver.js +38 -0
- package/dist/engine/scanner.js +78 -0
- package/dist/engine/symbol.js +31 -0
- package/dist/index.js +10 -6
- package/dist/utils/parsers/vue.js +16 -12
- package/dist/utils/processFile.js +20 -0
- package/package.json +2 -1
- package/src/commands/check.ts +29 -136
- package/src/commands/sync.ts +24 -135
- package/src/commands/watch.ts +166 -0
- package/src/engine/callResolver.ts +37 -0
- package/src/engine/engine.ts +39 -0
- package/src/engine/extract.ts +74 -0
- package/src/engine/flow.ts +54 -0
- package/src/engine/graph.ts +23 -0
- package/src/engine/project.ts +75 -0
- package/src/engine/resolver.ts +53 -0
- package/src/engine/scanner.ts +90 -0
- package/src/engine/symbol.ts +42 -0
- package/src/index.ts +11 -6
- package/src/types.ts +3 -5
- 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/extract.js +0 -136
- package/dist/utils/scanners/component-wrappers.js +0 -70
- package/dist/utils/scanners/function-wrappers.js +0 -85
- package/dist/utils/scanners/track.js +0 -52
- package/src/utils/extract.ts +0 -185
- package/src/utils/scanners/component-wrappers.ts +0 -107
- package/src/utils/scanners/function-wrappers.ts +0 -180
- package/src/utils/scanners/track.ts +0 -84
|
@@ -0,0 +1,135 @@
|
|
|
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.watch = watch;
|
|
7
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
11
|
+
const config_1 = require("../utils/config");
|
|
12
|
+
const engine_1 = require("../engine/engine");
|
|
13
|
+
const router_1 = require("../utils/parsers/router");
|
|
14
|
+
const vue_1 = require("../utils/parsers/vue");
|
|
15
|
+
const svelte_1 = require("../utils/parsers/svelte");
|
|
16
|
+
const astro_1 = require("../utils/parsers/astro");
|
|
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
|
|
35
|
+
async function watch() {
|
|
36
|
+
const config = await (0, config_1.loadConfig)();
|
|
37
|
+
if (!config)
|
|
38
|
+
return;
|
|
39
|
+
console.log(chalk_1.default.blue("Watching...\n"));
|
|
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) {
|
|
50
|
+
try {
|
|
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);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
console.log(chalk_1.default.gray(`skip: ${file}`));
|
|
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);
|
|
98
|
+
};
|
|
99
|
+
// WATCHER
|
|
100
|
+
const watcher = chokidar_1.default.watch(config.sync.include, {
|
|
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
|
+
}
|
|
128
|
+
});
|
|
129
|
+
// EXIT
|
|
130
|
+
process.on("SIGINT", async () => {
|
|
131
|
+
console.log(chalk_1.default.yellow("\nStopping watcher..."));
|
|
132
|
+
await watcher.close();
|
|
133
|
+
process.exit(0);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveFunctionFromCall = resolveFunctionFromCall;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
function resolveFunctionFromCall(expr) {
|
|
6
|
+
let symbol = expr.getSymbol();
|
|
7
|
+
if (!symbol && ts_morph_1.Node.isPropertyAccessExpression(expr)) {
|
|
8
|
+
symbol = expr.getNameNode().getSymbol();
|
|
9
|
+
}
|
|
10
|
+
if (!symbol)
|
|
11
|
+
return null;
|
|
12
|
+
const decls = symbol.getDeclarations() ?? [];
|
|
13
|
+
for (const d of decls) {
|
|
14
|
+
if (ts_morph_1.Node.isFunctionDeclaration(d))
|
|
15
|
+
return d;
|
|
16
|
+
if (ts_morph_1.Node.isVariableDeclaration(d)) {
|
|
17
|
+
const init = d.getInitializer();
|
|
18
|
+
if (init &&
|
|
19
|
+
(ts_morph_1.Node.isArrowFunction(init) ||
|
|
20
|
+
ts_morph_1.Node.isFunctionExpression(init))) {
|
|
21
|
+
return init;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (ts_morph_1.Node.isMethodDeclaration(d))
|
|
25
|
+
return d;
|
|
26
|
+
if (ts_morph_1.Node.isFunctionExpression(d) || ts_morph_1.Node.isArrowFunction(d)) {
|
|
27
|
+
return d;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventraEngine = void 0;
|
|
4
|
+
const project_1 = require("./project");
|
|
5
|
+
const scanner_1 = require("./scanner");
|
|
6
|
+
class EventraEngine {
|
|
7
|
+
project;
|
|
8
|
+
fileEvents = new Map();
|
|
9
|
+
constructor(entry) {
|
|
10
|
+
this.project = new project_1.ProjectGraph(entry);
|
|
11
|
+
}
|
|
12
|
+
scanFile(file, content) {
|
|
13
|
+
const source = this.project.getSource(file, content);
|
|
14
|
+
const checker = this.project.getChecker();
|
|
15
|
+
const events = (0, scanner_1.scanSource)(source, checker);
|
|
16
|
+
this.fileEvents.set(file, events);
|
|
17
|
+
return events;
|
|
18
|
+
}
|
|
19
|
+
updateFile(file, content) {
|
|
20
|
+
return this.scanFile(file, content);
|
|
21
|
+
}
|
|
22
|
+
getAllEvents() {
|
|
23
|
+
const all = new Set();
|
|
24
|
+
for (const set of this.fileEvents.values()) {
|
|
25
|
+
set.forEach(e => all.add(e));
|
|
26
|
+
}
|
|
27
|
+
return [...all];
|
|
28
|
+
}
|
|
29
|
+
removeFile(file) {
|
|
30
|
+
this.fileEvents.delete(file);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.EventraEngine = EventraEngine;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractEventsFromArgs = extractEventsFromArgs;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
const resolver_1 = require("./resolver");
|
|
6
|
+
function extractEventsFromArgs(args, checker) {
|
|
7
|
+
return args.flatMap(arg => extract(arg, checker));
|
|
8
|
+
}
|
|
9
|
+
function extract(node, checker) {
|
|
10
|
+
if (ts_morph_1.Node.isStringLiteral(node)) {
|
|
11
|
+
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
12
|
+
}
|
|
13
|
+
if (ts_morph_1.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
14
|
+
return [{ value: node.getLiteralText(), dynamic: false }];
|
|
15
|
+
}
|
|
16
|
+
if (ts_morph_1.Node.isTemplateExpression(node)) {
|
|
17
|
+
let result = node.getHead().getLiteralText();
|
|
18
|
+
for (const span of node.getTemplateSpans()) {
|
|
19
|
+
result += "*";
|
|
20
|
+
result += span.getLiteral().getLiteralText();
|
|
21
|
+
}
|
|
22
|
+
return [{ value: result, dynamic: true }];
|
|
23
|
+
}
|
|
24
|
+
if (ts_morph_1.Node.isObjectLiteralExpression(node)) {
|
|
25
|
+
return extractObject(node, checker);
|
|
26
|
+
}
|
|
27
|
+
const resolved = (0, resolver_1.resolveNodeValue)(node, checker);
|
|
28
|
+
if (resolved) {
|
|
29
|
+
return resolved.values.map(v => ({
|
|
30
|
+
value: v,
|
|
31
|
+
dynamic: resolved.dynamic,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
function extractObject(obj, checker) {
|
|
37
|
+
const res = [];
|
|
38
|
+
for (const prop of obj.getProperties()) {
|
|
39
|
+
if (!ts_morph_1.Node.isPropertyAssignment(prop))
|
|
40
|
+
continue;
|
|
41
|
+
const name = prop.getName();
|
|
42
|
+
const init = prop.getInitializer();
|
|
43
|
+
if (!init)
|
|
44
|
+
continue;
|
|
45
|
+
if (["event", "name", "action", "type"].includes(name)) {
|
|
46
|
+
res.push(...extract(init, checker));
|
|
47
|
+
}
|
|
48
|
+
if (ts_morph_1.Node.isObjectLiteralExpression(init)) {
|
|
49
|
+
res.push(...extractObject(init, checker));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return res;
|
|
53
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getForwardedParamIndex = getForwardedParamIndex;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
function getBodyNodes(fn) {
|
|
6
|
+
const body = fn.getBody();
|
|
7
|
+
if (!body)
|
|
8
|
+
return [];
|
|
9
|
+
if (ts_morph_1.Node.isBlock(body))
|
|
10
|
+
return body.getStatements();
|
|
11
|
+
return [body];
|
|
12
|
+
}
|
|
13
|
+
function getForwardedParamIndex(fn) {
|
|
14
|
+
if (!ts_morph_1.Node.isFunctionDeclaration(fn) &&
|
|
15
|
+
!ts_morph_1.Node.isArrowFunction(fn) &&
|
|
16
|
+
!ts_morph_1.Node.isFunctionExpression(fn))
|
|
17
|
+
return null;
|
|
18
|
+
const f = fn;
|
|
19
|
+
const params = f.getParameters();
|
|
20
|
+
for (const node of getBodyNodes(f)) {
|
|
21
|
+
const calls = node.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
|
|
22
|
+
for (const call of calls) {
|
|
23
|
+
const name = call.getExpression().getText();
|
|
24
|
+
if (!/(track|event|log|analytics|capture|send)/i.test(name))
|
|
25
|
+
continue;
|
|
26
|
+
const args = call.getArguments();
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
const arg = args[i];
|
|
29
|
+
if (ts_morph_1.Node.isIdentifier(arg)) {
|
|
30
|
+
const idx = params.findIndex(p => p.getName() === arg.getText());
|
|
31
|
+
if (idx !== -1)
|
|
32
|
+
return idx;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DependencyGraph = void 0;
|
|
4
|
+
class DependencyGraph {
|
|
5
|
+
reverse = new Map();
|
|
6
|
+
update(file, source) {
|
|
7
|
+
const imports = source
|
|
8
|
+
.getImportDeclarations()
|
|
9
|
+
.map(i => i.getModuleSpecifierSourceFile()?.getFilePath())
|
|
10
|
+
.filter(Boolean);
|
|
11
|
+
for (const dep of imports) {
|
|
12
|
+
if (!this.reverse.has(dep)) {
|
|
13
|
+
this.reverse.set(dep, new Set());
|
|
14
|
+
}
|
|
15
|
+
this.reverse.get(dep).add(file);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
getDependents(file) {
|
|
19
|
+
return [...(this.reverse.get(file) ?? [])];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.DependencyGraph = DependencyGraph;
|
|
@@ -0,0 +1,74 @@
|
|
|
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.ProjectGraph = void 0;
|
|
7
|
+
const ts_morph_1 = require("ts-morph");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
function normalize(file) {
|
|
11
|
+
return path_1.default.resolve(file);
|
|
12
|
+
}
|
|
13
|
+
function findTsConfig(start) {
|
|
14
|
+
let dir = path_1.default.dirname(start);
|
|
15
|
+
while (true) {
|
|
16
|
+
const file = path_1.default.join(dir, "tsconfig.json");
|
|
17
|
+
if (fs_1.default.existsSync(file))
|
|
18
|
+
return file;
|
|
19
|
+
const parent = path_1.default.dirname(dir);
|
|
20
|
+
if (parent === dir)
|
|
21
|
+
break;
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function getScriptKind(file) {
|
|
27
|
+
if (file.endsWith(".tsx"))
|
|
28
|
+
return ts_morph_1.ts.ScriptKind.TSX;
|
|
29
|
+
if (file.endsWith(".jsx"))
|
|
30
|
+
return ts_morph_1.ts.ScriptKind.JSX;
|
|
31
|
+
if (file.endsWith(".js"))
|
|
32
|
+
return ts_morph_1.ts.ScriptKind.JS;
|
|
33
|
+
if (file.endsWith(".vue"))
|
|
34
|
+
return ts_morph_1.ts.ScriptKind.TSX;
|
|
35
|
+
return ts_morph_1.ts.ScriptKind.TS;
|
|
36
|
+
}
|
|
37
|
+
class ProjectGraph {
|
|
38
|
+
project;
|
|
39
|
+
constructor(entry) {
|
|
40
|
+
const tsconfig = findTsConfig(entry);
|
|
41
|
+
this.project = tsconfig
|
|
42
|
+
? new ts_morph_1.Project({
|
|
43
|
+
tsConfigFilePath: tsconfig,
|
|
44
|
+
skipAddingFilesFromTsConfig: false,
|
|
45
|
+
})
|
|
46
|
+
: new ts_morph_1.Project({
|
|
47
|
+
useInMemoryFileSystem: true,
|
|
48
|
+
compilerOptions: {
|
|
49
|
+
allowJs: true,
|
|
50
|
+
jsx: ts_morph_1.ts.JsxEmit.Preserve,
|
|
51
|
+
moduleResolution: ts_morph_1.ts.ModuleResolutionKind.NodeJs,
|
|
52
|
+
target: ts_morph_1.ts.ScriptTarget.ESNext,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
getSource(file, content) {
|
|
57
|
+
const filePath = normalize(file);
|
|
58
|
+
let source = this.project.getSourceFile(filePath);
|
|
59
|
+
if (!source) {
|
|
60
|
+
source = this.project.createSourceFile(filePath, content, {
|
|
61
|
+
overwrite: true,
|
|
62
|
+
scriptKind: getScriptKind(filePath),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
source.replaceWithText(content);
|
|
67
|
+
}
|
|
68
|
+
return source;
|
|
69
|
+
}
|
|
70
|
+
getChecker() {
|
|
71
|
+
return this.project.getTypeChecker().compilerObject;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.ProjectGraph = ProjectGraph;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveNodeValue = resolveNodeValue;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
function resolveNodeValue(node, checker) {
|
|
6
|
+
if (!ts_morph_1.Node.isIdentifier(node))
|
|
7
|
+
return null;
|
|
8
|
+
const symbol = checker.getSymbolAtLocation(node.compilerNode);
|
|
9
|
+
if (!symbol)
|
|
10
|
+
return null;
|
|
11
|
+
const decls = symbol.getDeclarations() ?? [];
|
|
12
|
+
for (const d of decls) {
|
|
13
|
+
if (ts_morph_1.ts.isVariableDeclaration(d) && d.initializer) {
|
|
14
|
+
const init = d.initializer;
|
|
15
|
+
if (ts_morph_1.ts.isStringLiteral(init)) {
|
|
16
|
+
return { values: [init.text], dynamic: false };
|
|
17
|
+
}
|
|
18
|
+
if (ts_morph_1.ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
19
|
+
return { values: [init.text], dynamic: false };
|
|
20
|
+
}
|
|
21
|
+
if (ts_morph_1.ts.isTemplateExpression(init)) {
|
|
22
|
+
let result = init.head.text;
|
|
23
|
+
for (const span of init.templateSpans) {
|
|
24
|
+
result += "*";
|
|
25
|
+
result += span.literal.text;
|
|
26
|
+
}
|
|
27
|
+
return { values: [result], dynamic: true };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (ts_morph_1.ts.isParameter(d) && d.initializer) {
|
|
31
|
+
const init = d.initializer;
|
|
32
|
+
if (ts_morph_1.ts.isStringLiteral(init)) {
|
|
33
|
+
return { values: [init.text], dynamic: false };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanSource = scanSource;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
const extract_1 = require("./extract");
|
|
6
|
+
const callResolver_1 = require("./callResolver");
|
|
7
|
+
const flow_1 = require("./flow");
|
|
8
|
+
function getCallName(expr) {
|
|
9
|
+
if (ts_morph_1.Node.isIdentifier(expr))
|
|
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) {
|
|
25
|
+
const events = new Set();
|
|
26
|
+
// CALLS
|
|
27
|
+
for (const call of source.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)) {
|
|
28
|
+
const expr = call.getExpression();
|
|
29
|
+
const name = getCallName(expr);
|
|
30
|
+
if (!isTrackLike(name))
|
|
31
|
+
continue;
|
|
32
|
+
const args = call.getArguments();
|
|
33
|
+
if (!args.length)
|
|
34
|
+
continue;
|
|
35
|
+
let target = pickArg(args);
|
|
36
|
+
const fn = (0, callResolver_1.resolveFunctionFromCall)(expr);
|
|
37
|
+
if (fn) {
|
|
38
|
+
const index = (0, flow_1.getForwardedParamIndex)(fn);
|
|
39
|
+
if (index !== null && args[index]) {
|
|
40
|
+
target = args[index];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
(0, extract_1.extractEventsFromArgs)([target], checker).forEach(({ value }) => {
|
|
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
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return events;
|
|
75
|
+
}
|
|
76
|
+
function isValid(v) {
|
|
77
|
+
return !!v && v.length > 1 && !v.includes(" ");
|
|
78
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveNodeValue = resolveNodeValue;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
function resolveNodeValue(node, checker) {
|
|
6
|
+
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
7
|
+
const symbol = checker.getSymbolAtLocation(node.compilerNode);
|
|
8
|
+
if (!symbol)
|
|
9
|
+
return null;
|
|
10
|
+
const decls = symbol.getDeclarations() ?? [];
|
|
11
|
+
for (const d of decls) {
|
|
12
|
+
if (ts_morph_1.ts.isVariableDeclaration(d) && d.initializer) {
|
|
13
|
+
if (ts_morph_1.Node.isStringLiteral(d.initializer)) {
|
|
14
|
+
return {
|
|
15
|
+
values: [d.initializer.text],
|
|
16
|
+
dynamic: false
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (ts_morph_1.ts.isParameter(d) && d.initializer) {
|
|
21
|
+
if (ts_morph_1.Node.isStringLiteral(d.initializer)) {
|
|
22
|
+
return {
|
|
23
|
+
values: [d.initializer.text],
|
|
24
|
+
dynamic: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -42,28 +42,32 @@ program
|
|
|
42
42
|
.version("0.0.1");
|
|
43
43
|
program
|
|
44
44
|
.command("init")
|
|
45
|
-
.description("Initialize Eventra")
|
|
46
45
|
.action(async () => {
|
|
47
46
|
const { init } = await Promise.resolve().then(() => __importStar(require("./commands/init")));
|
|
48
47
|
await init();
|
|
49
48
|
});
|
|
50
49
|
program
|
|
51
50
|
.command("sync")
|
|
52
|
-
.description("Sync events")
|
|
53
51
|
.action(async () => {
|
|
54
52
|
const { sync } = await Promise.resolve().then(() => __importStar(require("./commands/sync")));
|
|
55
53
|
await sync();
|
|
56
54
|
});
|
|
57
55
|
program
|
|
58
56
|
.command("check")
|
|
59
|
-
.
|
|
60
|
-
.action(async () => {
|
|
57
|
+
.option("--fix", "auto fix issues")
|
|
58
|
+
.action(async (opts) => {
|
|
61
59
|
const { check } = await Promise.resolve().then(() => __importStar(require("./commands/check")));
|
|
62
|
-
await check();
|
|
60
|
+
await check({ fix: opts.fix });
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command("watch")
|
|
64
|
+
.description("Watch files and detect events in real-time")
|
|
65
|
+
.action(async () => {
|
|
66
|
+
const { watch } = await Promise.resolve().then(() => __importStar(require("./commands/watch")));
|
|
67
|
+
await watch();
|
|
63
68
|
});
|
|
64
69
|
program
|
|
65
70
|
.command("send")
|
|
66
|
-
.description("Send events")
|
|
67
71
|
.action(async () => {
|
|
68
72
|
const { send } = await Promise.resolve().then(() => __importStar(require("./commands/send")));
|
|
69
73
|
await send();
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseVue = parseVue;
|
|
4
4
|
function parseVue(content) {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
const scriptMatch = content.match(/<script[\s\S]*?>([\s\S]*?)<\/script>/);
|
|
6
|
+
const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
|
|
7
|
+
const script = scriptMatch?.[1] ?? "";
|
|
8
|
+
let template = templateMatch?.[1] ?? "";
|
|
9
|
+
template = template.replace(/<!--[\s\S]*?-->/g, "");
|
|
10
|
+
template = template
|
|
11
|
+
.replace(/v-if="[^"]+"/g, "")
|
|
12
|
+
.replace(/v-for="[^"]+"/g, "")
|
|
13
|
+
.replace(/@click=/g, "onClick=");
|
|
14
|
+
const jsx = `
|
|
15
|
+
const __VUE_JSX__ = (
|
|
16
|
+
<>
|
|
17
|
+
${template}
|
|
18
|
+
</>
|
|
19
|
+
);
|
|
17
20
|
`;
|
|
21
|
+
return script + "\n" + jsx;
|
|
18
22
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processFile = processFile;
|
|
4
|
+
const router_1 = require("./parsers/router");
|
|
5
|
+
const vue_1 = require("./parsers/vue");
|
|
6
|
+
const svelte_1 = require("./parsers/svelte");
|
|
7
|
+
const astro_1 = require("./parsers/astro");
|
|
8
|
+
function processFile(file, content) {
|
|
9
|
+
const parser = (0, router_1.detectParser)(file);
|
|
10
|
+
if (parser === "vue")
|
|
11
|
+
content = (0, vue_1.parseVue)(content);
|
|
12
|
+
if (parser === "svelte")
|
|
13
|
+
content = (0, svelte_1.parseSvelte)(content);
|
|
14
|
+
if (parser === "astro")
|
|
15
|
+
content = (0, astro_1.parseAstro)(content);
|
|
16
|
+
const virtualFile = file.endsWith(".vue")
|
|
17
|
+
? file + ".tsx"
|
|
18
|
+
: file;
|
|
19
|
+
return { content, virtualFile };
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventra_dev/eventra-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Eventra CLI",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"chalk": "^4.1.2",
|
|
23
|
+
"chokidar": "^5.0.0",
|
|
23
24
|
"commander": "^11.0.0",
|
|
24
25
|
"fast-glob": "^3.3.3",
|
|
25
26
|
"fs-extra": "^11.2.0",
|