index.mjs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env node
  2. /**
  3. * This script can be used to initiate the transition for a plugin from ESM source to
  4. * TS source. It will rename the files, update the imports, and add a `tsconfig.json`.
  5. */
  6. import { opendir, readFile, open, writeFile, rm } from 'node:fs/promises'
  7. import { argv } from 'node:process'
  8. import { extname } from 'node:path'
  9. import { existsSync } from 'node:fs'
  10. const packageRoot = new URL(`../../packages/${argv[2]}/`, import.meta.url)
  11. let dir
  12. try {
  13. dir = await opendir(new URL('./src/', packageRoot), { recursive: true })
  14. } catch (cause) {
  15. throw new Error(`Unable to find package "${argv[2]}"`, { cause })
  16. }
  17. const packageJSON = JSON.parse(
  18. await readFile(new URL('./package.json', packageRoot), 'utf-8'),
  19. )
  20. if (packageJSON.type !== 'module') {
  21. throw new Error('Cannot convert non-ESM package to TS')
  22. }
  23. const references = Object.keys(packageJSON.dependencies || {})
  24. .concat(Object.keys(packageJSON.peerDependencies || {}))
  25. .filter((pkg) => pkg.startsWith('@uppy/'))
  26. .map((pkg) => ({ path: `../${pkg.slice('@uppy/'.length)}` }))
  27. const depsNotYetConvertedToTS = references.filter(
  28. (ref) =>
  29. !existsSync(new URL(`${ref.path}/tsconfig.json`, packageRoot)),
  30. )
  31. if (depsNotYetConvertedToTS.length) {
  32. // We need to first convert the dependencies, otherwise we won't be working with the correct types.
  33. throw new Error('Some dependencies have not yet been converted to TS', {
  34. cause: depsNotYetConvertedToTS.map((ref) =>
  35. ref.path.replace(/^\.\./, '@uppy'),
  36. ),
  37. })
  38. }
  39. let tsConfig
  40. try {
  41. tsConfig = await open(new URL('./tsconfig.json', packageRoot), 'wx')
  42. } catch (cause) {
  43. throw new Error('It seems this package has already been transitioned to TS', {
  44. cause,
  45. })
  46. }
  47. for await (const dirent of dir) {
  48. if (!dirent.isDirectory()) {
  49. const { path: filepath } = dirent
  50. const ext = extname(filepath)
  51. await writeFile(
  52. filepath.replace(ext, ext.replace('js', 'ts')),
  53. (await readFile(filepath, 'utf-8')).replace(
  54. /((?:^|\n)import[^\n]*["']\.\.?\/[^'"]+\.)js(x?["'])/g,
  55. '$1ts$2',
  56. ),
  57. )
  58. await rm(filepath)
  59. }
  60. }
  61. await tsConfig.writeFile(
  62. `${JSON.stringify(
  63. {
  64. extends: '../../../tsconfig.shared',
  65. compilerOptions: {
  66. emitDeclarationOnly: false,
  67. noEmit: true,
  68. },
  69. include: ['./package.json', './src/**/*.'],
  70. references,
  71. },
  72. undefined,
  73. 2,
  74. )}\n`,
  75. )
  76. await tsConfig.close()
  77. await writeFile(
  78. new URL('./tsconfig.build.json', import.meta.url),
  79. `${JSON.stringify(
  80. {
  81. extends: '../../../tsconfig.shared',
  82. compilerOptions: {
  83. outDir: './lib',
  84. rootDir: './src',
  85. resolveJsonModule: false,
  86. noImplicitAny: false,
  87. skipLibCheck: true,
  88. },
  89. include: ['./src/**/*.'],
  90. exclude: ['./src/**/*.test.ts'],
  91. references,
  92. },
  93. undefined,
  94. 2,
  95. )}\n`,
  96. )
  97. console.log('Done')