|
1 | | -import puppeteer from 'puppeteer'; |
2 | | -import matter from 'gray-matter'; |
3 | | -import {execa} from 'execa'; |
4 | | -import path from 'path'; |
5 | | - |
6 | | -import {existsSync} from 'fs'; |
7 | | -import {writeFile, readFile, mkdir, rm} from 'fs/promises'; |
8 | | - |
9 | | -const imgDir = path.join(process.cwd(), 'public/og-images'); |
10 | | - |
11 | | -const shopifyLogo = `<svg height="30" width="30" viewBox="0 0 109.5 124.5" xmlns="http://www.w3.org/2000/svg" fill="#fff"> |
12 | | - <path d="M74.7,14.8c0,0-1.4,0.4-3.7,1.1c-0.4-1.3-1-2.8-1.8-4.4c-2.6-5-6.5-7.7-11.1-7.7c0,0,0,0,0,0 |
13 | | - c-0.3,0-0.6,0-1,0.1c-0.1-0.2-0.3-0.3-0.4-0.5c-2-2.2-4.6-3.2-7.7-3.1c-6,0.2-12,4.5-16.8,12.2c-3.4,5.4-6,12.2-6.7,17.5 |
14 | | - c-6.9,2.1-11.7,3.6-11.8,3.7c-3.5,1.1-3.6,1.2-4,4.5C9.1,40.7,0,111.2,0,111.2l75.6,13.1V14.6C75.2,14.7,74.9,14.7,74.7,14.8z |
15 | | - M57.2,20.2c-4,1.2-8.4,2.6-12.7,3.9c1.2-4.7,3.6-9.4,6.4-12.5c1.1-1.1,2.6-2.4,4.3-3.2C56.9,12,57.3,16.9,57.2,20.2z M49.1,4.3 |
16 | | - c1.4,0,2.6,0.3,3.6,0.9c-1.6,0.8-3.2,2.1-4.7,3.6c-3.8,4.1-6.7,10.5-7.9,16.6c-3.6,1.1-7.2,2.2-10.5,3.2 |
17 | | - C31.7,19.1,39.8,4.6,49.1,4.3z M37.4,59.3c0.4,6.4,17.3,7.8,18.3,22.9c0.7,11.9-6.3,20-16.4,20.6c-12.2,0.8-18.9-6.4-18.9-6.4 |
18 | | - l2.6-11c0,0,6.7,5.1,12.1,4.7c3.5-0.2,4.8-3.1,4.7-5.1c-0.5-8.4-14.3-7.9-15.2-21.7C23.8,51.8,31.4,40.1,48.2,39 |
19 | | - c6.5-0.4,9.8,1.2,9.8,1.2l-3.8,14.4c0,0-4.3-2-9.4-1.6C37.4,53.5,37.3,58.2,37.4,59.3z M61.2,19c0-3-0.4-7.3-1.8-10.9 |
20 | | - c4.6,0.9,6.8,6,7.8,9.1C65.4,17.7,63.4,18.3,61.2,19z"/> |
21 | | - <path d="M78.1,123.9l31.4-7.8c0,0-13.5-91.3-13.6-91.9c-0.1-0.6-0.6-1-1.1-1c-0.5,0-9.3-0.2-9.3-0.2s-5.4-5.2-7.4-7.2 |
22 | | - V123.9z"/> |
23 | | -</svg> |
24 | | -`; |
25 | | - |
26 | | -const defaultImage = `<svg viewBox="0 0 99 99" fill="none" xmlns="http://www.w3.org/2000/svg"> |
27 | | - <path d="M98.9999 49.5C98.9999 76.838 76.838 98.9999 49.5 98.9999C22.1619 98.9999 0 76.838 0 49.5C0 22.1619 22.1619 0 49.5 0C76.838 0 98.9999 22.1619 98.9999 49.5Z" fill="#fff"/> |
28 | | - <path d="M99.0001 49.6709C99.0001 76.9144 76.9149 98.9996 49.6714 98.9996C49.6714 71.7561 71.7566 49.6709 99.0001 49.6709Z" fill="#fff"/> |
29 | | - <path d="M49.5 0C49.5 27.3381 27.3381 49.5 0 49.5C27.3381 49.5 49.5 71.6618 49.5 98.9999C49.5 71.6618 71.6618 49.5 98.9999 49.5C71.6618 49.5 49.5 27.3381 49.5 0Z" fill="#000"/> |
30 | | -</svg> |
31 | | -`; |
32 | | - |
33 | | -const capitalizeFirstLetter = (value) => { |
34 | | - return value.charAt(0).toUpperCase() + value.slice(1); |
35 | | -}; |
36 | | - |
37 | | -const generateHTML = async (url, slug) => { |
38 | | - const title = capitalizeFirstLetter(slug || '') |
39 | | - .replace('.png', '') |
40 | | - .replace(/-/g, ' '); |
41 | | - |
42 | | - let htmlImg = `<div class="default-icon">${defaultImage}</div>`; |
43 | | - |
44 | | - if (url.startsWith('/components/')) { |
45 | | - const imgPath = path.join(process.cwd(), `public/images${url}.png`); |
46 | | - const image = await readFile(imgPath); |
47 | | - const base64 = Buffer.from(image).toString('base64'); |
48 | | - htmlImg = `<img src="data:image/png;base64,${base64}" class="component-image" />`; |
49 | | - } |
50 | | - |
51 | | - if (url.startsWith('/foundations/')) { |
52 | | - const mdFilePath = path.join(process.cwd(), `content${url}/index.md`); |
53 | | - const markdownContent = await readFile(mdFilePath, 'utf-8'); |
54 | | - const {data} = matter(markdownContent); |
55 | | - if (!data.icon) return; |
56 | | - const iconFilePath = path.join( |
57 | | - process.cwd(), |
58 | | - `../polaris-icons/dist/svg/${data.icon}.svg`, |
59 | | - ); |
60 | | - const iconData = await readFile(iconFilePath); |
61 | | - |
62 | | - htmlImg = `<div class="polaris-icon">${iconData}</div>`; |
63 | | - } |
64 | | - |
65 | | - const html = ` |
66 | | - <style> |
67 | | - @import url('https://fonts.googleapis.com/css2?family=Inter:wght@500'); |
68 | | -
|
69 | | - * { |
70 | | - margin: 0; |
71 | | - padding: 0; |
72 | | - box-sizing: border-box; |
73 | | - } |
74 | | -
|
75 | | - body { |
76 | | - font-family: inter; |
77 | | - width: 1200px; |
78 | | - height: 630px; |
79 | | - padding: 60px; |
80 | | - background: #000; |
81 | | - color: #fff; |
82 | | - perspective: 1800px; |
83 | | - transform-style: preserve-3d; |
84 | | - } |
85 | | -
|
86 | | - h1 { |
87 | | - font-size: 80px; |
88 | | - font-weight: 500; |
89 | | - letter-spacing: -0.01rem; |
90 | | - max-width: 520px; |
91 | | - } |
92 | | -
|
93 | | - .component-image { |
94 | | - position: absolute; |
95 | | - left: 100px; |
96 | | - top: 50%; |
97 | | - transform: translate3d(0, -50%, 0); |
98 | | - filter: contrast(1.1) invert(1) saturate(0) hue-rotate(180deg); |
99 | | - mix-blend-mode: lighten; |
100 | | - opacity: .33; |
101 | | - height: auto; |
102 | | - transform: rotateY(-60deg) translateY(-50%) scale(.9); |
103 | | - } |
104 | | -
|
105 | | - .polaris-icon, |
106 | | - .default-icon { |
107 | | - position: absolute; |
108 | | - left: 600px; |
109 | | - top: 50%; |
110 | | - transform: translate3d(0, -50%, 0); |
111 | | - opacity: .2; |
112 | | - } |
113 | | -
|
114 | | - .polaris-icon svg, |
115 | | - .default-icon svg { |
116 | | - width: 400px; |
117 | | - height: 400px; |
118 | | - } |
119 | | -
|
120 | | - .default-icon { |
121 | | - filter: brightness(1000%); |
122 | | - } |
123 | | -
|
124 | | - .polaris-icon { |
125 | | - filter: invert(); |
126 | | - } |
127 | | -
|
128 | | - .logo { |
129 | | - position: absolute; |
130 | | - bottom: 55px; |
131 | | - display: flex; |
132 | | - align-items: center; |
133 | | - gap: 12px; |
134 | | - opacity: .5; |
135 | | - font-size: 24px; |
136 | | - font-weight: 500; |
137 | | - } |
138 | | -</style> |
139 | | -
|
140 | | -<body> |
141 | | - <h1>${title}</h1> |
142 | | - ${htmlImg} |
143 | | - <div class="logo"> |
144 | | - ${shopifyLogo} Polaris |
145 | | - </div> |
146 | | -</body>`; |
147 | | - |
148 | | - return Buffer.from(html).toString('base64'); |
149 | | -}; |
150 | | - |
151 | | -const getPNG = async (url, browser) => { |
152 | | - const slug = url === '' ? 'home' : url.split('/').at(-1); |
153 | | - const imgPath = |
154 | | - url.split('/').length > 1 ? url.split('/').slice(0, -1).join('/') : url; |
155 | | - const html = await generateHTML(url, slug); |
156 | | - const encodedUrl = `data:text/html;charset=utf-8;base64,${html}`; |
157 | | - const page = await browser.newPage(); |
158 | | - await page.goto(encodedUrl, {waitUntil: 'networkidle0'}); |
159 | | - const image = await page.screenshot(); |
160 | | - if (!existsSync(`${imgDir}${imgPath}`)) { |
161 | | - await mkdir(`${imgDir}${imgPath}`, {recursive: true}); |
162 | | - } |
163 | | - await writeFile(`${imgDir}${imgPath}/${slug}.png`, image); |
164 | | -}; |
| 1 | +import genSiteMap from './gen-site-map.mjs'; |
| 2 | +import genSiteJson from './gen-site-json.mjs'; |
| 3 | +import genOgImages from './gen-og-images.mjs'; |
165 | 4 |
|
166 | 5 | const genAssets = async () => { |
167 | | - const outputFile = 'public/sitemap.xml'; |
168 | | - const outputPath = path.join(process.cwd(), `public/sitemap.xml`); |
169 | | - |
170 | | - const nextBin = path.join(process.cwd(), 'node_modules/.bin/next'); |
171 | | - const server = execa(nextBin, ['dev']); |
172 | | - |
173 | | - const {stdout} = await execa('npx', [ |
174 | | - 'get-site-urls', |
175 | | - 'http://localhost:3000', |
176 | | - `--output=${outputFile}`, |
177 | | - '--alias=https://polaris.shopify.com', |
178 | | - ]); |
179 | | - console.log(stdout); |
180 | | - |
181 | | - await server.kill(); |
182 | | - |
183 | | - if (existsSync(imgDir)) await rm(imgDir, {recursive: true}); |
184 | | - await mkdir(imgDir, {recursive: true}); |
185 | | - |
186 | | - const sitemap = await readFile(outputPath, 'utf-8'); |
187 | | - const urls = sitemap |
188 | | - .match(/loc>[^<]+/gi) |
189 | | - .map((match) => match.replace('loc>https://polaris.shopify.com', '')); |
190 | | - |
191 | | - const browser = await puppeteer.launch({ |
192 | | - defaultViewport: {width: 1200, height: 630}, |
193 | | - args: ['--no-sandbox', '--disable-setuid-sandbox'], |
194 | | - }); |
195 | | - |
196 | | - const generateImages = urls.map((url) => getPNG(url, browser)); |
197 | | - await Promise.all(generateImages); |
198 | | - |
199 | | - await browser.close(); |
200 | | - console.log(`✅ Created ${urls.length} og-images from sitemap`); |
| 6 | + await Promise.all([genSiteMap(), genSiteJson()]); |
| 7 | + await genOgImages(); |
201 | 8 | }; |
202 | 9 |
|
203 | 10 | await genAssets(); |
0 commit comments