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