Skip to content

Commit d955cc0

Browse files
authored
refactor: merge resume into site (#8)
1 parent c2e2fc2 commit d955cc0

34 files changed

+875
-195
lines changed

.vscode/settings.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
{
22
"cSpell.words": [
3+
"alexa",
34
"astro",
45
"astrojs",
6+
"automocking",
7+
"bearimy",
58
"fontsource",
9+
"hvac",
610
"iconify",
11+
"ihop",
712
"mcous",
13+
"mechatronics",
14+
"opentrons",
815
"preact",
16+
"preconfigured",
917
"rehype",
1018
"résumé",
1119
"sonarjs",
20+
"tada",
1221
"tailwindcss",
13-
"unocss"
22+
"typechecked",
23+
"unocss",
24+
"viam",
25+
"viam’s",
26+
"vite",
27+
"vitest"
1428
],
1529
"typescript.tsdk": "node_modules/typescript/lib",
1630
"eslint.validate": ["astro"]

astro.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import { defineConfig } from 'astro/config'
55
import remarkEmoji from 'remark-emoji'
66
import unoCss from 'unocss/astro'
77

8+
import { resumePDF } from './etc/resume-pdf'
9+
810
export default defineConfig({
911
site: 'https://michael.cousins.io',
1012
integrations: [
1113
mdx({ remarkPlugins: [[remarkEmoji, { accessible: 'true' }]] }),
1214
sitemap(),
1315
unoCss({ injectReset: true }),
1416
preact(),
17+
resumePDF(),
1518
],
1619
})

etc/resume-pdf.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { spawn } from 'node:child_process'
2+
import path from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
5+
import { chromium } from 'playwright'
6+
7+
const SOURCE_PATHNAME = '/resume/'
8+
const PDF_FILENAME = 'michael-cousins.pdf'
9+
10+
/**
11+
* Astro middleware to build and serve the resume PDF.
12+
*
13+
* @type {() => import('astro').AstroIntegration}
14+
*/
15+
export const resumePDF = () => {
16+
let baseURL = ''
17+
18+
return {
19+
name: 'resume-pdf',
20+
hooks: {
21+
'astro:config:done': ({ config }) => {
22+
baseURL = `http://localhost:${config.server.port}/`
23+
},
24+
'astro:server:setup': ({ server }) => {
25+
server.middlewares.use(`/${PDF_FILENAME}`, (_, response, next) => {
26+
renderResumePDF(baseURL)
27+
.then((contents) => {
28+
response.setHeader('Content-Type', 'application/pdf')
29+
response.end(contents)
30+
})
31+
.catch((error) => next(error))
32+
})
33+
},
34+
'astro:build:done': async ({ dir }) => {
35+
const outputFile = path.join(fileURLToPath(dir), PDF_FILENAME)
36+
37+
await buildResumePDF(baseURL, outputFile)
38+
},
39+
},
40+
}
41+
}
42+
43+
/**
44+
* Render the resume PDF to a buffer or file path
45+
*
46+
* @param {string} baseURL URL of the server
47+
* @param {string} [outputFile] output file, if needed
48+
* @returns buffer of PDF contents
49+
*/
50+
async function renderResumePDF(baseURL, outputFile) {
51+
const browser = await chromium.launch()
52+
const page = await browser.newPage({ baseURL })
53+
const pdfOptions = outputFile ? { path: outputFile } : {}
54+
55+
await page.goto(SOURCE_PATHNAME)
56+
const result = await page.pdf({ tagged: true, ...pdfOptions })
57+
await browser.close()
58+
59+
return result
60+
}
61+
62+
/**
63+
* Build the resume PDF to a file from the preview server.
64+
*
65+
* @param {string} baseURL URL of the server
66+
* @param {string} [outputFile] output file, if needed
67+
*/
68+
async function buildResumePDF(baseURL, outputFile) {
69+
const { end } = await launchPreviewServer()
70+
71+
try {
72+
await renderResumePDF(baseURL, outputFile)
73+
} finally {
74+
end()
75+
}
76+
}
77+
78+
/**
79+
* Launch the preview server.
80+
*
81+
* @returns {Promise<{end: () => void}>} the URL of the server
82+
*/
83+
async function launchPreviewServer() {
84+
const server = spawn('pnpm', ['exec', 'astro', 'preview'])
85+
const end = () => server.kill()
86+
87+
return new Promise((resolve, reject) => {
88+
server.stdout.on('data', () => {
89+
resolve({ end })
90+
})
91+
92+
server.stdout.on('error', (error) => {
93+
end()
94+
reject(error)
95+
})
96+
})
97+
}

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
"author": "Michael Cousins <michael@cousins.io> (https://michael.cousins.io)",
1111
"type": "module",
1212
"scripts": {
13-
"all": "astro sync && concurrently -g pnpm:lint pnpm:check pnpm:coverage pnpm:build",
13+
"all": "astro sync && pnpm run lint && pnpm run check && pnpm run coverage && pnpm run build",
1414
"build": "astro build",
1515
"check": "astro check",
1616
"coverage": "vitest run --coverage",
1717
"format": "prettier --write . && eslint --fix .",
1818
"lint": "prettier --check . && eslint .",
19+
"prepare": "playwright install chromium",
1920
"preview": "astro build && astro preview",
2021
"start": "astro dev",
2122
"test": "vitest"
@@ -24,6 +25,10 @@
2425
"parserOptions": {
2526
"project": "./tsconfig.json"
2627
},
28+
"env": {
29+
"browser": true,
30+
"node": true
31+
},
2732
"extends": [
2833
"@mcous/eslint-config",
2934
"@unocss",
@@ -40,6 +45,8 @@
4045
"varsIgnorePattern": "^_"
4146
}
4247
],
48+
"promise/always-return": "off",
49+
"promise/no-callback-in-promise": "off",
4350
"simple-import-sort/imports": "error",
4451
"simple-import-sort/exports": "error"
4552
},
@@ -95,7 +102,6 @@
95102
"@unocss/eslint-config": "^0.58.5",
96103
"@unocss/preset-icons": "^0.58.5",
97104
"@vitest/coverage-istanbul": "^1.3.1",
98-
"concurrently": "^8.2.2",
99105
"eslint": "^8.57.0",
100106
"eslint-config-prettier": "^9.1.0",
101107
"eslint-plugin-astro": "^0.31.4",
@@ -108,6 +114,7 @@
108114
"eslint-plugin-unicorn": "^51.0.1",
109115
"eslint-plugin-vitest": "^0.3.22",
110116
"jsdom": "^24.0.0",
117+
"playwright": "^1.42.1",
111118
"prettier": "^3.2.5",
112119
"prettier-plugin-astro": "^0.13.0",
113120
"remark-emoji": "^4.0.1",

0 commit comments

Comments
 (0)