import { defineConfig, transform } from '@videojs/compiler'; import { tailwind } from '@videojs/compiler/tailwind'; const CORE_COMPONENTS_SOURCE = '@videojs/core/components'; const ICON_COMPONENTS_SOURCE = '@videojs/icons/components'; const SOURCE_TAILWIND_SOURCE = './tailwind/video.tailwind'; const GENERATED_TAILWIND_SOURCE = '@videojs/skins/default/tailwind/video.tailwind'; const BUTTON_RENDER_TARGET = /Button$/; export default defineConfig({ input: { 'default-video': '../skins/src/default/video.skin.tsx', }, output: { dir: 'src/presets/video', entryFileNames: '[name].generated.tailwind.tsx', banner: '// Generated by @videojs/compiler. Do not edit.\n', }, plugins: [ transform((code) => { // Types const BaseVideoSkinProps = code.ref.import('./src/presets/types', 'BaseVideoSkinProps', { type: true }); const ReactNode = code.ref.import('react', 'ReactNode', { type: true }); // Utils const cn = code.ref.import('@videojs/utils/style', 'cn'); const isString = code.ref.import('@videojs/utils/predicate', 'isString'); const isRenderProp = code.ref.import('@/utils/use-render', 'isRenderProp'); // Components const Poster = code.ref.import('@/ui/poster', 'Poster'); return [ code.edit.import.rewrite({ [CORE_COMPONENTS_SOURCE]: coreComponentImport, [ICON_COMPONENTS_SOURCE]: '@/icons', [SOURCE_TAILWIND_SOURCE]: GENERATED_TAILWIND_SOURCE, }), tailwind({ mode: 'preserve' }), code.edit.jsx.element({ when: code.match.jsx.tag('Tooltip.Trigger'), transform: code.edit.jsx.moveChildToProp('render'), }), code.edit.jsx.element({ when: code.match.jsx.tag('Controls.Root'), transform: code.edit.jsx.addProp('data-controls', ''), }), code.edit.jsx.element({ when: code.match.jsx.tag('Container'), transform: code.edit.jsx.addPropsSpread('rest'), }), code.edit.jsx.element({ when: code.match.jsx.tag('Text'), transform: code.edit.jsx.replaceTag('span'), }), code.edit.jsx.element({ when: code.match.jsx.tag('Poster'), transform: () => { return code.create.jsx.renderIf( 'poster', code.create.jsx.element(Poster, { src: code.create.value.onlyIf({ value: 'poster', condition: isString }), render: code.create.value.onlyIf({ value: 'poster', condition: isRenderProp }), }) ); }, }), code.edit.jsx.element({ when: code.match.jsx.tag(BUTTON_RENDER_TARGET), transform: code.edit.jsx.addProp('type', 'button'), }), code.edit.jsx.prop({ when: code.match.all(code.match.jsx.prop('className'), code.match.value.array()), transform: ({ value }) => code.create.value.call(cn, code.create.value.arrayItems(value)), }), code.edit.interface.property({ when: code.match.all(code.match.interface.name(/Props$/), code.match.interface.property('children')), transform: code.edit.interface.setType(() => code.create.type.union(code.create.type.named(ReactNode), code.create.type.undefined()) ), }), code.edit.interface.declaration({ when: code.match.interface.name('DefaultVideoSkinProps'), transform: code.edit.interface.extends(BaseVideoSkinProps), }), code.edit.function.declaration({ when: code.match.function.name('DefaultVideoSkin'), transform: code.edit.function.addProps(['poster', { name: 'rest', spread: true }]), }), ]; }), ], }); function coreComponentImport(name: string): { source: string; name: string } { return { source: name === 'Container' ? '@/player/context' : `@/ui/${componentModuleName(name)}`, name, }; } function componentModuleName(name: string): string { return name .replace(/^AirPlay/, 'Airplay') .replace(/^PiP/, 'Pip') .replace(/([a-z0-9])([A-Z])/g, '$1-$2') .toLowerCase(); }