test.mjs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /* eslint-disable no-console, prefer-arrow-callback */
  2. import path from 'node:path'
  3. import fs from 'node:fs'
  4. import { fileURLToPath } from 'node:url'
  5. import glob from 'glob'
  6. import chalk from 'chalk'
  7. import { getPaths, omit } from './helpers.mjs'
  8. const root = fileURLToPath(new URL('../../', import.meta.url))
  9. const leadingLocaleName = 'en_US'
  10. const mode = process.argv[2]
  11. const pluginLocaleDependencies = {
  12. core: 'provider-views',
  13. }
  14. test()
  15. .then(() => {
  16. console.log('\n')
  17. console.log('No blocking issues found')
  18. })
  19. .catch((error) => {
  20. console.error(error)
  21. process.exit(1)
  22. })
  23. function test () {
  24. switch (mode) {
  25. case 'unused':
  26. return getPaths(`${root}/packages/@uppy/**/src/locale.js`)
  27. .then((paths) => paths.map((filePath) => path.basename(path.join(filePath, '..', '..'))))
  28. .then(getAllFilesPerPlugin)
  29. .then(unused)
  30. case 'warnings':
  31. return getPaths(`${root}/packages/@uppy/locales/src/*.js`)
  32. .then(importFiles)
  33. .then((locales) => ({
  34. leadingLocale: locales[leadingLocaleName],
  35. followerLocales: omit(locales, leadingLocaleName),
  36. }))
  37. .then(warnings)
  38. default:
  39. return Promise.reject(new Error(`Invalid mode "${mode}"`))
  40. }
  41. }
  42. async function importFiles (paths) {
  43. const locales = {}
  44. for (const filePath of paths) {
  45. const localeName = path.basename(filePath, '.js')
  46. // Note: `.default` should be removed when we move to ESM
  47. const locale = (await import(filePath)).default
  48. locales[localeName] = locale.strings
  49. }
  50. return locales
  51. }
  52. function getAllFilesPerPlugin (pluginNames) {
  53. const filesPerPlugin = {}
  54. function getFiles (name) {
  55. return glob
  56. .sync(`${root}/packages/@uppy/${name}/lib/**/*.js`)
  57. .filter((filePath) => !filePath.includes('locale.js'))
  58. .map((filePath) => fs.readFileSync(filePath, 'utf-8'))
  59. }
  60. for (const name of pluginNames) {
  61. filesPerPlugin[name] = getFiles(name)
  62. if (name in pluginLocaleDependencies) {
  63. filesPerPlugin[name] = filesPerPlugin[name].concat(
  64. getFiles(pluginLocaleDependencies[name]),
  65. )
  66. }
  67. }
  68. return filesPerPlugin
  69. }
  70. async function unused (filesPerPlugin, data) {
  71. for (const [name, fileStrings] of Object.entries(filesPerPlugin)) {
  72. const fileString = fileStrings.join('\n')
  73. const localePath = path.join(
  74. root,
  75. 'packages',
  76. '@uppy',
  77. name,
  78. 'src',
  79. 'locale.js',
  80. )
  81. const locale = (await import(localePath)).default
  82. for (const key of Object.keys(locale.strings)) {
  83. const regPat = new RegExp(
  84. `(i18n|i18nArray)\\([^\\)]*['\`"]${key}['\`"]`,
  85. 'g',
  86. )
  87. if (!fileString.match(regPat)) {
  88. return Promise.reject(new Error(`Unused locale key "${key}" in @uppy/${name}`))
  89. }
  90. }
  91. }
  92. return data
  93. }
  94. function warnings ({ leadingLocale, followerLocales }) {
  95. const entries = Object.entries(followerLocales)
  96. const logs = []
  97. for (const [name, locale] of entries) {
  98. const missing = Object.keys(leadingLocale).filter((key) => !(key in locale))
  99. const excess = Object.keys(locale).filter((key) => !(key in leadingLocale))
  100. logs.push('\n')
  101. logs.push(`--> Keys from ${leadingLocaleName} missing in ${name}`)
  102. logs.push('\n')
  103. for (const key of missing) {
  104. let value = leadingLocale[key]
  105. if (typeof value === 'object') {
  106. // For values with plural forms, just take the first one right now
  107. value = value[Object.keys(value)[0]]
  108. }
  109. logs.push(
  110. [
  111. `${chalk.cyan(name)} locale has missing string: '${chalk.red(key)}'`,
  112. `that is present in ${chalk.cyan(leadingLocaleName)}`,
  113. `with value: ${chalk.yellow(value)}`,
  114. ].join(' '),
  115. )
  116. }
  117. logs.push('\n')
  118. logs.push(`--> Keys from ${name} missing in ${leadingLocaleName}`)
  119. logs.push('\n')
  120. for (const key of excess) {
  121. logs.push(
  122. [
  123. `${chalk.cyan(name)} locale has excess string:`,
  124. `'${chalk.yellow(key)}' that is not present`,
  125. `in ${chalk.cyan(leadingLocaleName)}.`,
  126. ].join(' '),
  127. )
  128. }
  129. }
  130. console.log(logs.join('\n'))
  131. }