Screeps:如何使用 TypeScript 和 Webpack 设置源映射?
Screeps: How do I set up source map using TypeScript and Webpack?
我正在尝试使用 TypeScript (^2.9.2) 和 Webpack (^4.12.1) 从头开始设置我的新 Screeps(游戏)项目。如何正确设置源地图?我做了什么:
- 在tsconfig.json
中设置"sourceMap": true,
- 在webpack.config.js中设置
devtool: 'inline-source-map',
。我想内联是 Screeps 的必备条件?
- 在 webpack 配置中为
test: /\.ts$/, enforce: 'pre',
设置 loader: "source-map-loader",
以不丢失 TS 源映射。
在我的 main.ts 中,现在只是 console.log(foo);
结果:
ReferenceError: foo is not defined
at Object../src/main.ts:98:13
at __webpack_require__:20:30
at eval:84:18
at main:87:10
at eval:105:4
at Object.<anonymous>:2:143759
at Object.r.run:2:144268```
Screeps 客户端控制台就是这种情况。在 Firefox 中我只看到 foo is not defined
而没有任何更具体的信息。
有什么方法可以在堆栈跟踪中的某处获得 /src/main.ts:1:13
(正确的行号)?这看起来好像没有任何源映射,但在底部的 main.js 构建中有一个:
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL21haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esa0RBQTBDLGdDQUFnQztBQUMxRTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGdFQUF3RCxrQkFBa0I7QUFDMUU7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaURBQXlDLGlDQUFpQztBQUMxRSx3SEFBZ0gsbUJBQW1CLEVBQUU7QUFDckk7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUNsRkEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyIsImZpbGUiOiJtYWluLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9zcmMvbWFpbi50c1wiKTtcbiIsImNvbnNvbGUubG9nKCdJdCB3b3JrcycpO1xuIl0sInNvdXJjZVJvb3QiOiIifQ==
呸,我成功了。以下是需要完成的工作:
在 webpack 配置中:
将目标设置为'node'
config.target = 'node';
将输出库目标设置为commonjs2
config.output.libraryTarget = 'commonjs2';
设置你的常规开发工具
config.devtool = 'source-map';
内联源映射在 Screeps 中不起作用。
设置外部
config.externals = {
'main.js.map': 'main.js.map',
};
这样我们就可以在代码中写 require('main.js.map')
来在 Screeps 运行时加载源映射文件,这样 Webpack 就不会管它了。
在tsconfig.json设置config.compilerOptions.sourceMap
到true
.
将您的代码上传到 Screeps 服务器:
- 照常上传您的
main.js
捆绑包文件。
- 上传您的源映射文件。将其命名为
main.js.map.js
。最后一个 .js
很重要 - Screeps 游戏会切断它,留下所需的 main.js.map
.
在运行时自己解析你的source map!
yarn add source-map
/npm -i source-map --save
.
- 保留版本 ^0.6.1。 ^0.7(目前最新)仅是异步的。异步代码在 Screeps 中不起作用。
- 用它来手动构建堆栈跟踪消息错误。然后使用
console.log()
. 打印它
- 是的,这是在游戏中花费的 CPU。缓存你的错误信息,这样当你遇到新的错误时你只解析一次而不是每次都解析。
- 此外,source map 在模拟模式下无法工作(我希望我能早点知道)。
将您的代码包裹在错误映射器周围:
export const loop = () => {
errorMapper(tick)();
};
const tick = () => { /* your regular code for current tick */};
祝您调试愉快!
其实这是我的错误-mapper.ts:
import { escape } from 'lodash';
import { MappedPosition, SourceMapConsumer } from 'source-map'; // leave it at version ^0.6.1. ^0.7 is async only.
export default function errorMapper(tick: () => void): () => void {
return () => {
try {
tick();
} catch (error) {
if (error instanceof Error) {
const isSimulation: boolean = ('sim' in Game.rooms);
if (isSimulation) {
printOriginalError(error);
} else {
printStackTrace(error);
}
} else {
throw error;
}
}
};
}
// tslint:disable-next-line: no-var-requires
const consumer: SourceMapConsumer = new SourceMapConsumer(require('main.js.map')); // High CPU usage!
const cache: { [key: string]: string } = {};
function getSourceMapStackTrace(error: Error | string): string {
const originalStackTrace: string = error instanceof Error ? error.stack as string : error;
if (cache[originalStackTrace]) {
return cache[originalStackTrace];
}
const re = /^\s+at\s+(.+?\s+)?\(?([0-z._\-\\/]+):(\d+):(\d+)\)?$/gm;
let match: RegExpExecArray | null;
let outputStackTrace: string = error.toString();
// tslint:disable-next-line:no-conditional-assignment
while ((match = re.exec(originalStackTrace)) !== null) {
const nameFromOriginalStackTrace: string = match[1];
const isStackTraceLineControlledByMe: boolean = match[2] === 'main';
const lineFromOriginalStackTrace: number = parseInt(match[3], 10);
const columnFromOriginalStackTrace: number = parseInt(match[4], 10);
if (!isStackTraceLineControlledByMe) {
break;
}
const { name, source, line, column }: MappedPosition = consumer.originalPositionFor({
column: columnFromOriginalStackTrace,
line: lineFromOriginalStackTrace,
});
if (!line) {
break;
}
const finalName = (name) ? name : (nameFromOriginalStackTrace) ? nameFromOriginalStackTrace : '';
outputStackTrace += stripWebpackFromStackTrace(
`\n at ${finalName}(${source}:${line}:${column})`,
);
}
cache[originalStackTrace] = outputStackTrace;
return outputStackTrace;
}
function printOriginalError(error: Error) {
const message = `Source maps don't work in the Simulation mode.`;
console.log(`<span style="color: tomato">${message}\n${escape(error.stack)}</span>`);
}
function printStackTrace(error: Error) {
const errorMessage = escape(getSourceMapStackTrace(error));
console.log(`<span style="color: tomato">${errorMessage}</span>`);
Game.notify(errorMessage);
}
function stripWebpackFromStackTrace(text: string): string {
return text.replace('webpack:///', '');
}
另外,感谢 screeps-typescript-starter,因为它对我理解 source-map
库在 Screeps 用例中的使用有很大帮助。如果我不想编写和理解我所有的 Screeps 代码并坚持使用 Webpack,我可能不必在这个问题上花费太多。 :)
我正在尝试使用 TypeScript (^2.9.2) 和 Webpack (^4.12.1) 从头开始设置我的新 Screeps(游戏)项目。如何正确设置源地图?我做了什么:
- 在tsconfig.json 中设置
- 在webpack.config.js中设置
devtool: 'inline-source-map',
。我想内联是 Screeps 的必备条件? - 在 webpack 配置中为
test: /\.ts$/, enforce: 'pre',
设置loader: "source-map-loader",
以不丢失 TS 源映射。
"sourceMap": true,
在我的 main.ts 中,现在只是 console.log(foo);
结果:
ReferenceError: foo is not defined
at Object../src/main.ts:98:13
at __webpack_require__:20:30
at eval:84:18
at main:87:10
at eval:105:4
at Object.<anonymous>:2:143759
at Object.r.run:2:144268```
Screeps 客户端控制台就是这种情况。在 Firefox 中我只看到 foo is not defined
而没有任何更具体的信息。
有什么方法可以在堆栈跟踪中的某处获得 /src/main.ts:1:13
(正确的行号)?这看起来好像没有任何源映射,但在底部的 main.js 构建中有一个:
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL21haW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esa0RBQTBDLGdDQUFnQztBQUMxRTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGdFQUF3RCxrQkFBa0I7QUFDMUU7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaURBQXlDLGlDQUFpQztBQUMxRSx3SEFBZ0gsbUJBQW1CLEVBQUU7QUFDckk7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7QUNsRkEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyIsImZpbGUiOiJtYWluLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9zcmMvbWFpbi50c1wiKTtcbiIsImNvbnNvbGUubG9nKCdJdCB3b3JrcycpO1xuIl0sInNvdXJjZVJvb3QiOiIifQ==
呸,我成功了。以下是需要完成的工作:
在 webpack 配置中:
将目标设置为'node'
config.target = 'node';
将输出库目标设置为
commonjs2
config.output.libraryTarget = 'commonjs2';
设置你的常规开发工具
config.devtool = 'source-map';
内联源映射在 Screeps 中不起作用。
设置外部
config.externals = { 'main.js.map': 'main.js.map', };
这样我们就可以在代码中写
require('main.js.map')
来在 Screeps 运行时加载源映射文件,这样 Webpack 就不会管它了。在tsconfig.json设置
config.compilerOptions.sourceMap
到true
.将您的代码上传到 Screeps 服务器:
- 照常上传您的
main.js
捆绑包文件。 - 上传您的源映射文件。将其命名为
main.js.map.js
。最后一个.js
很重要 - Screeps 游戏会切断它,留下所需的main.js.map
.
- 照常上传您的
在运行时自己解析你的source map!
yarn add source-map
/npm -i source-map --save
.- 保留版本 ^0.6.1。 ^0.7(目前最新)仅是异步的。异步代码在 Screeps 中不起作用。
- 用它来手动构建堆栈跟踪消息错误。然后使用
console.log()
. 打印它
- 是的,这是在游戏中花费的 CPU。缓存你的错误信息,这样当你遇到新的错误时你只解析一次而不是每次都解析。
- 此外,source map 在模拟模式下无法工作(我希望我能早点知道)。
将您的代码包裹在错误映射器周围:
export const loop = () => { errorMapper(tick)(); }; const tick = () => { /* your regular code for current tick */};
祝您调试愉快!
其实这是我的错误-mapper.ts:
import { escape } from 'lodash';
import { MappedPosition, SourceMapConsumer } from 'source-map'; // leave it at version ^0.6.1. ^0.7 is async only.
export default function errorMapper(tick: () => void): () => void {
return () => {
try {
tick();
} catch (error) {
if (error instanceof Error) {
const isSimulation: boolean = ('sim' in Game.rooms);
if (isSimulation) {
printOriginalError(error);
} else {
printStackTrace(error);
}
} else {
throw error;
}
}
};
}
// tslint:disable-next-line: no-var-requires
const consumer: SourceMapConsumer = new SourceMapConsumer(require('main.js.map')); // High CPU usage!
const cache: { [key: string]: string } = {};
function getSourceMapStackTrace(error: Error | string): string {
const originalStackTrace: string = error instanceof Error ? error.stack as string : error;
if (cache[originalStackTrace]) {
return cache[originalStackTrace];
}
const re = /^\s+at\s+(.+?\s+)?\(?([0-z._\-\\/]+):(\d+):(\d+)\)?$/gm;
let match: RegExpExecArray | null;
let outputStackTrace: string = error.toString();
// tslint:disable-next-line:no-conditional-assignment
while ((match = re.exec(originalStackTrace)) !== null) {
const nameFromOriginalStackTrace: string = match[1];
const isStackTraceLineControlledByMe: boolean = match[2] === 'main';
const lineFromOriginalStackTrace: number = parseInt(match[3], 10);
const columnFromOriginalStackTrace: number = parseInt(match[4], 10);
if (!isStackTraceLineControlledByMe) {
break;
}
const { name, source, line, column }: MappedPosition = consumer.originalPositionFor({
column: columnFromOriginalStackTrace,
line: lineFromOriginalStackTrace,
});
if (!line) {
break;
}
const finalName = (name) ? name : (nameFromOriginalStackTrace) ? nameFromOriginalStackTrace : '';
outputStackTrace += stripWebpackFromStackTrace(
`\n at ${finalName}(${source}:${line}:${column})`,
);
}
cache[originalStackTrace] = outputStackTrace;
return outputStackTrace;
}
function printOriginalError(error: Error) {
const message = `Source maps don't work in the Simulation mode.`;
console.log(`<span style="color: tomato">${message}\n${escape(error.stack)}</span>`);
}
function printStackTrace(error: Error) {
const errorMessage = escape(getSourceMapStackTrace(error));
console.log(`<span style="color: tomato">${errorMessage}</span>`);
Game.notify(errorMessage);
}
function stripWebpackFromStackTrace(text: string): string {
return text.replace('webpack:///', '');
}
另外,感谢 screeps-typescript-starter,因为它对我理解 source-map
库在 Screeps 用例中的使用有很大帮助。如果我不想编写和理解我所有的 Screeps 代码并坚持使用 Webpack,我可能不必在这个问题上花费太多。 :)