Svelte - Carbon 组件导入中断调试

Svelte - Carbon component import breaks debugging

我设置了一个使用 Carbon Components 的 Svelte 和 Electron。我大部分时间都在使用它,但我一直在调试。

这是我用于测试的示例组件:

<script lang="ts">
    import { Tile } from 'carbon-components-svelte'
    let name = 'John Doe'

    function test() {
        console.log('You clicked me!')
    }
</script>

<div>
    <Tile>
        <h2>{name}</h2>
        <button on:click={test}>Click me</button>
    </Tile>
</div>

当我将组件加载到开发环境时,我无法在Chrome的开发工具中设置断点:

但是,如果我打开 svelte.config.js,最初看起来是:

import sveltePreprocess from 'svelte-preprocess'
import { optimizeImports } from "carbon-preprocess-svelte";

export default {
  preprocess: [ sveltePreprocess(), optimizeImports() ]
}

并删除 optimizeImports() 调用,然后断点可以正常工作。

有人知道为什么会这样吗?显然,这与 carbon-preprocess-svelte 库有关。我创建了一个重现该问题的基本项目:https://github.com/troncoso/carbon-svelte-bug

TL;DR: 内联源映射有点损坏,这是 carbon-preprocess-svelte.

中的错误

当您使用:import { Tile } from 'carbon-components-svelte' 时,默认模块解析将从位于 carbon-components-svelte/lib/index.js 的大伞文件导入命名导出 Tile,这是一个大的缩小代码包。显然这不是最优的。

optimizeImports 来自 carbon-preprocess-svelte 是一个预处理器,它试图通过读取您的导入代码然后将其重写为 import Tile from "carbon-components-svelte/src/Tile/Tile.svelte".

来优化它

显然,此预处理器中存在某种错误,会产生损坏的源映射。准确地说,它生成的源映射 遗漏了一些映射位置 。 Chrome devtool 依靠这些映射位置来设置断点。缺少时,无法设置。

当你手动重写导入时,你跳过了这个预处理器,从而避免了错误的行为。这与您目前的发现一致。

我们可能会在这里搁置我们的案例并得出结论,您应该暂时避免使用 optimizeImports,并将错误报告给碳团队。下面将深入探讨更多细节。


这是坏情况的内联源映射:

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FDQSxJQUFBLE1BQUEsK0NBQUE7Ozs7Ozs7Ozs7Ozs7O2FBUThCLFVBQ2xCO3VCQUFDLEdBQUs7Ozs7O0dBRGhCLFVBRVE7Ozs7O3dEQUZVLEdBQVM7Ozs7O3VEQUNoQixHQUFLOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztLQVJsQixLQUFBLEdBQUEsQ0FBQTs7T0FDQSxTQUFBO2tCQUNBLEtBQUEsSUFBQSxDQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7IiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIkNvdW50ZXIuc3ZlbHRlIl0sInNvdXJjZXNDb250ZW50IjpbIjxzY3JpcHQgbGFuZz1cInRzXCI+XG4gIGltcG9ydCBUaWxlIGZyb20gJ2NhcmJvbi1jb21wb25lbnRzLXN2ZWx0ZS9zcmMvVGlsZS9UaWxlLnN2ZWx0ZSdcbiAgbGV0IGNvdW50OiBudW1iZXIgPSAwXG4gIGNvbnN0IGluY3JlbWVudCA9ICgpID0+IHtcbiAgICBjb3VudCArPSAxXG4gIH1cbjwvc2NyaXB0PlxuXG48VGlsZT5cbiAgPGJ1dHRvbiBvbjpjbGljaz17aW5jcmVtZW50fT5cbiAgICBDbGlja3M6IHtjb3VudH1cbiAgPC9idXR0b24+XG48L1RpbGU+XG5cbjxzdHlsZT5cbiAgYnV0dG9uIHtcbiAgICBmb250LWZhbWlseTogaW5oZXJpdDtcbiAgICBmb250LXNpemU6IGluaGVyaXQ7XG4gICAgcGFkZGluZzogMWVtIDJlbTtcbiAgICBjb2xvcjogI2ZmM2UwMDtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgNjIsIDAsIDAuMSk7XG4gICAgYm9yZGVyLXJhZGl1czogMmVtO1xuICAgIGJvcmRlcjogMnB4IHNvbGlkIHJnYmEoMjU1LCA2MiwgMCwgMCk7XG4gICAgb3V0bGluZTogbm9uZTtcbiAgICB3aWR0aDogMjAwcHg7XG4gICAgZm9udC12YXJpYW50LW51bWVyaWM6IHRhYnVsYXItbnVtcztcbiAgICBjdXJzb3I6IHBvaW50ZXI7XG4gIH1cblxuICBidXR0b246Zm9jdXMge1xuICAgIGJvcmRlcjogMnB4IHNvbGlkICNmZjNlMDA7XG4gIH1cblxuICBidXR0b246YWN0aXZlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgNjIsIDAsIDAuMik7XG4gIH1cbjwvc3R5bGU+XG4iXX0=

这是好的案例之一

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FDQSxJQUFBLE1BQUEsK0NBQUE7Ozs7Ozs7Ozs7Ozs7O2FBUThCLFVBQ2xCO3VCQUFDLEdBQUs7Ozs7O0dBRGhCLFVBRVE7Ozs7O3dEQUZVLEdBQVM7Ozs7O3VEQUNoQixHQUFLOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztLQVJsQixLQUFBLEdBQUEsQ0FBQTs7T0FDQSxTQUFBO2tCQUNBLEtBQUEsSUFBQSxDQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7IiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIkNvdW50ZXIuc3ZlbHRlIl0sInNvdXJjZXNDb250ZW50IjpbIjxzY3JpcHQgbGFuZz1cInRzXCI+XG4gIGltcG9ydCBUaWxlIGZyb20gJ2NhcmJvbi1jb21wb25lbnRzLXN2ZWx0ZS9zcmMvVGlsZS9UaWxlLnN2ZWx0ZSdcbiAgbGV0IGNvdW50OiBudW1iZXIgPSAwXG4gIGNvbnN0IGluY3JlbWVudCA9ICgpID0+IHtcbiAgICBjb3VudCArPSAxXG4gIH1cbjwvc2NyaXB0PlxuXG48VGlsZT5cbiAgPGJ1dHRvbiBvbjpjbGljaz17aW5jcmVtZW50fT5cbiAgICBDbGlja3M6IHtjb3VudH1cbiAgPC9idXR0b24+XG48L1RpbGU+XG5cbjxzdHlsZT5cbiAgYnV0dG9uIHtcbiAgICBmb250LWZhbWlseTogaW5oZXJpdDtcbiAgICBmb250LXNpemU6IGluaGVyaXQ7XG4gICAgcGFkZGluZzogMWVtIDJlbTtcbiAgICBjb2xvcjogI2ZmM2UwMDtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgNjIsIDAsIDAuMSk7XG4gICAgYm9yZGVyLXJhZGl1czogMmVtO1xuICAgIGJvcmRlcjogMnB4IHNvbGlkIHJnYmEoMjU1LCA2MiwgMCwgMCk7XG4gICAgb3V0bGluZTogbm9uZTtcbiAgICB3aWR0aDogMjAwcHg7XG4gICAgZm9udC12YXJpYW50LW51bWVyaWM6IHRhYnVsYXItbnVtcztcbiAgICBjdXJzb3I6IHBvaW50ZXI7XG4gIH1cblxuICBidXR0b246Zm9jdXMge1xuICAgIGJvcmRlcjogMnB4IHNvbGlkICNmZjNlMDA7XG4gIH1cblxuICBidXR0b246YWN0aXZlIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgNjIsIDAsIDAuMik7XG4gIH1cbjwvc3R5bGU+XG4iXX0=

它们是 base64 编码的 json,如果我们解码它们,我们得到:

坏情况:

{
  "version": 3,
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;OAAiB,IAAA,MAAA,+CAAA;;;;;;;;;;;;;;aASa,UAClB;uBAAC,GAAK;;;;;GADhB,UAEQ;;;;;wDAFU,GAAS;;;;;uDAChB,GAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
  "names": [],
  "sources": ["Counter.svelte"],
  "sourcesContent": [
    "<script lang=\"ts\">\n  import { Tile } from 'carbon-components-svelte'\n  let count: number = 0\n  const increment = () => {\n    count += 1\n  }\n</script>\n\n<Tile>\n  <button on:click={increment}>\n    Clicks: {count}\n  </button>\n</Tile>\n\n<style>\n  button {\n    font-family: inherit;\n    font-size: inherit;\n    padding: 1em 2em;\n    color: #ff3e00;\n    background-color: rgba(255, 62, 0, 0.1);\n    border-radius: 2em;\n    border: 2px solid rgba(255, 62, 0, 0);\n    outline: none;\n    width: 200px;\n    font-variant-numeric: tabular-nums;\n    cursor: pointer;\n  }\n\n  button:focus {\n    border: 2px solid #ff3e00;\n  }\n\n  button:active {\n    background-color: rgba(255, 62, 0, 0.2);\n  }\n</style>\n"
  ]
}

好案例:

{
  "version": 3,
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;OACA,IAAA,MAAA,+CAAA;;;;;;;;;;;;;;aAQ8B,UAClB;uBAAC,GAAK;;;;;GADhB,UAEQ;;;;;wDAFU,GAAS;;;;;uDAChB,GAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KARlB,KAAA,GAAA,CAAA;;OACA,SAAA;kBACA,KAAA,IAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
  "names": [],
  "sources": ["Counter.svelte"],
  "sourcesContent": [
    "<script lang=\"ts\">\n  import Tile from 'carbon-components-svelte/src/Tile/Tile.svelte'\n  let count: number = 0\n  const increment = () => {\n    count += 1\n  }\n</script>\n\n<Tile>\n  <button on:click={increment}>\n    Clicks: {count}\n  </button>\n</Tile>\n\n<style>\n  button {\n    font-family: inherit;\n    font-size: inherit;\n    padding: 1em 2em;\n    color: #ff3e00;\n    background-color: rgba(255, 62, 0, 0.1);\n    border-radius: 2em;\n    border: 2px solid rgba(255, 62, 0, 0);\n    outline: none;\n    width: 200px;\n    font-variant-numeric: tabular-nums;\n    cursor: pointer;\n  }\n\n  button:focus {\n    border: 2px solid #ff3e00;\n  }\n\n  button:active {\n    background-color: rgba(255, 62, 0, 0.2);\n  }\n</style>\n"
  ]
}

问题出在 mappings 字段。您可以使用 source map decoder 了解这两个文件,然后单击 dump 按钮进行比较。您会看到错误案例文件中缺少一些映射信息。

本文Anatomy of source maps,如果您感兴趣,将带您了解js源图格式的技术细节(理解神秘的;;OAAiB,IAAA,MAAA,+CAAA;;部分)。

但现在让我们简单地阅读人类友好的转储:

Counter.svelte,137,27,5,0,null

translates to:

position 137:27 from compiled file `Counter.js`
maps to 
position 5:0 from original source file `Counter.svelte`

这是两个文件的转储。

坏情况映射转储:

Counter.svelte,24,7,1,17,null
Counter.svelte,24,11,1,17,null
Counter.svelte,24,17,1,17,null
Counter.svelte,24,64,1,17,null
Counter.svelte,38,13,10,30,null
Counter.svelte,38,23,11,12,null
Counter.svelte,39,23,11,13,null
Counter.svelte,39,26,11,18,null
Counter.svelte,44,3,10,2,null
Counter.svelte,44,13,12,10,null
Counter.svelte,49,56,10,20,null
Counter.svelte,49,59,10,29,null
Counter.svelte,54,55,11,13,null
Counter.svelte,54,58,11,18,null

好案例映射转储:

Counter.svelte,24,7,2,0,null
Counter.svelte,24,11,2,0,null
Counter.svelte,24,17,2,0,null
Counter.svelte,24,64,2,0,null
Counter.svelte,38,13,10,30,null
Counter.svelte,38,23,11,12,null
Counter.svelte,39,23,11,13,null
Counter.svelte,39,26,11,18,null
Counter.svelte,44,3,10,2,null
Counter.svelte,44,13,12,10,null
Counter.svelte,49,56,10,20,null
Counter.svelte,49,59,10,29,null
Counter.svelte,54,55,11,13,null
Counter.svelte,54,58,11,18,null
Counter.svelte,134,5,3,0,null
Counter.svelte,134,10,3,0,null
Counter.svelte,134,13,3,0,null
Counter.svelte,134,14,3,0,null
Counter.svelte,136,7,4,0,null
Counter.svelte,136,16,4,0,null
Counter.svelte,137,18,5,0,null
Counter.svelte,137,23,5,0,null
Counter.svelte,137,27,5,0,null
Counter.svelte,137,28,5,0,null

阅读@hackape 的回答了解详情。但最终它似乎是 carbon-preprocess-svelte 的错误,特别是 optimizeImports() 插件。我在该项目上创建了一个问题:https://github.com/carbon-design-system/carbon-preprocess-svelte/issues/18

与此同时,我想出了一个解决方案,确保源映射正常工作,并且我仍然遇到 tree shaking。我创建了一个 carbon.ts 文件:

import { Button } from 'carbon-components-svelte/src/Button'
import { Tile, ClickableTile } from 'carbon-components-svelte/src/Tile'
import { StructuredList, StructuredListHead, StructuredListBody, StructuredListRow,
    StructuredListCell } from 'carbon-components-svelte/src/StructuredList'
import { Header, HeaderGlobalAction, HeaderUtilities, Content }
    from 'carbon-components-svelte/src/UIShell'
import { Theme } from 'carbon-components-svelte/src/Theme'
import { TooltipDefinition } from 'carbon-components-svelte/src/TooltipDefinition'

export  {
    Button,
    Tile,
    ClickableTile,
    StructuredList,
    StructuredListBody,
    StructuredListCell,
    StructuredListHead,
    StructuredListRow,
    Header,
    HeaderGlobalAction,
    HeaderUtilities,
    Theme,
    TooltipDefinition,
    Content
}

当我需要一个组件时,我只是将它添加到这个文件中。然后我像这样导入组件:

<script lang="ts">
    import { Button } from '@/components/carbon'
</script>

我知道我可以自己在 svelte 文件中完成导入,但是这样当错误被修复时我可以更容易地找到和替换所有导入。只需搜索“@/components/carbon”并将其替换为“carbon-components-svelte”。