import { build } from 'vite' import path from 'path' import { readdir, readFile, stat, writeFile } from 'fs/promises' import { fileURLToPath } from 'url' import { viteStaticCopy } from 'vite-plugin-static-copy' import dts from 'vite-plugin-dts' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' import * as url from 'node:url' import { colorPalette } from './package/tools/color-palette.js' const __dirname = path.dirname(fileURLToPath(import.meta.url)) export async function readDeps(dir, prefix = []) { let output: { entry: string source: string name: string requires: string imports: string fileName: string types: string }[] = prefix.length === 0 ? [ { entry: './src/components/index.ts', source: './components/index', name: 'components', requires: './dist/components/index.cjs', imports: './dist/components/index.js', types: './dist/components/index.d.ts', fileName: 'components/index', }, { entry: './src/shared/tailwind-element/index.ts', source: './shared/tailwind-element/index.ts', name: 'tailwind-element', requires: './dist/shared/tailwind-element/index.cjs', imports: './dist/shared/tailwind-element/index.js', types: './dist/shared/tailwind-element/index.d.ts', fileName: 'shared/tailwind-element/index', }, ] : [] const list = await readdir(dir) for (const file of list) { if (file === '.' || file === '..') { continue } const info = await stat(path.join(dir, file)) if (info.isDirectory()) { if (file !== 'shared') { const subOutput = await readDeps(path.join(dir, file), [ ...prefix, file, ]) output = output.concat(subOutput) output.sort(({ name: a }, { name: b }) => { if (a > b) { return 1 } else if (a < b) { return -1 } return 0 }) } } else if (/components\//.test(dir) && /\.ts$/.test(file)) { if (file !== 'index.ts') { continue } const entry = `./${path.join(dir, file)}` const item = { entry, source: `./${path .relative(path.join(__dirname, 'src'), entry) .replace(/.ts$/, '')}`, name: path.basename(path.dirname(entry)), requires: `./${path .join('dist', path.relative(path.join(__dirname, 'src'), entry)) .replace(/.ts$/, '.cjs')}`, imports: `./${path .join('dist', path.relative(path.join(__dirname, 'src'), entry)) .replace(/.ts$/, '.js')}`, types: `./${path .join('dist', path.relative(path.join(__dirname, 'src'), entry)) .replace(/.ts$/, '.d.ts')}`, fileName: `${path.relative( path.join(__dirname, 'src'), entry.replace(/\.ts$/, '') )}`, } output.push(item) output.sort(({ name: a }, { name: b }) => { if (a > b) { return 1 } else if (a < b) { return -1 } return 0 }) } } return output } async function writeIndex() { const list = await readDeps('src') const filePath = path.join(__dirname, 'src/components/index.ts') const oldFile = await readFile(filePath, 'utf-8') const newFile = `// DO NOT MODIFY MANUALLY. This file is auto generated by Vite build process.\n${list .slice(1) .map(({ source }) => { const file = `./${path.relative( './components', source.replace(/\/index\.ts$/, '') )}` return `export * from '${file}'` }) .join('\n')}\n` if (oldFile !== newFile) { console.log(`writing ${filePath}`) await writeFile(filePath, newFile) } } /** * Write the color palette as CSS variables to a Sass file */ async function writeSass() { const filePath = path.join(__dirname, 'src/shared/styles/colors.scss') const oldFile = await readFile(filePath, 'utf-8') const output = [ '// DO NOT MODIFY MANUALLY. This file is auto generated by Vite build process.', ':root {', ] for (const [group, colorValues] of Object.entries(colorPalette)) { for (const [color, value] of Object.entries(colorValues)) { output.push(` --${group}-${color}: ${value};`) } } output.push('}', '') const newFile = output.join('\n') if (oldFile !== newFile) { console.log(`writing ${filePath}`) await writeFile(filePath, newFile) } } async function writePackage() { const list = await readDeps('src') const exp = { '.': { require: './dist/index.cjs', import: './dist/index.js', types: './dist/index.d.ts', }, './tailwind.config': './tailwind.config.cjs', './postcss.config': './postcss.config.cjs', './dist/assets/fonts/': './dist/assets/fonts/', './dist/assets/fonts': { require: './dist/assets/fonts/index.cjs', import: './dist/assets/fonts/index.js', types: './dist/assets/fonts/index.d.ts', }, './dist/assets/images/': './dist/assets/images/', './dist/assets/images': { require: './dist/assets/images/index.cjs', import: './dist/assets/images/index.js', types: './dist/assets/images/index.d.ts', }, './dist/assets/': './dist/assets/', './dist/assets': { require: './dist/assets/index.cjs', import: './dist/assets/index.js', types: './dist/assets/index.d.ts', }, './dist/styles/': './dist/styles/', './dist/styles': { require: './dist/styles/index.cjs', import: './dist/styles/index.js', types: './dist/styles/index.d.ts', }, './dist/sass/': './dist/sass/', './dist/sass': { require: './dist/sass/index.cjs', import: './dist/sass/index.js', types: './dist/sass/index.d.ts', }, './tools/color-palette': { require: './tools/color-palette.cjs', import: './tools/color-palette.js', types: './tools/color-palette.d.ts', }, './tools/copy-assets': { require: './tools/copy-assets.cjs', import: './tools/copy-assets.js', types: './tools/copy-assets.d.ts', }, './tools/': './tools/', './tools': { require: './tools/index.cjs', import: './tools/index.js', types: './tools/index.d.ts', }, './custom-elements': './custom-elements.json', './custom-elements.json': './custom-elements.json', './package.json': './package.json', } for (const { fileName, requires, imports, types } of list) { exp[`./dist/${fileName.replace(/\/index$/, '')}`] = { require: requires, import: imports, types, } } const oldContent = await readFile('./package/package.json', 'utf-8') const pack = JSON.parse(oldContent) pack.exports = exp const fullContent = await readFile('./package.json', 'utf-8') const fullPack = JSON.parse(fullContent) pack.version = fullPack.version for (const key of Object.keys(pack.dependencies)) { const newValue = (fullPack.dependencies || {})[key] || fullPack.devDependencies[key] if (newValue) { // Update published dependencies to be the same version we have in the workspace pack.dependencies[key] = newValue } } const newContent = `${JSON.stringify(pack, null, ' ')}\n` if (newContent !== oldContent) { console.log(`writing ./package.json`) await writeFile('./package/package.json', newContent) } return exp } const chokidar = { ignored: ['node_modules/**', 'tools/**', 'dist/**'], } const resolve = { alias: { '@': path.resolve(__dirname, './src'), }, } export async function run(argv: any) { const { mode } = argv await writeIndex() await writePackage() await writeSass() const list = await readDeps('src') const libs = [ { fileName: 'index', name: 'web_components', entry: './src/index.ts', }, { fileName: 'styles/index', name: 'web_components_styles', entry: './src/shared/styles/index.ts', }, { fileName: 'assets/index', name: 'web_components_assets', entry: './src/shared/assets/index.ts', }, { fileName: 'assets/fonts/index', name: 'web_components_fonts', entry: './src/shared/assets/fonts/index.ts', }, { fileName: 'assets/images/index', name: 'web_components_images', entry: './src/shared/assets/images/index.ts', }, { fileName: 'sass/index', name: 'web_components_sass', entry: './src/shared/styles/index.ts', }, { fileName: 'color-palette', name: 'web_component_color_palette', entry: './src/shared/tools/color-palette.ts', }, ].concat( list.map(({ entry, fileName, name }) => { console.log(`setting up ${entry}`) return { fileName, name: `web_components_${name.replace(/-/g, '_')}`, entry, } }) ) try { const entries = {} for (const { fileName, entry } of libs) { entries[fileName] = entry } await build({ configFile: false, resolve, build: { minify: false, lib: { entry: entries, formats: ['es', 'cjs'], }, target: 'esnext', emptyOutDir: true, outDir: './package/dist', watch: mode !== 'production' ? { clearScreen: false, chokidar, exclude: ['node_modules/**', 'tools/**', 'dist/**'], } : null, }, plugins: [ viteStaticCopy({ targets: [ { src: './src/shared/assets/fonts/*.woff2', dest: 'assets/fonts', }, { src: './src/shared/assets/images/*.{png,svg,jpg,jpeg}', dest: 'assets/images', }, { src: './src/shared/styles/**/*.scss', dest: 'sass', }, { src: ['./custom-elements.json'], dest: '..', }, ], }), dts({ exclude: [ './src/shared/tools', './src/shared/assets', './src/shared/styles', './src/shared/directives', ], outputDir: './package/dist', }), dts({ include: ['./src/shared/assets'], outputDir: './package/dist/assets', }), dts({ include: ['./src/shared/tools'], outputDir: './package/dist', }), dts({ include: ['./src/shared/styles'], outputDir: './package/dist/styles', }), dts({ include: ['./src/shared/styles'], outputDir: './package/dist/sass', }), ].filter(item => item), }) } catch (err) { console.error(err) } } if (import.meta.url.startsWith('file:')) { // (A) const modulePath = url.fileURLToPath(import.meta.url) if (process.argv[1] === modulePath) { // (B) const { argv } = yargs(hideBin(process.argv)) run(argv) .then(() => { if (argv['mode'] === 'production') { console.log('build finished') process.exit(0) } }) .catch(err => { console.error(err) process.exit(1) }) } }