Skip to content

Commit ddfacd8

Browse files
alex-pagechloerice
andauthored
Replace data/*.json files with build time .cache/site.json (#7074)
**This PR** - [x] Adds a build time script to generate the `.cache/site.json` file making sure the navigation and search do not need to be updated when creating new pages - [x] Replaces `components.json` and `foundations.json` with `.cache/site.json` - [x] Uses one file for the site data so that there is one `readFile` script needed to access it - [x] Uses one file for the site data so it can be cached effectively when read - [x] **Local dev:** Make the `.cache/site.json` file regenerate on changes to markdown files - [x] Fixes json file typescript issues **Future work** - [ ] **Production:** Do not read and store `.cache/site.json` in client side memory when generating the navigation - [ ] **Production:** Do not read and store `.cache/site.json` in client side memory when generating the components page categories Co-authored-by: Chloe Rice <[email protected]>
1 parent 5933fc5 commit ddfacd8

File tree

17 files changed

+139
-2372
lines changed

17 files changed

+139
-2372
lines changed

.changeset/eighty-cups-retire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'polaris.shopify.com': minor
3+
---
4+
5+
Replace data/\*.json files with build time .cache/site.json

polaris.shopify.com/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"private": true,
55
"scripts": {
66
"build": "yarn gen-assets && next build",
7-
"dev": "open http://localhost:3000 && next dev",
87
"start": "next start",
8+
"dev": "run-p dev:*",
9+
"dev:server": "open http://localhost:3000 && next dev",
10+
"dev:watch-md": "node scripts/watch-md.mjs",
911
"lint": "run-p lint:*",
1012
"lint:js": "TIMING=1 eslint --cache .",
1113
"lint:styles": "stylelint '**/*.{css,scss}'",
@@ -40,6 +42,7 @@
4042
"@types/prismjs": "^1.26.0",
4143
"@types/react": "*",
4244
"@types/lodash.throttle": "^4.1.7",
45+
"chokidar": "^3.5.3",
4346
"eslint-config-next": "12.1.0",
4447
"eslint": "8.10.0",
4548
"execa": "^6.1.0",

polaris.shopify.com/pages/api/search/v0/index.tsx

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
import type {NextApiRequest, NextApiResponse} from 'next';
22
import Fuse from 'fuse.js';
3+
import {metadata, MetadataProperties} from '@shopify/polaris-tokens';
4+
import iconMetadata from '@shopify/polaris-icons/metadata';
35

46
import {
57
SearchResults,
68
GroupedSearchResults,
79
searchResultCategories,
810
SearchResultCategory,
911
Status,
12+
SiteJSON,
1013
} from '../../../../src/types';
1114

1215
import {slugify, stripMarkdownLinks} from '../../../../src/utils/various';
1316

14-
import {metadata, MetadataProperties} from '@shopify/polaris-tokens';
15-
import iconMetadata from '@shopify/polaris-icons/metadata';
16-
import components from '../../../../src/data/components.json';
17-
import foundations from '../../../../src/data/foundations.json';
17+
import siteJson from '../../../../.cache/site.json';
18+
19+
const pages: SiteJSON = siteJson;
20+
21+
const componentSlugs = Object.keys(pages).filter((slug) =>
22+
slug.startsWith('components/'),
23+
);
24+
const foundationSlugs = Object.keys(pages).filter((slug) =>
25+
slug.startsWith('foundations/'),
26+
);
1827

1928
const MAX_RESULTS: {[key in SearchResultCategory]: number} = {
2029
foundations: 8,
@@ -29,13 +38,8 @@ const getSearchResults = (query: string) => {
2938
let results: SearchResults = [];
3039

3140
// Add components
32-
components.forEach(({frontMatter: {title, status}, description}) => {
33-
const typedStatus: Status | undefined = status
34-
? {
35-
value: status.value.toLowerCase() as Status['value'],
36-
message: status.message,
37-
}
38-
: undefined;
41+
componentSlugs.forEach((slug) => {
42+
const {status, title, description = ''} = pages[slug].frontMatter;
3943

4044
results.push({
4145
id: slugify(`components ${title}`),
@@ -46,7 +50,7 @@ const getSearchResults = (query: string) => {
4650
components: {
4751
title,
4852
description: stripMarkdownLinks(description),
49-
status: typedStatus,
53+
status: status as Status,
5054
},
5155
},
5256
});
@@ -102,20 +106,19 @@ const getSearchResults = (query: string) => {
102106
});
103107

104108
// Add foundations
105-
foundations.forEach((data) => {
106-
const {title, icon} = data.frontMatter;
107-
const {description, category} = data;
108-
const url = `/foundations/${category}/${slugify(title)}`;
109+
foundationSlugs.forEach((slug) => {
110+
const {title, icon = '', description = ''} = pages[slug].frontMatter;
111+
const category = slug.split('/')[2];
109112

110113
results.push({
111114
id: slugify(`foundations ${title}`),
112115
category: 'foundations',
113116
score: 0,
114-
url,
117+
url: slug,
115118
meta: {
116119
foundations: {
117120
title,
118-
icon: icon || '',
121+
icon,
119122
description,
120123
category: category || '',
121124
},

polaris.shopify.com/pages/components/[component].tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,20 @@ const Components = ({
4242
propsForComponent,
4343
}: Props) => {
4444
const navItems: NavItem[] = getComponentNav();
45-
const typedStatus: Status | undefined = status
46-
? {
47-
value: status.value.toLowerCase() as Status['value'],
48-
message: status.message,
49-
}
50-
: undefined;
45+
const statusBanner = status ? <StatusBanner status={status} /> : null;
46+
const propList = propsForComponent ? (
47+
<PropsTable props={propsForComponent} />
48+
) : null;
5149

5250
return (
5351
<Layout width="narrow" navItems={navItems} title={title}>
5452
<PageMeta title={title} description={description} />
5553

5654
<Longform>
5755
<Markdown text={description} />
58-
{typedStatus && <StatusBanner status={typedStatus} />}
56+
{statusBanner}
5957
<ComponentExamples examples={examples} />
60-
{propsForComponent && <PropsTable props={propsForComponent} />}
58+
{propList}
6159
<Markdown text={readme.body} />
6260
</Longform>
6361
</Layout>
Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import path from 'path';
22
import globby from 'globby';
3-
import {rm, mkdir, writeFile, readFile} from 'fs/promises';
4-
import {existsSync} from 'fs';
3+
import {existsSync, rmSync, mkdirSync, writeFileSync, readFileSync} from 'fs';
54
import matter from 'gray-matter';
65

76
const cacheDir = path.join(process.cwd(), '.cache');
87
const siteJsonFile = `${cacheDir}/site.json`;
98

10-
const getMdContent = async (filePath) => {
11-
const fileContent = await readFile(filePath, 'utf-8');
9+
const getMdContent = (filePath) => {
10+
const fileContent = readFileSync(filePath, 'utf-8');
1211
const {data, content} = matter(fileContent);
1312
const slug = filePath
1413
.replace(`${process.cwd()}/content/`, '')
@@ -17,23 +16,19 @@ const getMdContent = async (filePath) => {
1716
return {frontMatter: data, slug};
1817
};
1918

20-
const genSiteJson = async () => {
21-
if (existsSync(cacheDir)) await rm(cacheDir, {recursive: true});
22-
await mkdir(cacheDir, {recursive: true});
19+
const genSiteJson = () => {
20+
if (!existsSync(cacheDir)) mkdirSync(cacheDir, {recursive: true});
2321
const pathGlob = path.join(process.cwd(), 'content/**/*.md');
2422
const mdFiles = globby.sync(pathGlob);
2523

26-
const mdDataPromise = mdFiles.map((filePath) => getMdContent(filePath));
27-
const mdData = await Promise.all(mdDataPromise);
24+
const mdData = mdFiles.map((filePath) => getMdContent(filePath));
2825

2926
const data = {};
3027
mdData.forEach((md) => (data[md.slug] = {frontMatter: md.frontMatter}));
3128

32-
await writeFile(siteJsonFile, JSON.stringify(data), 'utf-8');
29+
writeFileSync(siteJsonFile, JSON.stringify(data), 'utf-8');
3330

3431
console.log(`✅ Generated ${siteJsonFile}`);
3532
};
3633

37-
genSiteJson();
38-
3934
export default genSiteJson;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import path from 'path';
2+
import chokidar from 'chokidar';
3+
4+
import genSiteJson from './gen-site-json.mjs';
5+
6+
const mdPath = path.join(process.cwd(), 'content');
7+
8+
chokidar.watch(mdPath).on('change', async () => await genSiteJson());

polaris.shopify.com/src/components/ComponentGrid/ComponentGrid.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Image from '../Image';
22
import Link from 'next/link';
3-
import {getReadableStatusValue, slugify} from '../../utils/various';
3+
import {slugify} from '../../utils/various';
44
import {Status} from '../../types';
55
import styles from './ComponentGrid.module.scss';
66
import StatusBadge from '../StatusBadge';
@@ -28,6 +28,9 @@ function ComponentGridItem({
2828
status,
2929
}: ComponentGridItemProps) {
3030
const searchAttributes = useGlobalSearchResult();
31+
const statusBadge = status ? (
32+
<StatusBadge status={{value: status.value, message: status.value}} />
33+
) : null;
3134

3235
return (
3336
<li key={title} className={styles.Component} {...searchAttributes}>
@@ -47,18 +50,7 @@ function ComponentGridItem({
4750
</div>
4851
<div className={styles.ComponentDescription}>
4952
<h4>
50-
{title}
51-
{status && (
52-
<>
53-
{' '}
54-
<StatusBadge
55-
status={{
56-
value: status.value,
57-
message: getReadableStatusValue(status.value),
58-
}}
59-
/>
60-
</>
61-
)}
53+
{`${title} `} {statusBadge}
6254
</h4>
6355
<p>{description}</p>
6456
</div>

polaris.shopify.com/src/components/ComponentsPage/ComponentsPage.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import ComponentGrid from '../ComponentGrid';
22
import Layout from '../Layout';
33

4-
import components from '../../data/components.json';
4+
import siteJson from '../../../.cache/site.json';
55
import {
66
getComponentCategories,
77
stripMarkdownLinks,
88
slugify,
99
getComponentNav,
1010
} from '../../utils/various';
11-
import {Status} from '../../types';
11+
import {Status, SiteJSON} from '../../types';
1212
import styles from './ComponentsPage.module.scss';
1313
import PageMeta from '../PageMeta';
1414

15+
const pages: SiteJSON = siteJson;
16+
17+
const components = Object.keys(pages).filter((slug) =>
18+
slug.startsWith('components/'),
19+
);
20+
1521
const componentCategories = getComponentCategories();
1622
const componentNav = getComponentNav();
1723

@@ -33,25 +39,22 @@ export default function ComponentsPage() {
3339
<ComponentGrid>
3440
{components
3541
.filter(
36-
(component) => component.frontMatter.category === category,
42+
(slug) => pages[slug].frontMatter.category === category,
3743
)
38-
.map(({frontMatter, description}) => {
39-
const {title, status} = frontMatter;
44+
.map((slug) => {
45+
const {
46+
title,
47+
status,
48+
description = '',
49+
} = pages[slug].frontMatter;
4050
const url = `/components/${slugify(title)}`;
41-
let typedStatus = status
42-
? {
43-
value: status.value.toLowerCase() as Status['value'],
44-
message: status.value,
45-
}
46-
: undefined;
47-
4851
return (
4952
<ComponentGrid.Item
5053
key={title}
5154
title={title}
5255
description={stripMarkdownLinks(description)}
5356
url={url}
54-
status={typedStatus}
57+
status={status as Status}
5558
/>
5659
);
5760
})}

polaris.shopify.com/src/components/PropsTable/PropsTable.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {PropsForComponent} from '../../types';
1+
import {PropsForComponent, StatusName} from '../../types';
22
import StatusBadge from '../StatusBadge';
33
import styles from './PropsTable.module.scss';
44

@@ -36,7 +36,7 @@ function PropsTable({props: {props}}: Props) {
3636
{' '}
3737
<StatusBadge
3838
status={{
39-
value: 'information',
39+
value: StatusName.Information,
4040
message: 'Required',
4141
}}
4242
/>
@@ -47,7 +47,7 @@ function PropsTable({props: {props}}: Props) {
4747
{' '}
4848
<StatusBadge
4949
status={{
50-
value: 'deprecated',
50+
value: StatusName.Deprecated,
5151
message: 'Deprecated',
5252
}}
5353
/>

polaris.shopify.com/src/components/StatusBadge/StatusBadge.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import styles from './StatusBadge.module.scss';
44
interface Props {
55
status: Status;
66
}
7-
function StatusBadge({status}: Props) {
7+
function StatusBadge({status: {value, message}}: Props) {
88
return (
9-
<div className={styles.StatusBadge} data-value={status.value}>
10-
{status.message}
9+
<div className={styles.StatusBadge} data-value={value.toLowerCase()}>
10+
{message}
1111
</div>
1212
);
1313
}

0 commit comments

Comments
 (0)