diff --git a/packages/uni-stacktracey/__tests__/uni-stacktracey.spec.ts b/packages/uni-stacktracey/__tests__/uni-stacktracey.spec.ts index e928b373deea923c3cadc91b77900b13d88d8957..b6dac5960b6c863e9a02158a06f8674bfefccaee 100644 --- a/packages/uni-stacktracey/__tests__/uni-stacktracey.spec.ts +++ b/packages/uni-stacktracey/__tests__/uni-stacktracey.spec.ts @@ -34,26 +34,23 @@ describe('uni-stacktracey', () => { test('uniStracktraceyPreset local', () => { stacktracey(uniErrorMsg, { preset: uniStracktraceyPreset({ - base: path.resolve( - __dirname, - '../test/__UNI__APPID__/1.0.0/.sourcemap/h5/' - ), + base: path.resolve(__dirname, '../test/__UNI_APPID__/h5/1.0.0/'), sourceRoot: '', }), }).then((res: string) => { expect(res).toEqual(`Error: Sentry Error -at src/pages/index/index.vue:44 -at src/pages/index/index.vue?be58:12 -at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:1864 -at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:2189 -at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:1864 -at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:2185 -at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:7076 -at node_modules/@sentry/browser/esm/helpers.js:74 `) +at src/pages/index/index.vue:44 +at src/pages/index/index.vue?be58:12:17 +at node_modules/@sentry/browser/esm/helpers.js:74:22 +at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:1864:25 +at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:2189:13 +at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:1864:25 +at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:2185:8 +at node_modules/@dcloudio/vue-cli-plugin-uni/packages/h5-vue/dist/vue.runtime.esm.js:7076:24`) }) }) - test('uniStracktraceyPreset local', () => { + test('utsStracktraceyPreset local', () => { stacktracey(utsErrorMsg, { preset: utsStracktraceyPreset({ base: path.resolve( diff --git a/packages/uni-stacktracey/dist/uni-stacktracey.cjs.js b/packages/uni-stacktracey/dist/uni-stacktracey.cjs.js index 3ed5bc1e61e6c030c7aa877bf51a7ffaca66e7f4..9cbb6a0fd5faffb3c5ee6c6a9367ac2ff749e2b7 100644 --- a/packages/uni-stacktracey/dist/uni-stacktracey.cjs.js +++ b/packages/uni-stacktracey/dist/uni-stacktracey.cjs.js @@ -1,54 +1,253 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -var fs = require('fs'); -var StackTracey = require('stacktracey'); -var sourceMap = require('source-map'); - -function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - -var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); -var StackTracey__default = /*#__PURE__*/_interopDefaultLegacy(StackTracey); - +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var fs = require('fs'); +var sourceMap = require('source-map'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); + +/* ------------------------------------------------------------------------ */ +const O = Object, isBrowser = typeof window !== 'undefined' && + window.window === window && + window.navigator, nodeRequire = isBrowser ? null : module.require, // to prevent bundlers from expanding the require call +lastOf = (x) => x[x.length - 1], nixSlashes$1 = (x) => x.replace(/\\/g, '/'), pathRoot = isBrowser ? window.location.href : nixSlashes$1(process.cwd()) + '/'; +/* ------------------------------------------------------------------------ */ +class StackTracey { + constructor(input, offset) { + this.itemsHeader = []; + this.isMP = false; + const originalInput = input, isParseableSyntaxError = input && input instanceof SyntaxError && !isBrowser; + /* new StackTracey () */ + if (!input) { + input = new Error(); + offset = offset === undefined ? 1 : offset; + } + /* new StackTracey (Error) */ + if (input instanceof Error) { + input = input.stack || ''; + } + /* new StackTracey (string) */ + if (typeof input === 'string') { + this.isMP = input.indexOf('MiniProgramError') !== -1; + input = this.rawParse(input) + .slice(offset) + .map((x) => this.extractEntryMetadata(x)); + } + /* new StackTracey (array) */ + if (Array.isArray(input)) { + if (isParseableSyntaxError) { + const rawLines = nodeRequire('util') + .inspect(originalInput) + .split('\n'), fileLine = rawLines[0].split(':'), line = fileLine.pop(), file = fileLine.join(':'); + if (file) { + input.unshift({ + file: nixSlashes$1(file), + line: line, + column: (rawLines[2] || '').indexOf('^') + 1, + sourceLine: rawLines[1], + callee: '(syntax error)', + syntaxError: true, + }); + } + } + this.items = input; + } + else { + this.items = []; + } + } + extractEntryMetadata(e) { + const decomposedPath = this.decomposePath(e.file || ''); + const fileRelative = decomposedPath[0]; + const externalDomain = decomposedPath[1]; + return O.assign(e, { + calleeShort: e.calleeShort || lastOf((e.callee || '').split('.')), + fileRelative: fileRelative, + fileShort: this.shortenPath(fileRelative), + fileName: lastOf((e.file || '').split('/')), + thirdParty: this.isThirdParty(fileRelative, externalDomain) && !e.index, + externalDomain: externalDomain, + }); + } + shortenPath(relativePath) { + return relativePath + .replace(/^node_modules\//, '') + .replace(/^webpack\/bootstrap\//, '') + .replace(/^__parcel_source_root\//, ''); + } + decomposePath(fullPath) { + let result = fullPath; + if (isBrowser) + result = result.replace(pathRoot, ''); + const externalDomainMatch = result.match(/^(http|https)\:\/\/?([^\/]+)\/(.*)/); + const externalDomain = externalDomainMatch + ? externalDomainMatch[2] + : undefined; + result = externalDomainMatch ? externalDomainMatch[3] : result; + // if (!isBrowser) result = nodeRequire!('path').relative(pathRoot, result) + return [ + nixSlashes$1(result).replace(/^.*\:\/\/?\/?/, ''), + externalDomain, + ]; + } + isThirdParty(relativePath, externalDomain) { + if (this.isMP) { + if (typeof externalDomain === 'undefined') + return false; + return externalDomain !== 'usr'; + } + return (externalDomain || + relativePath[0] === '~' || // webpack-specific heuristic + relativePath[0] === '/' || // external source + relativePath.indexOf('@dcloudio') !== -1 || + relativePath.indexOf('webpack/bootstrap') === 0); + } + rawParse(str) { + const lines = (str || '').split('\n'); + const entries = lines.map((line, index) => { + line = line.trim(); + let callee, fileLineColumn = [], native, planA, planB; + if ((planA = line.match(/at (.+) \(eval at .+ \((.+)\), .+\)/)) || // eval calls + (planA = line.match(/at (.+) \((.+)\)/)) || + (line.slice(0, 3) !== 'at ' && (planA = line.match(/(.*)@(.*)/)))) { + this.itemsHeader.push('%StacktraceyItem%'); + callee = planA[1]; + native = planA[2] === 'native'; + fileLineColumn = (planA[2].match(/(.*):(\d+):(\d+)/) || + planA[2].match(/(.*):(\d+)/) || + planA[2].match(/\[(.*)\]/) || + []).slice(1); + } + else if ((planB = line.match(/^(at\s*)*(.*)\s+(.+):(\d+):(\d+)/))) { + this.itemsHeader.push('%StacktraceyItem%'); + callee = planB[2].trim(); + fileLineColumn = planB.slice(3); + } + else { + this.itemsHeader.push(line); + return undefined; + } + /* Detect things like Array.reduce + TODO: detect more built-in types */ + if (callee && !fileLineColumn[0]) { + const type = callee.split('.')[0]; + if (type === 'Array') { + native = true; + } + } + return { + beforeParse: line, + callee: callee || '', + index: isBrowser && fileLineColumn[0] === window.location.href, + native: native || false, + file: nixSlashes$1(fileLineColumn[0] || ''), + line: parseInt(fileLineColumn[1] || '', 10) || undefined, + column: parseInt(fileLineColumn[2] || '', 10) || undefined, + }; + }); + return entries.filter((x) => x !== undefined); + } + maxColumnWidths() { + return { + callee: 30, + file: 60, + sourceLine: 80, + }; + } + asTable(opts) { + const maxColumnWidths = (opts && opts.maxColumnWidths) || this.maxColumnWidths(); + const trimmed = this + .filter((e) => !e.thirdParty) + .map((e) => parseItem(e, maxColumnWidths, this.isMP)); + const trimmedThirdParty = this + .filter((e) => e.thirdParty) + .map((e) => parseItem(e, maxColumnWidths, this.isMP)); + return { + items: trimmed.items, + thirdPartyItems: trimmedThirdParty.items, + }; + } +} +const trimEnd = (s, n) => s && (s.length > n ? s.slice(0, n - 1) + '…' : s); +const trimStart = (s, n) => s && (s.length > n ? '…' + s.slice(-(n - 1)) : s); +function parseItem(e, maxColumnWidths, isMP) { + const filePath = (isMP ? e.file && e.file : e.fileShort && e.fileShort) + + `${e.line ? ':' + e.line : ''}` + + `${e.column ? ':' + e.column : ''}`; + return [ + 'at ' + trimEnd(isMP ? e.callee : e.calleeShort, maxColumnWidths.callee), + trimStart(filePath || '', maxColumnWidths.file), + trimEnd((e.sourceLine || '').trim() || '', maxColumnWidths.sourceLine), + ]; +} +['map', 'filter', 'slice', 'concat'].forEach((method) => { + StackTracey.prototype[method] = function ( /*...args */) { + // no support for ...args in Node v4 :( + return new StackTracey(this.items[method].apply(this.items, arguments)); + }; +}); +/* ------------------------------------------------------------------------ */ +var StackTracey$1 = StackTracey; + const nixSlashes = (x) => x.replace(/\\/g, '/'); const sourcemapCatch = {}; function stacktracey(stacktrace, opts) { const parseStack = []; const stack = opts.preset.parseStacktrace(stacktrace); stack.items.forEach((item, index) => { - const fn = () => { + const fn = (item, index) => { const { line = 0, column = 0, file, fileName, fileRelative } = item; - try { + if (item.thirdParty) { + return Promise.resolve(); + } + function _getSourceMapContent(file, fileName, fileRelative) { return opts.preset .getSourceMapContent(file, fileName, fileRelative) .then((content) => { if (content) { return getConsumer(content).then((consumer) => { - const sourceMapContent = parseSourceMapContent(consumer, { + return parseSourceMapContent(consumer, { line, column, }); - if (sourceMapContent) { - const { source, sourcePath, sourceLine, sourceColumn, fileName = '', } = sourceMapContent; - stack.items[index] = Object.assign({}, item, { - file: source, - line: sourceLine, - column: sourceColumn, - fileShort: sourcePath, - fileRelative: sourcePath, - fileName, - }); - } }); } }); } + try { + return _getSourceMapContent(file, fileName, fileRelative).then((sourceMapContent) => { + if (sourceMapContent) { + const { source, sourcePath, sourceLine, sourceColumn, fileName = '', } = sourceMapContent; + stack.items[index] = Object.assign({}, item, { + file: source, + line: sourceLine, + column: sourceColumn, + fileShort: sourcePath, + fileRelative: source, + fileName, + thirdParty: isThirdParty(sourcePath), + }); + /** + * 以 .js 结尾 + * 包含 app-service.js 则需要再解析 两次 + * 不包含 app-service.js 则无需再解析 一次 + */ + const curItem = stack.items[index]; + if (stack.isMP && + curItem.beforeParse.indexOf('app-service') !== -1) { + return fn(curItem, index); + } + } + }); + } catch (error) { return Promise.resolve(); } }; - parseStack.push(fn()); + parseStack.push(fn(item, index)); }); return new Promise((resolve, reject) => { Promise.all(parseStack) @@ -69,6 +268,9 @@ function stacktracey(stacktrace, opts) { }); }); } +function isThirdParty(relativePath) { + return relativePath.indexOf('@dcloudio') !== -1; +} function getConsumer(content) { return new Promise((resolve, reject) => { if (sourceMap.SourceMapConsumer.with) { @@ -126,9 +328,24 @@ function parseSourceMapContent(consumer, obj) { }; } } +function joinItem(item) { + const a = item[0]; + const b = item[1] ? ` ${item[1]}` : ''; + const c = item[2] ? ` ${item[2]}` : ''; + return `${a}${b}${c}`; +} function uniStracktraceyPreset(opts) { - const { base, sourceRoot } = opts; + const { base, sourceRoot, splitThirdParty } = opts; + let stack; return { + /** + * + * 微信特殊处理 + * 微信解析步骤: + * 1. //usr/app-service.js -> 'weixin/__APP__/app-service.map.map' + * 2. //usr/pages/API/app-service.js -> 'weixin/pages/API/app-service.map.map' + * 3. uni-list-item/uni-list-item.js -> ${base}/uni-list-item/uni-list-item.js.map + */ parseSourceMapUrl(file, fileName, fileRelative) { // 组合 sourceMapUrl if (fileRelative.indexOf('(') !== -1) @@ -138,18 +355,62 @@ function uniStracktraceyPreset(opts) { if (sourceRoot) { return `${fileRelative.replace(sourceRoot, base + '/')}.map`; } - return `${base}/${fileRelative}.map`; + let baseAfter = ''; + if (stack.isMP) { + if (fileRelative.indexOf('app-service.js') !== -1) { + baseAfter = (base.match(/\w$/) ? '/' : '') + 'weixin'; + if (fileRelative === fileName) { + baseAfter += '/__APP__'; + } + fileRelative = fileRelative.replace('.js', '.map'); + } + if (baseAfter && !!fileRelative.match(/^\w/)) + baseAfter += '/'; + } + return `${base}${baseAfter}${fileRelative}.map`; }, getSourceMapContent(file, fileName, fileRelative) { - return Promise.resolve(getSourceMapContent(this.parseSourceMapUrl(file, fileName, fileRelative))); + if (stack.isMP && fileRelative.indexOf('.js') === -1) { + return Promise.resolve(''); + } + const sourcemapUrl = this.parseSourceMapUrl(file, fileName, fileRelative); + return Promise.resolve(getSourceMapContent(sourcemapUrl)); }, parseStacktrace(stacktrace) { - return new StackTracey__default["default"](stacktrace); + stack = new StackTracey$1(stacktrace); + return stack; }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { const errorName = stacktrace.split('\n')[0]; - return ((errorName.indexOf('at') === -1 ? `${errorName}\n` : '') + - (stack.asTable ? stack.asTable({ maxColumnWidths }) : '')); + const lines = stack.asTable + ? stack.asTable(maxColumnWidths ? { maxColumnWidths } : undefined) + : { items: [], thirdPartyItems: [] }; + if (lines.items.length || lines.thirdPartyItems.length) { + const { items: stackLines, thirdPartyItems: stackThirdPartyLines } = lines; + const userError = stack.itemsHeader + .map((item) => { + if (item === '%StacktraceyItem%') { + const _stack = stackLines.shift(); + return _stack ? joinItem(_stack) : ''; + } + return item; + }) + .filter(Boolean) + .join('\n'); + const thirdParty = stackThirdPartyLines.length + ? stackThirdPartyLines.map(joinItem).join('\n') + : ''; + if (splitThirdParty) { + return { + userError, + thirdParty, + }; + } + return userError + '\n' + thirdParty; + } + else { + return errorName; + } }, }; } @@ -205,6 +466,7 @@ function utsStracktraceyPreset(opts) { .filter((x) => x !== undefined); return { items: entries, + itemsHeader: [], }; }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { @@ -220,8 +482,8 @@ function utsStracktraceyPreset(opts) { .join('\n'); }, }; -} - -exports.stacktracey = stacktracey; -exports.uniStracktraceyPreset = uniStracktraceyPreset; -exports.utsStracktraceyPreset = utsStracktraceyPreset; +} + +exports.stacktracey = stacktracey; +exports.uniStracktraceyPreset = uniStracktraceyPreset; +exports.utsStracktraceyPreset = utsStracktraceyPreset; diff --git a/packages/uni-stacktracey/dist/uni-stacktracey.es.js b/packages/uni-stacktracey/dist/uni-stacktracey.es.js index ab88d6e499b46581118dfc6d5c5f0ba9be7315b1..a433533f9f37987c34aa034d0e72fe22f54f2ef3 100644 --- a/packages/uni-stacktracey/dist/uni-stacktracey.es.js +++ b/packages/uni-stacktracey/dist/uni-stacktracey.es.js @@ -1,7 +1,189 @@ import fs from 'fs'; -import StackTracey from 'stacktracey'; import { SourceMapConsumer } from 'source-map'; +/* ------------------------------------------------------------------------ */ +const O = Object, isBrowser = typeof window !== 'undefined' && + window.window === window && + window.navigator, nodeRequire = isBrowser ? null : module.require, // to prevent bundlers from expanding the require call +lastOf = (x) => x[x.length - 1], nixSlashes$1 = (x) => x.replace(/\\/g, '/'), pathRoot = isBrowser ? window.location.href : nixSlashes$1(process.cwd()) + '/'; +/* ------------------------------------------------------------------------ */ +class StackTracey { + constructor(input, offset) { + this.itemsHeader = []; + this.isMP = false; + const originalInput = input, isParseableSyntaxError = input && input instanceof SyntaxError && !isBrowser; + /* new StackTracey () */ + if (!input) { + input = new Error(); + offset = offset === undefined ? 1 : offset; + } + /* new StackTracey (Error) */ + if (input instanceof Error) { + input = input.stack || ''; + } + /* new StackTracey (string) */ + if (typeof input === 'string') { + this.isMP = input.indexOf('MiniProgramError') !== -1; + input = this.rawParse(input) + .slice(offset) + .map((x) => this.extractEntryMetadata(x)); + } + /* new StackTracey (array) */ + if (Array.isArray(input)) { + if (isParseableSyntaxError) { + const rawLines = nodeRequire('util') + .inspect(originalInput) + .split('\n'), fileLine = rawLines[0].split(':'), line = fileLine.pop(), file = fileLine.join(':'); + if (file) { + input.unshift({ + file: nixSlashes$1(file), + line: line, + column: (rawLines[2] || '').indexOf('^') + 1, + sourceLine: rawLines[1], + callee: '(syntax error)', + syntaxError: true, + }); + } + } + this.items = input; + } + else { + this.items = []; + } + } + extractEntryMetadata(e) { + const decomposedPath = this.decomposePath(e.file || ''); + const fileRelative = decomposedPath[0]; + const externalDomain = decomposedPath[1]; + return O.assign(e, { + calleeShort: e.calleeShort || lastOf((e.callee || '').split('.')), + fileRelative: fileRelative, + fileShort: this.shortenPath(fileRelative), + fileName: lastOf((e.file || '').split('/')), + thirdParty: this.isThirdParty(fileRelative, externalDomain) && !e.index, + externalDomain: externalDomain, + }); + } + shortenPath(relativePath) { + return relativePath + .replace(/^node_modules\//, '') + .replace(/^webpack\/bootstrap\//, '') + .replace(/^__parcel_source_root\//, ''); + } + decomposePath(fullPath) { + let result = fullPath; + if (isBrowser) + result = result.replace(pathRoot, ''); + const externalDomainMatch = result.match(/^(http|https)\:\/\/?([^\/]+)\/(.*)/); + const externalDomain = externalDomainMatch + ? externalDomainMatch[2] + : undefined; + result = externalDomainMatch ? externalDomainMatch[3] : result; + // if (!isBrowser) result = nodeRequire!('path').relative(pathRoot, result) + return [ + nixSlashes$1(result).replace(/^.*\:\/\/?\/?/, ''), + externalDomain, + ]; + } + isThirdParty(relativePath, externalDomain) { + if (this.isMP) { + if (typeof externalDomain === 'undefined') + return false; + return externalDomain !== 'usr'; + } + return (externalDomain || + relativePath[0] === '~' || // webpack-specific heuristic + relativePath[0] === '/' || // external source + relativePath.indexOf('@dcloudio') !== -1 || + relativePath.indexOf('webpack/bootstrap') === 0); + } + rawParse(str) { + const lines = (str || '').split('\n'); + const entries = lines.map((line, index) => { + line = line.trim(); + let callee, fileLineColumn = [], native, planA, planB; + if ((planA = line.match(/at (.+) \(eval at .+ \((.+)\), .+\)/)) || // eval calls + (planA = line.match(/at (.+) \((.+)\)/)) || + (line.slice(0, 3) !== 'at ' && (planA = line.match(/(.*)@(.*)/)))) { + this.itemsHeader.push('%StacktraceyItem%'); + callee = planA[1]; + native = planA[2] === 'native'; + fileLineColumn = (planA[2].match(/(.*):(\d+):(\d+)/) || + planA[2].match(/(.*):(\d+)/) || + planA[2].match(/\[(.*)\]/) || + []).slice(1); + } + else if ((planB = line.match(/^(at\s*)*(.*)\s+(.+):(\d+):(\d+)/))) { + this.itemsHeader.push('%StacktraceyItem%'); + callee = planB[2].trim(); + fileLineColumn = planB.slice(3); + } + else { + this.itemsHeader.push(line); + return undefined; + } + /* Detect things like Array.reduce + TODO: detect more built-in types */ + if (callee && !fileLineColumn[0]) { + const type = callee.split('.')[0]; + if (type === 'Array') { + native = true; + } + } + return { + beforeParse: line, + callee: callee || '', + index: isBrowser && fileLineColumn[0] === window.location.href, + native: native || false, + file: nixSlashes$1(fileLineColumn[0] || ''), + line: parseInt(fileLineColumn[1] || '', 10) || undefined, + column: parseInt(fileLineColumn[2] || '', 10) || undefined, + }; + }); + return entries.filter((x) => x !== undefined); + } + maxColumnWidths() { + return { + callee: 30, + file: 60, + sourceLine: 80, + }; + } + asTable(opts) { + const maxColumnWidths = (opts && opts.maxColumnWidths) || this.maxColumnWidths(); + const trimmed = this + .filter((e) => !e.thirdParty) + .map((e) => parseItem(e, maxColumnWidths, this.isMP)); + const trimmedThirdParty = this + .filter((e) => e.thirdParty) + .map((e) => parseItem(e, maxColumnWidths, this.isMP)); + return { + items: trimmed.items, + thirdPartyItems: trimmedThirdParty.items, + }; + } +} +const trimEnd = (s, n) => s && (s.length > n ? s.slice(0, n - 1) + '…' : s); +const trimStart = (s, n) => s && (s.length > n ? '…' + s.slice(-(n - 1)) : s); +function parseItem(e, maxColumnWidths, isMP) { + const filePath = (isMP ? e.file && e.file : e.fileShort && e.fileShort) + + `${e.line ? ':' + e.line : ''}` + + `${e.column ? ':' + e.column : ''}`; + return [ + 'at ' + trimEnd(isMP ? e.callee : e.calleeShort, maxColumnWidths.callee), + trimStart(filePath || '', maxColumnWidths.file), + trimEnd((e.sourceLine || '').trim() || '', maxColumnWidths.sourceLine), + ]; +} +['map', 'filter', 'slice', 'concat'].forEach((method) => { + StackTracey.prototype[method] = function ( /*...args */) { + // no support for ...args in Node v4 :( + return new StackTracey(this.items[method].apply(this.items, arguments)); + }; +}); +/* ------------------------------------------------------------------------ */ +var StackTracey$1 = StackTracey; + // @ts-ignore { // @ts-ignore @@ -18,38 +200,56 @@ function stacktracey(stacktrace, opts) { const parseStack = []; const stack = opts.preset.parseStacktrace(stacktrace); stack.items.forEach((item, index) => { - const fn = () => { + const fn = (item, index) => { const { line = 0, column = 0, file, fileName, fileRelative } = item; - try { + if (item.thirdParty) { + return Promise.resolve(); + } + function _getSourceMapContent(file, fileName, fileRelative) { return opts.preset .getSourceMapContent(file, fileName, fileRelative) .then((content) => { if (content) { return getConsumer(content).then((consumer) => { - const sourceMapContent = parseSourceMapContent(consumer, { + return parseSourceMapContent(consumer, { line, column, }); - if (sourceMapContent) { - const { source, sourcePath, sourceLine, sourceColumn, fileName = '', } = sourceMapContent; - stack.items[index] = Object.assign({}, item, { - file: source, - line: sourceLine, - column: sourceColumn, - fileShort: sourcePath, - fileRelative: sourcePath, - fileName, - }); - } }); } }); } + try { + return _getSourceMapContent(file, fileName, fileRelative).then((sourceMapContent) => { + if (sourceMapContent) { + const { source, sourcePath, sourceLine, sourceColumn, fileName = '', } = sourceMapContent; + stack.items[index] = Object.assign({}, item, { + file: source, + line: sourceLine, + column: sourceColumn, + fileShort: sourcePath, + fileRelative: source, + fileName, + thirdParty: isThirdParty(sourcePath), + }); + /** + * 以 .js 结尾 + * 包含 app-service.js 则需要再解析 两次 + * 不包含 app-service.js 则无需再解析 一次 + */ + const curItem = stack.items[index]; + if (stack.isMP && + curItem.beforeParse.indexOf('app-service') !== -1) { + return fn(curItem, index); + } + } + }); + } catch (error) { return Promise.resolve(); } }; - parseStack.push(fn()); + parseStack.push(fn(item, index)); }); return new Promise((resolve, reject) => { Promise.all(parseStack) @@ -70,6 +270,9 @@ function stacktracey(stacktrace, opts) { }); }); } +function isThirdParty(relativePath) { + return relativePath.indexOf('@dcloudio') !== -1; +} function getConsumer(content) { return new Promise((resolve, reject) => { if (SourceMapConsumer.with) { @@ -127,9 +330,24 @@ function parseSourceMapContent(consumer, obj) { }; } } +function joinItem(item) { + const a = item[0]; + const b = item[1] ? ` ${item[1]}` : ''; + const c = item[2] ? ` ${item[2]}` : ''; + return `${a}${b}${c}`; +} function uniStracktraceyPreset(opts) { - const { base, sourceRoot } = opts; + const { base, sourceRoot, splitThirdParty } = opts; + let stack; return { + /** + * + * 微信特殊处理 + * 微信解析步骤: + * 1. //usr/app-service.js -> 'weixin/__APP__/app-service.map.map' + * 2. //usr/pages/API/app-service.js -> 'weixin/pages/API/app-service.map.map' + * 3. uni-list-item/uni-list-item.js -> ${base}/uni-list-item/uni-list-item.js.map + */ parseSourceMapUrl(file, fileName, fileRelative) { // 组合 sourceMapUrl if (fileRelative.indexOf('(') !== -1) @@ -139,18 +357,62 @@ function uniStracktraceyPreset(opts) { if (sourceRoot) { return `${fileRelative.replace(sourceRoot, base + '/')}.map`; } - return `${base}/${fileRelative}.map`; + let baseAfter = ''; + if (stack.isMP) { + if (fileRelative.indexOf('app-service.js') !== -1) { + baseAfter = (base.match(/\w$/) ? '/' : '') + 'weixin'; + if (fileRelative === fileName) { + baseAfter += '__APP__'; + } + fileRelative = fileRelative.replace('.js', '.map'); + } + if (baseAfter && !!fileRelative.match(/^\w/)) + baseAfter += '/'; + } + return `${base}${baseAfter}${fileRelative}.map`; }, getSourceMapContent(file, fileName, fileRelative) { - return Promise.resolve(getSourceMapContent(this.parseSourceMapUrl(file, fileName, fileRelative))); + if (stack.isMP && fileRelative.indexOf('.js') === -1) { + return Promise.resolve(''); + } + const sourcemapUrl = this.parseSourceMapUrl(file, fileName, fileRelative); + return Promise.resolve(getSourceMapContent(sourcemapUrl)); }, parseStacktrace(stacktrace) { - return new StackTracey(stacktrace); + stack = new StackTracey$1(stacktrace); + return stack; }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { const errorName = stacktrace.split('\n')[0]; - return ((errorName.indexOf('at') === -1 ? `${errorName}\n` : '') + - (stack.asTable ? stack.asTable({ maxColumnWidths }) : '')); + const lines = stack.asTable + ? stack.asTable(maxColumnWidths ? { maxColumnWidths } : undefined) + : { items: [], thirdPartyItems: [] }; + if (lines.items.length || lines.thirdPartyItems.length) { + const { items: stackLines, thirdPartyItems: stackThirdPartyLines } = lines; + const userError = stack.itemsHeader + .map((item) => { + if (item === '%StacktraceyItem%') { + const _stack = stackLines.shift(); + return _stack ? joinItem(_stack) : ''; + } + return item; + }) + .filter(Boolean) + .join('\n'); + const thirdParty = stackThirdPartyLines.length + ? stackThirdPartyLines.map(joinItem).join('\n') + : ''; + if (splitThirdParty) { + return { + userError, + thirdParty, + }; + } + return userError + '\n' + thirdParty; + } + else { + return errorName; + } }, }; } @@ -206,6 +468,7 @@ function utsStracktraceyPreset(opts) { .filter((x) => x !== undefined); return { items: entries, + itemsHeader: [], }; }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { diff --git a/packages/uni-stacktracey/src/index.ts b/packages/uni-stacktracey/src/index.ts index 2780621e67f4a56c79941fc3a304c6799f7156a6..e5b484a0178bbd18f862c0e29f96b5fe66bc3376 100644 --- a/packages/uni-stacktracey/src/index.ts +++ b/packages/uni-stacktracey/src/index.ts @@ -1,5 +1,5 @@ import fs from 'fs' -import StackTracey from 'stacktracey' +import StackTracey from './stacktracey' import { SourceMapConsumer, BasicSourceMapConsumer, @@ -27,8 +27,17 @@ type StacktraceyItems = StackTracey.Entry & { } type Stacktracey = { items: StacktraceyItems[] + itemsHeader: StackTracey['itemsHeader'] asTable?: StackTracey['asTable'] } + +type asTableResult = + | string + | { + userError: string + thirdParty: string + } + interface StacktraceyPreset { /** * 解析错误栈信息 @@ -43,7 +52,7 @@ interface StacktraceyPreset { stack: Stacktracey maxColumnWidths?: StackTracey.MaxColumnWidths stacktrace: string - }): string + }): asTableResult /** * 编译后的文件名地址 * @param file @@ -69,51 +78,82 @@ interface StacktraceyOptions { export function stacktracey( stacktrace: string, opts: StacktraceyOptions -): Promise { +): Promise { const parseStack: Array> = [] const stack = opts.preset.parseStacktrace(stacktrace) stack.items.forEach((item, index) => { - const fn = () => { + const fn = ( + item: StacktraceyItems, + index: number + ): Promise => { const { line = 0, column = 0, file, fileName, fileRelative } = item - try { + if (item.thirdParty) { + return Promise.resolve() + } + + function _getSourceMapContent( + file: string, + fileName: string, + fileRelative: string + ) { return opts.preset .getSourceMapContent(file, fileName, fileRelative) .then((content) => { if (content) { return getConsumer(content).then((consumer) => { - const sourceMapContent = parseSourceMapContent(consumer, { + return parseSourceMapContent(consumer, { line, column, }) - - if (sourceMapContent) { - const { - source, - sourcePath, - sourceLine, - sourceColumn, - fileName = '', - } = sourceMapContent - - stack.items[index] = Object.assign({}, item, { - file: source, - line: sourceLine, - column: sourceColumn, - fileShort: sourcePath, - fileRelative: sourcePath, - fileName, - }) - } }) } }) + } + + try { + return _getSourceMapContent(file, fileName, fileRelative).then( + (sourceMapContent) => { + if (sourceMapContent) { + const { + source, + sourcePath, + sourceLine, + sourceColumn, + fileName = '', + } = sourceMapContent + + stack.items[index] = Object.assign({}, item, { + file: source, + line: sourceLine, + column: sourceColumn, + fileShort: sourcePath, + fileRelative: source, + fileName, + thirdParty: isThirdParty(sourcePath), + }) + + /** + * 以 .js 结尾 + * 包含 app-service.js 则需要再解析 两次 + * 不包含 app-service.js 则无需再解析 一次 + */ + const curItem = stack.items[index] + if ( + (stack as StackTracey).isMP && + curItem.beforeParse.indexOf('app-service') !== -1 + ) { + return fn(curItem, index) + } + } + } + ) } catch (error) { return Promise.resolve() } } - parseStack.push(fn()) + parseStack.push(fn(item, index)) }) return new Promise((resolve, reject) => { @@ -136,6 +176,10 @@ export function stacktracey( }) } +function isThirdParty(relativePath: string) { + return relativePath.indexOf('@dcloudio') !== -1 +} + function getConsumer( content: string ): Promise { @@ -182,19 +226,17 @@ function getSourceMapContent(sourcemapUrl: string) { } } -type SourceMapContent = - | undefined - | { - source: string - sourcePath: string - sourceLine: number - sourceColumn: number - fileName: string | undefined - } +type SourceMapContent = { + source: string + sourcePath: string + sourceLine: number + sourceColumn: number + fileName: string | undefined +} function parseSourceMapContent( consumer: BasicSourceMapConsumer | IndexedSourceMapConsumer, obj: Position -): SourceMapContent { +): SourceMapContent | undefined { // source -> 'uni-app:///node_modules/@sentry/browser/esm/helpers.js' const { source, @@ -219,13 +261,32 @@ function parseSourceMapContent( interface UniStracktraceyPresetOptions { base: string sourceRoot: string + splitThirdParty?: boolean +} + +function joinItem(item: string[]) { + const a = item[0] + const b = item[1] ? ` ${item[1]}` : '' + const c = item[2] ? ` ${item[2]}` : '' + return `${a}${b}${c}` } + export function uniStracktraceyPreset( opts: UniStracktraceyPresetOptions ): StacktraceyPreset { - const { base, sourceRoot } = opts + const { base, sourceRoot, splitThirdParty } = opts + + let stack: StackTracey return { + /** + * + * 微信特殊处理 + * 微信解析步骤: + * 1. //usr/app-service.js -> 'weixin/__APP__/app-service.map.map' + * 2. //usr/pages/API/app-service.js -> 'weixin/pages/API/app-service.map.map' + * 3. uni-list-item/uni-list-item.js -> ${base}/uni-list-item/uni-list-item.js.map + */ parseSourceMapUrl(file, fileName, fileRelative) { // 组合 sourceMapUrl if (fileRelative.indexOf('(') !== -1) @@ -234,24 +295,65 @@ export function uniStracktraceyPreset( if (sourceRoot) { return `${fileRelative.replace(sourceRoot, base + '/')}.map` } - return `${base}/${fileRelative}.map` + let baseAfter = '' + if (stack.isMP) { + if (fileRelative.indexOf('app-service.js') !== -1) { + baseAfter = (base.match(/\w$/) ? '/' : '') + 'weixin' + if (fileRelative === fileName) { + baseAfter += '/__APP__' + } + fileRelative = fileRelative.replace('.js', '.map') + } + if (baseAfter && !!fileRelative.match(/^\w/)) baseAfter += '/' + } + return `${base}${baseAfter}${fileRelative}.map` }, getSourceMapContent(file, fileName, fileRelative) { - return Promise.resolve( - getSourceMapContent( - this.parseSourceMapUrl(file, fileName, fileRelative) - ) - ) + if (stack.isMP && fileRelative.indexOf('.js') === -1) { + return Promise.resolve('') + } + const sourcemapUrl = this.parseSourceMapUrl(file, fileName, fileRelative) + return Promise.resolve(getSourceMapContent(sourcemapUrl)) }, parseStacktrace(stacktrace) { - return new StackTracey(stacktrace) + stack = new StackTracey(stacktrace) + return stack }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { const errorName = stacktrace.split('\n')[0] - return ( - (errorName.indexOf('at') === -1 ? `${errorName}\n` : '') + - (stack.asTable ? stack.asTable({ maxColumnWidths }) : '') - ) + const lines = stack.asTable + ? stack.asTable(maxColumnWidths ? { maxColumnWidths } : undefined) + : { items: [], thirdPartyItems: [] } + if (lines.items.length || lines.thirdPartyItems.length) { + const { items: stackLines, thirdPartyItems: stackThirdPartyLines } = + lines + + const userError = stack.itemsHeader + .map((item) => { + if (item === '%StacktraceyItem%') { + const _stack = stackLines.shift() + + return _stack ? joinItem(_stack) : '' + } + return item + }) + .filter(Boolean) + .join('\n') + const thirdParty = stackThirdPartyLines.length + ? stackThirdPartyLines.map(joinItem).join('\n') + : '' + + if (splitThirdParty) { + return { + userError, + thirdParty, + } + } + + return userError + '\n' + thirdParty + } else { + return errorName + } }, } } @@ -312,7 +414,7 @@ export function utsStracktraceyPreset( return undefined } - const fileName = planA[1] + const fileName: string = planA[1] ? (planB = planA[1].match(/(.*)*\/(.+)/) || [])[2] || '' : '' @@ -336,6 +438,7 @@ export function utsStracktraceyPreset( return { items: entries as StackTracey.Entry[], + itemsHeader: [], } }, asTableStacktrace({ maxColumnWidths, stacktrace, stack }) { diff --git a/packages/uni-stacktracey/src/stacktracey.ts b/packages/uni-stacktracey/src/stacktracey.ts new file mode 100644 index 0000000000000000000000000000000000000000..a808cdab3e5822c73dfd31b6d48558fad3e259ca --- /dev/null +++ b/packages/uni-stacktracey/src/stacktracey.ts @@ -0,0 +1,310 @@ +'use strict' + +interface EntryMetadata { + beforeParse: string + callee: string + index: boolean + native: boolean + file: string + line: number | undefined + column: number | undefined + calleeShort?: string +} + +/* ------------------------------------------------------------------------ */ + +const O = Object, + isBrowser = + /* eslint-disable */ + typeof window !== 'undefined' && + /* eslint-disable */ + window.window === window && + /* eslint-disable */ + window.navigator, + nodeRequire = isBrowser ? null : module.require, // to prevent bundlers from expanding the require call + lastOf = (x: Array) => x[x.length - 1], + nixSlashes = (x: string) => x.replace(/\\/g, '/'), + pathRoot = isBrowser ? window.location.href : nixSlashes(process.cwd()) + '/' + +/* ------------------------------------------------------------------------ */ + +class StackTracey { + items: StackTracey.Entry[] + itemsHeader: string[] = [] + isMP: boolean = false + + constructor(input: string | Error | any, offset?: number) { + const originalInput = input, + isParseableSyntaxError = + input && input instanceof SyntaxError && !isBrowser + + /* new StackTracey () */ + + if (!input) { + input = new Error() + offset = offset === undefined ? 1 : offset + } + + /* new StackTracey (Error) */ + + if (input instanceof Error) { + input = input.stack || '' + } + + /* new StackTracey (string) */ + + if (typeof input === 'string') { + this.isMP = input.indexOf('MiniProgramError') !== -1 + input = (this.rawParse(input) as EntryMetadata[]) + .slice(offset) + .map((x: EntryMetadata) => this.extractEntryMetadata(x)) + } + + /* new StackTracey (array) */ + + if (Array.isArray(input)) { + if (isParseableSyntaxError) { + const rawLines = nodeRequire!('util') + .inspect(originalInput) + .split('\n'), + fileLine = rawLines[0].split(':'), + line = fileLine.pop(), + file = fileLine.join(':') + + if (file) { + input.unshift({ + file: nixSlashes(file), + line: line, + column: (rawLines[2] || '').indexOf('^') + 1, + sourceLine: rawLines[1], + callee: '(syntax error)', + syntaxError: true, + }) + } + } + + this.items = input + } else { + this.items = [] + } + } + + extractEntryMetadata(e: EntryMetadata) { + const decomposedPath = this.decomposePath(e.file || '') + const fileRelative = decomposedPath[0] + const externalDomain = decomposedPath[1] + + return O.assign(e, { + calleeShort: e.calleeShort || lastOf((e.callee || '').split('.')), + fileRelative: fileRelative, + fileShort: this.shortenPath(fileRelative), + fileName: lastOf((e.file || '').split('/')), + thirdParty: this.isThirdParty(fileRelative, externalDomain) && !e.index, + externalDomain: externalDomain, + }) + } + + shortenPath(relativePath: string) { + return relativePath + .replace(/^node_modules\//, '') + .replace(/^webpack\/bootstrap\//, '') + .replace(/^__parcel_source_root\//, '') + } + + decomposePath(fullPath: string): string[] { + let result = fullPath + + if (isBrowser) result = result.replace(pathRoot, '') + + const externalDomainMatch = result.match( + /^(http|https)\:\/\/?([^\/]+)\/(.*)/ + ) + const externalDomain = externalDomainMatch + ? externalDomainMatch[2] + : undefined + result = externalDomainMatch ? externalDomainMatch[3] : result + + // if (!isBrowser) result = nodeRequire!('path').relative(pathRoot, result) + + return [ + nixSlashes(result).replace(/^.*\:\/\/?\/?/, ''), // cut webpack:/// and webpack:/ things + externalDomain!, + ] + } + + isThirdParty(relativePath: string, externalDomain: string) { + if (this.isMP) { + if (typeof externalDomain === 'undefined') return false + return externalDomain !== 'usr' + } + return ( + externalDomain || + relativePath[0] === '~' || // webpack-specific heuristic + relativePath[0] === '/' || // external source + relativePath.indexOf('@dcloudio') !== -1 || + relativePath.indexOf('webpack/bootstrap') === 0 + ) + } + + rawParse(str: string) { + const lines = (str || '').split('\n') + + const entries = lines.map((line, index) => { + line = line.trim() + + let callee, + fileLineColumn = [], + native, + planA, + planB + + if ( + (planA = line.match(/at (.+) \(eval at .+ \((.+)\), .+\)/)) || // eval calls + (planA = line.match(/at (.+) \((.+)\)/)) || + (line.slice(0, 3) !== 'at ' && (planA = line.match(/(.*)@(.*)/))) + ) { + this.itemsHeader.push('%StacktraceyItem%') + callee = planA[1] + native = planA[2] === 'native' + fileLineColumn = ( + planA[2].match(/(.*):(\d+):(\d+)/) || + planA[2].match(/(.*):(\d+)/) || + planA[2].match(/\[(.*)\]/) || + [] + ).slice(1) + } else if ((planB = line.match(/^(at\s*)*(.*)\s+(.+):(\d+):(\d+)/))) { + this.itemsHeader.push('%StacktraceyItem%') + callee = planB[2].trim() + fileLineColumn = planB.slice(3) + } else { + this.itemsHeader.push(line) + return undefined + } + + /* Detect things like Array.reduce + TODO: detect more built-in types */ + + if (callee && !fileLineColumn[0]) { + const type = callee.split('.')[0] + if (type === 'Array') { + native = true + } + } + + return { + beforeParse: line, + callee: callee || '', + /* eslint-disable */ + index: isBrowser && fileLineColumn[0] === window.location.href, + native: native || false, + file: nixSlashes(fileLineColumn[0] || ''), + line: parseInt(fileLineColumn[1] || '', 10) || undefined, + column: parseInt(fileLineColumn[2] || '', 10) || undefined, + } + }) + + return entries.filter((x) => x !== undefined) + } + + maxColumnWidths() { + return { + callee: 30, + file: 60, + sourceLine: 80, + } + } + + asTable(opts?: { maxColumnWidths: StackTracey.MaxColumnWidths }) { + const maxColumnWidths = + (opts && opts.maxColumnWidths) || this.maxColumnWidths() + + const trimmed = (this as any) + .filter((e: StackTracey.Entry) => !e.thirdParty) + .map((e: StackTracey.Entry) => parseItem(e, maxColumnWidths, this.isMP)) + + const trimmedThirdParty = (this as any) + .filter((e: StackTracey.Entry) => e.thirdParty) + .map((e: StackTracey.Entry) => parseItem(e, maxColumnWidths, this.isMP)) + + return { + items: trimmed.items as Array, + thirdPartyItems: trimmedThirdParty.items as Array, + } + } +} + +const trimEnd = (s: string, n: number) => + s && (s.length > n ? s.slice(0, n - 1) + '…' : s) +const trimStart = (s: string, n: number) => + s && (s.length > n ? '…' + s.slice(-(n - 1)) : s) + +function parseItem( + e: StackTracey.Entry, + maxColumnWidths: StackTracey.MaxColumnWidths, + isMP: boolean +) { + const filePath = + (isMP ? e.file && e.file : e.fileShort && e.fileShort) + + `${e.line ? ':' + e.line : ''}` + + `${e.column ? ':' + e.column : ''}` + return [ + 'at ' + trimEnd(isMP ? e.callee : e.calleeShort, maxColumnWidths.callee), + trimStart(filePath || '', maxColumnWidths.file), + trimEnd((e.sourceLine || '').trim() || '', maxColumnWidths.sourceLine), + ] +} + +/* Array methods + ------------------------------------------------------------------------ */ + +;['map', 'filter', 'slice', 'concat'].forEach((method) => { + ;(StackTracey.prototype as any)[method] = function (/*...args */) { + // no support for ...args in Node v4 :( + return new StackTracey(this.items[method].apply(this.items, arguments)) + } +}) + +/* ------------------------------------------------------------------------ */ + +export default StackTracey + +declare namespace StackTracey { + interface SourceFile { + path: string + text: string + lines: string[] + error?: Error + } + + interface Location { + file: string + line?: number + column?: number + } + + interface Entry extends Location { + beforeParse: string + callee: string + index: boolean + native: boolean + + calleeShort: string + fileRelative: string + fileShort: string + fileName: string + thirdParty: boolean + + hide?: boolean + sourceLine?: string + sourceFile?: SourceFile + error?: Error + line?: number + column?: number + } + + interface MaxColumnWidths { + callee: number + file: number + sourceLine: number + } +} diff --git a/packages/uni-stacktracey/test/__UNI__APPID__/1.0.0/.sourcemap/h5/static/js/chunk-vendors.75525bd5.js.map b/packages/uni-stacktracey/test/__UNI_APPID__/h5/1.0.0/static/js/chunk-vendors.75525bd5.js.map similarity index 100% rename from packages/uni-stacktracey/test/__UNI__APPID__/1.0.0/.sourcemap/h5/static/js/chunk-vendors.75525bd5.js.map rename to packages/uni-stacktracey/test/__UNI_APPID__/h5/1.0.0/static/js/chunk-vendors.75525bd5.js.map diff --git a/packages/uni-stacktracey/test/__UNI__APPID__/1.0.0/.sourcemap/h5/static/js/pages-index-index.3ab0d0e5.js.map b/packages/uni-stacktracey/test/__UNI_APPID__/h5/1.0.0/static/js/pages-index-index.3ab0d0e5.js.map similarity index 100% rename from packages/uni-stacktracey/test/__UNI__APPID__/1.0.0/.sourcemap/h5/static/js/pages-index-index.3ab0d0e5.js.map rename to packages/uni-stacktracey/test/__UNI_APPID__/h5/1.0.0/static/js/pages-index-index.3ab0d0e5.js.map diff --git a/packages/uni-stacktracey/test/index.js b/packages/uni-stacktracey/test/index.js index 9289e94fec84aaefff6215d6a95fb9b60bda1c7a..cb89cb6d7df4ecd666a8e690cf98dec7831d132d 100644 --- a/packages/uni-stacktracey/test/index.js +++ b/packages/uni-stacktracey/test/index.js @@ -20,6 +20,17 @@ Run with --stacktrace option to get the stack trace. Run with --info or --debug BUILD FAILED in 2s ` +stacktracey(utsErrorMsg, { + preset: utsStracktraceyPreset({ + base: path.resolve( + __dirname, + './nativeplugins-sourceMap/DCloud-UTSPlugin/' + ), + sourceRoot: 'DCloud-UTSPlugin/android/src/', + }), +}).then((res) => { + console.log('res :>> ', res) +}) const uniErrorMsg = `Error: Sentry Error at a.throwError(/static/js/pages-index-index.3ab0d0e5.js:1:567) @@ -30,24 +41,12 @@ at ee(/static/js/chunk-vendors.75525bd5.js:34:11927) at HTMLElement.n(/static/js/chunk-vendors.75525bd5.js:34:13824) at HTMLElement.o._wrapper(/static/js/chunk-vendors.75525bd5.js:34:53966) at HTMLElement.i(/static/js/chunk-vendors.75525bd5.js:7:609894)` - stacktracey(uniErrorMsg, { preset: uniStracktraceyPreset({ base: path.resolve(__dirname, './__UNI__APPID__/1.0.0/.sourcemap/h5/'), sourceRoot: '', + // splitThirdParty: true }), }).then((res) => { console.log('res :>> ', res) }) -/* stacktracey(utsErrorMsg, { - preset: utsStracktraceyPreset({ - base: path.resolve( - __dirname, - './nativeplugins-sourceMap/DCloud-UTSPlugin/' - ), - sourceRoot: 'DCloud-UTSPlugin/android/src/', - }), -}).then((res) => { - console.log('res :>> ', res) -}) - */