Skip to content

Commit c21d80e

Browse files
authoredJul 21, 2024
Merge pull request cardstack#1442 from cardstack/prerendered-css-optimization
Remove unnecessary CSS in prerendered cards payload
2 parents 7f734b9 + 2bbb4f0 commit c21d80e

File tree

5 files changed

+249
-204
lines changed

5 files changed

+249
-204
lines changed
 

‎packages/host/tests/integration/card-prerender-test.gts

+6-7
Original file line numberDiff line numberDiff line change
@@ -233,22 +233,21 @@ module('Integration | card-prerender', function (hooks) {
233233

234234
assert.strictEqual(
235235
results.prerenderedCardCssItems[0].cssModuleId,
236-
'http://test-realm/test/person',
236+
'http://test-realm/test/fancy-person',
237237
);
238238

239239
assert.true(
240-
results.prerenderedCardCssItems[0].source.includes('.border'),
241-
'css for person card looks correct',
240+
results.prerenderedCardCssItems[0].source.includes('.fancy-border'),
241+
'css for fancy person card looks correct',
242242
);
243-
244243
assert.strictEqual(
245244
results.prerenderedCardCssItems[1].cssModuleId,
246-
'http://test-realm/test/fancy-person',
245+
'http://test-realm/test/person',
247246
);
248247

249248
assert.true(
250-
results.prerenderedCardCssItems[1].source.includes('.fancy-border'),
251-
'css for fancy person card looks correct',
249+
results.prerenderedCardCssItems[1].source.includes('.border'),
250+
'css for person card looks correct',
252251
);
253252

254253
assert.strictEqual(

‎packages/realm-server/tests/realm-server-test.ts

+8-93
Original file line numberDiff line numberDiff line change
@@ -1698,10 +1698,10 @@ module('Realm Server', function (hooks) {
16981698
'embedded html looks correct',
16991699
);
17001700

1701-
assert.strictEqual(
1702-
json.data[0].relationships['prerendered-card-css'].data.length,
1703-
0,
1704-
'no css is included',
1701+
assert.deepEqual(
1702+
json.included,
1703+
[],
1704+
'no css is present in the response',
17051705
);
17061706

17071707
assert.strictEqual(json.meta.page.total, 1, 'total count is correct');
@@ -1852,17 +1852,7 @@ module('Realm Server', function (hooks) {
18521852
json.data[0].attributes.html
18531853
.replace(/\s+/g, ' ')
18541854
.includes('Person Aaron'),
1855-
'embedded html looks correct',
1856-
);
1857-
assert.strictEqual(
1858-
json.data[0].relationships['prerendered-card-css'].data.length,
1859-
1,
1860-
'there is one css for card of type Person',
1861-
);
1862-
assert.deepEqual(
1863-
json.data[0].relationships['prerendered-card-css'].data[0],
1864-
{ type: 'prerendered-card-css', id: 'http://127.0.0.1:4444/person' },
1865-
'css relationship is correct',
1855+
'embedded html looks correct (CardDef template)',
18661856
);
18671857

18681858
// 2nd card: Person Craig
@@ -1871,17 +1861,7 @@ module('Realm Server', function (hooks) {
18711861
json.data[1].attributes.html
18721862
.replace(/\s+/g, ' ')
18731863
.includes('Person Craig'),
1874-
'embedded html for Craig looks correct',
1875-
);
1876-
assert.strictEqual(
1877-
json.data[1].relationships['prerendered-card-css'].data.length,
1878-
1,
1879-
'there is one css for card of type Person',
1880-
);
1881-
assert.deepEqual(
1882-
json.data[1].relationships['prerendered-card-css'].data[0],
1883-
{ type: 'prerendered-card-css', id: 'http://127.0.0.1:4444/person' },
1884-
'css relationship is correct',
1864+
'embedded html for Craig looks correct (CardDef template)',
18851865
);
18861866

18871867
// 3rd card: FancyPerson Jane
@@ -1890,26 +1870,7 @@ module('Realm Server', function (hooks) {
18901870
json.data[2].attributes.html
18911871
.replace(/\s+/g, ' ')
18921872
.includes('FancyPerson Jane'),
1893-
'embedded html for Jane looks correct',
1894-
);
1895-
assert.strictEqual(
1896-
json.data[2].relationships['prerendered-card-css'].data.length,
1897-
2,
1898-
'there are two css for card of type FancyPerson',
1899-
);
1900-
assert.deepEqual(
1901-
json.data[2].relationships['prerendered-card-css'].data,
1902-
[
1903-
{
1904-
type: 'prerendered-card-css',
1905-
id: 'http://127.0.0.1:4444/person',
1906-
},
1907-
{
1908-
type: 'prerendered-card-css',
1909-
id: 'http://127.0.0.1:4444/fancy-person',
1910-
},
1911-
],
1912-
'instance of type FancyPerson has css from both Person and FancyPerson',
1873+
'embedded html for Jane looks correct (CardDef template)',
19131874
);
19141875

19151876
// 4th card: FancyPerson Jimmy
@@ -1918,53 +1879,7 @@ module('Realm Server', function (hooks) {
19181879
json.data[3].attributes.html
19191880
.replace(/\s+/g, ' ')
19201881
.includes('FancyPerson Jimmy'),
1921-
'embedded html for Jimmy looks correct',
1922-
);
1923-
assert.strictEqual(
1924-
json.data[3].relationships['prerendered-card-css'].data.length,
1925-
2,
1926-
'there are two css for card of type FancyPerson',
1927-
);
1928-
assert.deepEqual(
1929-
json.data[3].relationships['prerendered-card-css'].data,
1930-
[
1931-
{
1932-
type: 'prerendered-card-css',
1933-
id: 'http://127.0.0.1:4444/person',
1934-
},
1935-
{
1936-
type: 'prerendered-card-css',
1937-
id: 'http://127.0.0.1:4444/fancy-person',
1938-
},
1939-
],
1940-
'instance of type FancyPerson has css from both Person and FancyPerson',
1941-
);
1942-
1943-
assert.strictEqual(
1944-
json.included.length,
1945-
2,
1946-
'css sources are not duplicated - only 2 css sources are included for 4 instances (one for Person and one for FancyPerson',
1947-
);
1948-
1949-
assert.strictEqual(json.included[0].type, 'prerendered-card-css');
1950-
assert.strictEqual(json.included[0].id, 'http://127.0.0.1:4444/person');
1951-
assert.ok(
1952-
/^\.border\[data-scopedcss-[a-f0-9]+-[a-f0-9]+\] \{ border: 1px solid red; \}$/.test(
1953-
json.included[0].attributes.content.replace(/\s+/g, ' '),
1954-
),
1955-
'css content for Person is correct',
1956-
);
1957-
1958-
assert.strictEqual(json.included[1].type, 'prerendered-card-css');
1959-
assert.strictEqual(
1960-
json.included[1].id,
1961-
'http://127.0.0.1:4444/fancy-person',
1962-
);
1963-
assert.ok(
1964-
/^\.fancy-border\[data-scopedcss-[a-f0-9]+-[a-f0-9]+\] \{ border: 1px solid pink; \}$/.test(
1965-
json.included[1].attributes.content.replace(/\s+/g, ' '),
1966-
),
1967-
'css content for FancyPerson is correct',
1882+
'embedded html for Jimmy looks correct (CardDef template)',
19681883
);
19691884

19701885
assert.strictEqual(json.meta.page.total, 4, 'total count is correct');

‎packages/runtime-common/card-document.ts

+1-11
Original file line numberDiff line numberDiff line change
@@ -278,23 +278,13 @@ export function transformResultsToPrerenderedCardsDoc(results: {
278278
attributes: {
279279
html: card.html,
280280
},
281-
relationships: {
282-
'prerendered-card-css': {
283-
data: card.cssModuleIds.map((cssModuleId) => {
284-
return {
285-
type: 'prerendered-card-css',
286-
id: cssModuleId,
287-
};
288-
}),
289-
},
290-
},
291281
}));
292282

293283
let included = prerenderedCardCssItems.map((css) => ({
294284
type: 'prerendered-card-css',
295285
id: css.cssModuleId,
296286
attributes: {
297-
content: css.source,
287+
source: css.source,
298288
},
299289
}));
300290

‎packages/runtime-common/index-query-engine.ts

+29-61
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
loadCard,
99
internalKeyFor,
1010
identifyCard,
11+
ResolvedCodeRef,
1112
} from './index';
1213
import {
1314
type Expression,
@@ -111,7 +112,6 @@ export interface PrerenderedCardCssItem {
111112
export interface PrerenderedCard {
112113
url: string;
113114
html: string;
114-
cssModuleIds: string[];
115115
}
116116

117117
export interface QueryResultsMeta {
@@ -432,10 +432,10 @@ export class IndexQueryEngine {
432432
throw new Error(`htmlFormat must be either 'embedded' or 'atom'`);
433433
}
434434

435-
let ref: CodeRef;
435+
let ref: ResolvedCodeRef;
436436
let filterOnValue = filter && 'type' in filter ? filter.type : filter?.on;
437437
if (filterOnValue) {
438-
ref = filterOnValue;
438+
ref = filterOnValue as ResolvedCodeRef;
439439
} else {
440440
ref = baseCardRef;
441441
}
@@ -460,45 +460,39 @@ export class IndexQueryEngine {
460460
results: (Partial<BoxelIndexTable> & { html: string })[];
461461
};
462462

463-
let cardFileAliases = results.map((c) => c.file_alias);
464463
let realmVersion = meta.page.realmVersion;
465464

466465
let conditions: CardExpression[] = [
467466
['i.realm_version = ', param(realmVersion)],
468-
['i.type =', param('instance')],
469-
[
470-
'i.file_alias IN',
471-
...addExplicitParens(
472-
separatedByCommas(
473-
cardFileAliases.map((cardFileAlias) => [param(cardFileAlias!)]),
474-
),
475-
),
476-
],
467+
['i.type =', param('module')],
468+
['i.file_alias =', param(ref.module)],
477469
];
478470

479-
// This query will give us a list of indexed css entries and their corresponding instances that have that particular css module as a dependency
471+
// This query will give us all the CSS that relates to the card ref's module and its dependencies
480472
let cssSourceQuery = [
481-
`WITH instance_deps AS (
482-
SELECT file_alias as instance_file_alias, deps_each.value AS instance_dep_file_alias
473+
`WITH module_deps AS (
474+
SELECT file_alias as module_file_alias, deps_each.value AS module_dep_file_alias
483475
FROM boxel_index i, jsonb_array_elements_text(i.deps) AS deps_each
484476
WHERE`,
485477
...every(conditions),
478+
`UNION ALL SELECT `, // UNION ALL to include the css from module itself as well, not just its dependencies
479+
param(ref.module),
480+
`, `,
481+
param(ref.module),
486482
`),
487483
css_deps AS (
488484
SELECT
489-
bi.source AS css_source,
490485
bi.file_alias AS css_module_id,
491-
array_agg(id.instance_file_alias) AS instances
486+
bi.source AS css_source
492487
FROM boxel_index bi
493-
JOIN instance_deps id ON bi.file_alias = id.instance_dep_file_alias
494-
WHERE bi.type = 'css'
495-
GROUP BY bi.source, bi.file_alias
496-
)
497-
SELECT
498-
css_module_id,
499-
array_to_json(instances) AS instances,
500-
css_source
501-
FROM css_deps;
488+
JOIN module_deps md ON bi.file_alias = md.module_dep_file_alias WHERE`,
489+
...every([
490+
[`bi.realm_version = `, param(realmVersion)],
491+
['bi.type =', param('css')],
492+
]),
493+
`)
494+
SELECT DISTINCT css_module_id, css_source
495+
FROM css_deps
502496
`,
503497
];
504498

@@ -507,50 +501,24 @@ export class IndexQueryEngine {
507501
loader,
508502
)) as {
509503
css_module_id: string;
510-
instances: string[];
511504
css_source: string;
512505
}[];
513506

514-
// Prepare a structure where we can easily access css entries for deps of a particular card
515-
let cardInstanceCss = groupedCssInstanceData.reduce(
516-
(
517-
acc: Record<string, { cssModuleId: string; cssSource: string }[]>,
518-
row,
519-
) => {
520-
let instances =
521-
typeof row.instances === 'string'
522-
? JSON.parse(row.instances)
523-
: row.instances; // SQLite client returns array as string
524-
instances.forEach((instance: string) => {
525-
if (!acc[instance]) {
526-
acc[instance] = [];
527-
}
528-
acc[instance].push({
529-
cssModuleId: row.css_module_id,
530-
cssSource: row.css_source,
531-
});
532-
});
533-
return acc;
534-
},
535-
{},
536-
);
537-
538507
let prerenderedCards = results.map((card) => {
539508
return {
540509
url: card.url!,
541510
html: card.html,
542-
cssModuleIds:
543-
cardInstanceCss[card.file_alias!]?.map((item) => item.cssModuleId) ??
544-
[],
545511
};
546512
});
547513

548-
let prerenderedCardCssItems = groupedCssInstanceData.map((cssRecord) => {
549-
return {
550-
cssModuleId: cssRecord.css_module_id,
551-
source: cssRecord.css_source,
552-
};
553-
});
514+
let prerenderedCardCssItems = groupedCssInstanceData
515+
.map((cssRecord) => {
516+
return {
517+
cssModuleId: cssRecord.css_module_id,
518+
source: cssRecord.css_source,
519+
};
520+
})
521+
.sort((a, b) => a.cssModuleId.localeCompare(b.cssModuleId)); // Sort by css module id for determinism (especially in tests). We do it in JS because SQLite and Postgres have different sorting behavior for urls
554522

555523
return { prerenderedCards, prerenderedCardCssItems, meta };
556524
}

0 commit comments

Comments
 (0)