你好
TS版本:1.1
给定
function foo() {
var x = 10;
var y = 11;
}
我们曾经得到
function foo() {
var x = 10;
var y = 11;
}
在新的编译器中,缺少换行符
function foo() {
var x = 10;
var y = 11;
}
(两个编译器都删除了第一个空行,但是新的编译器又走了一步。)
这可能会影响在浏览器中调试JavaScript时的体验。
添加一些背景信息...新编译器删除所有空白行的原因是,这是我们唯一可以_consistently_做的事情。 对于需要重写的构造,例如类和模块声明,保留空行总是很容易出错。 我们在保留评论方面面临着完全相同的问题。 因此,尽管我很乐意解决此问题,但这并不是一件容易的事。
@NoelAbrahams我很好奇您在调试时会看到什么问题?
@ahejlsberg @NoelAbrahams我创建了一个在原始CodePlex项目中发出的原型,该原型实际上在注释和换行符保存方面做得很棒。 即使在大量重写的结构上(如箭头函数转换为函数表达式),它几乎总是会执行您直观期望的操作。
行保留主要是在保留旧代码时仅重用换行符,并在进行转换时使用相对间距的功能。 即如果您有:
module M {
}
module N {
}
然后,当您为'N'发出IIFE时,您会说“我们应该在要重写的模块和先前的语法元素之间保留琐事”。
这是我的发射器原型工作之前/之后的工作方式,该原型以高度保真度保留了注释/换行符:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter2/ecmascript5/Parser.ts
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter2/ecmascript5/Parser.ts.expected
您还可以在这里看到很多示例:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/
我没有实现的唯一功能是“对齐”。 例如,如果您的代码在原始代码中以某种方式进行了对齐(这与参数声明非常相似),我们希望发出的代码也保留该代码。
但这将是微不足道的。
也就是说,将结构从TS转换为JS确实试图保留缩进。 您可以在这里看到:
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/ClassDeclaration/ClassDeclaration2.ts
https://typescript.codeplex.com/SourceControl/latest#tests/Fidelity/emitter/ecmascript5/ClassDeclaration/ClassDeclaration2.ts.expected
请注意,即使我们将嵌套的模块和类转换为IIFE,也要正确地声明语句(即使它们分散在多行中)
@ahejlsberg ,在浏览器中调试时没有重大问题。 当与实际的TypeScript源代码完全对应时,它使浏览JavaScript代码和定位用于设置断点的行变得更加容易。
我个人可以没有空行地生活,但是由于TS已竭尽全力来保存和发出_beautiful _ JavaScript(:smile :),所以似乎很自然地实现了这一点。
@ahejlsberg @NoelAbrahams在浏览器中调试存在一个与此对话稍有相关的问题。 当使用setter / getter链(如jquery)或链接promise时,新的换行符在翻译过程中会丢失。 话虽如此,这是使用Arrow函数时的一个巨大痛点。
举个例子:
(<any> x).a('#test')
.b('test')
.c(() => 'foo')
.d(() => 'bar')
.e(() => 5)
.f(() => 6);
成为:
x.a('#test').b('test').c(function () { return 'foo'; }).d(function () { return 'bar'; }).e(function () { return 5; }).f(function () { return 6; });
使用Chrome和sourceMaps,仍然会跳过断点。
http://www.typescriptlang.org/Playground#src =(%3Cany%3E%20x).a('%23test')%0A%20%20%20%20.b('test')%0A% 09.c(()%20%3D%3E%20'foo')%0A%09.d(()%20%3D%3E%20'bar')%0A%09.e(()%20 %3D%3E%205)%0A%09.f(()%20%3D%3E%206)%3B
@mtraynham ,实际上我认为您强调的问题是一个稍微不同的问题。
在以前的版本中,内联函数的主体始终在新行中发出:
// TS
var x = () => 'foo';
// JS - old
var x = function () {
return 'foo';
};
// JS - new
var x = function () { return 'foo'; };
我也发现这是一个问题-有时不得不回去创建function
以便在浏览器中调试时可以设置断点。
@NoelAbrahams啊,是的,我一直在使用这个完全相同的临时解决方案...我不确定这是否是导致此问题(消除换行)的适当错误,还是我应该打开另一个?
我为单独的问题创建了#2259。
作为工程主管,探索将我们的javascript开发社区迁移为打字稿时,新的行功能确实会有所帮助。 打字稿的主要吸引力之一是保持在打字稿生成的Javascript中创建的代码的可读性和结构。
很高兴看到注释保留在最近的更新中,并添加了“ --removeComments”命令行指令。
我也希望这样做的原因与@timjmartel类似-当开发人员看到发出的JS外观_nice_时,他们不太愿意采用。 保留(至少垂直)空白使代码看起来不太像是由机器生成的,而是更像是人类编写的idomatic JS代码。
如果我们的团队决定放弃TS,而是继续使用已转译的JS,则如果它们具有对人类友好的空白,则采用发出的JS源会容易得多。
关于空行,现在是否有可能被发出,即使它们被命中或遗漏? 可以使用“ --keepEmptyLines”选项来询问这种实验性功能,它并不是拥有漂亮的JS而是拥有更具可读性的JS。
关于链式函数调用,是否可以对线路进行一次调用,以使用户设置断点? 同样,可以通过“ --oneCallForLine”选项询问此功能,如果它是另一“命中或失败”的事情。
感谢您的关注。
实际上,链接的函数调用可以由源代码美化器拆分,因此将此类功能嵌入TypeScript没有意义。
这不应该那么难,不应该在tsconfig.json中只有一个选项
preserveWhitespace: true/false
?
但是我不认为这是一个编译器选项: https :
我才发现。 不保留空格的一个很好的理由是,它确实有助于防止意外编辑.js而不是.ts。 我猜想要做的一件事是将.js文件写为只读/仅执行。 因此,这可能不是问题。
话虽这么说,我不认为tsc会将.js文件写为只读/仅执行,有没有办法配置tsc来做到这一点?
不,不是此刻。 可以随意为此打开一个单独的问题。
@DanielRosenwasser,您是在说如果我们要保留空白,我们应该打开一个单独的问题? 这个问题还不够吗? 再过一个月后,再也不要介意大声笑了,我意识到您是在说要对已编译文件的读取/写入/执行权限打开单独的问题:)
拥有这个会很好。
+1
+1
+1
人群想要preserveWhitespace: true/false
@ORE软件++
之所以重要,是因为TypeScript应该“优雅地降级”到JS。 现在,它无法保留新行,使得JS有点难以阅读,尤其是在编写TypeScript的情况下,但是应该将JS交付到其他地方。
+1 preserveWhitespace: true/false
使用esformatter添加换行符。
使用以下配置文件:
{
"lineBreak": {
"before": {
"FunctionDeclaration": ">=2",
"FunctionDeclarationOpeningBrace": 0,
"FunctionDeclarationClosingBrace": 1,
"MethodDefinition": ">=2",
"ClassDeclaration": ">=2"
},
"after": {
"FunctionDeclaration": ">=2",
"FunctionDeclarationOpeningBrace": 1,
"MethodDefinitionClosingBrace": ">=2",
"ClassClosingBrace": ">=2"
}
}
}
@mtraynham您的示例:
(<any> x).a('#test')
.b('test')
.c(() => 'foo')
.d(() => 'bar')
.e(() => 5)
.f(() => 6);
用最新的编译器生成此JS:
x.a('#test')
.b('test')
.c(function () { return 'foo'; })
.d(function () { return 'bar'; })
.e(function () { return 5; })
.f(function () { return 6; });
(请参阅TS PlayGround https://goo.gl/JViurr)
自TS版本1.1(创建此问题的TypeScript版本)以来,发生了很多变化。 也许这个问题可以解决? @ahejlsberg ?
@ valera-rozuvan我改为打开#2259。 我的问题与换行有关,但与该错误描述的问题不完全相同。 #2259前一段时间(2015年5月)被关闭。
这是bril -andrew esformatter配置,但已修复错误,(当类声明包含import单词时,没有换行符):
{
"lineBreak": {
"before": {
"FunctionDeclaration": ">=2",
"FunctionDeclarationOpeningBrace": 0,
"FunctionDeclarationClosingBrace": 1,
"MethodDefinition": ">=2",
"ClassDeclaration": ">=2",
"ExportNamedDeclaration": 2
},
"after": {
"FunctionDeclaration": ">=2",
"FunctionDeclarationOpeningBrace": 1,
"MethodDefinitionClosingBrace": ">=2",
"ClassClosingBrace": ">=2"
}
}
}
我认为使用esformatter不能解决整个问题。 当然,它可以在函数等周围自动插入空行。但是对我来说,函数内的空行更为重要。 我们在散文中使用空白行(如段落):将个人想法分组。
函数内的空白行有助于传达函数的结构。 没有它们,我发现可读性会受到影响。
@ahejlsberg使用ts-jest时,我在单元测试输出中看到不正确的行号,并且此问题似乎是由js输出中删除的空行引起的。 我很好奇为什么很难在最终js中保留这些行。 那里有更多信息吗? 我们可以以某种方式帮助实现这一目标吗? :)
还是已经合并但尚未发布? -> V4下一个大版本#3143
@JimTheMan如果您使用源映射,那么也许source-map-support
包可以帮助您在输出中获取正确的堆栈跟踪。
我也遇到了这个问题。 我通过创建一个diff补丁并还原补丁中的空白更改提出了解决方法。 jsdiff允许您创建结构化的补丁对象并根据需要对其进行操作。
import * as diff from 'diff';
const patch =
diff.parsePatch(diff.createPatch('file', oldText, newText, '', ''));
const hunks = patch[0].hunks;
for (let i = 0; i < hunks.length; ++i) {
let lineOffset = 0;
const hunk = hunks[i];
hunk.lines = hunk.lines.map(line => {
if (line === '-') {
lineOffset++;
return ' ';
}
return line;
});
hunk.newLines += lineOffset;
for (let j = i + 1; j < hunks.length; ++j) {
hunks[j].newStart += lineOffset;
}
}
return diff.applyPatch(oldText, patch);
使用此替代方法,您可以保留原始文件中的所有换行符。
@zeroliu是否在编译步骤中引入了明显的时间延迟?
@ahejlsberg您认为值得解决此问题吗?
@ valera-rozuvan取决于您的项目的大小。 对于使用10-ish LOC 100-1000 LOC文件的用例,它不会带来任何明显的延迟。
这里有什么解决方案吗? 我也遇到麻烦了...
当我的队友@emadum提醒我tsc可以保留注释时,我正在尝试在编译器中修复此问题。 这是一条小口子管道,似乎在保存换行符方面做得相当不错:
const gulp = require('gulp');
const ts = require('gulp-typescript');
const through = require('through2');
function preserveNewlines() {
return through.obj(function(file, encoding, callback) {
const data = file.contents.toString('utf8');
const fixedUp = data.replace(/\n\n/g, '\n/** THIS_IS_A_NEWLINE **/');
file.contents = Buffer.from(fixedUp, 'utf8');
callback(null, file);
});
}
function restoreNewlines() {
return through.obj(function(file, encoding, callback) {
const data = file.contents.toString('utf8');
const fixedUp = data.replace(/\/\*\* THIS_IS_A_NEWLINE \*\*\//g, '\n');
file.contents = Buffer.from(fixedUp, 'utf8');
callback(null, file);
});
}
gulp.task('default', function () {
return gulp.src('src/**/*.ts')
.pipe(preserveNewlines())
.pipe(ts({
removeComments: false
}))
.pipe(restoreNewlines())
.pipe(gulp.dest('lib'));
});
我认为,更巧妙的评论可以防止误报,但对于今天的我们来说似乎行之有效。 我只在一个相对较小的文件上进行了测试,但是那里的性能开销很小。
hth
@mbroadst
我最终以您的想法为基础,并最终对其进行扩展,直到它成为npm模块:
https://www.npmjs.com/package/gulp-preserve-typescript-whitespace
我在自述文件中记下了您的帖子,希望您不介意:)
最有用的评论
人群想要
preserveWhitespace: true/false
@ORE软件++