Skip to content

Commit 9643cbb

Browse files
committed
chore: working on build script
1 parent 4f3f15a commit 9643cbb

File tree

5 files changed

+161
-110
lines changed

5 files changed

+161
-110
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,5 @@ dist
128128
.yarn/build-state.yml
129129
.yarn/install-state.gz
130130
.pnp.*
131+
132+
/dist/

biome.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"correctness": {
1717
"all": true,
1818
"noUnusedImports": "warn"
19+
},
20+
"suspicious": {
21+
"noExplicitAny": "off"
1922
}
2023
},
2124
"ignore": ["coverage/", "dist/", "node_modules/", "docs/"]

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
"homepage": "https://github.com/FullStackBulletin/fullstack-books#readme",
1515
"devDependencies": {
1616
"@biomejs/biome": "1.6.1",
17+
"@types/markdown-it": "^13.0.7",
1718
"@types/node": "^20.11.28",
1819
"@types/turndown": "^5.0.4",
1920
"cheerio": "1.0.0-rc.12",
21+
"markdown-it": "^14.1.0",
2022
"mkdirp": "^3.0.1",
2123
"slugify": "^1.6.6",
2224
"turndown": "^7.1.3",

pnpm-lock.yaml

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/build.ts

Lines changed: 98 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,75 @@
1-
import { writeFile, readFile, copyFile } from 'node:fs/promises'
1+
import { cp, readFile, writeFile } from 'node:fs/promises'
22
import { dirname, join } from 'node:path'
33
import { fileURLToPath } from 'node:url'
4+
// biome-ignore lint/nursery/noUndeclaredDependencies: <explanation>
5+
import markdownit from 'markdown-it'
46
import { mkdirp } from 'mkdirp'
57
import slugify from 'slugify'
68
import { parse } from 'yaml'
7-
import quotes, { type Quote, type Author, type AuthorDescription, type RawQuote, type AuthorWithQuotes } from '../src/quotes.js'
89

9-
const REPO_URL = 'https://github.com/FullStackBulletin/tech-quotes'
10-
const GH_PAGES_URL = 'https://fullStackbulletin.github.io/tech-quotes'
10+
const slug = slugify.default
11+
const md = markdownit()
12+
const REPO_URL = 'https://github.com/FullStackBulletin/fullstack-books'
13+
const GH_PAGES_URL = 'https://fullStackbulletin.github.io/fullstack-books'
1114
const baseUrl = process.env.BASE_URL ?? GH_PAGES_URL
1215

13-
const currentDir = dirname(fileURLToPath(import.meta.url))
14-
const destPath: string = join(currentDir, '..', 'dist')
15-
const quotesPath: string = join(currentDir, '..', 'dist', 'quotes')
16-
const authorsPath: string = join(currentDir, '..', 'dist', 'authors')
16+
const __dirname = dirname(fileURLToPath(import.meta.url))
17+
const destPath = join(__dirname, '..', 'dist')
18+
const booksPath = join(__dirname, '..', 'dist', 'books')
19+
const authorsPath = join(__dirname, '..', 'dist', 'authors')
20+
const srcCoversPath = join(__dirname, '..', 'src', 'covers')
21+
const coversPath = join(__dirname, '..', 'dist', 'covers')
1722

1823
// Creates the `dest` and `dest/quotes` folders if they don't exist
19-
await Promise.all([
20-
mkdirp(destPath),
21-
mkdirp(quotesPath),
22-
mkdirp(authorsPath)
23-
])
24+
await Promise.all([mkdirp(destPath), mkdirp(booksPath), mkdirp(authorsPath)])
25+
await cp(srcCoversPath, coversPath)
26+
console.log(`Copied ${srcCoversPath} to ${coversPath}`)
27+
28+
// load and parse raw data from source file
29+
const sourcePath = join(__dirname, '..', 'src', 'books.yml')
30+
const rawData = parse(await readFile(sourcePath, 'utf-8'))
31+
32+
function mapAuthor(author: string) {
33+
const authorSlug = slug(author, {
34+
lower: true,
35+
strict: true,
36+
})
37+
38+
return {
39+
name: author,
40+
slug: authorSlug,
41+
url: `${baseUrl}/authors/authorSlug.json`,
42+
}
43+
}
44+
45+
const books = rawData.map(
46+
(book: {
47+
title: string
48+
description: string
49+
authors: string[]
50+
cover: string
51+
}) => {
52+
return {
53+
...book,
54+
url: `${baseUrl}/books/${slug(book.title)}.json`,
55+
cover: `${baseUrl}/covers/${book.cover}`,
56+
descriptionHtml: md(book.description),
57+
authors: book.authors.map(mapAuthor),
58+
}
59+
},
60+
)
61+
62+
const authors = []
63+
const booksByAuthor: Record<string, any> = {}
64+
for (const book of books) {
65+
for (const author of book.authors) {
66+
if (!booksByAuthor[author.slug]) {
67+
booksByAuthor[author.slug] = []
68+
authors.push(author)
69+
}
70+
booksByAuthor[author.slug].push(book)
71+
}
72+
}
2473

2574
// Creates an index.html file that redirects to the GitHub repo
2675
const index = `<html>
@@ -41,118 +90,57 @@ const fourOhFour = `<html>
4190
<body>Not found</body>
4291
</html>`
4392

93+
// create books files
4494
await writeFile(`${destPath}/404.html`, fourOhFour)
4595
console.log(`Written ${destPath}/404.html`)
4696

47-
const stats = {
48-
total: quotes.length,
49-
all: `${baseUrl}/quotes/all.json`,
50-
first: `${baseUrl}/quotes/0.json`,
51-
last: `${baseUrl}/quotes/${quotes.length - 1}.json`,
52-
urlPrefix: `${baseUrl}/quotes`
53-
}
54-
55-
await writeFile(`${quotesPath}/stats.json`, JSON.stringify(stats, null, 2))
56-
console.log(`Written ${quotesPath}/stats.json`)
57-
58-
// Creates a JSON file for each quote and an all.json file with all the quotes
59-
function mapQuote (id: string, quote: RawQuote): Quote {
60-
const idAsNumber = Number(id)
61-
return {
62-
id: idAsNumber,
63-
text: quote.text,
64-
author: makeAuthor(quote.authorName, quote.authorDescription, quote.authorWiki),
65-
url: `${baseUrl}/quotes/${idAsNumber}.json`
66-
}
67-
}
68-
69-
function makeAuthor (name: string, description: AuthorDescription, wiki?: `https://en.wikipedia.org/wiki/${string}`): Author {
70-
const slug = slugify.default(name, { lower: true, strict: true })
71-
72-
return {
73-
id: slug,
74-
name,
75-
description,
76-
wiki,
77-
url: `${baseUrl}/authors/${slug}.json`
78-
}
79-
}
80-
81-
function removeAuthorFromQuote (quote: Quote): Omit<Quote, 'author'> {
82-
const { author, ...rest } = quote
83-
return rest
84-
}
85-
86-
const all = {
87-
metadata: {
88-
total: quotes.length,
89-
first: 0,
90-
last: quotes.length - 1
91-
},
92-
quotes: Object.entries(quotes).map(([id, quote]) => mapQuote(id, quote))
97+
const bookStats = {
98+
total: books.length,
99+
all: `${baseUrl}/books/all.json`,
100+
ids: `${baseUrl}/books/ids.json`,
101+
urlPrefix: `${baseUrl}/books`,
93102
}
94103

95-
await writeFile(`${quotesPath}/all.json`, JSON.stringify(all, null, 2))
96-
console.log(`Written ${quotesPath}/all.json`)
97-
98-
// As it goes through the various quotes starts to accumulate authors and quotes
99-
const authorsWithQuotes = new Map<string, AuthorWithQuotes>()
104+
await writeFile(`${booksPath}/stats.json`, JSON.stringify(bookStats, null, 2))
105+
console.log(`Written ${booksPath}/stats.json`)
100106

101-
for (const quote of all.quotes) {
102-
const dest = join(quotesPath, `${String(quote.id)}.json`)
103-
await writeFile(dest, JSON.stringify(quote, null, 2))
104-
105-
const authorEntry = authorsWithQuotes.get(quote.author.id)
106-
if (typeof authorEntry !== 'undefined') {
107-
authorEntry.quotes.push(removeAuthorFromQuote(quote))
108-
} else {
109-
authorsWithQuotes.set(quote.author.id, {
110-
...quote.author,
111-
quotes: [removeAuthorFromQuote(quote)]
112-
})
113-
}
107+
const bookIds = books.map((book: { slug: string }) => book.slug)
108+
await writeFile(`${booksPath}/ids.json`, JSON.stringify(bookIds, null, 2))
109+
console.log(`Written ${booksPath}/ids.json`)
114110

115-
console.log(`Written ${String(dest)}`)
116-
}
111+
await writeFile(`${booksPath}/all.json`, JSON.stringify(books, null, 2))
112+
console.log(`Written ${booksPath}/all.json`)
117113

118-
// persists authors
119-
let totalAuthors = 0
120-
for (const author of authorsWithQuotes.values()) {
121-
const dest = join(authorsPath, `${author.id}.json`)
122-
await writeFile(dest, JSON.stringify(author, null, 2))
123-
console.log(`Written ${String(dest)}`)
124-
totalAuthors++
114+
for (const book of books) {
115+
const dest = join(booksPath, `${book.slug}.json`)
116+
await writeFile(dest, JSON.stringify(book, null, 2))
117+
console.log(`Written ${dest}`)
125118
}
126119

127-
// creates stats.json for authors
128-
const authorsStats = {
129-
total: totalAuthors,
120+
// create authors files
121+
const authorStats = {
122+
total: authors.length,
130123
all: `${baseUrl}/authors/all.json`,
131-
urlPrefix: `${baseUrl}/authors`
124+
ids: `${baseUrl}/authors/ids.json`,
125+
urlPrefix: `${baseUrl}/authors`,
132126
}
133127

134-
await writeFile(`${authorsPath}/stats.json`, JSON.stringify(authorsStats, null, 2))
128+
await writeFile(
129+
`${authorsPath}/stats.json`,
130+
JSON.stringify(authorStats, null, 2),
131+
)
135132
console.log(`Written ${authorsPath}/stats.json`)
136133

137-
// Create all.json for authors
138-
const allAuthors = {
139-
metadata: {
140-
total: totalAuthors
141-
},
142-
authors: [...authorsWithQuotes.keys()]
143-
}
134+
const authorIds = authors.map((author: { slug: string }) => author.slug)
135+
await writeFile(`${authorsPath}/ids.json`, JSON.stringify(authorIds, null, 2))
136+
console.log(`Written ${destPath}/ids.json`)
144137

145-
await writeFile(`${authorsPath}/all.json`, JSON.stringify(allAuthors, null, 2))
138+
await writeFile(`${authorsPath}/all.json`, JSON.stringify(authors, null, 2))
146139
console.log(`Written ${authorsPath}/all.json`)
147140

148-
// copy open api file and converts it to json
149-
const openApiPath = join(currentDir, '..', 'src', 'openapi.yml')
150-
const openApiDestYaml = join(destPath, 'openapi.yml')
151-
await copyFile(openApiPath, openApiDestYaml)
152-
console.log(`Written ${openApiDestYaml}`)
153-
154-
const openApiYaml = await readFile(openApiPath, 'utf-8')
155-
const openApiJson = parse(openApiYaml)
156-
const openApiDestJson = join(destPath, 'openapi.json')
157-
await writeFile(openApiDestJson, JSON.stringify(openApiJson, null, 2))
158-
console.log(`Written ${openApiDestJson}`)
141+
for (const author of authors) {
142+
const authorWithBooks = { ...author, books: booksByAuthor[author.slug] }
143+
const dest = join(authorsPath, `${author.slug}.json`)
144+
await writeFile(dest, JSON.stringify(authorWithBooks, null, 2))
145+
console.log(`Written ${dest}`)
146+
}

0 commit comments

Comments
 (0)