@@ -9,6 +9,57 @@ import { difference, isEqual } from 'lodash-es'
9
9
10
10
import { allVersions } from '#src/versions/lib/all-versions.js'
11
11
import getApplicableVersions from '#src/versions/lib/get-applicable-versions.js'
12
+ import type { MarkdownFrontmatter } from '@/types'
13
+
14
+ // Type definitions - extending existing type to add missing fields and make most fields optional
15
+ type FrontmatterData = Partial < MarkdownFrontmatter > & {
16
+ autogenerated ?: string
17
+ [ key : string ] : any
18
+ }
19
+
20
+ type SourceContentItem = {
21
+ data : FrontmatterData
22
+ content : string
23
+ }
24
+
25
+ type SourceContent = {
26
+ [ key : string ] : SourceContentItem
27
+ }
28
+
29
+ type IndexOrder = {
30
+ [ key : string ] : {
31
+ startsWith ?: string [ ]
32
+ }
33
+ }
34
+
35
+ type UpdateContentDirectoryOptions = {
36
+ targetDirectory : string
37
+ sourceContent : SourceContent
38
+ frontmatter : FrontmatterData
39
+ indexOrder ?: IndexOrder
40
+ }
41
+
42
+ type UpdateDirectoryOptions = {
43
+ rootDirectoryOnly ?: boolean
44
+ shortTitle ?: boolean
45
+ indexOrder ?: IndexOrder
46
+ }
47
+
48
+ type ChildUpdates = {
49
+ itemsToAdd : string [ ]
50
+ itemsToRemove : string [ ]
51
+ }
52
+
53
+ type DirectoryInfo = {
54
+ directoryContents : string [ ]
55
+ directoryFiles : string [ ]
56
+ childDirectories : string [ ]
57
+ }
58
+
59
+ type ChildrenComparison = {
60
+ childrenOnDisk : string [ ]
61
+ indexChildren : string [ ]
62
+ }
12
63
13
64
const ROOT_INDEX_FILE = 'content/index.md'
14
65
export const MARKDOWN_COMMENT = '\n<!-- Content after this section is automatically generated -->\n'
@@ -20,15 +71,19 @@ export async function updateContentDirectory({
20
71
sourceContent,
21
72
frontmatter,
22
73
indexOrder,
23
- } ) {
74
+ } : UpdateContentDirectoryOptions ) : Promise < void > {
24
75
const sourceFiles = Object . keys ( sourceContent )
25
76
await createDirectory ( targetDirectory )
26
77
await removeMarkdownFiles ( targetDirectory , sourceFiles , frontmatter . autogenerated )
27
78
await updateMarkdownFiles ( targetDirectory , sourceContent , frontmatter , indexOrder )
28
79
}
29
80
30
81
// Remove markdown files that are no longer in the source data
31
- async function removeMarkdownFiles ( targetDirectory , sourceFiles , autogeneratedType ) {
82
+ async function removeMarkdownFiles (
83
+ targetDirectory : string ,
84
+ sourceFiles : string [ ] ,
85
+ autogeneratedType : string | undefined ,
86
+ ) : Promise < void > {
32
87
// Copy the autogenerated Markdown files to the target directory
33
88
const autogeneratedFiles = await getAutogeneratedFiles ( targetDirectory , autogeneratedType )
34
89
// If the first array contains items that the second array does not,
@@ -42,29 +97,37 @@ async function removeMarkdownFiles(targetDirectory, sourceFiles, autogeneratedTy
42
97
43
98
// Gets a list of all files under targetDirectory that have the
44
99
// `autogenerated` frontmatter set to `autogeneratedType`.
45
- async function getAutogeneratedFiles ( targetDirectory , autogeneratedType ) {
100
+ async function getAutogeneratedFiles (
101
+ targetDirectory : string ,
102
+ autogeneratedType : string | undefined ,
103
+ ) : Promise < string [ ] > {
46
104
const files = walk ( targetDirectory , {
47
105
includeBasePath : true ,
48
- childDirectories : false ,
106
+ directories : false ,
49
107
globs : [ '**/*.md' ] ,
50
108
ignore : [ '**/README.md' , '**/index.md' ] ,
51
109
} )
52
110
return (
53
111
await Promise . all (
54
- files . map ( async ( file ) => {
112
+ files . map ( async ( file : string ) => {
55
113
const { data } = matter ( await readFile ( file , 'utf-8' ) )
56
114
if ( data . autogenerated === autogeneratedType ) {
57
115
return file
58
116
}
59
117
} ) ,
60
118
)
61
- ) . filter ( Boolean )
119
+ ) . filter ( Boolean ) as string [ ]
62
120
}
63
121
64
122
// The `sourceContent` object contains the new content and target file
65
123
// path for the Markdown files. Ex:
66
124
// { <targetFile>: { data: <frontmatter>, content: <markdownContent> } }
67
- async function updateMarkdownFiles ( targetDirectory , sourceContent , frontmatter , indexOrder = { } ) {
125
+ async function updateMarkdownFiles (
126
+ targetDirectory : string ,
127
+ sourceContent : SourceContent ,
128
+ frontmatter : FrontmatterData ,
129
+ indexOrder : IndexOrder = { } ,
130
+ ) : Promise < void > {
68
131
for ( const [ file , newContent ] of Object . entries ( sourceContent ) ) {
69
132
await updateMarkdownFile ( file , newContent . data , newContent . content )
70
133
}
@@ -82,11 +145,11 @@ async function updateMarkdownFiles(targetDirectory, sourceContent, frontmatter,
82
145
// edit the modifiable content of the file. If the Markdown file doesn't
83
146
// exists, we create a new Markdown file.
84
147
async function updateMarkdownFile (
85
- file ,
86
- sourceData ,
87
- sourceContent ,
88
- commentDelimiter = MARKDOWN_COMMENT ,
89
- ) {
148
+ file : string ,
149
+ sourceData : FrontmatterData ,
150
+ sourceContent : string ,
151
+ commentDelimiter : string = MARKDOWN_COMMENT ,
152
+ ) : Promise < void > {
90
153
if ( existsSync ( file ) ) {
91
154
// update only the versions property of the file, assuming
92
155
// the other properties have already been added and edited
@@ -132,10 +195,10 @@ async function updateMarkdownFile(
132
195
// ensure that the Markdown files have been updated and any files
133
196
// that need to be deleted have been removed.
134
197
async function updateDirectory (
135
- directory ,
136
- frontmatter ,
137
- { rootDirectoryOnly = false , shortTitle = false , indexOrder = { } } = { } ,
138
- ) {
198
+ directory : string ,
199
+ frontmatter : FrontmatterData ,
200
+ { rootDirectoryOnly = false , shortTitle = false , indexOrder = { } } : UpdateDirectoryOptions = { } ,
201
+ ) : Promise < void > {
139
202
const initialDirectoryListing = await getDirectoryInfo ( directory )
140
203
// If there are no children on disk, remove the directory
141
204
if ( initialDirectoryListing . directoryContents . length === 0 && ! rootDirectoryOnly ) {
@@ -162,7 +225,7 @@ async function updateDirectory(
162
225
const { childrenOnDisk, indexChildren } = getChildrenToCompare (
163
226
indexFile ,
164
227
directoryContents ,
165
- data . children ,
228
+ data . children || [ ] ,
166
229
)
167
230
168
231
const itemsToAdd = difference ( childrenOnDisk , indexChildren )
@@ -199,12 +262,16 @@ async function updateDirectory(
199
262
// Children properties include a leading slash except when the
200
263
// index.md file is the root index.md file. We also want to
201
264
// remove the file extension from the files on disk.
202
- function getChildrenToCompare ( indexFile , directoryContents , fmChildren ) {
265
+ function getChildrenToCompare (
266
+ indexFile : string ,
267
+ directoryContents : string [ ] ,
268
+ fmChildren : string [ ] | undefined ,
269
+ ) : ChildrenComparison {
203
270
if ( ! fmChildren ) {
204
271
throw new Error ( `No children property found in ${ indexFile } ` )
205
272
}
206
273
207
- const isEarlyAccess = ( item ) => isRootIndexFile ( indexFile ) && item === 'early-access'
274
+ const isEarlyAccess = ( item : string ) => isRootIndexFile ( indexFile ) && item === 'early-access'
208
275
209
276
// Get the list of children from the directory contents
210
277
const childrenOnDisk = directoryContents
@@ -233,18 +300,24 @@ function getChildrenToCompare(indexFile, directoryContents, fmChildren) {
233
300
//
234
301
// 3. If the index file is not autogenerated, we leave the ordering
235
302
// as is and append new children to the end.
236
- function updateIndexChildren ( data , childUpdates , indexFile , indexOrder , rootIndex = false ) {
303
+ function updateIndexChildren (
304
+ data : FrontmatterData ,
305
+ childUpdates : ChildUpdates ,
306
+ indexFile : string ,
307
+ indexOrder : IndexOrder ,
308
+ rootIndex : boolean = false ,
309
+ ) : FrontmatterData {
237
310
const { itemsToAdd, itemsToRemove } = childUpdates
238
311
const childPrefix = rootIndex ? '' : '/'
239
312
240
313
// Get a new list of children with added and removed items
241
- const children = [ ...data . children ]
314
+ const children = [ ...( data . children || [ ] ) ]
242
315
// remove the '/' prefix used in index.md children
243
316
. map ( ( item ) => item . replace ( childPrefix , '' ) )
244
317
. filter ( ( item ) => ! itemsToRemove . includes ( item ) )
245
318
children . push ( ...itemsToAdd )
246
319
247
- const orderedIndexChildren = [ ]
320
+ const orderedIndexChildren : string [ ] = [ ]
248
321
249
322
// Only used for tests. During testing, the content directory is
250
323
// in a temp directory so the paths are not relative to
@@ -280,7 +353,11 @@ function updateIndexChildren(data, childUpdates, indexFile, indexOrder, rootInde
280
353
281
354
// Gets the contents of the index.md file from disk if it exits or
282
355
// creates a new index.md file with the default frontmatter.
283
- async function getIndexFileContents ( indexFile , frontmatter , shortTitle = false ) {
356
+ async function getIndexFileContents (
357
+ indexFile : string ,
358
+ frontmatter : FrontmatterData ,
359
+ shortTitle : boolean = false ,
360
+ ) : Promise < { data : FrontmatterData ; content : string } > {
284
361
const directory = path . dirname ( indexFile )
285
362
const indexFileContent = {
286
363
data : {
@@ -300,8 +377,11 @@ async function getIndexFileContents(indexFile, frontmatter, shortTitle = false)
300
377
// Builds the index.md versions frontmatter by consolidating
301
378
// the versions from each Markdown file in the directory + the
302
379
// index.md files in any subdirectories of directory.
303
- async function getIndexFileVersions ( directory , files ) {
304
- const versions = new Set ( )
380
+ async function getIndexFileVersions (
381
+ directory : string ,
382
+ files : string [ ] ,
383
+ ) : Promise < { [ key : string ] : string } > {
384
+ const versions = new Set < string > ( )
305
385
await Promise . all (
306
386
files . map ( async ( file ) => {
307
387
const filepath = path . join ( directory , file )
@@ -319,7 +399,7 @@ async function getIndexFileVersions(directory, files) {
319
399
throw new Error ( `Frontmatter in ${ filepath } does not contain versions.` )
320
400
}
321
401
const fmVersions = getApplicableVersions ( data . versions )
322
- fmVersions . forEach ( ( version ) => versions . add ( version ) )
402
+ fmVersions . forEach ( ( version : string ) => versions . add ( version ) )
323
403
} ) ,
324
404
)
325
405
const versionArray = [ ...versions ]
@@ -343,9 +423,11 @@ and returns the frontmatter equivalent JSON:
343
423
ghes: '*'
344
424
}
345
425
*/
346
- export async function convertVersionsToFrontmatter ( versions ) {
347
- const frontmatterVersions = { }
348
- const numberedReleases = { }
426
+ export async function convertVersionsToFrontmatter (
427
+ versions : string [ ] ,
428
+ ) : Promise < { [ key : string ] : string } > {
429
+ const frontmatterVersions : { [ key : string ] : string } = { }
430
+ const numberedReleases : { [ key : string ] : { availableReleases : ( string | undefined ) [ ] } } = { }
349
431
350
432
// Currently, only GHES is numbered. Number releases have to be
351
433
// handled differently because they use semantic versioning.
@@ -362,7 +444,9 @@ export async function convertVersionsToFrontmatter(versions) {
362
444
// a release is no longer supported.
363
445
const i = docsVersion . releases . indexOf ( docsVersion . currentRelease )
364
446
if ( ! numberedReleases [ docsVersion . shortName ] ) {
365
- const availableReleases = Array ( docsVersion . releases . length ) . fill ( undefined )
447
+ const availableReleases : ( string | undefined ) [ ] = Array ( docsVersion . releases . length ) . fill (
448
+ undefined ,
449
+ )
366
450
availableReleases [ i ] = docsVersion . currentRelease
367
451
numberedReleases [ docsVersion . shortName ] = {
368
452
availableReleases,
@@ -388,7 +472,7 @@ export async function convertVersionsToFrontmatter(versions) {
388
472
. join ( ' || ' )
389
473
frontmatterVersions [ key ] = semVer
390
474
} else {
391
- const semVer = [ ]
475
+ const semVer : string [ ] = [ ]
392
476
if ( ! availableReleases [ availableReleases . length - 1 ] ) {
393
477
const startVersion = availableReleases . filter ( Boolean ) . pop ( )
394
478
semVer . push ( `>=${ startVersion } ` )
@@ -402,7 +486,7 @@ export async function convertVersionsToFrontmatter(versions) {
402
486
} )
403
487
const sortedFrontmatterVersions = Object . keys ( frontmatterVersions )
404
488
. sort ( )
405
- . reduce ( ( acc , key ) => {
489
+ . reduce ( ( acc : { [ key : string ] : string } , key ) => {
406
490
acc [ key ] = frontmatterVersions [ key ]
407
491
return acc
408
492
} , { } )
@@ -412,7 +496,7 @@ export async function convertVersionsToFrontmatter(versions) {
412
496
// This is uncommon, but we potentially could have the case where an
413
497
// article was versioned for say 3.2, not for 3.3, and then again
414
498
// versioned for 3.4. This will result in a custom semantic version range
415
- function checkVersionContinuity ( versions ) {
499
+ function checkVersionContinuity ( versions : ( string | undefined ) [ ] ) : boolean {
416
500
const availableVersions = [ ...versions ]
417
501
418
502
// values at the beginning or end of the array are not gaps but normal
@@ -427,18 +511,18 @@ function checkVersionContinuity(versions) {
427
511
}
428
512
429
513
// Returns true if the indexFile is the root index.md file
430
- function isRootIndexFile ( indexFile ) {
514
+ function isRootIndexFile ( indexFile : string ) : boolean {
431
515
return indexFile === ROOT_INDEX_FILE
432
516
}
433
517
434
518
// Creates a new directory if it doesn't exist
435
- async function createDirectory ( targetDirectory ) {
519
+ async function createDirectory ( targetDirectory : string ) : Promise < void > {
436
520
if ( ! existsSync ( targetDirectory ) ) {
437
521
await mkdirp ( targetDirectory )
438
522
}
439
523
}
440
524
441
- async function getDirectoryInfo ( directory ) {
525
+ async function getDirectoryInfo ( directory : string ) : Promise < DirectoryInfo > {
442
526
if ( ! existsSync ( directory ) ) {
443
527
throw new Error ( `Directory ${ directory } did not exist when attempting to get directory info.` )
444
528
}
@@ -454,7 +538,7 @@ async function getDirectoryInfo(directory) {
454
538
return { directoryContents, directoryFiles, childDirectories }
455
539
}
456
540
457
- function appendVersionComment ( stringifiedContent ) {
541
+ function appendVersionComment ( stringifiedContent : string ) : string {
458
542
return stringifiedContent . replace (
459
543
'\nversions:\n' ,
460
544
`\nversions: # DO NOT MANUALLY EDIT. CHANGES WILL BE OVERWRITTEN BY A 🤖\n` ,
0 commit comments