Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

lint is up to 40x slower than 0.8.106 on a long linked stylesheet: the canvas-zoom rule strips comments once per matched rule, and lint now reads stylesheets linked from sub-compositions

已关闭 适合新手
#5,681 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

已经有一个关联 PR 被合并了。

  • #5712 来自 @rudycelekli —— 已合并

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
75/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
css, typescript

调研方向

慢路径是 packages/lint/src/rules/composition.ts 中的 stylesheetCanvasZoomHits,其中的 while 循环在每次匹配时都调用 stripCssComments(style.content)(约第 364 行)。先阅读 collectHeavyOverlayHooks(第 203-206 行),它已经对注释只剥离一次,然后将同样的模式应用到循环中。完成标准是:复现用例的 lint 耗时大致随规则数量线性增长,且 lint 的输出保持不变。

由索引模型根据 Issue 内容生成。

描述

bug triage/needs-triage
Describe the bug

lint takes 10 to 60 s a project in 0.8.158 where 0.8.106 took 0.5 to 1.6 s on the same projects and machine (one project measured alternately: 11.5 and 12.5 s on 0.8.158 against 2.2 and 1.7 s on 0.8.106). On a quiet machine, two projects measured alternately on 0.8.152 took 3.1 and 3.2 s against 0.8.106's 0.5 and 0.5 s, and 7.1 and 7.4 s against 0.6 and 0.5 s. check runs the lint too, so every check pays it, and a check split into parts pays it in every part.

A CPU profile of one 14.2 s run: 6.5 s in stripCssComments and 4.6 s in stylesheetCanvasZoomHits (chunk-7F3B5YYT.js of the package's dist, around line 6470). The canvas-zoom rule's loop is

while ((match = ruleWithBody.exec(stripCssComments(style.content))) !== null)

so it strips the whole stylesheet's comments again for every rule it matches, and the time grows with the square of the stylesheet's length. Our stylesheet is about 2,750 lines with some 800 rules, which is where it shows.

The loop itself is older: 0.8.106 is as slow on the same 3,000 rules when they sit in an inline <style> (4.4 to 5.2 s on 0.8.106, 4.2 to 4.8 s on 0.8.158). What changed is which stylesheets lint reads. A <link rel="stylesheet"> inside a sub-composition's <template> is now also resolved from the project root (packages/lint/src/project.ts, lines 108-113 at v0.8.162), where 0.8.106 looked only beside the sub-composition's file and skipped it. That matches how the runtime mounts the template, but it sends every such stylesheet through the quadratic loop, once for each composition that links it (line 246). Our sub-compositions each link the one shared stylesheet.

Measured on 0.8.158 (0.8.152 behaves the same). The code is unchanged in v0.8.162: packages/lint/src/rules/composition.ts, line 364 still calls stripCssComments(style.content) inside the while.

Minimal reproduction

A blank project with one sub-composition that links a long stylesheet (3,000 simple rules with a comment each):

npx hyperframes init repro --non-interactive --example blank
cd repro
mkdir compositions
# write index.html and compositions/scene.html as below, then:
node -e "require('fs').writeFileSync('long.css', Array.from({ length: 3000 }, (_, i) => '/* rule ' + i + ' */\n.r' + i + ' { color: #f4f4f5; }\n').join(''))"

index.html (the blank template plus the #scene clip, with the example comment removed from the script):

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=1920, height=1080" />
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      html,
      body {
        margin: 0;
        width: 1920px;
        height: 1080px;
        overflow: hidden;
        background: #0a0a0a;
      }
      #root {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-family: Inter, ui-sans-serif, system-ui, sans-serif;
      }
      #title {
        color: #f4f4f5;
        font-size: 72px;
        font-weight: 600;
        letter-spacing: -0.03em;
      }
    </style>
  </head>
  <body>
    <div
      id="root"
      data-composition-id="main"
      data-start="0"
      data-duration="10"
      data-width="1920"
      data-height="1080"
    >
      <h1 id="title" class="clip" data-start="0" data-duration="10" data-track-index="0">Title</h1>
      <div
        id="scene"
        data-composition-id="scene"
        data-composition-src="compositions/scene.html"
        data-start="0"
        data-duration="10"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>
    </div>
    <script>
      const tl = gsap.timeline({ paused: true });
      window.__timelines["main"] = tl;
      tl.seek(0);
    </script>
  </body>
</html>

compositions/scene.html:

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <template>
      <link rel="stylesheet" href="long.css" />
      <div id="root" data-composition-id="scene" data-width="1920" data-height="1080"></div>
      <script>
        window.__timelines["scene"] = gsap.timeline({ paused: true });
      </script>
    </template>
  </body>
</html>
Steps to reproduce
  1. Set up the reproduction above.
  2. In repro, time npx [email protected] lint and npx [email protected] lint, alternately, a few times each (both report 0 errors, 0 warnings).
  3. Change 3000 in the node -e line to 1000 or 6000, regenerate long.css and time again: on 0.8.158 the time grows with the square of the rule count.
Expected behavior

lint about as fast as 0.8.106 on the same project (0.4 to 0.5 s here), its time growing linearly with the stylesheet's length.

Actual behavior

Wall-clock times of lint on the reproduction, versions run alternately (the installed CLI started with node, so no npx resolution is included):

rules in long.css 0.8.106 0.8.152 0.8.158
1,000 0.47, 0.48 s 0.95, 0.83 s
3,000 0.44, 0.42, 0.44 s 5.3, 5.1, 4.8 s 4.4, 5.4, 6.7 s
6,000 0.45, 0.48 s 21.4, 17.3 s

A CPU profile of 0.8.158 on the 3,000-rule reproduction: of 3.6 s, 1.4 s in stripCssComments, 1.3 s in stylesheetCanvasZoomHits and 0.6 s in garbage collection.

Environment
OS:          Windows 11 (build 26200)
Node.js:     v24.14.0
HyperFrames: 0.8.158 and 0.8.152 against 0.8.106 (code unchanged at v0.8.162)
Chrome:      Chrome Headless Shell 152.0.7977.30
Additional context

Request: strip the comments once, before the loop (and run the regular expression over that one string), as collectHeavyOverlayHooks in the same file already does (composition.ts, lines 203-206 at v0.8.162).

主要语言
TypeScript
星标
54.1k
派生
4.9k
平均合并
15 小时 19 分钟
30 天内合并 PR
769

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

heygen-com/hyperframes 的其他 Issue

查看 heygen-com/hyperframes 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。