index.mjs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /* eslint-disable no-console, prefer-arrow-callback */
  2. import path from 'node:path'
  3. import fs from 'node:fs'
  4. import { open, readFile, writeFile } from 'node:fs/promises'
  5. import { fileURLToPath } from 'node:url'
  6. import dedent from 'dedent'
  7. import { remark } from 'remark'
  8. import { headingRange } from 'mdast-util-heading-range'
  9. import remarkFrontmatter from 'remark-frontmatter'
  10. import remarkConfig from '../remark-lint-uppy/index.js'
  11. import { getLocales, sortObjectAlphabetically } from './helpers.mjs'
  12. const { settings: remarkSettings } = remarkConfig
  13. const root = fileURLToPath(new URL('../../', import.meta.url))
  14. const localesPath = path.join(root, 'packages', '@uppy', 'locales')
  15. const templatePath = path.join(localesPath, 'template.js')
  16. const englishLocalePath = path.join(localesPath, 'src', 'en_US.js')
  17. async function getLocalesAndCombinedLocale () {
  18. const locales = await getLocales(`${root}/packages/@uppy/**/src/locale.js`)
  19. const combinedLocale = {}
  20. for (const [pluginName, locale] of Object.entries(locales)) {
  21. for (const [key, value] of Object.entries(locale.strings)) {
  22. if (key in combinedLocale && value !== combinedLocale[key]) {
  23. throw new Error(`'${key}' from ${pluginName} already exists in locale pack.`)
  24. }
  25. combinedLocale[key] = value
  26. }
  27. }
  28. return [locales, sortObjectAlphabetically(combinedLocale)]
  29. }
  30. function generateTypes (pluginName, locale) {
  31. const allowedStringTypes = Object.keys(locale.strings)
  32. .map((key) => ` | '${key}'`)
  33. .join('\n')
  34. const pluginClassName = pluginName
  35. .split('-')
  36. .map((str) => str.replace(/^\w/, (c) => c.toUpperCase()))
  37. .join('')
  38. const localePath = path.join(
  39. root,
  40. 'packages',
  41. '@uppy',
  42. pluginName,
  43. 'types',
  44. 'generatedLocale.d.ts',
  45. )
  46. const localeTypes = dedent`
  47. /* eslint-disable */
  48. import type { Locale } from '@uppy/core'
  49. type ${pluginClassName}Locale = Locale<
  50. ${allowedStringTypes}
  51. >
  52. export default ${pluginClassName}Locale
  53. `
  54. return writeFile(localePath, localeTypes)
  55. }
  56. async function generateLocaleDocs (pluginName) {
  57. const fileName = `${pluginName}.md`
  58. const docPath = path.join(root, 'website', 'src', 'docs', fileName)
  59. const localePath = path.join(root, 'packages', '@uppy', pluginName, 'src', 'locale.js')
  60. const rangeOptions = { test: 'locale: {}', ignoreFinalDefinitions: true }
  61. let docFile
  62. try {
  63. docFile = await open(docPath, 'r+')
  64. } catch (err) {
  65. console.error(
  66. `⚠️ Could not find markdown documentation file for "${pluginName}". Make sure the plugin name matches the markdown file name.`,
  67. )
  68. throw err
  69. }
  70. const file = await remark()
  71. .data('settings', remarkSettings)
  72. .use(remarkFrontmatter)
  73. .use(() => (tree) => {
  74. // Replace all nodes after the locale heading until the next heading (or eof)
  75. headingRange(tree, rangeOptions, (start, _, end) => [
  76. start,
  77. {
  78. type: 'html',
  79. // `module.exports` is not allowed by eslint in our docs.
  80. // The script outputs an extra newline which also isn't excepted by eslint
  81. // TODO: remove the no-restricted-globals when switch to ESM is completed.
  82. value: '<!-- eslint-disable no-restricted-globals, no-multiple-empty-lines -->',
  83. },
  84. {
  85. type: 'code',
  86. lang: 'js',
  87. meta: null,
  88. value: fs.readFileSync(localePath, 'utf-8'),
  89. },
  90. end,
  91. ])
  92. })
  93. .process(await docFile.readFile())
  94. const { bytesWritten } = await docFile.write(String(file), 0, 'utf-8')
  95. await docFile.truncate(bytesWritten)
  96. await docFile.close()
  97. }
  98. const [[locales, combinedLocale], fileString] = await Promise.all([
  99. getLocalesAndCombinedLocale(),
  100. readFile(templatePath, 'utf-8'),
  101. ])
  102. const formattedLocale = JSON.stringify(combinedLocale, null, ' ')
  103. await Promise.all([
  104. // Populate template
  105. writeFile(englishLocalePath, fileString.replace('en_US.strings = {}', `en_US.strings = ${formattedLocale}`))
  106. .then(() => console.log(`✅ Generated '${englishLocalePath}'`)),
  107. // Create locale files
  108. ...Object.entries(locales).flatMap(([pluginName, locale]) => [
  109. generateLocaleDocs(pluginName)
  110. .then(() => console.log(`✅ Generated locale docs for ${pluginName}`)),
  111. generateTypes(pluginName, locale)
  112. .then(() => console.log(`✅ Generated types for ${pluginName}`)),
  113. ]),
  114. ])