Register
Login
Resources
Docs Blog Datasets Glossary Case Studies Tutorials & Webinars
Product
Data Engine LLMs Platform Enterprise
Pricing Explore
Connect to our Discord channel

reconcile-category-dirs-with-ids.js 5.3 KB

You have to be logged in to leave a comment. Sign In
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  1. #!/usr/bin/env node
  2. const fs = require('fs')
  3. const path = require('path')
  4. const frontmatter = require('../lib/read-frontmatter')
  5. const walk = require('walk-sync')
  6. const slash = require('slash')
  7. const GithubSlugger = require('github-slugger')
  8. const { XmlEntities } = require('html-entities')
  9. const loadSiteData = require('../lib/site-data')
  10. const renderContent = require('../lib/render-content')
  11. const slugger = new GithubSlugger()
  12. const entities = new XmlEntities()
  13. const contentDir = path.join(process.cwd(), 'content')
  14. // [start-readme]
  15. //
  16. // An automated test checks for discrepancies between category directory names and
  17. // slugified category titles as IDs.
  18. //
  19. // If the test fails, a human needs to run this script to update the directory
  20. // names and add appropriate redirects.
  21. //
  22. // **This script is not currently supported on Windows.**
  23. //
  24. // [end-readme]
  25. // TODO fix path separators in the redirect
  26. if (process.platform.startsWith('win')) {
  27. console.log('This script cannot be run on Windows at this time! Exiting...')
  28. process.exit()
  29. }
  30. // Execute!
  31. main()
  32. async function main () {
  33. const englishCategoryIndices = getEnglishCategoryIndices()
  34. const siteData = await getEnglishSiteData()
  35. for (const categoryIndex of englishCategoryIndices) {
  36. const contents = fs.readFileSync(categoryIndex, 'utf8')
  37. const { data, content } = frontmatter(contents)
  38. // Get the parent directory name
  39. const categoryDirPath = path.dirname(categoryIndex)
  40. const categoryDirName = path.basename(categoryDirPath)
  41. const title = await renderContent(data.title, { site: siteData }, { textOnly: true })
  42. slugger.reset()
  43. const expectedSlug = slugger.slug(entities.decode(title))
  44. // If the directory name already matches the expected slug, bail out now
  45. if (categoryDirName === expectedSlug) continue
  46. // Figure out the new path for the category
  47. const categoryDirParentDir = path.dirname(categoryDirPath)
  48. const newPath = path.join(categoryDirParentDir, expectedSlug)
  49. // Figure out redirect path
  50. const relativeOldPath = path.relative(contentDir, categoryDirPath)
  51. const redirectPath = '/' + slash(relativeOldPath)
  52. // Log it
  53. const relativeNewPath = path.relative(contentDir, newPath)
  54. console.log(`Renaming category directory:
  55. Old: "${relativeOldPath}"
  56. New: "${relativeNewPath}"
  57. Redirect: "${redirectPath}"
  58. `)
  59. // Add a new redirect to the frontmatter
  60. if (!data.redirect_from) {
  61. data.redirect_from = []
  62. }
  63. data.redirect_from.push(redirectPath)
  64. // Update the category index file on disk
  65. fs.writeFileSync(categoryIndex, frontmatter.stringify(content, data, { lineWidth: 10000 }))
  66. // Update all of the category's articles on disk as well to add a new redirect to their frontmatter
  67. for (const articleFileName of fs.readdirSync(categoryDirPath)) {
  68. const articlePath = path.join(categoryDirPath, articleFileName)
  69. // Figure out redirect path
  70. const articlePathMinusExtension = path.join(categoryDirPath, path.basename(articleFileName, '.md'))
  71. const redirectArticlePath = '/' + slash(path.relative(contentDir, articlePathMinusExtension))
  72. // Log it
  73. const relativeOldArticlePath = path.relative(contentDir, articlePath)
  74. const newArticlePath = path.join(categoryDirParentDir, expectedSlug, articleFileName)
  75. const relativeNewArticlePath = path.relative(contentDir, newArticlePath)
  76. console.log(`Adding redirect to article:
  77. Old: "${relativeOldArticlePath}"
  78. New: "${relativeNewArticlePath}"
  79. Redirect: "${redirectArticlePath}"
  80. `)
  81. const articleContents = fs.readFileSync(articlePath, 'utf8')
  82. const { data: articleData, content: articleContent } = frontmatter(articleContents)
  83. // Add a new redirect to the frontmatter
  84. if (!articleData.redirect_from) {
  85. articleData.redirect_from = []
  86. }
  87. articleData.redirect_from.push(redirectArticlePath)
  88. // Update the article file on disk
  89. fs.writeFileSync(articlePath, frontmatter.stringify(articleContent, articleData, { lineWidth: 10000 }))
  90. }
  91. // Update the reference to this category in the product index file on disk
  92. //
  93. // NOTE: This approach may update the same product index multiple times per
  94. // script run but TBH I'm OK with that in a manually executed script
  95. const productIndexPath = path.join(categoryDirParentDir, 'index.md')
  96. const productIndexContents = fs.readFileSync(productIndexPath, 'utf8')
  97. const { data: productIndexData, content: productIndex } = frontmatter(productIndexContents)
  98. const revisedProductIndex = productIndex.replace(new RegExp(`(\\s+)(?:/${categoryDirName})(\\s+)`, 'g'), `$1/${expectedSlug}$2`)
  99. fs.writeFileSync(productIndexPath, frontmatter.stringify(revisedProductIndex, productIndexData, { lineWidth: 10000 }))
  100. console.log(`*** Updated product index "${productIndexPath}" for ☝️\n`)
  101. // Finally, rename the directory
  102. fs.renameSync(categoryDirPath, newPath)
  103. }
  104. }
  105. function getEnglishCategoryIndices () {
  106. const walkOptions = {
  107. globs: ['*/*/**/index.md'],
  108. ignore: ['{rest,graphql,developers}/**', 'enterprise/admin/index.md', '**/articles/**'],
  109. directories: false,
  110. includeBasePath: true
  111. }
  112. return walk(contentDir, walkOptions)
  113. }
  114. async function getEnglishSiteData () {
  115. const siteData = await loadSiteData()
  116. return siteData.en.site
  117. }
Tip!

Press p or to see the previous file or, n or to see the next file

Comments

Loading...