For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/stage/2/proposal-string-dedent.md.
  • 简体中文
  • String.dedent S2

    提案概览
    提案速览

    该提案引入了 String.dedent 标签函数用于模板字面量,以自动移除公共缩进,解决嵌入多行文本时的可读性问题。它指定了处理开头/结尾行和仅空白行的规则,以及与其他标签函数的组合方式。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    String dedent

    提案发起人: @jridgewell, @hemanth

    作者: @mmkal

    状态: Stage 2

    问题

    当尝试在 JS 代码中嵌入格式化文本(例如,Markdown 内容或 JS 程序的源代码)时,开发者不得不为了代码或输出的可读性做出尴尬的让步。例如,为了使嵌入的文本与周围代码看起来一致,我们会编写:

    class MyClass {
      print() {
        console.log(`
          create table student(
            id int primary key,
            name text
          )
        `);
      }
    }

    这会输出(使用 ^ 标记行首,· 标记前导空格):

    ^
    ^······create table student(
    ^········id int primary key,
    ^········name text
    ^······)
    ^····

    为了使输出看起来合理,我们的代码变得难以阅读:

    class MyClass {
      print() {
        console.log(`create table student(
      id int primary key,
      name text
    )`);
      }
    }

    这输出了一个合理的输出:

    create table student(
      id int primary key,
      name text
    )

    使用库

    借助可以写出合理的代码并获得合理的输出。

    import dedent from 'dedent'
    
    class MyClass {
      print() {
        console.log(dedent`
          create table student(
            id int primary key,
            name text
          )
        `);
      }
    }

    这输出了合理的:

    create table student(
      id int primary key,
      name text
    )

    然而,这些库会产生运行时成本,并且在执行“去缩进”的方式上存在微妙的不一致。最流行的包停滞不前,没有错误修复,并且对模板对象的 .raw 数组的解析存在问题,而且没有任何库能够将去缩进的文本传递给标签模板函数。

    pythonInterpreter`
      print('Hello Python World')
    `; // IndentationError: unexpected indent
    
    const dedented = dedent`
      print('Hello Python World')
    `;
    
    pythonInterpreter`${dedented}`; // <- 这不能正常工作。

    此外,即使一个用户态库支持传递给标签模板,在像 Array.isTemplateObject 这样的提案中,该数组也不是真正的模板对象。这会损害标签模板函数区分实际程序源代码中存在的去缩进模板(并赋予更高的信任级别)与动态生成的字符串(可能包含用户生成的利用字符串)的能力。

    提议的解决方案

    实现一个 String.dedent 标签模板函数,用于标签模板字面量,其行为几乎与常规的单反引号模板字面量相同,但有几个关键区别:

    • 开头行(紧挨着开头 `` 右侧的所有内容)必须只包含一个字面换行符。
    • 开头行的字面换行符被移除。
    • 结尾行(紧挨着结尾 `` 左侧的所有内容)可以包含空白字符,但空白字符会被移除。
    • 结尾行之前的字面换行符被移除。
    • 仅包含空白字符的行被清空。
    • 计算所有非空内容行(不是开头或结尾行的行)的“公共缩进”。
    • 从每一行的开头移除该公共缩进。

    可以尝试使用 REPL 实现来体验。

    上述示例可以这样解决:

    class MyClass {
      print() {
        console.log(String.dedent`
          create table student(
            id int primary key,
            name text
          )
        `);
      }
    }

    这输出了合理的:

    create table student(
      id int primary key,
      name text
    )

    表达式可以直接支持,也可以与另一个标签模板函数组合:

    const message = 'Hello Python World';
    String.dedent(pythonInterpreter)`
      print('${message}')
    `;

    在其他语言中

    问答

    为什么不使用库?

    总结上面的问题部分:

    • 避免对绝大多数多行字符串所需的行为产生依赖(dedent 每周有数百万次下载)。
    • 避免多个当前实现之间的不一致。
    • 提高性能。
    • 更好的可发现性 - 该功能可以公开记录,并在代码示例中使用,而无需依赖像 dedent 这样的包。
    • 为代码生成器提供一种输出具有正确缩进属性的可读代码的方式(例如,jest 内联快照)。
    • 支持具有定制表达式参数行为的“去缩进”标签模板字面量函数(例如 slonik)。
    • 允许格式化器/检查器安全地强制代码风格,而不需要与多个库的运行时行为相结合。

    附加链接