1 | "use strict";
|
2 | var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
|
3 | if (!privateMap.has(receiver)) {
|
4 | throw new TypeError("attempted to set private field on non-instance");
|
5 | }
|
6 | privateMap.set(receiver, value);
|
7 | return value;
|
8 | };
|
9 | var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
|
10 | if (!privateMap.has(receiver)) {
|
11 | throw new TypeError("attempted to get private field on non-instance");
|
12 | }
|
13 | return privateMap.get(receiver);
|
14 | };
|
15 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
16 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
17 | };
|
18 | var _resolve, _reject;
|
19 | const fs_1 = __importDefault(require("fs"));
|
20 | const assert_1 = __importDefault(require("assert"));
|
21 | const callsites_1 = __importDefault(require("callsites"));
|
22 | const acorn_loose_1 = require("acorn-loose");
|
23 | const estree_walker_1 = require("estree-walker");
|
24 | const esbuild_1 = require("esbuild");
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | const getFileCode = async (filePath) => (await fs_1.default.promises.readFile(filePath)).toString();
|
40 | const isCallExpression = (node) => node.type === 'CallExpression';
|
41 | const isIdentifier = (node) => node.type === 'Identifier';
|
42 | function getCallerFilePath() {
|
43 | const stack = callsites_1.default();
|
44 | const currentFilePath = stack.shift().getFileName();
|
45 | let callerFilePath;
|
46 | while (stack.length > 0) {
|
47 | callerFilePath = stack.shift().getFileName();
|
48 | if (currentFilePath !== callerFilePath) {
|
49 | break;
|
50 | }
|
51 | }
|
52 | return callerFilePath;
|
53 | }
|
54 | function findNode(fileCode, conditionCallback) {
|
55 | const ast = acorn_loose_1.parse(fileCode, {
|
56 | ecmaVersion: 'latest',
|
57 | sourceType: 'module',
|
58 | });
|
59 | let nodeString;
|
60 | estree_walker_1.walk(ast, {
|
61 | enter(node) {
|
62 | if (nodeString) {
|
63 | this.skip();
|
64 | return;
|
65 | }
|
66 | const nodeMatch = conditionCallback(node);
|
67 | if (nodeMatch) {
|
68 | nodeString = fileCode.slice(nodeMatch.start, nodeMatch.end);
|
69 | }
|
70 | },
|
71 | });
|
72 | return nodeString;
|
73 | }
|
74 | async function getDbgrHookCode(fileCode, isTs) {
|
75 | let dbgrHookCode = findNode(fileCode, (node) => {
|
76 | if (isCallExpression(node)
|
77 | && isIdentifier(node.callee)
|
78 | && node.callee.name === 'dbgr') {
|
79 | const [dbgrHook, evalCallback] = node.arguments;
|
80 | assert_1.default(dbgrHook === null || dbgrHook === void 0 ? void 0 : dbgrHook.type.endsWith('FunctionExpression'), 'Dbgr hook function is missing');
|
81 | assert_1.default(evalCallback === null || evalCallback === void 0 ? void 0 : evalCallback.type.endsWith('FunctionExpression'), 'Eval callback function is missing');
|
82 | return dbgrHook;
|
83 | }
|
84 | });
|
85 | assert_1.default(dbgrHookCode, 'Dbgr call not found');
|
86 |
|
87 | dbgrHookCode = `(${dbgrHookCode})`;
|
88 | if (isTs) {
|
89 | const { code } = await esbuild_1.transform(dbgrHookCode, {
|
90 | loader: 'ts',
|
91 | });
|
92 | dbgrHookCode = code;
|
93 | }
|
94 | return dbgrHookCode;
|
95 | }
|
96 | class Deferred {
|
97 | constructor() {
|
98 | this.isResolved = false;
|
99 | _resolve.set(this, void 0);
|
100 | _reject.set(this, void 0);
|
101 | this.$ = new Promise((resolve, reject) => {
|
102 | __classPrivateFieldSet(this, _resolve, resolve);
|
103 | __classPrivateFieldSet(this, _reject, reject);
|
104 | });
|
105 | this.resolve = this.resolve.bind(this);
|
106 | }
|
107 | resolve(value) {
|
108 | this.isResolved = true;
|
109 | __classPrivateFieldGet(this, _resolve).call(this, value);
|
110 | }
|
111 | }
|
112 | _resolve = new WeakMap(), _reject = new WeakMap();
|
113 | async function dbgr(dbgrHook, evalCallback) {
|
114 | assert_1.default(typeof dbgrHook === 'function', 'Dbgr hook must be a function');
|
115 | assert_1.default((typeof evalCallback === 'function'
|
116 | && evalCallback.length === 1
|
117 | && /\(?_\)?\s?=>\s?eval\(_\)/.test(evalCallback.toString())), 'Invalid eval callback');
|
118 | const callerFilePath = getCallerFilePath();
|
119 | const isTs = callerFilePath.endsWith('.ts');
|
120 | let lastCode = await getFileCode(callerFilePath);
|
121 | let lastDbgrHookCode = await getDbgrHookCode(lastCode, isTs);
|
122 | const deferred = new Deferred();
|
123 | await dbgrHook(deferred.resolve);
|
124 | let watcher;
|
125 | if (!deferred.isResolved) {
|
126 | watcher = fs_1.default.watch(callerFilePath, async (eventName) => {
|
127 | if (eventName !== 'change') {
|
128 | return;
|
129 | }
|
130 | const newCode = await getFileCode(callerFilePath);
|
131 | if (newCode === lastCode) {
|
132 | return;
|
133 | }
|
134 | lastCode = newCode;
|
135 | const dbgrHookCode = await getDbgrHookCode(newCode, isTs);
|
136 | if (dbgrHookCode === lastDbgrHookCode) {
|
137 | return;
|
138 | }
|
139 | lastDbgrHookCode = dbgrHookCode;
|
140 | evalCallback(dbgrHookCode)(deferred.resolve);
|
141 | });
|
142 | }
|
143 | await deferred.$;
|
144 | if (watcher) {
|
145 | watcher.close();
|
146 | }
|
147 | }
|
148 | module.exports = dbgr;
|