mirror of
https://github.com/zoriya/v10.git
synced 2026-08-09 15:46:42 +00:00
153 lines
5.7 KiB
JavaScript
153 lines
5.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/* eslint-disable no-console */
|
|
|
|
/**
|
|
* Build Styles Script
|
|
*
|
|
* This unified script handles the complete CSS build process for skins:
|
|
*
|
|
* 1. COMPILATION: Runs Tailwind CLI to compile CSS files
|
|
* - preflight.css (browser resets)
|
|
* - frosted.css (frosted skin)
|
|
* - minimal.css (minimal skin)
|
|
*
|
|
* 2. SCOPING: Scopes preflight selectors to `.vjs` class to prevent global resets
|
|
* - Transforms `* { margin: 0 }` → `.vjs * { margin: 0 }`
|
|
* - Special cases like `html`, `body`, `:root` → `.vjs`
|
|
*
|
|
* 3. CONCATENATION: Prepends scoped preflight to each skin CSS file
|
|
* - frosted.css = scoped-preflight + frosted styles
|
|
* - minimal.css = scoped-preflight + minimal styles
|
|
* - Deletes standalone preflight.css (no longer needed)
|
|
*
|
|
* Why scope preflight?
|
|
* - Tailwind's preflight resets affect ALL elements globally
|
|
* - Scoping to `.vjs` ensures resets only apply inside video player containers
|
|
*
|
|
* Why concatenate?
|
|
* - Users only need to import one CSS file per skin
|
|
* - Simpler API: import '@videojs/react/skins/frosted.css'
|
|
*/
|
|
|
|
import { execSync } from 'node:child_process';
|
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import process from 'node:process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import postcss from 'postcss';
|
|
import prefixSelector from 'postcss-prefix-selector';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const rootDir = join(__dirname, '..');
|
|
|
|
const preflightFile = 'dist/skins/preflight.css';
|
|
const frostedFile = 'dist/skins/frosted.css';
|
|
const minimalFile = 'dist/skins/minimal.css';
|
|
|
|
export default async function buildStyles() {
|
|
console.log('🎨 Building skin styles...');
|
|
|
|
try {
|
|
// ============================================================================
|
|
// STEP 1: Compile CSS files with Tailwind CLI
|
|
// ============================================================================
|
|
console.log(' → Compiling CSS with Tailwind...');
|
|
|
|
// Create output directory
|
|
const execOpts = { cwd: rootDir, stdio: 'inherit', encoding: 'utf8' };
|
|
const skinsDir = join(rootDir, 'dist', 'skins');
|
|
|
|
mkdirSync(skinsDir, { recursive: true });
|
|
|
|
// Compile preflight (browser resets)
|
|
execSync(
|
|
'pnpm exec tailwindcss -i src/skins/preflight.css -o dist/skins/preflight.css --minify',
|
|
execOpts,
|
|
);
|
|
|
|
// Compile frosted skin
|
|
execSync(
|
|
'pnpm exec tailwindcss -i src/skins/frosted/frosted-skin.css -o dist/skins/frosted.css --minify',
|
|
execOpts,
|
|
);
|
|
|
|
// Compile minimal skin
|
|
execSync(
|
|
'pnpm exec tailwindcss -i src/skins/minimal/minimal-skin.css -o dist/skins/minimal.css --minify',
|
|
execOpts,
|
|
);
|
|
|
|
// Verify all CSS files were created
|
|
const preflightPath = join(rootDir, preflightFile);
|
|
const frostedPath = join(rootDir, frostedFile);
|
|
const minimalPath = join(rootDir, minimalFile);
|
|
|
|
if (!existsSync(preflightPath)) {
|
|
throw new Error(`Tailwind failed to create ${preflightFile}`);
|
|
}
|
|
if (!existsSync(frostedPath)) {
|
|
throw new Error(`Tailwind failed to create ${frostedFile}`);
|
|
}
|
|
if (!existsSync(minimalPath)) {
|
|
throw new Error(`Tailwind failed to create ${minimalFile}`);
|
|
}
|
|
|
|
// ============================================================================
|
|
// STEP 2: Scope preflight CSS to .vjs
|
|
// ============================================================================
|
|
console.log(' → Scoping preflight selectors to .vjs...');
|
|
|
|
const preflightCss = readFileSync(preflightPath, 'utf-8');
|
|
|
|
// Use PostCSS with postcss-prefix-selector plugin to transform all selectors
|
|
const result = await postcss([
|
|
prefixSelector({
|
|
prefix: '.vjs',
|
|
transform(prefix, selector, prefixedSelector) {
|
|
// Special case transformations:
|
|
// - `html`, `body`, `:root` should become just `.vjs` (not `.vjs html`)
|
|
if (selector === 'body' || selector === 'html' || selector === ':root') {
|
|
return prefix;
|
|
} else if (selector === ':host') {
|
|
// Keep :host as-is (for web components)
|
|
return selector;
|
|
} else {
|
|
// All other selectors: prepend .vjs
|
|
return prefixedSelector;
|
|
}
|
|
},
|
|
}),
|
|
]).process(preflightCss, { from: preflightFile, to: preflightFile });
|
|
|
|
// Clean up any duplicate .vjs classes that may have been generated
|
|
const scopedPreflight = result.css.replace(/(\.vjs\s+)+/g, '.vjs ');
|
|
|
|
// ============================================================================
|
|
// STEP 3: Concatenate scoped preflight into each skin
|
|
// ============================================================================
|
|
console.log(' → Concatenating preflight into frosted.css...');
|
|
|
|
const frostedCss = readFileSync(frostedPath, 'utf-8');
|
|
const frostedWithPreflight = `${scopedPreflight}\n${frostedCss}`;
|
|
writeFileSync(frostedPath, frostedWithPreflight, 'utf-8');
|
|
|
|
console.log(' → Concatenating preflight into minimal.css...');
|
|
|
|
const minimalCss = readFileSync(minimalPath, 'utf-8');
|
|
const minimalWithPreflight = `${scopedPreflight}\n${minimalCss}`;
|
|
writeFileSync(minimalPath, minimalWithPreflight, 'utf-8');
|
|
|
|
// ============================================================================
|
|
// STEP 4: Clean up - delete standalone preflight.css
|
|
// ============================================================================
|
|
console.log(' → Removing standalone preflight.css...');
|
|
unlinkSync(preflightPath);
|
|
|
|
console.log('✅ Skin styles built successfully');
|
|
} catch (error) {
|
|
console.error('❌ Failed to build styles:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|