1
0

build.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /* eslint-env node */
  2. /* eslint-disable no-console */
  3. // Build JS and CSS using esbuild and Lightning CSS
  4. import { promises as fs } from 'node:fs';
  5. import path from 'node:path';
  6. import browserslist from 'browserslist';
  7. import esbuild from 'esbuild';
  8. import * as lightningcss from 'lightningcss';
  9. // Packages to build but exclude from bundle
  10. const externalPackages = [
  11. 'chart.js/auto',
  12. 'alpinejs/dist/cdn.min.js',
  13. '@alpinejs/collapse/dist/cdn.min.js',
  14. 'xterm',
  15. 'xterm-addon-webgl',
  16. 'xterm-addon-canvas',
  17. ];
  18. // Build main bundle
  19. async function buildJS() {
  20. const inputPath = './web/js/src/index.js';
  21. try {
  22. await esbuild.build({
  23. entryPoints: [inputPath],
  24. outfile: './web/js/dist/main.min.js',
  25. bundle: true,
  26. minify: true,
  27. sourcemap: true,
  28. external: externalPackages,
  29. });
  30. console.log('✅ JavaScript build completed for', inputPath);
  31. } catch (error) {
  32. console.error('❌ Error building JavaScript:', error);
  33. process.exit(1);
  34. }
  35. }
  36. // Build external packages
  37. async function buildExternalJS() {
  38. try {
  39. const buildPromises = externalPackages.map(async (pkg) => {
  40. const outputPath = getOutputPath(pkg);
  41. await esbuild.build({
  42. entryPoints: [pkg],
  43. outfile: outputPath,
  44. bundle: true,
  45. minify: true,
  46. format: 'esm',
  47. });
  48. console.log(`✅ Dependency build completed for ${pkg}`);
  49. });
  50. await Promise.all(buildPromises);
  51. } catch (error) {
  52. console.error('❌ Error building external packages:', error);
  53. process.exit(1);
  54. }
  55. }
  56. function getOutputPath(pkg) {
  57. let pkgName;
  58. if (pkg.startsWith('alpinejs')) {
  59. pkgName = 'alpinejs';
  60. } else if (pkg.startsWith('@alpinejs/collapse')) {
  61. pkgName = 'alpinejs-collapse';
  62. } else {
  63. pkgName = pkg.replace(/\//g, '-');
  64. }
  65. return `./web/js/dist/${pkgName}.min.js`;
  66. }
  67. // Process a CSS file
  68. async function processCSS(inputFile, outputFile) {
  69. try {
  70. await ensureDir(path.dirname(outputFile));
  71. const css = await fs.readFile(inputFile);
  72. const bundle = await lightningcss.bundleAsync({
  73. filename: inputFile,
  74. sourceMap: true,
  75. code: Buffer.from(css),
  76. minify: true,
  77. targets: lightningcss.browserslistToTargets(browserslist()),
  78. drafts: { customMedia: true, nesting: true },
  79. visitor: {
  80. Url: (node) => {
  81. // Fix relative paths for webfonts
  82. if (node.url.startsWith('../webfonts/')) {
  83. return {
  84. url: node.url.replace('../webfonts/', '/webfonts/'),
  85. loc: node.loc,
  86. };
  87. }
  88. return node;
  89. },
  90. },
  91. resolver: {
  92. resolve(specifier, from) {
  93. if (!specifier.endsWith('.css')) {
  94. specifier += '.css';
  95. }
  96. if (specifier.startsWith('node:')) {
  97. return `node_modules/${specifier.replace('node:', '')}`;
  98. }
  99. return `${path.dirname(from)}/${specifier}`;
  100. },
  101. },
  102. });
  103. await fs.writeFile(outputFile, bundle.code);
  104. await fs.writeFile(`${outputFile}.map`, bundle.map);
  105. console.log(`✅ CSS build completed for ${inputFile}`);
  106. } catch (error) {
  107. console.error(`❌ Error processing CSS for ${inputFile}:`, error);
  108. process.exit(1);
  109. }
  110. }
  111. // Build CSS
  112. async function buildCSS() {
  113. const themesSourcePath = './web/css/src/themes/';
  114. const cssEntries = await fs.readdir(themesSourcePath);
  115. const cssBuildPromises = cssEntries
  116. .filter((entry) => path.extname(entry) === '.css')
  117. .map(async (entry) => {
  118. const entryName = entry.replace('.css', '.min.css');
  119. const inputPath = path.join(themesSourcePath, entry);
  120. const outputPath = `./web/css/themes/${entryName}`;
  121. await processCSS(inputPath, outputPath);
  122. });
  123. await Promise.all(cssBuildPromises);
  124. }
  125. // Ensure a directory exists
  126. async function ensureDir(dir) {
  127. try {
  128. await fs.mkdir(dir, { recursive: true });
  129. } catch (error) {
  130. if (error.code !== 'EEXIST') {
  131. throw error;
  132. }
  133. }
  134. }
  135. // Build all assets
  136. async function build() {
  137. console.log('🚀 Building JS and CSS...');
  138. await buildJS();
  139. await buildExternalJS();
  140. await buildCSS();
  141. console.log('🎉 Build completed.');
  142. }
  143. // Execute build
  144. build();