smipper 17.0.0 → 17.0.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/index.js +236 -148
- package/index.ts +373 -0
- package/package.json +9 -4
- package/prettier.config.js +6 -0
- package/tsconfig.json +28 -0
package/index.js
CHANGED
|
@@ -1,59 +1,104 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
13
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
14
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
15
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
16
|
+
function step(op) {
|
|
17
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
18
|
+
while (_) try {
|
|
19
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
20
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
21
|
+
switch (op[0]) {
|
|
22
|
+
case 0: case 1: t = op; break;
|
|
23
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
24
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
25
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
26
|
+
default:
|
|
27
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
28
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
29
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
30
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
31
|
+
if (t[2]) _.ops.pop();
|
|
32
|
+
_.trys.pop(); continue;
|
|
33
|
+
}
|
|
34
|
+
op = body.call(thisArg, _);
|
|
35
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
36
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
exports.__esModule = true;
|
|
40
|
+
var fs = require("fs");
|
|
41
|
+
var got = require("got");
|
|
42
|
+
var path = require("path");
|
|
43
|
+
var sourceMap = require("source-map");
|
|
44
|
+
var url = require("url");
|
|
45
|
+
var verbose = false;
|
|
46
|
+
var stack;
|
|
47
|
+
var json = false;
|
|
48
|
+
var retries = 3;
|
|
49
|
+
var jsc = 0;
|
|
50
|
+
for (var i = 2; i < process.argv.length; ++i) {
|
|
18
51
|
try {
|
|
19
|
-
|
|
20
|
-
if (verbose)
|
|
52
|
+
var arg = process.argv[i] || "";
|
|
53
|
+
if (verbose) {
|
|
21
54
|
console.error("got arg", arg);
|
|
55
|
+
}
|
|
22
56
|
if (arg === "-f" || arg === "--file") {
|
|
23
57
|
stack = fs.readFileSync(process.argv[++i]).toString();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
}
|
|
59
|
+
else if (arg.startsWith("-f")) {
|
|
60
|
+
stack = fs.readFileSync(arg.substring(2)).toString();
|
|
61
|
+
}
|
|
62
|
+
else if (arg.startsWith("--file=")) {
|
|
63
|
+
stack = fs.readFileSync(arg.substring(7)).toString();
|
|
64
|
+
}
|
|
65
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
29
66
|
verbose = true;
|
|
30
|
-
}
|
|
67
|
+
}
|
|
68
|
+
else if (arg === "--version") {
|
|
31
69
|
console.log(JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version);
|
|
32
70
|
process.exit(0);
|
|
33
|
-
}
|
|
71
|
+
}
|
|
72
|
+
else if (arg === "--json") {
|
|
34
73
|
json = true;
|
|
35
|
-
}
|
|
74
|
+
}
|
|
75
|
+
else if (arg === "--jsc") {
|
|
76
|
+
// jsc has the wrong column for some reason
|
|
36
77
|
jsc = 1;
|
|
37
|
-
}
|
|
38
|
-
|
|
78
|
+
}
|
|
79
|
+
else if (arg.startsWith("--retries")) {
|
|
80
|
+
retries = parseInt(arg.substring(9));
|
|
39
81
|
if (isNaN(retries) || retries < 0) {
|
|
40
82
|
console.error("smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-");
|
|
41
83
|
process.exit(1);
|
|
42
84
|
}
|
|
43
|
-
}
|
|
85
|
+
}
|
|
86
|
+
else if (arg === "-h" || arg === "--help") {
|
|
44
87
|
console.error("smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-");
|
|
45
88
|
process.exit(0);
|
|
46
|
-
}
|
|
89
|
+
}
|
|
90
|
+
else if (arg === "-") {
|
|
47
91
|
// stdin
|
|
48
|
-
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
49
94
|
stack = arg;
|
|
50
95
|
}
|
|
51
|
-
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
52
98
|
console.error("Error: " + err.toString());
|
|
53
99
|
process.exit(1);
|
|
54
100
|
}
|
|
55
101
|
}
|
|
56
|
-
|
|
57
102
|
if (!stack) {
|
|
58
103
|
stack = fs.readFileSync("/dev/stdin").toString();
|
|
59
104
|
if (!stack) {
|
|
@@ -61,195 +106,226 @@ if (!stack) {
|
|
|
61
106
|
process.exit(0);
|
|
62
107
|
}
|
|
63
108
|
}
|
|
64
|
-
|
|
109
|
+
function build(functionName, url, line, column) {
|
|
110
|
+
return "".concat(functionName ? "at " + functionName + " " : "").concat(url, ":").concat(line, ":").concat(column);
|
|
111
|
+
}
|
|
65
112
|
function findFile(dir, fn) {
|
|
66
|
-
|
|
67
|
-
for (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
113
|
+
var list = fs.readdirSync(dir);
|
|
114
|
+
for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
|
|
115
|
+
var file = list_1[_i];
|
|
116
|
+
var match = file === fn;
|
|
117
|
+
file = dir + "/" + file;
|
|
118
|
+
var stat = fs.statSync(file);
|
|
71
119
|
if (stat && stat.isDirectory()) {
|
|
72
|
-
|
|
120
|
+
var ret = findFile(file, fn);
|
|
73
121
|
if (ret) {
|
|
74
122
|
return ret;
|
|
75
123
|
}
|
|
76
|
-
}
|
|
124
|
+
}
|
|
125
|
+
else if (match) {
|
|
77
126
|
return "file://" + file;
|
|
78
127
|
}
|
|
79
128
|
}
|
|
80
129
|
return undefined;
|
|
81
130
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
const found = findFile(process.cwd(), path.basename(url.substring(6)));
|
|
86
|
-
if (found)
|
|
131
|
+
function rewriteLocalControl(url) {
|
|
132
|
+
var found = findFile(process.cwd(), path.basename(url.substring(6)));
|
|
133
|
+
if (found) {
|
|
87
134
|
return found;
|
|
135
|
+
}
|
|
88
136
|
throw new Error("Couldn't resolve localcontrol " + url);
|
|
89
137
|
}
|
|
90
|
-
|
|
91
|
-
function
|
|
92
|
-
{
|
|
93
|
-
return new Promise((resolve, reject) => {
|
|
138
|
+
function load(path) {
|
|
139
|
+
return new Promise(function (resolve, reject) {
|
|
94
140
|
if (path.startsWith("http://localcontrol.netflix.com/")) {
|
|
95
141
|
try {
|
|
96
142
|
path = rewriteLocalControl(path);
|
|
97
|
-
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
98
145
|
reject(err);
|
|
99
146
|
return;
|
|
100
147
|
}
|
|
101
148
|
}
|
|
102
|
-
|
|
103
149
|
if (path.startsWith("file:///")) {
|
|
104
150
|
try {
|
|
105
|
-
resolve(fs.readFileSync(path.
|
|
106
|
-
}
|
|
151
|
+
resolve(fs.readFileSync(path.substring(7), "utf8"));
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
107
154
|
reject(err);
|
|
108
155
|
}
|
|
109
156
|
return;
|
|
110
157
|
}
|
|
111
|
-
|
|
112
158
|
if (!path.startsWith("http://") && !path.startsWith("https://")) {
|
|
113
159
|
reject(new Error("Not a url"));
|
|
114
160
|
return;
|
|
115
161
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
162
|
+
// let retryIndex = 0;
|
|
163
|
+
function get() {
|
|
164
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
165
|
+
var response;
|
|
166
|
+
return __generator(this, function (_a) {
|
|
167
|
+
switch (_a.label) {
|
|
168
|
+
case 0: return [4 /*yield*/, got.get(path, { timeout: 10000 })];
|
|
169
|
+
case 1:
|
|
170
|
+
response = _a.sent();
|
|
171
|
+
console.log(response.headers);
|
|
172
|
+
return [2 /*return*/, response.body || ""];
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
126
176
|
}
|
|
127
|
-
|
|
128
177
|
get().then(resolve, reject);
|
|
129
178
|
});
|
|
130
179
|
}
|
|
131
|
-
|
|
132
|
-
const sourceMaps = {};
|
|
180
|
+
var sourceMaps = {};
|
|
133
181
|
function loadUri(path) {
|
|
182
|
+
var _this = this;
|
|
134
183
|
if (path[0] === "/") {
|
|
135
184
|
path = "file://" + path;
|
|
136
185
|
}
|
|
137
|
-
return new Promise((resolve, reject)
|
|
138
|
-
if (verbose)
|
|
186
|
+
return new Promise(function (resolve, reject) {
|
|
187
|
+
if (verbose) {
|
|
139
188
|
console.log("loading", path);
|
|
189
|
+
}
|
|
140
190
|
if (!(path in sourceMaps)) {
|
|
141
191
|
sourceMaps[path] = {
|
|
142
|
-
resolvers: [
|
|
143
|
-
rejecters: [
|
|
192
|
+
resolvers: [resolve],
|
|
193
|
+
rejecters: [reject]
|
|
144
194
|
};
|
|
145
|
-
load(path)
|
|
146
|
-
|
|
147
|
-
|
|
195
|
+
load(path)
|
|
196
|
+
.then(function (jsData) {
|
|
197
|
+
var idx = jsData.lastIndexOf("//# sourceMappingURL=");
|
|
198
|
+
if (verbose) {
|
|
148
199
|
console.error("Got the file", jsData.length, idx);
|
|
149
|
-
|
|
200
|
+
}
|
|
201
|
+
if (idx == -1) {
|
|
150
202
|
return path + ".map";
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
203
|
+
}
|
|
204
|
+
var mapUrl = jsData.substring(idx + 21);
|
|
205
|
+
var match = /^(data:.+\/.+;)base64,/.exec(mapUrl);
|
|
154
206
|
if (match) {
|
|
155
|
-
return mapUrl.
|
|
207
|
+
return mapUrl.substring(match[1].length);
|
|
156
208
|
}
|
|
157
209
|
if (mapUrl.indexOf("://") != -1) {
|
|
158
210
|
return mapUrl;
|
|
159
211
|
}
|
|
160
212
|
// console.log(mapUrl);
|
|
161
|
-
return
|
|
162
|
-
})
|
|
163
|
-
|
|
213
|
+
return new url.URL(mapUrl, path).href;
|
|
214
|
+
})
|
|
215
|
+
.then(function (mapUrl) {
|
|
216
|
+
// console.log(mapUrl.substring(0, 20));
|
|
164
217
|
if (mapUrl.startsWith("base64,")) {
|
|
165
|
-
return Buffer.from(mapUrl.
|
|
218
|
+
return Buffer.from(mapUrl.substring(7), "base64").toString();
|
|
166
219
|
}
|
|
167
220
|
return load(mapUrl);
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
221
|
+
})
|
|
222
|
+
.then(function (sourceMapData) { return __awaiter(_this, void 0, void 0, function () {
|
|
223
|
+
var parsed, smap, pending;
|
|
224
|
+
return __generator(this, function (_a) {
|
|
225
|
+
switch (_a.label) {
|
|
226
|
+
case 0:
|
|
227
|
+
parsed = JSON.parse(sourceMapData);
|
|
228
|
+
return [4 /*yield*/, new sourceMap.SourceMapConsumer(parsed)];
|
|
229
|
+
case 1:
|
|
230
|
+
smap = _a.sent();
|
|
231
|
+
pending = sourceMaps[path];
|
|
232
|
+
// sourceMaps.delete(path);
|
|
233
|
+
if (pending) {
|
|
234
|
+
pending.resolvers.forEach(function (func) {
|
|
235
|
+
func(smap);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return [2 /*return*/];
|
|
239
|
+
}
|
|
174
240
|
});
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
pending
|
|
241
|
+
}); })["catch"](function (err) {
|
|
242
|
+
var pending = sourceMaps[path];
|
|
243
|
+
if (!pending) {
|
|
244
|
+
throw new Error("Gotta have pending");
|
|
245
|
+
}
|
|
246
|
+
// sourceMaps.delete(path);
|
|
247
|
+
pending.rejecters.forEach(function (func) {
|
|
178
248
|
func(err);
|
|
179
249
|
});
|
|
180
250
|
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
var cur = sourceMaps[path];
|
|
254
|
+
if (verbose) {
|
|
184
255
|
console.error("path is in source maps already", cur);
|
|
256
|
+
}
|
|
257
|
+
if (!cur) {
|
|
258
|
+
throw new Error("Gotta have cur");
|
|
259
|
+
}
|
|
185
260
|
cur.resolvers.push(resolve);
|
|
186
261
|
cur.rejecters.push(reject);
|
|
187
262
|
}
|
|
188
263
|
});
|
|
189
264
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
if (verbose)
|
|
265
|
+
function processFrame(functionName, url, line, column) {
|
|
266
|
+
if (verbose) {
|
|
194
267
|
console.log("got frame", functionName, url, line, column);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
268
|
+
}
|
|
269
|
+
if (functionName.endsWith("@")) {
|
|
270
|
+
functionName = functionName.substring(0, functionName.length - 1);
|
|
271
|
+
}
|
|
272
|
+
return new Promise(function (resolve) {
|
|
273
|
+
var newUrl, newLine, newColumn;
|
|
274
|
+
if (verbose) {
|
|
200
275
|
console.error("calling loadUri", url);
|
|
276
|
+
}
|
|
201
277
|
if (!url.includes("://") && url[0] !== "/" && fs.existsSync(url)) {
|
|
202
|
-
url =
|
|
278
|
+
url = "file://".concat(process.cwd(), "/").concat(url);
|
|
203
279
|
}
|
|
204
|
-
return loadUri(url)
|
|
205
|
-
|
|
280
|
+
return loadUri(url)
|
|
281
|
+
.then(function (smap) {
|
|
282
|
+
if (verbose) {
|
|
206
283
|
console.error("got map", url, Object.keys(smap));
|
|
207
|
-
|
|
284
|
+
}
|
|
208
285
|
// it appears that we're supposed to reduce the column
|
|
209
286
|
// number by 1 when we get this from jsc
|
|
210
|
-
|
|
287
|
+
var pos = smap.originalPositionFor({ line: line, column: column - jsc });
|
|
211
288
|
if (!pos.source) {
|
|
212
|
-
if (verbose)
|
|
289
|
+
if (verbose) {
|
|
213
290
|
console.error("nothing here", pos);
|
|
291
|
+
}
|
|
214
292
|
throw new Error("Mapping not found");
|
|
215
293
|
}
|
|
216
|
-
|
|
217
294
|
// smc.sourceContentFor(pos.source);
|
|
218
|
-
|
|
219
295
|
newUrl = pos.source;
|
|
220
|
-
newLine = pos.line;
|
|
221
|
-
newColumn = pos.column;
|
|
222
|
-
})
|
|
223
|
-
if (verbose)
|
|
296
|
+
newLine = pos.line || 0;
|
|
297
|
+
newColumn = pos.column || 0;
|
|
298
|
+
})["catch"](function (err) {
|
|
299
|
+
if (verbose) {
|
|
224
300
|
console.error("didn't get map", url, err);
|
|
301
|
+
}
|
|
225
302
|
// console.error(err);
|
|
226
|
-
})
|
|
303
|
+
})["finally"](function () {
|
|
227
304
|
if (json) {
|
|
228
|
-
|
|
305
|
+
var ret = {
|
|
229
306
|
functionName: functionName,
|
|
230
307
|
sourceURL: newUrl ? newUrl : url,
|
|
231
308
|
line: newUrl ? newLine : line,
|
|
232
309
|
column: newUrl ? newColumn : column
|
|
233
310
|
};
|
|
234
|
-
if (newUrl) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
311
|
+
// if (newUrl) {
|
|
312
|
+
// ret.oldSourceURL = url;
|
|
313
|
+
// ret.oldLine = line;
|
|
314
|
+
// ret.oldColumn = column;
|
|
315
|
+
// }
|
|
239
316
|
// if (newUrl) {
|
|
240
317
|
// ret.newUrl = newUrl;
|
|
241
318
|
// ret.newLine = newLine;
|
|
242
319
|
// ret.newColumn = newColumn;
|
|
243
320
|
// }
|
|
244
321
|
resolve(ret);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
let str;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
var str = void 0;
|
|
250
325
|
if (newUrl) {
|
|
251
|
-
str =
|
|
252
|
-
}
|
|
326
|
+
str = "".concat(build(functionName, newUrl, newLine, newColumn), " (").concat(build("", url, line, column), ")");
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
253
329
|
str = build(functionName, url, line, column);
|
|
254
330
|
}
|
|
255
331
|
resolve(str);
|
|
@@ -257,56 +333,68 @@ function processFrame(functionName, url, line, column)
|
|
|
257
333
|
});
|
|
258
334
|
});
|
|
259
335
|
}
|
|
260
|
-
|
|
261
336
|
if (json) {
|
|
262
|
-
|
|
337
|
+
var parsed = void 0;
|
|
263
338
|
try {
|
|
264
339
|
parsed = JSON.parse(stack);
|
|
265
|
-
if (!Array.isArray(parsed))
|
|
340
|
+
if (!Array.isArray(parsed)) {
|
|
266
341
|
throw new Error("Expected array");
|
|
267
|
-
|
|
268
|
-
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
console.error("Can't parse json ".concat(err));
|
|
269
346
|
process.exit(1);
|
|
270
347
|
}
|
|
271
|
-
Promise.all(parsed.map((frame)
|
|
272
|
-
if (verbose)
|
|
348
|
+
Promise.all(parsed.map(function (frame) {
|
|
349
|
+
if (verbose) {
|
|
273
350
|
console.error("got frame frame", frame);
|
|
351
|
+
}
|
|
274
352
|
return processFrame(frame.functionName || "", frame.sourceURL || "", frame.line, frame.column);
|
|
275
|
-
}))
|
|
276
|
-
|
|
353
|
+
}))
|
|
354
|
+
.then(function (results) {
|
|
355
|
+
results.forEach(function (frame, index) {
|
|
356
|
+
if (typeof frame !== "object") {
|
|
357
|
+
throw new Error("Huh?");
|
|
358
|
+
}
|
|
277
359
|
frame.index = index;
|
|
278
360
|
});
|
|
279
361
|
// console.log("shit", results);
|
|
280
362
|
console.log(JSON.stringify(results, null, 4));
|
|
281
|
-
})
|
|
363
|
+
})["catch"](function (error) {
|
|
282
364
|
console.error("Got an error", error);
|
|
283
365
|
process.exit(2);
|
|
284
366
|
});
|
|
285
|
-
}
|
|
286
|
-
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
Promise.all(stack
|
|
370
|
+
.split("\n")
|
|
371
|
+
.filter(function (x) { return x; })
|
|
372
|
+
.map(function (x) {
|
|
287
373
|
x = x.trim();
|
|
288
|
-
|
|
374
|
+
var match = / *at *([^ ]*).* \(?([^ ]+):([0-9]+):([0-9]+)/.exec(x);
|
|
289
375
|
if (!match) {
|
|
290
376
|
match = /([^ ]+@)?(.*):([0-9]+):([0-9]+)/.exec(x);
|
|
291
377
|
}
|
|
292
|
-
if (verbose)
|
|
378
|
+
if (verbose) {
|
|
293
379
|
console.error(x, " => ", match);
|
|
380
|
+
}
|
|
294
381
|
if (!match) {
|
|
295
|
-
|
|
382
|
+
var nolinecol = /([^ ]*)(.*)/.exec(x);
|
|
296
383
|
if (nolinecol) {
|
|
297
384
|
return nolinecol[0];
|
|
298
385
|
}
|
|
299
386
|
return x;
|
|
300
387
|
}
|
|
301
388
|
return processFrame(match[1] || "", match[2], parseInt(match[3]), parseInt(match[4]));
|
|
302
|
-
}))
|
|
389
|
+
}))
|
|
390
|
+
.then(function (results) {
|
|
303
391
|
if (!results.length) {
|
|
304
392
|
throw new Error("Empty output");
|
|
305
393
|
}
|
|
306
|
-
results.forEach(str
|
|
394
|
+
results.forEach(function (str) {
|
|
307
395
|
console.log(str);
|
|
308
396
|
});
|
|
309
|
-
})
|
|
397
|
+
})["catch"](function (error) {
|
|
310
398
|
console.error("Got an error", error);
|
|
311
399
|
process.exit(3);
|
|
312
400
|
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as got from "got";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as sourceMap from "source-map";
|
|
8
|
+
import * as url from "url";
|
|
9
|
+
|
|
10
|
+
let verbose = false;
|
|
11
|
+
let stack;
|
|
12
|
+
let json = false;
|
|
13
|
+
let retries = 3;
|
|
14
|
+
let jsc = 0;
|
|
15
|
+
|
|
16
|
+
for (let i = 2; i < process.argv.length; ++i) {
|
|
17
|
+
try {
|
|
18
|
+
const arg = process.argv[i] || "";
|
|
19
|
+
if (verbose) {
|
|
20
|
+
console.error("got arg", arg);
|
|
21
|
+
}
|
|
22
|
+
if (arg === "-f" || arg === "--file") {
|
|
23
|
+
stack = fs.readFileSync(process.argv[++i]).toString();
|
|
24
|
+
} else if (arg.startsWith("-f")) {
|
|
25
|
+
stack = fs.readFileSync(arg.substring(2)).toString();
|
|
26
|
+
} else if (arg.startsWith("--file=")) {
|
|
27
|
+
stack = fs.readFileSync(arg.substring(7)).toString();
|
|
28
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
29
|
+
verbose = true;
|
|
30
|
+
} else if (arg === "--version") {
|
|
31
|
+
console.log(JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
} else if (arg === "--json") {
|
|
34
|
+
json = true;
|
|
35
|
+
} else if (arg === "--jsc") {
|
|
36
|
+
// jsc has the wrong column for some reason
|
|
37
|
+
jsc = 1;
|
|
38
|
+
} else if (arg.startsWith("--retries")) {
|
|
39
|
+
retries = parseInt(arg.substring(9));
|
|
40
|
+
if (isNaN(retries) || retries < 0) {
|
|
41
|
+
console.error(
|
|
42
|
+
"smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-"
|
|
43
|
+
);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
47
|
+
console.error(
|
|
48
|
+
"smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-"
|
|
49
|
+
);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
} else if (arg === "-") {
|
|
52
|
+
// stdin
|
|
53
|
+
} else {
|
|
54
|
+
stack = arg;
|
|
55
|
+
}
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error("Error: " + err.toString());
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!stack) {
|
|
63
|
+
stack = fs.readFileSync("/dev/stdin").toString();
|
|
64
|
+
if (!stack) {
|
|
65
|
+
console.error("Nothing to do");
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function build(functionName: string, url: string, line: number, column: number): string {
|
|
71
|
+
return `${functionName ? "at " + functionName + " " : ""}${url}:${line}:${column}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function findFile(dir: string, fn: string): string | undefined {
|
|
75
|
+
const list = fs.readdirSync(dir);
|
|
76
|
+
for (let file of list) {
|
|
77
|
+
const match = file === fn;
|
|
78
|
+
file = dir + "/" + file;
|
|
79
|
+
const stat = fs.statSync(file);
|
|
80
|
+
if (stat && stat.isDirectory()) {
|
|
81
|
+
const ret = findFile(file, fn);
|
|
82
|
+
if (ret) {
|
|
83
|
+
return ret;
|
|
84
|
+
}
|
|
85
|
+
} else if (match) {
|
|
86
|
+
return "file://" + file;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function rewriteLocalControl(url: string) {
|
|
93
|
+
const found = findFile(process.cwd(), path.basename(url.substring(6)));
|
|
94
|
+
if (found) {
|
|
95
|
+
return found;
|
|
96
|
+
}
|
|
97
|
+
throw new Error("Couldn't resolve localcontrol " + url);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function load(path: string): Promise<string> {
|
|
101
|
+
return new Promise<string>((resolve, reject) => {
|
|
102
|
+
if (path.startsWith("http://localcontrol.netflix.com/")) {
|
|
103
|
+
try {
|
|
104
|
+
path = rewriteLocalControl(path);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
reject(err);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (path.startsWith("file:///")) {
|
|
112
|
+
try {
|
|
113
|
+
resolve(fs.readFileSync(path.substring(7), "utf8"));
|
|
114
|
+
} catch (err) {
|
|
115
|
+
reject(err);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!path.startsWith("http://") && !path.startsWith("https://")) {
|
|
121
|
+
reject(new Error("Not a url"));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// let retryIndex = 0;
|
|
126
|
+
async function get(): Promise<string> {
|
|
127
|
+
const response = await got.get(path, { timeout: 10000 });
|
|
128
|
+
console.log(response.headers);
|
|
129
|
+
return response.body || "";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get().then(resolve, reject);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
type SourceMapResolve = (res: sourceMap.SourceMapConsumer) => void;
|
|
137
|
+
type Rejecter = (err: Error) => void;
|
|
138
|
+
type SourceMapData = {
|
|
139
|
+
resolvers: SourceMapResolve[];
|
|
140
|
+
rejecters: Rejecter[];
|
|
141
|
+
};
|
|
142
|
+
const sourceMaps: Record<string, SourceMapData> = {};
|
|
143
|
+
function loadUri(path: string): Promise<sourceMap.SourceMapConsumer> {
|
|
144
|
+
if (path[0] === "/") {
|
|
145
|
+
path = "file://" + path;
|
|
146
|
+
}
|
|
147
|
+
return new Promise((resolve: SourceMapResolve, reject: Rejecter) => {
|
|
148
|
+
if (verbose) {
|
|
149
|
+
console.log("loading", path);
|
|
150
|
+
}
|
|
151
|
+
if (!(path in sourceMaps)) {
|
|
152
|
+
sourceMaps[path] = {
|
|
153
|
+
resolvers: [resolve],
|
|
154
|
+
rejecters: [reject]
|
|
155
|
+
};
|
|
156
|
+
load(path)
|
|
157
|
+
.then((jsData: string) => {
|
|
158
|
+
const idx = jsData.lastIndexOf("//# sourceMappingURL=");
|
|
159
|
+
if (verbose) {
|
|
160
|
+
console.error("Got the file", jsData.length, idx);
|
|
161
|
+
}
|
|
162
|
+
if (idx == -1) {
|
|
163
|
+
return path + ".map";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const mapUrl = jsData.substring(idx + 21);
|
|
167
|
+
const match = /^(data:.+\/.+;)base64,/.exec(mapUrl);
|
|
168
|
+
if (match) {
|
|
169
|
+
return mapUrl.substring(match[1].length);
|
|
170
|
+
}
|
|
171
|
+
if (mapUrl.indexOf("://") != -1) {
|
|
172
|
+
return mapUrl;
|
|
173
|
+
}
|
|
174
|
+
// console.log(mapUrl);
|
|
175
|
+
return new url.URL(mapUrl, path).href;
|
|
176
|
+
})
|
|
177
|
+
.then((mapUrl) => {
|
|
178
|
+
// console.log(mapUrl.substring(0, 20));
|
|
179
|
+
if (mapUrl.startsWith("base64,")) {
|
|
180
|
+
return Buffer.from(mapUrl.substring(7), "base64").toString();
|
|
181
|
+
}
|
|
182
|
+
return load(mapUrl);
|
|
183
|
+
})
|
|
184
|
+
.then(async (sourceMapData: string) => {
|
|
185
|
+
const parsed = JSON.parse(sourceMapData);
|
|
186
|
+
const smap: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(parsed);
|
|
187
|
+
const pending = sourceMaps[path];
|
|
188
|
+
// sourceMaps.delete(path);
|
|
189
|
+
if (pending) {
|
|
190
|
+
pending.resolvers.forEach((func: SourceMapResolve) => {
|
|
191
|
+
func(smap);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.catch((err) => {
|
|
196
|
+
const pending = sourceMaps[path];
|
|
197
|
+
if (!pending) {
|
|
198
|
+
throw new Error("Gotta have pending");
|
|
199
|
+
}
|
|
200
|
+
// sourceMaps.delete(path);
|
|
201
|
+
pending.rejecters.forEach((func: (err: Error) => void) => {
|
|
202
|
+
func(err);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
const cur = sourceMaps[path];
|
|
207
|
+
if (verbose) {
|
|
208
|
+
console.error("path is in source maps already", cur);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!cur) {
|
|
212
|
+
throw new Error("Gotta have cur");
|
|
213
|
+
}
|
|
214
|
+
cur.resolvers.push(resolve);
|
|
215
|
+
cur.rejecters.push(reject);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
type Frame = {
|
|
221
|
+
functionName: string;
|
|
222
|
+
sourceURL: string;
|
|
223
|
+
line: number;
|
|
224
|
+
column: number;
|
|
225
|
+
index?: number;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
function processFrame(functionName: string, url: string, line: number, column: number): Promise<Frame | string> {
|
|
229
|
+
if (verbose) {
|
|
230
|
+
console.log("got frame", functionName, url, line, column);
|
|
231
|
+
}
|
|
232
|
+
if (functionName.endsWith("@")) {
|
|
233
|
+
functionName = functionName.substring(0, functionName.length - 1);
|
|
234
|
+
}
|
|
235
|
+
return new Promise((resolve) => {
|
|
236
|
+
let newUrl: string, newLine: number, newColumn: number;
|
|
237
|
+
if (verbose) {
|
|
238
|
+
console.error("calling loadUri", url);
|
|
239
|
+
}
|
|
240
|
+
if (!url.includes("://") && url[0] !== "/" && fs.existsSync(url)) {
|
|
241
|
+
url = `file://${process.cwd()}/${url}`;
|
|
242
|
+
}
|
|
243
|
+
return loadUri(url)
|
|
244
|
+
.then((smap) => {
|
|
245
|
+
if (verbose) {
|
|
246
|
+
console.error("got map", url, Object.keys(smap));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// it appears that we're supposed to reduce the column
|
|
250
|
+
// number by 1 when we get this from jsc
|
|
251
|
+
const pos = smap.originalPositionFor({ line, column: column - jsc });
|
|
252
|
+
if (!pos.source) {
|
|
253
|
+
if (verbose) {
|
|
254
|
+
console.error("nothing here", pos);
|
|
255
|
+
}
|
|
256
|
+
throw new Error("Mapping not found");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// smc.sourceContentFor(pos.source);
|
|
260
|
+
|
|
261
|
+
newUrl = pos.source;
|
|
262
|
+
|
|
263
|
+
newLine = pos.line || 0;
|
|
264
|
+
newColumn = pos.column || 0;
|
|
265
|
+
})
|
|
266
|
+
.catch((err) => {
|
|
267
|
+
if (verbose) {
|
|
268
|
+
console.error("didn't get map", url, err);
|
|
269
|
+
}
|
|
270
|
+
// console.error(err);
|
|
271
|
+
})
|
|
272
|
+
.finally(() => {
|
|
273
|
+
if (json) {
|
|
274
|
+
const ret = {
|
|
275
|
+
functionName: functionName,
|
|
276
|
+
sourceURL: newUrl ? newUrl : url,
|
|
277
|
+
line: newUrl ? newLine : line,
|
|
278
|
+
column: newUrl ? newColumn : column
|
|
279
|
+
};
|
|
280
|
+
// if (newUrl) {
|
|
281
|
+
// ret.oldSourceURL = url;
|
|
282
|
+
// ret.oldLine = line;
|
|
283
|
+
// ret.oldColumn = column;
|
|
284
|
+
// }
|
|
285
|
+
// if (newUrl) {
|
|
286
|
+
// ret.newUrl = newUrl;
|
|
287
|
+
// ret.newLine = newLine;
|
|
288
|
+
// ret.newColumn = newColumn;
|
|
289
|
+
// }
|
|
290
|
+
resolve(ret);
|
|
291
|
+
} else {
|
|
292
|
+
let str;
|
|
293
|
+
if (newUrl) {
|
|
294
|
+
str = `${build(functionName, newUrl, newLine, newColumn)} (${build("", url, line, column)})`;
|
|
295
|
+
} else {
|
|
296
|
+
str = build(functionName, url, line, column);
|
|
297
|
+
}
|
|
298
|
+
resolve(str);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (json) {
|
|
305
|
+
let parsed;
|
|
306
|
+
try {
|
|
307
|
+
parsed = JSON.parse(stack);
|
|
308
|
+
if (!Array.isArray(parsed)) {
|
|
309
|
+
throw new Error("Expected array");
|
|
310
|
+
}
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error(`Can't parse json ${err}`);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
Promise.all(
|
|
316
|
+
parsed.map((frame) => {
|
|
317
|
+
if (verbose) {
|
|
318
|
+
console.error("got frame frame", frame);
|
|
319
|
+
}
|
|
320
|
+
return processFrame(frame.functionName || "", frame.sourceURL || "", frame.line, frame.column);
|
|
321
|
+
})
|
|
322
|
+
)
|
|
323
|
+
.then((results) => {
|
|
324
|
+
results.forEach((frame: Frame | string, index: number) => {
|
|
325
|
+
if (typeof frame !== "object") {
|
|
326
|
+
throw new Error("Huh?");
|
|
327
|
+
}
|
|
328
|
+
frame.index = index;
|
|
329
|
+
});
|
|
330
|
+
// console.log("shit", results);
|
|
331
|
+
console.log(JSON.stringify(results, null, 4));
|
|
332
|
+
})
|
|
333
|
+
.catch((error) => {
|
|
334
|
+
console.error("Got an error", error);
|
|
335
|
+
process.exit(2);
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
Promise.all(
|
|
339
|
+
stack
|
|
340
|
+
.split("\n")
|
|
341
|
+
.filter((x) => x)
|
|
342
|
+
.map((x) => {
|
|
343
|
+
x = x.trim();
|
|
344
|
+
let match = / *at *([^ ]*).* \(?([^ ]+):([0-9]+):([0-9]+)/.exec(x);
|
|
345
|
+
if (!match) {
|
|
346
|
+
match = /([^ ]+@)?(.*):([0-9]+):([0-9]+)/.exec(x);
|
|
347
|
+
}
|
|
348
|
+
if (verbose) {
|
|
349
|
+
console.error(x, " => ", match);
|
|
350
|
+
}
|
|
351
|
+
if (!match) {
|
|
352
|
+
const nolinecol = /([^ ]*)(.*)/.exec(x);
|
|
353
|
+
if (nolinecol) {
|
|
354
|
+
return nolinecol[0];
|
|
355
|
+
}
|
|
356
|
+
return x;
|
|
357
|
+
}
|
|
358
|
+
return processFrame(match[1] || "", match[2], parseInt(match[3]), parseInt(match[4]));
|
|
359
|
+
})
|
|
360
|
+
)
|
|
361
|
+
.then((results) => {
|
|
362
|
+
if (!results.length) {
|
|
363
|
+
throw new Error("Empty output");
|
|
364
|
+
}
|
|
365
|
+
results.forEach((str) => {
|
|
366
|
+
console.log(str);
|
|
367
|
+
});
|
|
368
|
+
})
|
|
369
|
+
.catch((error) => {
|
|
370
|
+
console.error("Got an error", error);
|
|
371
|
+
process.exit(3);
|
|
372
|
+
});
|
|
373
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smipper",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"build": "node ./node_modules/.bin/tsc index.ts && chmod +x ./index.js",
|
|
7
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
9
|
},
|
|
9
|
-
"bin": {
|
|
10
|
-
"smipper": "index.js"
|
|
11
|
-
},
|
|
12
10
|
"author": "",
|
|
13
11
|
"license": "ISC",
|
|
14
12
|
"dependencies": {
|
|
15
13
|
"got": "^9.6.0",
|
|
16
14
|
"source-map": "^0.7.4"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/got": "^9.6.12",
|
|
18
|
+
"@types/node": "^18.7.18",
|
|
19
|
+
"@types/source-map": "^0.5.7",
|
|
20
|
+
"prettier": "^2.7.1",
|
|
21
|
+
"typescript": "^4.8.3"
|
|
17
22
|
}
|
|
18
23
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": ["**/.*/", "examples/**", "autobahn/**", "node_modules/**", "dist/**", "*.def.ts"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "es2020",
|
|
5
|
+
"typeRoots": ["./src/typings", "./node_modules/@nf-types"],
|
|
6
|
+
"noUncheckedIndexedAccess": true,
|
|
7
|
+
"strictBindCallApply": true,
|
|
8
|
+
"newLine": "LF",
|
|
9
|
+
"module": "es6",
|
|
10
|
+
"declaration": false,
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"pretty": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"strictNullChecks": true,
|
|
16
|
+
"strictPropertyInitialization": true,
|
|
17
|
+
"downlevelIteration": true,
|
|
18
|
+
"strict": true,
|
|
19
|
+
"sourceMap": true,
|
|
20
|
+
"incremental": true,
|
|
21
|
+
"lib": ["es2019"],
|
|
22
|
+
"baseUrl": ".",
|
|
23
|
+
"paths": {
|
|
24
|
+
"~/*": ["src/*"]
|
|
25
|
+
},
|
|
26
|
+
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }]
|
|
27
|
+
}
|
|
28
|
+
}
|