-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhancement: add / fix turndown rules, improve CLI #43
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,115 @@ const { program } = require('commander') | |
const mkdirp = require('mkdirp') | ||
const del = require('del') | ||
const WPAPI = require('wpapi') | ||
const turndown = require('turndown') | ||
const turndownService = new turndown({ | ||
const TurndownService = require('turndown') | ||
const {tables} = require('turndown-plugin-gfm') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. turndownコアにはtableタグをパースする機能はないため、こちらのコメントにある通り、別のライブラリを導入しました。 |
||
|
||
// Languages that can be specified in the code markdown | ||
const codeLanguages = { | ||
css: 'css', | ||
bash: 'bash', | ||
php: 'php', | ||
yaml: 'yaml', | ||
xml: 'xml', | ||
jscript: 'javascript', | ||
} | ||
|
||
// Rules that remove escapes in code blocks | ||
const unEscapes = [ | ||
[/\\\\/g, '\\'], | ||
[/\\\*/g, '*'], | ||
[/\\-/g, '-'], | ||
[/^\\+ /g, '+ '], | ||
[/\\=/g, '='], | ||
[/\\`/g, '`'], | ||
[/\\~~~/g, '~~~'], | ||
[/\\\[/g, '['], | ||
[/\\\]/g, ']'], | ||
[/\\>/g, '>'], | ||
[/\\_/g, '_'], | ||
[/\"/g, '"'], | ||
[/\</g, '<'], | ||
[/\>/g, '>'], | ||
] | ||
|
||
const turndownService = new TurndownService({ | ||
headingStyle: 'atx', | ||
codeBlockStyle: 'fenced', | ||
emDelimiter: '*', | ||
}) | ||
turndownService.use(tables); | ||
|
||
// Remove Glossary | ||
turndownService.addRule('glossary', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. glossasy の除去を、文字列置換ではなく turndown の |
||
filter: node => { | ||
const classList = node.getAttribute('class'); | ||
if ( classList ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. f2fa42e にて対応いたしました。 |
||
return classList === 'glossary-item-hidden-content'; | ||
} | ||
return false; | ||
}, | ||
replacement: () => { | ||
return ''; | ||
} | ||
}); | ||
|
||
// Remove code trigger anchor | ||
turndownService.addRule('code-trigger-anchor', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コードブロックを開閉するための#リンク(Expand full source code / Collapse full source code)を除去します。 |
||
filter: node => { | ||
const classList = node.getAttribute('class'); | ||
if ( classList ) { | ||
return classList.includes('show-complete-source') || classList.includes(`less-complete-source`); | ||
} | ||
return false; | ||
}, | ||
replacement: () => { | ||
return ''; | ||
} | ||
}); | ||
|
||
// Transform dt tag to strong tag | ||
turndownService.addRule('dt-to-strong', { | ||
filter: ['dt'], | ||
replacement: (content, node, options) => { | ||
return options.strongDelimiter + content + options.strongDelimiter | ||
} | ||
}); | ||
|
||
// Transform pre code block to code markdown | ||
turndownService.addRule('precode to code', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. このルールが一番ややこしいと思うのですが、以下のロジックで処理しています。
|
||
filter: node => { | ||
const classList = node.getAttribute('class'); | ||
const isCode = node.nodeName === 'PRE' && classList && classList.includes('brush:'); | ||
return isCode; | ||
}, | ||
replacement: (content, node) => { | ||
const classList = node.getAttribute('class'); | ||
|
||
// Search for a language that is match the list of code languages | ||
t-hamano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const codeLanguage = Object.keys(codeLanguages).reduce((currentLanguage, language) => { | ||
if ( classList.includes(language) ) { | ||
return codeLanguages[language]; | ||
} | ||
return currentLanguage; | ||
}, undefined); | ||
|
||
// Unescape contents | ||
let newContent = unEscapes.reduce((accumulator, unEscape) => { | ||
return accumulator.replace(unEscape[0], unEscape[1]) | ||
}, content) | ||
|
||
// Remove br tag | ||
newContent = newContent.replace(/^<br \/>\n\n|<br \/>\n/g, "\n"); | ||
// Remove first and last paragraph tag | ||
newContent = newContent.replace(/^<\/p>|<p>$/g, ''); | ||
// Remove first new line | ||
newContent = newContent.replace(/^\n/, ''); | ||
// Convert to language-aware markdown | ||
newContent = codeLanguage ? `\`\`\`${codeLanguage}\n` + newContent + '```' : "```\n" + newContent + '```'; | ||
t-hamano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return newContent; | ||
} | ||
}); | ||
|
||
const getAll = (request) => { | ||
return request.then((response) => { | ||
|
@@ -35,6 +138,7 @@ const generateJson = async ( | |
outputDir, | ||
regenerate | ||
) => { | ||
team = team ? `${team}/` : '' | ||
handbook = handbook ? handbook : 'handbook' | ||
subdomain = `${ | ||
subdomain ? (subdomain === 'w.org' ? '' : subdomain) : 'make' | ||
|
@@ -60,12 +164,14 @@ const generateJson = async ( | |
}) | ||
|
||
const wp = new WPAPI({ | ||
endpoint: `https://${subdomain}wordpress.org/${team}/wp-json`, | ||
endpoint: `https://${subdomain}wordpress.org/${team}wp-json`, | ||
}) | ||
|
||
|
||
wp.handbooks = wp.registerRoute('wp/v2', `/${handbook}/(?P<id>)`) | ||
|
||
console.log( | ||
`Connecting to https://${subdomain}wordpress.org/${team}/wp-json/wp/v2/${handbook}/` | ||
`Connecting to https://${subdomain}wordpress.org/${team}wp-json/wp/v2/${handbook}/` | ||
) | ||
|
||
getAll(wp.handbooks()).then(async (allPosts) => { | ||
|
@@ -83,18 +189,16 @@ const generateJson = async ( | |
rootPath = `https://${subdomain}wordpress.org/${team}/${handbook}/` | ||
} | ||
} | ||
|
||
for (const item of allPosts) { | ||
const path = item.link.split(rootPath)[1].replace(/\/$/, '') || 'index' | ||
const filePath = | ||
path.split('/').length > 1 | ||
? path.substring(0, path.lastIndexOf('/')) + '/' | ||
: '' | ||
// remove <span class='glossary-item-hidden-content'>inner contents | ||
const preContent = item.content.rendered.replace( | ||
/<span class=\'glossary-item-hidden-content\'>.*?<\/span><\/span>/g, | ||
'' | ||
) | ||
const markdownContent = turndownService.turndown(preContent) | ||
|
||
const content = item.content.rendered; | ||
const markdownContent = turndownService.turndown(content) | ||
const markdown = `# ${item.title.rendered}\n\n${markdownContent}` | ||
|
||
await mkdirp(`${outputDir}/${filePath}`) | ||
|
@@ -156,8 +260,10 @@ const generateJson = async ( | |
|
||
program | ||
.version(packageJson.version) | ||
.arguments('<team>') | ||
.description('Generate a menu JSON file for WordPress.org handbook') | ||
mirucon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.option( | ||
'-t, --team <team>', | ||
'Specify team name' | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 現状のテーマハンドブックやプラグインは、 |
||
.option( | ||
'-b, --handbook <handbook>', | ||
'Specify handbook name (default "handbook")' | ||
|
@@ -174,9 +280,9 @@ program | |
'-r --regenerate', | ||
'If this option is supplied, the directory you specified as output directory will once deleted, and it will regenerate all the files in the directory' | ||
) | ||
.action((team, options) => { | ||
.action((options) => { | ||
generateJson( | ||
team, | ||
options.team, | ||
options.handbook, | ||
options.subDomain, | ||
options.outputDir, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
いくつかのハンドブックのエンドポイントが変わった?せいか、Exampleのコマンドでは取得出来ないものがあったため、修正に加え、コマンド例も追加しました。