Typescript: 发射不保留空行

创建于 2014-10-07  ·  36评论  ·  资料来源: microsoft/TypeScript

你好

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时的体验。

Bug help wanted

最有用的评论

人群想要preserveWhitespace: true/false
@ORE软件++

所有36条评论

添加一些背景信息...新编译器删除所有空白行的原因是,这是我们唯一可以_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

我在自述文件中记下了您的帖子,希望您不介意:)

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

bgrieder picture bgrieder  ·  3评论

manekinekko picture manekinekko  ·  3评论

Zlatkovsky picture Zlatkovsky  ·  3评论

siddjain picture siddjain  ·  3评论

blendsdk picture blendsdk  ·  3评论