Skip to content

Commit 1e10053

Browse files
fix(API): refactor API data compilation to be done at build time (#183)
* fix(API): refactor API data compilation to be done at build time * fix cloudflare excluding api routes from worker bundle * fix unit tests for api routes * move tests outside of pages dir so that astro doesn't try to render them * lint * limit memory usage for build
1 parent 55bb455 commit 1e10053

33 files changed

+2023
-38
lines changed

.gitignore

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ pnpm-debug.log*
2828

2929
.eslintcache
3030

31-
## Ignore content.ts
32-
src/content.ts
31+
## Ignore generated files
32+
src/content.ts
33+
src/apiIndex.json
34+
textContent/*.mdx
35+
36+
coverage/

astro.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,5 @@ export default defineConfig({
1919
}
2020
}
2121
},
22-
2322
adapter: cloudflare()
2423
});

cli/__tests__/convertToMDX.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import { convertToMDX } from '../convertToMDX.ts'
55
jest.mock('fs/promises', () => ({
66
readFile: jest.fn(),
77
writeFile: jest.fn(),
8-
access: jest.fn().mockResolvedValue(undefined), // Mock access to always resolve (file exists)
98
}))
109

1110
jest.mock('glob', () => ({
1211
glob: jest.fn(),
1312
}))
1413

14+
jest.mock('../fileExists', () => ({
15+
fileExists: jest.fn().mockResolvedValue(true), // Mock fileExists to always return true (file exists)
16+
}))
17+
1518
beforeEach(() => {
1619
jest.clearAllMocks()
1720
})

cli/__tests__/createCollectionContent.test.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ it('should call writeFile with the expected file location and content without th
6464
await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false)
6565

6666
const expectedContent = [
67-
{ name: 'test', base: '/config/dir/src/docs', pattern: '**/*.md' }
67+
{ name: 'test', base: '/config/dir/src/docs', pattern: '**/*.md', version: 'v6' }
6868
]
6969

7070
expect(writeFile).toHaveBeenCalledWith(
@@ -208,11 +208,12 @@ it('should handle content with packageName by finding package in node_modules',
208208
await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false)
209209

210210
const expectedContent = [
211-
{
211+
{
212212
base: '/config/dir/node_modules/@patternfly/react-core',
213-
name: 'test',
214-
packageName: '@patternfly/react-core',
215-
pattern: '**/*.md'
213+
name: 'test',
214+
packageName: '@patternfly/react-core',
215+
pattern: '**/*.md',
216+
version: 'v6'
216217
}
217218
]
218219

@@ -242,11 +243,12 @@ it('should handle content with packageName when package is not found locally but
242243
await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false)
243244

244245
const expectedContent = [
245-
{
246+
{
246247
base: '/config/node_modules/@patternfly/react-core',
247-
name: 'test',
248-
packageName: '@patternfly/react-core',
249-
pattern: '**/*.md'
248+
name: 'test',
249+
packageName: '@patternfly/react-core',
250+
pattern: '**/*.md',
251+
version: 'v6'
250252
}
251253
]
252254

@@ -274,11 +276,12 @@ it('should handle content with packageName when package is not found anywhere',
274276
await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false)
275277

276278
const expectedContent = [
277-
{
279+
{
278280
base: null,
279-
name: 'test',
280-
packageName: '@patternfly/react-core',
281-
pattern: '**/*.md'
281+
name: 'test',
282+
packageName: '@patternfly/react-core',
283+
pattern: '**/*.md',
284+
version: 'v6'
282285
}
283286
]
284287

@@ -307,12 +310,13 @@ it('should handle mixed content with both base and packageName entries', async (
307310
await createCollectionContent('/foo/', '/config/dir/pf-docs.config.mjs', false)
308311

309312
const expectedContent = [
310-
{ name: 'docs', base: '/config/dir/src/docs', pattern: '**/*.md' },
311-
{
313+
{ name: 'docs', base: '/config/dir/src/docs', pattern: '**/*.md', version: 'v6' },
314+
{
312315
base: '/config/dir/node_modules/@patternfly/react-core',
313-
name: 'components',
314-
packageName: '@patternfly/react-core',
315-
pattern: '**/*.md'
316+
name: 'components',
317+
packageName: '@patternfly/react-core',
318+
pattern: '**/*.md',
319+
version: 'v6'
316320
}
317321
]
318322

cli/__tests__/fileExists.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { access } from 'fs/promises'
2+
import { fileExists } from '../fileExists'
3+
4+
jest.mock('fs/promises', () => ({
5+
access: jest.fn(),
6+
}))
7+
8+
it('returns true when file exists', async () => {
9+
;(access as jest.Mock).mockResolvedValue(undefined)
10+
11+
const result = await fileExists('/path/to/existing/file.txt')
12+
13+
expect(result).toBe(true)
14+
expect(access).toHaveBeenCalledWith('/path/to/existing/file.txt')
15+
})
16+
17+
it('returns false when file does not exist', async () => {
18+
;(access as jest.Mock).mockRejectedValue(
19+
new Error('ENOENT: no such file or directory'),
20+
)
21+
22+
const result = await fileExists('/path/to/nonexistent/file.txt')
23+
24+
expect(result).toBe(false)
25+
expect(access).toHaveBeenCalledWith('/path/to/nonexistent/file.txt')
26+
})
27+
28+
it('returns false when access throws any error', async () => {
29+
;(access as jest.Mock).mockRejectedValue(new Error('Permission denied'))
30+
31+
const result = await fileExists('/path/to/file.txt')
32+
33+
expect(result).toBe(false)
34+
expect(access).toHaveBeenCalledWith('/path/to/file.txt')
35+
})

cli/cli.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { symLinkConfig } from './symLinkConfig.js'
1212
import { buildPropsData } from './buildPropsData.js'
1313
import { hasFile } from './hasFile.js'
1414
import { convertToMDX } from './convertToMDX.js'
15-
import { mkdir } from 'fs/promises'
15+
import { mkdir, copyFile } from 'fs/promises'
16+
import { fileExists } from './fileExists.js'
1617

1718
const currentDir = process.cwd()
1819
const config = await getConfig(`${currentDir}/pf-docs.config.mjs`)
@@ -86,6 +87,26 @@ async function transformMDContentToMDX() {
8687
}
8788
}
8889

90+
async function initializeApiIndex() {
91+
const templateIndexPath = join(astroRoot, 'cli', 'templates', 'apiIndex.json')
92+
const targetIndexPath = join(astroRoot, 'src', 'apiIndex.json')
93+
94+
const indexExists = await fileExists(targetIndexPath)
95+
96+
// early return if the file exists from a previous build
97+
if (indexExists) {
98+
console.log('apiIndex.json already exists, skipping initialization')
99+
return
100+
}
101+
102+
try {
103+
await copyFile(templateIndexPath, targetIndexPath)
104+
console.log('Initialized apiIndex.json')
105+
} catch (e: any) {
106+
console.error('Error copying apiIndex.json template:', e)
107+
}
108+
}
109+
89110
async function buildProject(): Promise<DocsConfig | undefined> {
90111
await updateContent(program)
91112
await generateProps(program, true)
@@ -103,6 +124,7 @@ async function buildProject(): Promise<DocsConfig | undefined> {
103124
return config
104125
}
105126

127+
await initializeApiIndex()
106128
await transformMDContentToMDX()
107129

108130
build({
@@ -172,6 +194,7 @@ program.command('init').action(async () => {
172194

173195
program.command('start').action(async () => {
174196
await updateContent(program)
197+
await initializeApiIndex()
175198

176199
// if a props file hasn't been generated yet, but the consumer has propsData, it will cause a runtime error so to
177200
// prevent that we're just creating a props file regardless of what they say if one doesn't exist yet

cli/convertToMDX.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { readFile, writeFile, access } from 'fs/promises'
1+
import { readFile, writeFile } from 'fs/promises'
22
import { glob } from 'glob'
33
import path from 'path'
4+
import { fileExists } from './fileExists.js'
45

56
function handleTsExamples(content: string): string {
67
//regex link: https://regexr.com/8f0bu
@@ -57,10 +58,6 @@ function convertCommentsToMDX(content: string): string {
5758
)
5859
}
5960

60-
async function fileExists(file: string): Promise<boolean> {
61-
return access(file).then(() => true).catch(() => false)
62-
}
63-
6461
async function processFile(file: string): Promise<void> {
6562
const exists = await fileExists(file)
6663

cli/createCollectionContent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export async function createCollectionContent(
8080
verboseModeLog('repoRootDir: ', repoRootDir, '\n')
8181

8282
const contentWithAbsolutePaths = content.map((contentEntry) => {
83+
const version = contentEntry.version || 'v6'
84+
8385
if (contentEntry.base) {
8486
const absoluteBase = resolve(configDir, contentEntry.base)
8587

@@ -89,6 +91,7 @@ export async function createCollectionContent(
8991
return {
9092
...contentEntry,
9193
base: absoluteBase,
94+
version
9295
}
9396
}
9497

@@ -103,6 +106,7 @@ export async function createCollectionContent(
103106
return {
104107
...contentEntry,
105108
base: null,
109+
version
106110
}
107111
}
108112

@@ -116,6 +120,7 @@ export async function createCollectionContent(
116120
return {
117121
base: packagePath,
118122
...contentEntry,
123+
version
119124
}
120125
})
121126

cli/fileExists.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { access } from 'fs/promises'
2+
3+
export async function fileExists(file: string): Promise<boolean> {
4+
return access(file)
5+
.then(() => true)
6+
.catch(() => false)
7+
}

cli/getConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export interface CollectionDefinition {
33
base?: string
44
packageName?: string
5+
version?: string
56
pattern: string
67
name: string
78
}

0 commit comments

Comments
 (0)