build-plugins.mjs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #!/usr/bin/env node
  2. /*!
  3. * Script to build our plugins to use them separately.
  4. * Copyright 2020-2025 The Bootstrap Authors
  5. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  6. */
  7. import path from 'node:path'
  8. import { fileURLToPath } from 'node:url'
  9. import { babel } from '@rollup/plugin-babel'
  10. import { globby } from 'globby'
  11. import { rollup } from 'rollup'
  12. import banner from './banner.mjs'
  13. const __filename = fileURLToPath(import.meta.url)
  14. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  15. const sourcePath = path.resolve(__dirname, '../js/src/').replace(/\\/g, '/')
  16. const jsFiles = await globby(`${sourcePath}/**/*.js`)
  17. // Array which holds the resolved plugins
  18. const resolvedPlugins = []
  19. // Trims the "js" extension and uppercases => first letter, hyphens, backslashes & slashes
  20. const filenameToEntity = filename => filename.replace('.js', '')
  21. .replace(/(?:^|-|\/|\\)[a-z]/g, str => str.slice(-1).toUpperCase())
  22. for (const file of jsFiles) {
  23. resolvedPlugins.push({
  24. src: file,
  25. dist: file.replace('src', 'dist'),
  26. fileName: path.basename(file),
  27. className: filenameToEntity(path.basename(file))
  28. // safeClassName: filenameToEntity(path.relative(sourcePath, file))
  29. })
  30. }
  31. const build = async plugin => {
  32. /**
  33. * @type {import('rollup').GlobalsOption}
  34. */
  35. const globals = {}
  36. const bundle = await rollup({
  37. input: plugin.src,
  38. plugins: [
  39. babel({
  40. // Only transpile our source code
  41. exclude: 'node_modules/**',
  42. // Include the helpers in each file, at most one copy of each
  43. babelHelpers: 'bundled'
  44. })
  45. ],
  46. external(source) {
  47. // Pattern to identify local files
  48. const pattern = /^(\.{1,2})\//
  49. // It's not a local file, e.g a Node.js package
  50. if (!pattern.test(source)) {
  51. globals[source] = source
  52. return true
  53. }
  54. const usedPlugin = resolvedPlugins.find(plugin => {
  55. return plugin.src.includes(source.replace(pattern, ''))
  56. })
  57. if (!usedPlugin) {
  58. throw new Error(`Source ${source} is not mapped!`)
  59. }
  60. // We can change `Index` with `UtilIndex` etc if we use
  61. // `safeClassName` instead of `className` everywhere
  62. globals[path.normalize(usedPlugin.src)] = usedPlugin.className
  63. return true
  64. }
  65. })
  66. await bundle.write({
  67. banner: banner(plugin.fileName),
  68. format: 'umd',
  69. name: plugin.className,
  70. sourcemap: true,
  71. globals,
  72. generatedCode: 'es2015',
  73. file: plugin.dist
  74. })
  75. console.log(`Built ${plugin.className}`)
  76. }
  77. (async () => {
  78. try {
  79. const basename = path.basename(__filename)
  80. const timeLabel = `[${basename}] finished`
  81. console.log('Building individual plugins...')
  82. console.time(timeLabel)
  83. await Promise.all(Object.values(resolvedPlugins).map(plugin => build(plugin)))
  84. console.timeEnd(timeLabel)
  85. } catch (error) {
  86. console.error(error)
  87. process.exit(1)
  88. }
  89. })()