vite.config.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { fileURLToPath } from 'node:url'
  2. import { createRequire } from 'node:module'
  3. import { transformAsync } from '@babel/core'
  4. import t from '@babel/types'
  5. import autoprefixer from 'autoprefixer'
  6. import postcssLogical from 'postcss-logical'
  7. import postcssDirPseudoClass from 'postcss-dir-pseudo-class'
  8. const ROOT = new URL('../../', import.meta.url)
  9. const PACKAGES_ROOT = fileURLToPath(new URL('./packages/', ROOT))
  10. // To enable the plugin, it looks like we need to interact with the resolution
  11. // algorithm, but we need to stop afterwards otherwise it messes up somewhere
  12. // else. This hack can be removed when we get rid of JSX inside of .js files.
  13. let counter = 0
  14. const moduleTypeCache = new Map()
  15. function isTypeModule (file) {
  16. const packageFolder = file.slice(0, file.indexOf('/src/') + 1)
  17. const cachedValue = moduleTypeCache.get(packageFolder)
  18. if (cachedValue != null) return cachedValue
  19. // eslint-disable-next-line import/no-dynamic-require, global-require
  20. const { type } = createRequire(packageFolder)('./package.json')
  21. const typeModule = type === 'module'
  22. moduleTypeCache.set(packageFolder, typeModule)
  23. return typeModule
  24. }
  25. const packageLibImport = /^@uppy\/([^/])\/lib\/(.+)$/
  26. const packageEntryImport = /^@uppy\/([^/])$/
  27. function isSpecifierTypeModule (specifier) {
  28. const packageLib = packageLibImport.exec(specifier)
  29. if (packageLib != null) {
  30. return isTypeModule(`${PACKAGES_ROOT}@uppy/${packageLib[1]}/src/${packageLib[2]}`)
  31. }
  32. const packageEntry = packageEntryImport.exec(specifier)
  33. if (packageEntry != null) {
  34. return isTypeModule(`${PACKAGES_ROOT}@uppy/${packageEntry[1]}/src/index.js`)
  35. }
  36. return false
  37. }
  38. const JS_FILE_EXTENSION = /\.jsx?$/
  39. /**
  40. * @type {import('vite').UserConfig}
  41. */
  42. const config = {
  43. envDir: fileURLToPath(ROOT),
  44. build: {
  45. commonjsOptions: {
  46. defaultIsModuleExports: true,
  47. },
  48. },
  49. css: {
  50. postcss: {
  51. plugins: [
  52. autoprefixer,
  53. postcssLogical(),
  54. postcssDirPseudoClass(),
  55. ],
  56. },
  57. },
  58. esbuild: {
  59. jsxFactory: 'h',
  60. jsxFragment: 'Fragment',
  61. },
  62. resolve: {
  63. alias: [
  64. {
  65. find: /^uppy\/(.+)$/,
  66. replacement: `${PACKAGES_ROOT}uppy/$1`,
  67. },
  68. {
  69. find: /^@uppy\/([^/]+)$/,
  70. replacement: `${PACKAGES_ROOT}@uppy/$1/src/index.js`,
  71. },
  72. {
  73. find: /^@uppy\/([^/]+)\/lib\/(.+)$/,
  74. replacement: `${PACKAGES_ROOT}@uppy/$1/src/$2`,
  75. },
  76. // {
  77. // find: /^@uppy\/([^/]+)\/(.+)$/,
  78. // replacement: PACKAGES_ROOT + "@uppy/$1/src/$2",
  79. // },
  80. ],
  81. },
  82. plugins: [
  83. // TODO: remove plugin when we switch to ESM and get rid of JSX inside .js files.
  84. {
  85. name: 'vite-plugin-jsx-commonjs',
  86. // TODO: remove this hack when we get rid of JSX inside .js files.
  87. enforce: 'pre',
  88. // eslint-disable-next-line consistent-return
  89. resolveId (id) {
  90. if (id.startsWith(PACKAGES_ROOT) && JS_FILE_EXTENSION.test(id)) {
  91. return id
  92. }
  93. // TODO: remove this hack when we get rid of JSX inside .js files.
  94. if (counter++ < 2) {
  95. return id
  96. }
  97. },
  98. transform (code, id) {
  99. if (id.startsWith(PACKAGES_ROOT) && JS_FILE_EXTENSION.test(id)) {
  100. return transformAsync(code, isTypeModule(id) ? {
  101. plugins: [
  102. id.endsWith('.jsx') ? ['@babel/plugin-transform-react-jsx', { pragma: 'h' }] : {},
  103. {
  104. // On type: "module" packages, we still want to rewrite import
  105. // statements that tries to access a named export from a CJS
  106. // module to using only the default import.
  107. visitor: {
  108. ImportDeclaration (path) {
  109. const { specifiers, source: { value } } = path.node
  110. if (value.startsWith('@uppy/') && !isSpecifierTypeModule(value)
  111. && specifiers.some(node => node.type !== 'ImportDefaultSpecifier')) {
  112. const oldSpecifiers = specifiers[0].type === 'ImportDefaultSpecifier'
  113. // If there's a default import, it must come first.
  114. ? specifiers.splice(1)
  115. // If there's no default import, we create one from a random identifier.
  116. : specifiers.splice(0, specifiers.length, t.importDefaultSpecifier(t.identifier(`_import_${counter++}`)))
  117. if (oldSpecifiers[0].type === 'ImportNamespaceSpecifier') {
  118. // import defaultVal, * as namespaceImport from '@uppy/package'
  119. // is transformed into:
  120. // import defaultVal from '@uppy/package'; const namespaceImport = defaultVal
  121. path.insertAfter(
  122. t.variableDeclaration('const', [t.variableDeclarator(
  123. oldSpecifiers[0].local,
  124. specifiers[0].local,
  125. )]),
  126. )
  127. } else {
  128. // import defaultVal, { exportedVal as importedName, other } from '@uppy/package'
  129. // is transformed into:
  130. // import defaultVal from '@uppy/package'; const { exportedVal: importedName, other } = defaultVal
  131. path.insertAfter(t.variableDeclaration('const', [t.variableDeclarator(
  132. t.objectPattern(
  133. oldSpecifiers.map(specifier => t.objectProperty(
  134. t.identifier(specifier.imported.name),
  135. specifier.local,
  136. )),
  137. ),
  138. specifiers[0].local,
  139. )]))
  140. }
  141. }
  142. },
  143. },
  144. },
  145. ],
  146. } : {
  147. plugins: [
  148. ['@babel/plugin-transform-react-jsx', { pragma: 'h' }],
  149. 'transform-commonjs',
  150. ],
  151. })
  152. }
  153. return code
  154. },
  155. },
  156. ],
  157. }
  158. export default config