@coveo/atomic 3.63.0 → 3.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +194 -8
- package/dist/cjs/components/commerce/atomic-commerce-breadbox/atomic-commerce-breadbox.cjs +1 -0
- package/dist/cjs/components/commerce/atomic-commerce-category-facet/atomic-commerce-category-facet.cjs +1 -1
- package/dist/cjs/components/commerce/atomic-commerce-facet/atomic-commerce-facet.cjs +1 -1
- package/dist/cjs/components/commerce/atomic-product-field-condition/atomic-product-field-condition.cjs +1 -4
- package/dist/cjs/components/commerce/atomic-product-template/atomic-product-template.cjs +15 -1
- package/dist/cjs/components/common/atomic-focus-trap/atomic-focus-trap.cjs +11 -1
- package/dist/cjs/components/common/atomic-layout-section/layout.cjs +6 -2
- package/dist/cjs/components/common/facets/facet-common.cjs +21 -2
- package/dist/cjs/components/common/facets/facet-search/facet-search-aria-live.cjs +21 -8
- package/dist/cjs/components/common/query-error/icon.cjs +7 -26
- package/dist/cjs/components/common/query-error/icons.cjs +68 -0
- package/dist/cjs/components/common/template-controller/template-utils.cjs +9 -0
- package/dist/cjs/components/insight/atomic-insight-facet/atomic-insight-facet.cjs +1 -1
- package/dist/cjs/components/insight/atomic-insight-result-children-template/atomic-insight-result-children-template.cjs +1 -5
- package/dist/cjs/components/insight/atomic-insight-result-template/atomic-insight-result-template.cjs +15 -1
- package/dist/cjs/components/insight/atomic-insight-smart-snippet/atomic-insight-smart-snippet.cjs +3 -3
- package/dist/cjs/components/recommendations/atomic-recs-result-template/atomic-recs-result-template.cjs +15 -1
- package/dist/cjs/components/search/atomic-category-facet/atomic-category-facet.cjs +1 -1
- package/dist/cjs/components/search/atomic-color-facet/atomic-color-facet.cjs +1 -1
- package/dist/cjs/components/search/atomic-facet/atomic-facet.cjs +1 -1
- package/dist/cjs/components/search/atomic-facet-manager/atomic-facet-manager.cjs +7 -3
- package/dist/cjs/components/search/atomic-field-condition/atomic-field-condition.cjs +1 -4
- package/dist/cjs/components/search/atomic-popover/atomic-popover.cjs +1 -2
- package/dist/cjs/components/search/atomic-result-children-template/atomic-result-children-template.cjs +15 -1
- package/dist/cjs/components/search/atomic-result-template/atomic-result-template.cjs +15 -1
- package/dist/cjs/components/search/atomic-search-layout/atomic-search-layout.tw.css.cjs +12 -2
- package/dist/cjs/decorators/bind-state.cjs +38 -8
- package/dist/cjs/decorators/with-tailwind-styles.cjs +2 -1
- package/dist/cjs/global/environment.cjs +1 -1
- package/dist/cjs/utils/init-queue.cjs +6 -4
- package/dist/cjs/utils/replace-breakpoint-utils.cjs +9 -4
- package/dist/cjs/versions.cjs +1 -1
- package/dist/esm/components/commerce/atomic-commerce-breadbox/atomic-commerce-breadbox.js +1 -0
- package/dist/esm/components/commerce/atomic-commerce-category-facet/atomic-commerce-category-facet.js +1 -1
- package/dist/esm/components/commerce/atomic-commerce-facet/atomic-commerce-facet.js +1 -1
- package/dist/esm/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.js +1 -1
- package/dist/esm/components/commerce/atomic-product-field-condition/atomic-product-field-condition.js +2 -5
- package/dist/esm/components/commerce/atomic-product-template/atomic-product-template.js +16 -2
- package/dist/esm/components/common/atomic-focus-trap/atomic-focus-trap.js +11 -1
- package/dist/esm/components/common/atomic-layout-section/layout.js +6 -2
- package/dist/esm/components/common/facets/facet-common.js +16 -3
- package/dist/esm/components/common/facets/facet-search/facet-search-aria-live.js +21 -8
- package/dist/esm/components/common/query-error/icon.js +5 -23
- package/dist/esm/components/common/query-error/icons.js +17 -0
- package/dist/esm/components/common/template-controller/template-utils.js +7 -1
- package/dist/esm/components/insight/atomic-insight-facet/atomic-insight-facet.js +1 -1
- package/dist/esm/components/insight/atomic-insight-result-children/atomic-insight-result-children.js +1 -1
- package/dist/esm/components/insight/atomic-insight-result-children-template/atomic-insight-result-children-template.js +2 -6
- package/dist/esm/components/insight/atomic-insight-result-template/atomic-insight-result-template.js +16 -2
- package/dist/esm/components/insight/atomic-insight-smart-snippet/atomic-insight-smart-snippet.js +3 -3
- package/dist/esm/components/recommendations/atomic-recs-result-template/atomic-recs-result-template.js +16 -2
- package/dist/esm/components/search/atomic-category-facet/atomic-category-facet.js +1 -1
- package/dist/esm/components/search/atomic-color-facet/atomic-color-facet.js +1 -1
- package/dist/esm/components/search/atomic-facet/atomic-facet.js +1 -1
- package/dist/esm/components/search/atomic-facet-manager/atomic-facet-manager.js +8 -4
- package/dist/esm/components/search/atomic-field-condition/atomic-field-condition.js +2 -5
- package/dist/esm/components/search/atomic-popover/atomic-popover.js +1 -2
- package/dist/esm/components/search/atomic-result-children-template/atomic-result-children-template.js +16 -2
- package/dist/esm/components/search/atomic-result-template/atomic-result-template.js +16 -2
- package/dist/esm/components/search/atomic-search-box/atomic-search-box.js +1 -1
- package/dist/esm/components/search/atomic-search-layout/atomic-search-layout.tw.css.js +1 -1
- package/dist/esm/decorators/bind-state.js +38 -8
- package/dist/esm/decorators/with-tailwind-styles.js +2 -1
- package/dist/esm/global/environment.js +1 -1
- package/dist/esm/utils/init-queue.js +6 -4
- package/dist/esm/utils/replace-breakpoint-utils.js +9 -4
- package/dist/esm/versions.js +1 -1
- package/dist/types/components/commerce/atomic-product-template/atomic-product-template.d.ts +12 -0
- package/dist/types/components/common/atomic-focus-trap/atomic-focus-trap.d.ts +1 -0
- package/dist/types/components/common/atomic-smart-snippet-source/atomic-smart-snippet-source.d.ts +4 -4
- package/dist/types/components/common/facets/facet-common.d.ts +2 -0
- package/dist/types/components/common/facets/facet-search/facet-search-aria-live.d.ts +7 -1
- package/dist/types/components/common/query-error/icon.d.ts +6 -0
- package/dist/types/components/common/query-error/icons.d.ts +5 -0
- package/dist/types/components/common/template-controller/template-utils.d.ts +7 -0
- package/dist/types/components/insight/atomic-insight-result-children-template/atomic-insight-result-children-template.d.ts +4 -4
- package/dist/types/components/insight/atomic-insight-result-template/atomic-insight-result-template.d.ts +12 -0
- package/dist/types/components/recommendations/atomic-recs-result-template/atomic-recs-result-template.d.ts +12 -0
- package/dist/types/components/search/atomic-facet-manager/atomic-facet-manager.d.ts +6 -1
- package/dist/types/components/search/atomic-result-children-template/atomic-result-children-template.d.ts +12 -0
- package/dist/types/components/search/atomic-result-template/atomic-result-template.d.ts +12 -0
- package/dist/types/utils/init-queue.d.ts +3 -2
- package/docs/adr/0001-group-shared-modules-in-one-cdn-chunk.md +147 -0
- package/package.json +17 -14
|
@@ -17,16 +17,16 @@ export declare class AtomicInsightResultChildrenTemplate extends LitElement impl
|
|
|
17
17
|
*/
|
|
18
18
|
conditions: InsightResultTemplateCondition[];
|
|
19
19
|
/**
|
|
20
|
-
* The
|
|
20
|
+
* The comma-separated list of fields that must all be defined on a child result for the template to apply.
|
|
21
21
|
*
|
|
22
|
-
* For example, a template with the following attribute only applies to child results whose `filetype` and `sourcetype` fields are defined:
|
|
22
|
+
* For example, a template with the following attribute only applies to child results whose `filetype` and `sourcetype` fields are both defined:
|
|
23
23
|
* `if-defined="filetype,sourcetype"`
|
|
24
24
|
*/
|
|
25
25
|
ifDefined?: string;
|
|
26
26
|
/**
|
|
27
|
-
* The
|
|
27
|
+
* The comma-separated list of fields that must all be undefined on a child result for the template to apply.
|
|
28
28
|
*
|
|
29
|
-
* For example, a template with the following attribute only applies to child results
|
|
29
|
+
* For example, a template with the following attribute only applies to child results that have neither a `filetype` nor a `sourcetype` field:
|
|
30
30
|
* `if-not-defined="filetype,sourcetype"`
|
|
31
31
|
*/
|
|
32
32
|
ifNotDefined?: string;
|
|
@@ -19,6 +19,18 @@ export declare class AtomicInsightResultTemplate extends LitElement implements L
|
|
|
19
19
|
* `document.querySelector('#target-template').conditions = [(result) => /singapore/i.test(result.title)];`
|
|
20
20
|
*/
|
|
21
21
|
conditions: InsightResultTemplateCondition[];
|
|
22
|
+
/**
|
|
23
|
+
* The comma-separated list of fields that must all be defined on a result for the template to apply.
|
|
24
|
+
*
|
|
25
|
+
* For example, a template with the following attribute only applies to results whose `author` and `date` fields are both defined: `if-defined="author,date"`
|
|
26
|
+
*/
|
|
27
|
+
ifDefined?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The comma-separated list of fields that must all be undefined on a result for the template to apply.
|
|
30
|
+
*
|
|
31
|
+
* For example, a template with the following attribute only applies to results that have neither an `author` nor a `date` field: `if-not-defined="author,date"`
|
|
32
|
+
*/
|
|
33
|
+
ifNotDefined?: string;
|
|
22
34
|
/**
|
|
23
35
|
* The field and values that define which result items the condition must be applied to.
|
|
24
36
|
*
|
|
@@ -24,6 +24,18 @@ export declare class AtomicRecsResultTemplate extends LitElement implements LitE
|
|
|
24
24
|
* `document.querySelector('#target-template').conditions = [(result) => /singapore/i.test(result.title)];`
|
|
25
25
|
*/
|
|
26
26
|
conditions: RecsResultTemplateCondition[];
|
|
27
|
+
/**
|
|
28
|
+
* The comma-separated list of fields that must all be defined on a result for the template to apply.
|
|
29
|
+
* For example, a template with the following attribute only applies to results whose `author` and `date` fields are both defined:
|
|
30
|
+
* `if-defined="author,date"`
|
|
31
|
+
*/
|
|
32
|
+
ifDefined?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The comma-separated list of fields that must all be undefined on a result for the template to apply.
|
|
35
|
+
* For example, a template with the following attribute only applies to results that have neither an `author` nor a `date` field:
|
|
36
|
+
* `if-not-defined="author,date"`
|
|
37
|
+
*/
|
|
38
|
+
ifNotDefined?: string;
|
|
27
39
|
/**
|
|
28
40
|
* The field and values that define which result items the condition must be applied to.
|
|
29
41
|
* For example, a template with the following attribute only applies to result items whose `filetype` is `lithiummessage` or `YouTubePlaylist`:
|
|
@@ -12,7 +12,10 @@ declare const AtomicFacetManager_base: import("../../../mixins/mixin-common").Co
|
|
|
12
12
|
* 2. **Managing visibility** - Controls which facets should be visible or hidden based on available values and dependencies.
|
|
13
13
|
* 3. **Managing collapse state** - Automatically expands or collapses facets based on the `collapse-facets-after` property.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* A facet wrapped in an `atomic-popover` is managed like any other facet, and its popover moves along with it.
|
|
16
|
+
* Because a popover always displays its facet expanded, such a facet is never collapsed and does not count towards `collapse-facets-after`.
|
|
17
|
+
*
|
|
18
|
+
* @slot default - Facet components, or `atomic-popover` components wrapping a facet, are slotted within to leverage this functionality.
|
|
16
19
|
*/
|
|
17
20
|
export declare class AtomicFacetManager extends AtomicFacetManager_base implements InitializableComponent<Bindings> {
|
|
18
21
|
private static readonly propsSchema;
|
|
@@ -26,6 +29,8 @@ export declare class AtomicFacetManager extends AtomicFacetManager_base implemen
|
|
|
26
29
|
*
|
|
27
30
|
* Using the value `0` collapses all facets.
|
|
28
31
|
* Using the value `-1` disables the feature and keeps all facets expanded. Useful when you want to set the collapse state for each facet individually.
|
|
32
|
+
*
|
|
33
|
+
* Facets wrapped in an `atomic-popover` are always expanded and are not counted.
|
|
29
34
|
*/
|
|
30
35
|
collapseFacetsAfter: number;
|
|
31
36
|
constructor();
|
|
@@ -18,6 +18,18 @@ export declare class AtomicResultChildrenTemplate extends LitElement implements
|
|
|
18
18
|
* `document.querySelector('#target-template').conditions = [(result) => /singapore/i.test(result.title)];`
|
|
19
19
|
*/
|
|
20
20
|
conditions: ResultTemplateCondition[];
|
|
21
|
+
/**
|
|
22
|
+
* The comma-separated list of fields that must all be defined on a child result for the template to apply.
|
|
23
|
+
* For example, a template with the following attribute only applies to child results whose `author` and `date` fields are both defined:
|
|
24
|
+
* `if-defined="author,date"`
|
|
25
|
+
*/
|
|
26
|
+
ifDefined?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The comma-separated list of fields that must all be undefined on a child result for the template to apply.
|
|
29
|
+
* For example, a template with the following attribute only applies to child results that have neither an `author` nor a `date` field:
|
|
30
|
+
* `if-not-defined="author,date"`
|
|
31
|
+
*/
|
|
32
|
+
ifNotDefined?: string;
|
|
21
33
|
/**
|
|
22
34
|
* The field and values that define which child results the condition must be applied to.
|
|
23
35
|
* For example, a template with the following attribute only applies to child results whose `filetype` is `lithiummessage` or `YouTubePlaylist`:
|
|
@@ -20,6 +20,18 @@ export declare class AtomicResultTemplate extends LitElement implements LitEleme
|
|
|
20
20
|
* `document.querySelector('#target-template').conditions = [(result) => /singapore/i.test(result.title)];`
|
|
21
21
|
*/
|
|
22
22
|
conditions: ResultTemplateCondition[];
|
|
23
|
+
/**
|
|
24
|
+
* The comma-separated list of fields that must all be defined on a result for the template to apply.
|
|
25
|
+
* For example, a template with the following attribute only applies to results whose `author` and `date` fields are both defined:
|
|
26
|
+
* `if-defined="author,date"`
|
|
27
|
+
*/
|
|
28
|
+
ifDefined?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The comma-separated list of fields that must all be undefined on a result for the template to apply.
|
|
31
|
+
* For example, a template with the following attribute only applies to results that have neither an `author` nor a `date` field:
|
|
32
|
+
* `if-not-defined="author,date"`
|
|
33
|
+
*/
|
|
34
|
+
ifNotDefined?: string;
|
|
23
35
|
/**
|
|
24
36
|
* The field and values that define which results the condition must be applied to.
|
|
25
37
|
* For example, a template with the following attribute only applies to results whose `sourcetype` is `YouTube` or `Salesforce`:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { InitializeEventHandler } from './initialization-common-utils';
|
|
2
2
|
export type InitializeEvent = CustomEvent<InitializeEventHandler>;
|
|
3
|
+
type ElementMap<V> = Map<Element, V> | WeakMap<Element, V>;
|
|
3
4
|
declare global {
|
|
4
5
|
interface Window {
|
|
5
6
|
initQueueNamespace: {
|
|
6
|
-
eventQueueMap:
|
|
7
|
-
parentReadyMap:
|
|
7
|
+
eventQueueMap: ElementMap<QueuedEvent[]>;
|
|
8
|
+
parentReadyMap: ElementMap<boolean>;
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: Proposed
|
|
3
|
+
date: 2026-10-15
|
|
4
|
+
related:
|
|
5
|
+
- https://coveord.atlassian.net/browse/KIT-6364
|
|
6
|
+
- https://github.com/coveo/ui-kit/pull/8682
|
|
7
|
+
- https://github.com/coveo/ui-kit/pull/8681
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Group shared modules in one CDN chunk
|
|
11
|
+
|
|
12
|
+
## Context and Problem Statement
|
|
13
|
+
|
|
14
|
+
`rsbuild.cdn.mjs` splits Atomic's CDN bundle with rsbuild's `split-by-module` strategy and no minimum chunk size. Every npm package becomes one chunk, and every set of modules that the same components share becomes a chunk of its own. Components load on demand: for each one, the runtime fetches the list of chunks its code needs, then runs it.
|
|
15
|
+
|
|
16
|
+
Atomic's components share many small modules, such as Lit, base classes, controllers and utilities, so this produces hundreds of tiny chunks. With #8681 applied, a typical search page fetches 162 Atomic files, 109 of them under 2 KB, a rich search page fetches 225 and a commerce page 196. Each Atomic release is deployed under its own CDN path, so no file is reused across releases. Every first visit pays for every request, and small files compress poorly.
|
|
17
|
+
|
|
18
|
+
The decision is how to group the shared code so that pages fetch far fewer files without downloading much code they never run, and without changing what the CDN entry files load.
|
|
19
|
+
|
|
20
|
+
## Decision Drivers
|
|
21
|
+
|
|
22
|
+
- Time until a page's components are defined and its content is rendered, on a mid-range phone first and on desktop second.
|
|
23
|
+
- Files and bytes on four page shapes: a search box alone (often on every page of a site), a typical search page, a rich search page and a commerce page.
|
|
24
|
+
- The entry files keep loading what they load today. Pages load `atomic.esm.js` or `index.js`, and custom components import `initializeBindings` and other helpers from `index.esm.js`, which loads 2 KB gzipped at startup.
|
|
25
|
+
- Chunking may change which file delivers a module, but never which code runs or when.
|
|
26
|
+
- A short rule that keeps working as components are added.
|
|
27
|
+
|
|
28
|
+
## Considered Options
|
|
29
|
+
|
|
30
|
+
### Option A: `split-by-module` alone
|
|
31
|
+
|
|
32
|
+
- **Summary:** The build with #8681 applied.
|
|
33
|
+
- **Pros:** A page downloads only the Atomic modules its components use.
|
|
34
|
+
- **Cons:** 65 to 225 files per page. On mobile, every page is slower than with options C (at N = 5 and above) and D to F.
|
|
35
|
+
|
|
36
|
+
### Option B: rspack's default `splitChunks`
|
|
37
|
+
|
|
38
|
+
- **Summary:** `chunks: 'all'` with rspack's production defaults: a 20 KB minimum chunk size, plus its default and vendor groups.
|
|
39
|
+
- **Pros:** Fewest files, and the fastest page with only a search box.
|
|
40
|
+
- **Cons:** Shared modules that can't fill a 20 KB chunk are copied into every chunk that uses them. The rich page downloads 491 KB gzipped instead of 271 KB, and on mobile it is ready 20% later than with option C at N = 10.
|
|
41
|
+
|
|
42
|
+
### Option C: One chunk for the Atomic and Lit modules that at least N chunks use
|
|
43
|
+
|
|
44
|
+
- **Summary:** An `atomic-shared` cache group covers modules from Atomic's `src` and from the Lit runtime (`lit`, `lit-html`, `lit-element`, `@lit/reactive-element`, `@lit/context` and `tslib`) that at least N chunks use. It was measured for N from 2 to 50.
|
|
45
|
+
- **Pros:** One file replaces most of the small ones, and N sets how much shared code small pages download.
|
|
46
|
+
- **Cons:** At N = 9 or less, a module that `index.esm.js` loads at startup qualifies. `index.esm.js` then starts by downloading the whole shared chunk: 48 KB gzipped at N = 9 and 77 KB at N = 5, instead of 2 KB. At N = 2, `atomic.esm.js` and `index.js` also wait for a 164 KB chunk. Above N = 10, the typical, rich and commerce pages get slower at every step.
|
|
47
|
+
|
|
48
|
+
### Option D: Atomic's source only, N = 10
|
|
49
|
+
|
|
50
|
+
- **Summary:** Option C without the Lit runtime.
|
|
51
|
+
- **Pros:** None measured.
|
|
52
|
+
- **Cons:** Every page still loads Lit's packages, now as separate files. That means 4 to 6 more files per page, and pages are 1% to 2% slower on mobile.
|
|
53
|
+
|
|
54
|
+
### Option E: Every bundled module, N = 10
|
|
55
|
+
|
|
56
|
+
- **Summary:** Option C extended to npm packages. Today it adds DOMPurify and dayjs with its `customParseFormat` plugin, which every measured page loads, and `escape-html`, which every page except the search-box one loads. Externals such as Headless never qualify, because they have no source file.
|
|
57
|
+
- **Pros:** 2 to 3 fewer files and up to 2.7 KB less per page. The rule is the literal reading of "modules that at least 10 chunks use".
|
|
58
|
+
- **Cons:** It is no faster on the typical, rich and commerce pages, and only 21 ms faster on the search-box page (mobile). The chunk that every page loads first grows from 47.6 KB to 62.1 KB gzipped, and any npm package that 10 chunks use later joins it without anyone choosing it. Headless stays out only because externals have no source file: if that ever changed, every page would import every Headless bundle.
|
|
59
|
+
|
|
60
|
+
### Option F: One shared chunk per line of business, N = 10
|
|
61
|
+
|
|
62
|
+
- **Summary:** Option E, with modules from the `search` and `commerce` component folders in `atomic-shared-search` and `atomic-shared-commerce`.
|
|
63
|
+
- **Pros:** The search-box page skips 30 KB of decoded result-template code and is ready 44 ms sooner on mobile.
|
|
64
|
+
- **Cons:** It is no faster on the typical, rich and commerce pages, because folders don't match what pages load. Commerce components use the result sections and text components in the `search` folder, so commerce pages load `atomic-shared-search`, and the rich search page loads `atomic-shared-commerce`. It also needs six cache groups to keep in sync with the component folders.
|
|
65
|
+
|
|
66
|
+
### Measurements
|
|
67
|
+
|
|
68
|
+
Every option was built from the same commit, with #8681's one-chunk-per-dayjs-locale group, and measured the same way:
|
|
69
|
+
|
|
70
|
+
- Pages were served over HTTP/2 with gzip from a local server. The same server replayed recorded API answers, so only the Atomic files differed between options.
|
|
71
|
+
- Every load started with an empty cache.
|
|
72
|
+
- Two network profiles were used. Mobile is Lighthouse's throttling: 150 ms latency, 1.6 Mbps and a 4× slower CPU. Desktop is 100 ms latency and 5 Mbps.
|
|
73
|
+
- Each figure is the median of five mobile loads or three desktop loads, interleaved across options.
|
|
74
|
+
|
|
75
|
+
"Ready" is when every Atomic element on the page is defined and the results or products have rendered.
|
|
76
|
+
|
|
77
|
+
Mobile, ready in, with the number of Atomic files fetched:
|
|
78
|
+
|
|
79
|
+
| Option | Search box | Typical search | Rich search | Commerce |
|
|
80
|
+
| ------------- | --------------- | --------------- | ---------------- | ---------------- |
|
|
81
|
+
| A | 2.91 s (65) | 4.21 s (162) | 5.27 s (225) | 6.09 s (196) |
|
|
82
|
+
| B | 2.53 s (15) | 4.05 s (50) | 5.59 s (92) | 5.89 s (67) |
|
|
83
|
+
| C, N = 5 | 2.77 s (23) | 3.60 s (50) | 4.55 s (97) | 5.42 s (78) |
|
|
84
|
+
| C, N = 7 | 2.70 s (27) | 3.63 s (64) | 4.58 s (118) | 5.46 s (94) |
|
|
85
|
+
| **C, N = 10** | **2.68 s (30)** | **3.66 s (76)** | **4.66 s (134)** | **5.54 s (108)** |
|
|
86
|
+
| C, N = 20 | 2.66 s (34) | 3.85 s (106) | 4.86 s (168) | 5.76 s (142) |
|
|
87
|
+
| C, N = 50 | 2.69 s (41) | 4.02 s (135) | 5.05 s (198) | 5.92 s (169) |
|
|
88
|
+
| D | 2.74 s (36) | 3.72 s (80) | 4.71 s (138) | 5.59 s (112) |
|
|
89
|
+
|
|
90
|
+
The three closest options were then measured again together, with seven mobile loads each:
|
|
91
|
+
|
|
92
|
+
| Option | Search box | Typical search | Rich search | Commerce |
|
|
93
|
+
| ------------- | --------------- | --------------- | ---------------- | ---------------- |
|
|
94
|
+
| **C, N = 10** | **2.66 s (30)** | **3.65 s (76)** | **4.62 s (134)** | **5.52 s (108)** |
|
|
95
|
+
| E | 2.64 s (28) | 3.63 s (74) | 4.63 s (132) | 5.50 s (105) |
|
|
96
|
+
| F | 2.62 s (28) | 3.63 s (75) | 4.63 s (134) | 5.50 s (107) |
|
|
97
|
+
|
|
98
|
+
Atomic JavaScript downloaded, gzipped:
|
|
99
|
+
|
|
100
|
+
| Option | Search box | Typical search | Rich search | Commerce |
|
|
101
|
+
| ------------- | ------------ | -------------- | ------------ | ------------ |
|
|
102
|
+
| A | 130.0 KB | 209.4 KB | 301.2 KB | 244.2 KB |
|
|
103
|
+
| B | 112.5 KB | 267.8 KB | 491.5 KB | 326.1 KB |
|
|
104
|
+
| C, N = 5 | 154.7 KB | 188.4 KB | 266.5 KB | 212.6 KB |
|
|
105
|
+
| **C, N = 10** | **132.4 KB** | **180.6 KB** | **270.6 KB** | **214.7 KB** |
|
|
106
|
+
| C, N = 20 | 123.6 KB | 190.2 KB | 281.7 KB | 226.8 KB |
|
|
107
|
+
| E | 130.2 KB | 180.1 KB | 270.1 KB | 212.0 KB |
|
|
108
|
+
| F | 125.3 KB | 179.5 KB | 271.4 KB | 213.3 KB |
|
|
109
|
+
|
|
110
|
+
The desktop profile shows the same trends with smaller gaps.
|
|
111
|
+
|
|
112
|
+
## Decision Outcome
|
|
113
|
+
|
|
114
|
+
Chosen option: C with N = 10.
|
|
115
|
+
|
|
116
|
+
### Rationale
|
|
117
|
+
|
|
118
|
+
- N = 10 is the lowest threshold that leaves every entry file as it is. `index.esm.js` and the two chunks it imports are byte-identical. `atomic.esm.js` and `index.js` differ only in their table of chunk file names. The runtime chunk differs only in the list of files each component loads, and in the module id for 2 of the 206 components (`atomic-icon` and `atomic-recs-error`), which rspack now combines with their helpers differently.
|
|
119
|
+
- Raising N above 10 slows the typical, rich and commerce pages at every step, on both profiles. The search-box page doesn't benefit: it is within 1% on mobile and no faster on desktop. N = 10 is the fastest setting that keeps the entry files unchanged.
|
|
120
|
+
- Lowering N would make the larger pages up to 2.4% faster on mobile (N = 5), but every page that imports `index.esm.js` would first download 48 to 164 KB gzipped instead of 2 KB. At N = 5 the search-box page is also 3% slower.
|
|
121
|
+
- Lit belongs in the shared chunk. Every page loads it, and leaving it out (option D) is slower on every page.
|
|
122
|
+
- On mobile, options E and F come within 0.5% of C on the typical, rich and commerce pages, in both directions, and on desktop within 2.5%. They are consistently faster only on a page with nothing but a search box, by 21 ms and 44 ms on mobile. That gain doesn't justify a shared chunk that any third-party package can join (E), or six folder-based groups whose folders don't match what pages load (F).
|
|
123
|
+
- rspack's defaults (option B) win only on the search-box page, because they copy shared code into every chunk.
|
|
124
|
+
|
|
125
|
+
## Consequences
|
|
126
|
+
|
|
127
|
+
- **Positive:** On mobile, compared with option A, the search box is ready 0.23 s sooner, the typical search page 0.55 s sooner, the rich page 0.61 s sooner and the commerce page 0.56 s sooner. The typical search page fetches 76 files instead of 162, and 180.6 KB gzipped instead of 209.4 KB.
|
|
128
|
+
- **Negative:** Every page downloads the whole shared chunk (194 KB, 47.6 KB gzipped), including modules it never runs. On the search-box page that is 72 KB of decoded code, mostly components that render results and products, such as result sections and placeholders. The page downloads 2.4 KB gzipped more than with option A while fetching 35 fewer files. Typical, rich and commerce pages download 5 to 6 KB of decoded code they never run.
|
|
129
|
+
- **Neutral:** Modules run in the same order as before, when the code that imports them runs; only the file that delivers them changes. The seven modules that main already duplicates stay duplicated: six utilities copied into `index.esm.js`, and the loader in both `atomic.esm.js` and `index.js`. The shared chunk's file name changes with nearly every release, which costs nothing, because every release has its own CDN path.
|
|
130
|
+
|
|
131
|
+
## Implementation and Follow-up
|
|
132
|
+
|
|
133
|
+
The `shared` cache group in `rsbuild.cdn.mjs` implements the decision. It sits next to the `dayjsLocales` group from #8681.
|
|
134
|
+
|
|
135
|
+
The build was verified as follows:
|
|
136
|
+
|
|
137
|
+
- Every chunk is a plain map of module factories, so loading one runs nothing. The exceptions are five wrappers that import Headless and Bueno, which are unchanged.
|
|
138
|
+
- Every module that a lazily loaded component requires is in a file its import loads. This holds for all 206 components, and for the 145 dynamic imports inside them (dayjs locales, the query-error illustrations and a Stencil runtime import), from every component that can trigger them.
|
|
139
|
+
- All 206 components register, with the same console output as on main.
|
|
140
|
+
- The CDN smoke tests and CI's CDN checks pass.
|
|
141
|
+
- A custom component that uses `initializeBindings` works.
|
|
142
|
+
- Dates render the same in 10 languages, and after switching language at runtime.
|
|
143
|
+
|
|
144
|
+
Revisit after adding many components or upgrading rsbuild or rspack:
|
|
145
|
+
|
|
146
|
+
- Check that `index.esm.js` still imports only its two small chunks.
|
|
147
|
+
- Measure N = 10 against its neighbors again.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coveo/atomic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.64.1",
|
|
4
4
|
"description": "A web-component library for building modern UIs interfacing with the Coveo platform",
|
|
5
5
|
"homepage": "https://docs.coveo.com/en/atomic/latest/",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"./custom-elements-manifest": "./custom-elements.json"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@coveo/atomic-legacy": "0.1.
|
|
58
|
+
"@coveo/atomic-legacy": "0.1.9",
|
|
59
59
|
"@coveo/bueno": "1.1.9",
|
|
60
|
-
"@coveo/headless": "3.
|
|
60
|
+
"@coveo/headless": "3.58.0",
|
|
61
61
|
"@coveo/relay-event-types": "20.7.1",
|
|
62
62
|
"@lit/context": "1.1.6",
|
|
63
63
|
"@open-wc/lit-helpers": "0.7.0",
|
|
@@ -80,12 +80,13 @@
|
|
|
80
80
|
"@playwright/test": "1.62.1",
|
|
81
81
|
"@rollup/plugin-replace": "6.0.3",
|
|
82
82
|
"@rslib/core": "0.23.2",
|
|
83
|
-
"@
|
|
84
|
-
"@storybook/addon-
|
|
85
|
-
"@storybook/addon-
|
|
86
|
-
"@storybook/addon-
|
|
83
|
+
"@size-limit/preset-small-lib": "14.1.0",
|
|
84
|
+
"@storybook/addon-a11y": "10.6.2",
|
|
85
|
+
"@storybook/addon-docs": "10.6.2",
|
|
86
|
+
"@storybook/addon-mcp": "10.6.2",
|
|
87
|
+
"@storybook/addon-vitest": "10.6.2",
|
|
87
88
|
"@storybook/icons": "2.1.0",
|
|
88
|
-
"@storybook/web-components-vite": "10.6.
|
|
89
|
+
"@storybook/web-components-vite": "10.6.2",
|
|
89
90
|
"@tailwindcss/postcss": "4.3.3",
|
|
90
91
|
"@tailwindcss/vite": "4.3.3",
|
|
91
92
|
"@types/escape-html": "1.0.4",
|
|
@@ -108,8 +109,9 @@
|
|
|
108
109
|
"react": "19.2.8",
|
|
109
110
|
"remark-gfm": "4.0.1",
|
|
110
111
|
"shadow-dom-testing-library": "1.14.1",
|
|
111
|
-
"
|
|
112
|
-
"storybook
|
|
112
|
+
"size-limit": "14.1.0",
|
|
113
|
+
"storybook": "10.6.2",
|
|
114
|
+
"storybook-addon-pseudo-states": "10.6.2",
|
|
113
115
|
"tailwindcss": "4.3.3",
|
|
114
116
|
"ts-dedent": "2.3.0",
|
|
115
117
|
"ts-lit-plugin": "2.0.2",
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
},
|
|
120
122
|
"peerDependencies": {
|
|
121
123
|
"@coveo/bueno": "^1.1.9",
|
|
122
|
-
"@coveo/headless": "^3.
|
|
124
|
+
"@coveo/headless": "^3.58.0",
|
|
123
125
|
"typescript": ">=5.0.0"
|
|
124
126
|
},
|
|
125
127
|
"peerDependenciesMeta": {
|
|
@@ -138,8 +140,9 @@
|
|
|
138
140
|
},
|
|
139
141
|
"scripts": {
|
|
140
142
|
"publint": "publint",
|
|
141
|
-
"clean": "node ../../utils/ci/rm-rf.mjs dist/* dist-storybook/* www/* docs/* playwright-report/*",
|
|
143
|
+
"clean": "node ../../utils/ci/rm-rf.mjs dist/* dist-storybook/* dist-storybook-e2e/* www/* docs/* playwright-report/*",
|
|
142
144
|
"build:storybook": "storybook build -o dist-storybook",
|
|
145
|
+
"build:storybook:e2e": "STORYBOOK_INVOKED_BY=playwright storybook build -o dist-storybook-e2e --quiet",
|
|
143
146
|
"build:cem": "cem analyze",
|
|
144
147
|
"build:locales": "node scripts/build-locales.mjs",
|
|
145
148
|
"build:assets": "node scripts/build-assets.mjs",
|
|
@@ -155,7 +158,6 @@
|
|
|
155
158
|
"e2e": "playwright test",
|
|
156
159
|
"e2e:csp": "node ./csp/test.mjs || exit 0",
|
|
157
160
|
"test": "pnpm run test:lit",
|
|
158
|
-
"test:stencil": "stencil test --spec -- src/utils/initialization-utils.spec.ts",
|
|
159
161
|
"test:lit": "vitest run --project=atomic-default",
|
|
160
162
|
"test:watch": "vitest --project=atomic-default",
|
|
161
163
|
"test:storybook": "vitest run --project=storybook",
|
|
@@ -165,6 +167,7 @@
|
|
|
165
167
|
"validate:definitions": "tsc --noEmit --esModuleInterop --skipLibCheck ./dist/types/components.d.ts",
|
|
166
168
|
"chromatic": "chromatic",
|
|
167
169
|
"validate:light-dom-styles": "node ./scripts/check-light-dom-host-selectors.mjs",
|
|
168
|
-
"validate:no-new-light-dom": "node ./scripts/check-no-new-light-dom-components.mjs"
|
|
170
|
+
"validate:no-new-light-dom": "node ./scripts/check-no-new-light-dom-components.mjs",
|
|
171
|
+
"size": "node ../../scripts/size-limit.mjs run"
|
|
169
172
|
}
|
|
170
173
|
}
|