From 1b93be8080f9f5d96fd3b30f44f2866aca36426d Mon Sep 17 00:00:00 2001 From: Teoxoy <28601907+Teoxoy@users.noreply.github.com> Date: Thu, 16 Jul 2020 14:04:08 +0800 Subject: [PATCH 1/4] sourcemap stacktraces --- package-lock.json | 52 +++++++++- package.json | 1 + .../src/server/middleware/get_page_handler.ts | 5 + .../server/middleware/sourcemap_stacktrace.ts | 95 +++++++++++++++++++ 4 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 runtime/src/server/middleware/sourcemap_stacktrace.ts diff --git a/package-lock.json b/package-lock.json index e05c92944..ccc54f78c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1150,6 +1150,13 @@ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "requires": { "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } } }, "cli-cursor": { @@ -4969,9 +4976,9 @@ "dev": true }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" }, "source-map-resolve": { "version": "0.5.2", @@ -4994,6 +5001,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-url": { @@ -5441,6 +5456,14 @@ "commander": "^2.20.0", "source-map": "~0.6.1", "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "terser-webpack-plugin": { @@ -5459,6 +5482,14 @@ "terser": "^4.0.0", "webpack-sources": "^1.3.0", "worker-farm": "^1.7.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "text-table": { @@ -5622,6 +5653,13 @@ "requires": { "commander": "~2.20.0", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } } }, "unbzip2-stream": { @@ -5862,6 +5900,14 @@ "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "which": { diff --git a/package.json b/package.json index 39da97557..9216d6f8c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "html-minifier": "^4.0.0", "http-link-header": "^1.0.2", "shimport": "^1.0.1", + "source-map": "^0.7.3", "sourcemap-codec": "^1.4.6", "string-hash": "^1.1.3" }, diff --git a/runtime/src/server/middleware/get_page_handler.ts b/runtime/src/server/middleware/get_page_handler.ts index da086d78f..024dbe813 100644 --- a/runtime/src/server/middleware/get_page_handler.ts +++ b/runtime/src/server/middleware/get_page_handler.ts @@ -6,6 +6,7 @@ import devalue from 'devalue'; import fetch from 'node-fetch'; import URL from 'url'; import { Manifest, Page, Req, Res } from './types'; +import { sourcemapStacktrace } from './sourcemap_stacktrace'; import { build_dir, dev, src_dir } from '@sapper/internal/manifest-server'; import App from '@sapper/internal/App.svelte'; @@ -233,6 +234,10 @@ export function get_page_handler( l++; }); + if (error instanceof Error && error.stack) { + error.stack = sourcemapStacktrace(error.stack); + } + const props = { stores: { page: { diff --git a/runtime/src/server/middleware/sourcemap_stacktrace.ts b/runtime/src/server/middleware/sourcemap_stacktrace.ts new file mode 100644 index 000000000..40903918b --- /dev/null +++ b/runtime/src/server/middleware/sourcemap_stacktrace.ts @@ -0,0 +1,95 @@ +import fs from 'fs'; +import path from 'path'; +import { SourceMapConsumer, RawSourceMap } from 'source-map'; + +function retrieveSourceMapURL(contents: string) { + const reversed = contents + .split('\n') + .reverse() + .join('\n'); + + const match = /\/[/*]#[ \t]+sourceMappingURL=([^\s'"]+?)(?:[ \t]+|$)/gm.exec(reversed); + if (match) return match[1]; + + return undefined; +} + +const fileCache = new Map(); + +function getFileContents(path: string) { + if (fileCache.has(path)) { + return fileCache.get(path); + } + if (fs.existsSync(path)) { + try { + const data = fs.readFileSync(path, 'utf8'); + fileCache.set(path, data); + return data; + } catch { + return undefined; + } + } +} + +function sourcemapStacktrace(stack: string) { + const replaceFn = (line: string) => + line.replace( + /^ {4}at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?)\)?/, + (input, varName, filePath, line, column) => { + if (!filePath) return input; + + const contents = getFileContents(filePath); + if (!contents) return input; + + const srcMapPathOrBase64 = retrieveSourceMapURL(contents); + if (!srcMapPathOrBase64) return input; + + let dir = path.dirname(filePath); + let srcMapData: string; + + if (/^data:application\/json[^,]+base64,/.test(srcMapPathOrBase64)) { + const rawData = srcMapPathOrBase64.slice(srcMapPathOrBase64.indexOf(',') + 1); + try { + srcMapData = Buffer.from(rawData, 'base64').toString(); + } catch { + return input; + } + } else { + const absSrcMapPath = path.resolve(dir, srcMapPathOrBase64); + const data = getFileContents(absSrcMapPath); + if (!data) return input; + + srcMapData = data; + dir = path.dirname(absSrcMapPath); + } + + let rawSourceMap: RawSourceMap; + try { + rawSourceMap = JSON.parse(srcMapData); + } catch { + return input; + } + + const consumer = new SourceMapConsumer(rawSourceMap); + const pos = consumer.originalPositionFor({ + line: Number(line), + column: Number(column) + }); + if (!pos.source) return input; + + const absSrcPath = path.resolve(dir, pos.source); + const urlPart = `${absSrcPath}:${pos.line || 0}:${pos.column || 0}`; + + if (!varName) return ` at ${urlPart}`; + return ` at ${varName} (${urlPart})`; + } + ); + + fileCache.clear(); + return stack + .split('\n') + .map(replaceFn) + .join('\n'); +} + +export { sourcemapStacktrace }; \ No newline at end of file From bd6abfe67bbd4d112a636e8f21ccb3f0cf5b395b Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Tue, 4 Aug 2020 22:00:34 +0700 Subject: [PATCH 2/4] fix source-map version, add test, and cleanup --- package-lock.json | 600 +++++++++++++++++- package.json | 2 +- .../src/server/middleware/get_page_handler.ts | 4 +- .../server/middleware/sourcemap_stacktrace.ts | 170 ++--- test/apps/errors/rollup.config.js | 2 +- test/apps/errors/src/routes/_error.svelte | 2 + test/apps/errors/test.ts | 6 + 7 files changed, 695 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccc54f78c..47559a827 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2422,6 +2422,600 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -4976,9 +5570,9 @@ "dev": true }, "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.2", diff --git a/package.json b/package.json index 9216d6f8c..152265c94 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "html-minifier": "^4.0.0", "http-link-header": "^1.0.2", "shimport": "^1.0.1", - "source-map": "^0.7.3", + "source-map": "^0.6.1", "sourcemap-codec": "^1.4.6", "string-hash": "^1.1.3" }, diff --git a/runtime/src/server/middleware/get_page_handler.ts b/runtime/src/server/middleware/get_page_handler.ts index 024dbe813..3dfc51067 100644 --- a/runtime/src/server/middleware/get_page_handler.ts +++ b/runtime/src/server/middleware/get_page_handler.ts @@ -6,7 +6,7 @@ import devalue from 'devalue'; import fetch from 'node-fetch'; import URL from 'url'; import { Manifest, Page, Req, Res } from './types'; -import { sourcemapStacktrace } from './sourcemap_stacktrace'; +import { sourcemap_stacktrace } from './sourcemap_stacktrace'; import { build_dir, dev, src_dir } from '@sapper/internal/manifest-server'; import App from '@sapper/internal/App.svelte'; @@ -235,7 +235,7 @@ export function get_page_handler( }); if (error instanceof Error && error.stack) { - error.stack = sourcemapStacktrace(error.stack); + error.stack = sourcemap_stacktrace(error.stack); } const props = { diff --git a/runtime/src/server/middleware/sourcemap_stacktrace.ts b/runtime/src/server/middleware/sourcemap_stacktrace.ts index 40903918b..7bd333dd4 100644 --- a/runtime/src/server/middleware/sourcemap_stacktrace.ts +++ b/runtime/src/server/middleware/sourcemap_stacktrace.ts @@ -2,94 +2,96 @@ import fs from 'fs'; import path from 'path'; import { SourceMapConsumer, RawSourceMap } from 'source-map'; -function retrieveSourceMapURL(contents: string) { - const reversed = contents - .split('\n') - .reverse() - .join('\n'); +function get_sourcemap_url(contents: string) { + const reversed = contents + .split('\n') + .reverse() + .join('\n'); - const match = /\/[/*]#[ \t]+sourceMappingURL=([^\s'"]+?)(?:[ \t]+|$)/gm.exec(reversed); - if (match) return match[1]; + const match = /\/[/*]#[ \t]+sourceMappingURL=([^\s'"]+?)(?:[ \t]+|$)/gm.exec(reversed); + if (match) return match[1]; - return undefined; + return undefined; } -const fileCache = new Map(); - -function getFileContents(path: string) { - if (fileCache.has(path)) { - return fileCache.get(path); - } - if (fs.existsSync(path)) { - try { - const data = fs.readFileSync(path, 'utf8'); - fileCache.set(path, data); - return data; - } catch { - return undefined; - } - } +const file_cache = new Map(); + +function get_file_contents(path: string) { + if (file_cache.has(path)) { + return file_cache.get(path); + } + if (fs.existsSync(path)) { + try { + const data = fs.readFileSync(path, 'utf8'); + file_cache.set(path, data); + return data; + } catch { + return undefined; + } + } } -function sourcemapStacktrace(stack: string) { - const replaceFn = (line: string) => - line.replace( - /^ {4}at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?)\)?/, - (input, varName, filePath, line, column) => { - if (!filePath) return input; - - const contents = getFileContents(filePath); - if (!contents) return input; - - const srcMapPathOrBase64 = retrieveSourceMapURL(contents); - if (!srcMapPathOrBase64) return input; - - let dir = path.dirname(filePath); - let srcMapData: string; - - if (/^data:application\/json[^,]+base64,/.test(srcMapPathOrBase64)) { - const rawData = srcMapPathOrBase64.slice(srcMapPathOrBase64.indexOf(',') + 1); - try { - srcMapData = Buffer.from(rawData, 'base64').toString(); - } catch { - return input; - } - } else { - const absSrcMapPath = path.resolve(dir, srcMapPathOrBase64); - const data = getFileContents(absSrcMapPath); - if (!data) return input; - - srcMapData = data; - dir = path.dirname(absSrcMapPath); - } - - let rawSourceMap: RawSourceMap; - try { - rawSourceMap = JSON.parse(srcMapData); - } catch { - return input; - } - - const consumer = new SourceMapConsumer(rawSourceMap); - const pos = consumer.originalPositionFor({ - line: Number(line), - column: Number(column) - }); - if (!pos.source) return input; - - const absSrcPath = path.resolve(dir, pos.source); - const urlPart = `${absSrcPath}:${pos.line || 0}:${pos.column || 0}`; - - if (!varName) return ` at ${urlPart}`; - return ` at ${varName} (${urlPart})`; - } - ); - - fileCache.clear(); - return stack - .split('\n') - .map(replaceFn) - .join('\n'); +export function sourcemap_stacktrace(stack: string) { + const replace = (line: string) => + line.replace( + /^ {4}at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?)\)?/, + (input, var_name, file_path, line, column) => { + if (!file_path) return input; + + const contents = get_file_contents(file_path); + if (!contents) return input; + + const sourcemap_path_or_base64 = get_sourcemap_url(contents); + if (!sourcemap_path_or_base64) return input; + + let dir = path.dirname(file_path); + let sourcemap_data: string; + + if (/^data:application\/json[^,]+base64,/.test(sourcemap_path_or_base64)) { + const raw_data = sourcemap_path_or_base64.slice(sourcemap_path_or_base64.indexOf(',') + 1); + try { + sourcemap_data = Buffer.from(raw_data, 'base64').toString(); + } catch { + return input; + } + } else { + const abs_sourcemap_path = path.resolve(dir, sourcemap_path_or_base64); + const data = get_file_contents(abs_sourcemap_path); + + if (!data) return input; + + sourcemap_data = data; + dir = path.dirname(abs_sourcemap_path); + } + + let raw_source_map: RawSourceMap; + try { + raw_source_map = JSON.parse(sourcemap_data); + } catch { + return input; + } + + const consumer = new SourceMapConsumer(raw_source_map); + const pos = consumer.originalPositionFor({ + line: Number(line), + column: Number(column), + bias: SourceMapConsumer.LEAST_UPPER_BOUND + }); + + if (!pos.source) return input; + + const abs_source_path = path.resolve(dir, pos.source); + const urlPart = `${abs_source_path}:${pos.line || 0}:${pos.column || 0}`; + + if (!var_name) return ` at ${urlPart}`; + return ` at ${var_name} (${urlPart})`; + } + ); + + file_cache.clear(); + + return stack + .split('\n') + .map(replace) + .join('\n'); } - -export { sourcemapStacktrace }; \ No newline at end of file diff --git a/test/apps/errors/rollup.config.js b/test/apps/errors/rollup.config.js index ce8c3a113..c9b43e690 100644 --- a/test/apps/errors/rollup.config.js +++ b/test/apps/errors/rollup.config.js @@ -27,7 +27,7 @@ export default { server: { input: config.server.input(), - output: config.server.output(), + output: Object.assign({}, config.server.output(), { sourcemap: true }), plugins: [ replace({ 'process.browser': false, diff --git a/test/apps/errors/src/routes/_error.svelte b/test/apps/errors/src/routes/_error.svelte index d76724e3e..7f674bdd2 100644 --- a/test/apps/errors/src/routes/_error.svelte +++ b/test/apps/errors/src/routes/_error.svelte @@ -15,3 +15,5 @@

{mounted}

{error.message}

+ +{error.stack} diff --git a/test/apps/errors/test.ts b/test/apps/errors/test.ts index f54c15cbf..a00039f58 100644 --- a/test/apps/errors/test.ts +++ b/test/apps/errors/test.ts @@ -71,6 +71,12 @@ describe('errors', function() { ); }); + it('display correct stack traces on server error', async () => { + await r.load('/throw'); + + assert.ok((await r.text('span')).includes('throw.svelte:3:12')); + }); + it('handles error on client', async () => { await r.load('/'); await r.sapper.start(); From dc16cd4da775bc6d133c19efc95e71ba54e10abc Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Tue, 4 Aug 2020 23:14:13 +0700 Subject: [PATCH 3/4] fix variable naming and file exists checking --- .../server/middleware/sourcemap_stacktrace.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/runtime/src/server/middleware/sourcemap_stacktrace.ts b/runtime/src/server/middleware/sourcemap_stacktrace.ts index 7bd333dd4..a1a515b2a 100644 --- a/runtime/src/server/middleware/sourcemap_stacktrace.ts +++ b/runtime/src/server/middleware/sourcemap_stacktrace.ts @@ -20,14 +20,13 @@ function get_file_contents(path: string) { if (file_cache.has(path)) { return file_cache.get(path); } - if (fs.existsSync(path)) { - try { - const data = fs.readFileSync(path, 'utf8'); - file_cache.set(path, data); - return data; - } catch { - return undefined; - } + + try { + const data = fs.readFileSync(path, 'utf8'); + file_cache.set(path, data); + return data; + } catch { + return undefined; } } @@ -81,10 +80,10 @@ export function sourcemap_stacktrace(stack: string) { if (!pos.source) return input; const abs_source_path = path.resolve(dir, pos.source); - const urlPart = `${abs_source_path}:${pos.line || 0}:${pos.column || 0}`; + const url_part = `${abs_source_path}:${pos.line || 0}:${pos.column || 0}`; - if (!var_name) return ` at ${urlPart}`; - return ` at ${var_name} (${urlPart})`; + if (!var_name) return ` at ${url_part}`; + return ` at ${var_name} (${url_part})`; } ); From fd7258ec65475371a74c1912de3641fb2e4ae93f Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Wed, 5 Aug 2020 09:27:05 +0700 Subject: [PATCH 4/4] simplify variable names and improve test --- package-lock.json | 644 ++---------------- .../server/middleware/sourcemap_stacktrace.ts | 28 +- test/apps/errors/src/routes/_trace.js | 3 + test/apps/errors/src/routes/index.svelte | 3 +- test/apps/errors/src/routes/trace.svelte | 7 + test/apps/errors/test.ts | 9 +- 6 files changed, 72 insertions(+), 622 deletions(-) create mode 100644 test/apps/errors/src/routes/_trace.js create mode 100644 test/apps/errors/src/routes/trace.svelte diff --git a/package-lock.json b/package-lock.json index 47559a827..772a5fb30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -769,6 +769,16 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", @@ -1092,7 +1102,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } } } }, @@ -2133,6 +2147,17 @@ "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } } }, "fast-deep-equal": { @@ -2186,6 +2211,13 @@ "flat-cache": "^2.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2393,609 +2425,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -4211,6 +3640,13 @@ "thenify-all": "^1.0.0" } }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", diff --git a/runtime/src/server/middleware/sourcemap_stacktrace.ts b/runtime/src/server/middleware/sourcemap_stacktrace.ts index a1a515b2a..f845cc61d 100644 --- a/runtime/src/server/middleware/sourcemap_stacktrace.ts +++ b/runtime/src/server/middleware/sourcemap_stacktrace.ts @@ -40,37 +40,37 @@ export function sourcemap_stacktrace(stack: string) { const contents = get_file_contents(file_path); if (!contents) return input; - const sourcemap_path_or_base64 = get_sourcemap_url(contents); - if (!sourcemap_path_or_base64) return input; + const sourcemap_url = get_sourcemap_url(contents); + if (!sourcemap_url) return input; let dir = path.dirname(file_path); let sourcemap_data: string; - if (/^data:application\/json[^,]+base64,/.test(sourcemap_path_or_base64)) { - const raw_data = sourcemap_path_or_base64.slice(sourcemap_path_or_base64.indexOf(',') + 1); + if (/^data:application\/json[^,]+base64,/.test(sourcemap_url)) { + const raw_data = sourcemap_url.slice(sourcemap_url.indexOf(',') + 1); try { sourcemap_data = Buffer.from(raw_data, 'base64').toString(); } catch { return input; } } else { - const abs_sourcemap_path = path.resolve(dir, sourcemap_path_or_base64); - const data = get_file_contents(abs_sourcemap_path); + const sourcemap_path = path.resolve(dir, sourcemap_url); + const data = get_file_contents(sourcemap_path); if (!data) return input; sourcemap_data = data; - dir = path.dirname(abs_sourcemap_path); + dir = path.dirname(sourcemap_path); } - let raw_source_map: RawSourceMap; + let raw_sourcemap: RawSourceMap; try { - raw_source_map = JSON.parse(sourcemap_data); + raw_sourcemap = JSON.parse(sourcemap_data); } catch { return input; } - const consumer = new SourceMapConsumer(raw_source_map); + const consumer = new SourceMapConsumer(raw_sourcemap); const pos = consumer.originalPositionFor({ line: Number(line), column: Number(column), @@ -79,11 +79,11 @@ export function sourcemap_stacktrace(stack: string) { if (!pos.source) return input; - const abs_source_path = path.resolve(dir, pos.source); - const url_part = `${abs_source_path}:${pos.line || 0}:${pos.column || 0}`; + const source_path = path.resolve(dir, pos.source); + const source = `${source_path}:${pos.line || 0}:${pos.column || 0}`; - if (!var_name) return ` at ${url_part}`; - return ` at ${var_name} (${url_part})`; + if (!var_name) return ` at ${source}`; + return ` at ${var_name} (${source})`; } ); diff --git a/test/apps/errors/src/routes/_trace.js b/test/apps/errors/src/routes/_trace.js new file mode 100644 index 000000000..b51b46286 --- /dev/null +++ b/test/apps/errors/src/routes/_trace.js @@ -0,0 +1,3 @@ +export function oops() { + throw new Error('oops'); +} diff --git a/test/apps/errors/src/routes/index.svelte b/test/apps/errors/src/routes/index.svelte index 5ccc389a2..6c6c7a395 100644 --- a/test/apps/errors/src/routes/index.svelte +++ b/test/apps/errors/src/routes/index.svelte @@ -3,4 +3,5 @@ nope blog/nope throw -preload-reject \ No newline at end of file +preload-reject +trace diff --git a/test/apps/errors/src/routes/trace.svelte b/test/apps/errors/src/routes/trace.svelte new file mode 100644 index 000000000..734db07c5 --- /dev/null +++ b/test/apps/errors/src/routes/trace.svelte @@ -0,0 +1,7 @@ + diff --git a/test/apps/errors/test.ts b/test/apps/errors/test.ts index a00039f58..d83ecec94 100644 --- a/test/apps/errors/test.ts +++ b/test/apps/errors/test.ts @@ -71,10 +71,13 @@ describe('errors', function() { ); }); - it('display correct stack traces on server error', async () => { - await r.load('/throw'); + it('display correct stack trace sequences on server error referring to source file', async () => { + await r.load('/trace'); + + const stack = (await r.text('span')).split('\n'); - assert.ok((await r.text('span')).includes('throw.svelte:3:12')); + assert.ok(stack[1] && stack[1].includes('_trace.js:2:11')); + assert.ok(stack[2] && stack[2].includes('trace.svelte:5:6')); }); it('handles error on client', async () => {