[Feature Request]: Shorter scoped class names and class-name maps
メンテナーはふだん 2 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 35/100
- issue の種類
- 機能追加
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- react, scss, typescript, webpack
調査の方向性
Start with src/build/tasks/postcss/generate-scoped-name.ts and the writeCssModule implementation; trace how buildStyles generates internal helpers and maps. Review the test-utils selector and snapshot coverage, then measure production bundles and verify class-map lookup behavior. Done means the proposed compact names and generated maps preserve the documented selectors, keys, values, ordering, and compatibility while demonstrating the reported bundle savings.
索引モデルが issue の本文から書いたものです。
説明
Description
Scoped CSS class names and the JS maps that reference them are about 10% of the minified JS and a quarter of the raw CSS in a typical multi-component app. Two changes to the CSS-module output of theming-build would cut about 8 KB of brotli JS and 4 KB of brotli CSS from a 39-component console page, with no public API change. The code lives in cloudscape-design/theming-core, which has no issue tracker, so I'm filing here.
Today, src/build/tasks/postcss/generate-scoped-name.ts produces awsui_{name}_{fileHash}_{contentHash}_{line}, and writeCssModule emits each styles.css.js as an object literal that repeats every key inside its value:
export default {
"variant-normal": "awsui_variant-normal_vjswe_1xyge_131",
"disabled": "awsui_disabled_vjswe_1xyge_152",
// ...
};
Proposal:
-
Replace the line number with a constant:
awsui_{name}_{fileHash}_{contentHash}_0. The line adds no uniqueness, since names are unique within a file and both hashes are per file. It isn't a reliable line either: it comes fromcss.indexOf('.' + name), so in buttonicongets the line oficon-left. Because it differs for every class, it is the part of each name brotli can't compress. Keeping a numeric fifth segment means test-utils-core'strimContentHash, which drops the last two segments, still yields the same[class*="awsui_{name}_{fileHash}"]selectors, and snapshot serializers that match_\d+keep working. -
With the line gone, every value is
'awsui_' + key + suffix, so emit the maps as:
import './styles.scoped.css';
import createClassMap from '../internal/generated/css-module-map.js';
export default /*#__PURE__*/ createClassMap('_vjswe_1xyge_0', 'content button variant-normal disabled ...');
The helper is a 3-line loop that buildStyles writes into internal/generated/. The result is an ordinary object with the same keys, values and order, so static, computed and missing-key lookups behave as before. styles.selectors.js (test-utils) stays a literal.
Measured with webpack 5 in production mode against main (209caeca3). The console app uses 39 components including AppLayout, Table and PropertyFilter; the form app uses 10 basic components. Bytes saved by both changes together:
| Raw | Brotli | |
|---|---|---|
| Console JS, terser | 57 KB | 7.8 KB (3.5%) |
| Console JS, esbuild minify | 78 KB | 9.4 KB (3.6%) |
| Console CSS | 11.7 KB | 3.8 KB (5.5%) |
| Form JS, terser | 25.5 KB | 3.2 KB (4.2%) |
| Form CSS | 3.5 KB | 1.5 KB (3.9%) |
The savings hold on webpack 4, webpack 5 with swc minify, Vite 8, the esbuild bundler and rspack; the class-map change alone saves 6.6 to 9.8 KB brotli on the console page depending on toolchain.
Compatibility, checked with a prototype that rewrote a built lib:
- Public API: none. Every class name changes once, which already happens whenever a file's CSS changes. Unlike #228, the
awsui_prefix and theawsui_{name}_{fileHash}portion that test-utils and consumer integration tests rely on stay the same. - test-utils: selectors-flavor queries passed with new DOM and old test-utils and with old DOM and new test-utils. The test-utils-selectors snapshot is unchanged. The legacy literal in table
findExpandToggletargets older versions and still works. - Multiple versions on one page: isolation is unchanged because the content hash stays, and each copy carries its own helper.
- Themed builds: buildThemedComponents regenerates maps with the consumer's theming-build, so the helper must be written by buildStyles/writeCssModule itself. That also covers board, chat and code-view, which pick up the new format on their next build.
- Runtime: building the maps at module init replaces parsing the literals, and startup CPU was a wash in Node (about 2 ms either way for all 238 maps). Maps built by keyed stores are dictionary-mode in V8; lookups stay in the nanosecond range, but a benchmark before landing is reasonable.
- Server-side rendering output was byte-identical for the components checked.
Follow-up: trimContentHash finds the stable prefix by counting underscore-separated parts from the end, which is why the _0 placeholder is needed. Parsing from the start would make test-utils independent of everything after the file hash: keep awsui_, the name, and the file hash, matching /^awsui_((?:[^_]|__)+)_([a-z0-9]+)/. This works for today's names, the _0 names, and names with no fifth part. It relies on class keys never containing a single underscore. That holds for all 2,882 keys today, including the 24 BEM-style keys such as error__message, and theming-build could enforce it at build time. A rule keyed on whether the last part is numeric would not work, because 2 of the current 224 content hashes are all digits. Once that version of test-utils-core is out, _0 could be dropped in a release that also raises the components dependency floor on test-utils-core. The reverse direction is already safe: selectors built from older names still match new DOM. That last step saves only raw bytes (about 9 KB CSS and 2 KB JS on the console page, under 0.1 KB brotli), and it would break consumer snapshot serializers that expect a numeric last part, so it is optional.
Not proposed here: short ids instead of readable names would save about 9 KB more brotli on the console page, but they change the stable prefix for classes that consumers target, so they need a separate discussion. Shorter random hashes save raw bytes but not brotli.
Code of Conduct
- I agree to follow this project's Code of Conduct
- I checked the current issues for duplicate requests
- 主要言語
- TypeScript
- スター
- 2.7k
- フォーク
- 248
- 平均マージ
- 3日 14時間
- マージ済み PR(30日)
- 83
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
cloudscape-design/components のほかの issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
cloudscape-design/components#5184 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
cloudscape-design/components#5152 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
enhancement
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
cloudscape-design/components#5149 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
cloudscape-design/components#5117 · コメント 2 件 ·
メンテナーはふだん 2 日以内に返信
-
[Bug/Feature Request]: `usePerformanceMarks` has ID generation incompatible with Next.js Partial Prerendering対応中かも @amanabiy が 22 日前に担当しました。 オープンenhancement
cloudscape-design/components#5023 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 2 日以内に返信
cloudscape-design/components の issue をすべて見る
似ている issue
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P3
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
openclaw/openclaw#182077 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
aikido high security
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
dequelabs/axe-linter-action#161 ·
-
[Feature Request] Update the DimCode agent icon to the 45:32 brand mark対応中かも @eric8810 が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
lyswhut/lx-music-desktop#3026 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
abhigyanpatwari/GitNexus#3704 ·
メンテナーはふだん 1 日以内に返信