mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(compiler)!: move component generation to core
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { AirPlayButtonProps } from './airplay-button-core';
|
||||
import { AirPlayButtonDataAttrs } from './airplay-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { AlertDialogProps } from './alert-dialog-core';
|
||||
import { AlertDialogDataAttrs } from './alert-dialog-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { BufferingIndicatorProps } from './buffering-indicator-core';
|
||||
import { BufferingIndicatorDataAttrs } from './buffering-indicator-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { CaptionsButtonProps } from './captions-button-core';
|
||||
import { CaptionsButtonDataAttrs } from './captions-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { CaptionsRadioGroupProps } from './captions-radio-group-core';
|
||||
import { CaptionsRadioGroupDataAttrs } from './captions-radio-group-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { CastButtonProps } from './cast-button-core';
|
||||
import { CastButtonDataAttrs } from './cast-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AUTO-GENERATED by `@videojs/compiler`. DO NOT EDIT.
|
||||
import { createComponent } from '@videojs/compiler/jsx-runtime';
|
||||
// AUTO-GENERATED by `@videojs/core/scripts/generate-components`. DO NOT EDIT.
|
||||
import { createComponent } from '../../jsx-runtime';
|
||||
|
||||
import AirPlayButtonDef from './airplay-button/airplay-button-component';
|
||||
import AlertDialogDef from './alert-dialog/alert-dialog-component';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import { ControlsDataAttrs } from './controls-data-attrs';
|
||||
|
||||
export default defineComponent()({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import type { AlertDialogProps } from '../alert-dialog/alert-dialog-core';
|
||||
import { defineComponent } from '../manifest';
|
||||
import { ErrorDialogDataAttrs } from './error-dialog-data-attrs';
|
||||
|
||||
export default defineComponent<AlertDialogProps>()({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { FullscreenButtonProps } from './fullscreen-button-core';
|
||||
import { FullscreenButtonDataAttrs } from './fullscreen-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { LiveButtonProps } from './live-button-core';
|
||||
import { LiveButtonDataAttrs } from './live-button-data-attrs';
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
declare const __PROPS_BRAND__: unique symbol;
|
||||
declare const __EMPTY_PROPS__: unique symbol;
|
||||
|
||||
export type EmptyProps = {
|
||||
readonly [__EMPTY_PROPS__]?: never;
|
||||
};
|
||||
|
||||
export interface ComponentManifest<
|
||||
Props extends object = EmptyProps,
|
||||
Parts extends readonly string[] = readonly string[],
|
||||
PartProps extends Partial<Record<Parts[number], object>> = Partial<Record<Parts[number], object>>,
|
||||
> {
|
||||
name: string;
|
||||
parts?: Parts;
|
||||
dataAttrs?: Record<string, string>;
|
||||
partProps?: PartProps;
|
||||
readonly [__PROPS_BRAND__]?: Props;
|
||||
}
|
||||
|
||||
export type InferProps<T> =
|
||||
T extends ComponentManifest<infer Props, readonly string[], Partial<Record<string, object>>> ? Props : never;
|
||||
|
||||
export type InferParts<T> =
|
||||
T extends ComponentManifest<object, infer Parts, Partial<Record<string, object>>>
|
||||
? readonly string[] extends Parts
|
||||
? never
|
||||
: Parts[number]
|
||||
: never;
|
||||
|
||||
export type InferPartProps<T, K extends string> =
|
||||
T extends ComponentManifest<object, readonly string[], infer PartProps>
|
||||
? K extends keyof PartProps
|
||||
? PartProps[K]
|
||||
: never
|
||||
: never;
|
||||
|
||||
/** Define a component manifest. */
|
||||
export function defineComponent<Props extends object = EmptyProps>() {
|
||||
return <
|
||||
const Parts extends readonly string[] = readonly string[],
|
||||
const PartProps extends Partial<Record<Parts[number], object>> = Partial<Record<Parts[number], object>>,
|
||||
>(
|
||||
manifest: Omit<ComponentManifest<Props, Parts, PartProps>, typeof __PROPS_BRAND__>
|
||||
): ComponentManifest<Props, Parts, PartProps> => manifest as ComponentManifest<Props, Parts, PartProps>;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { MenuProps } from './menu-core';
|
||||
import { MenuDataAttrs } from './menu-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { MuteButtonProps } from './mute-button-core';
|
||||
import { MuteButtonDataAttrs } from './mute-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { PiPButtonProps } from './pip-button-core';
|
||||
import { PiPButtonDataAttrs } from './pip-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { PlayButtonProps } from './play-button-core';
|
||||
import { PlayButtonDataAttrs } from './play-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { PlaybackRateButtonProps } from './playback-rate-button-core';
|
||||
import { PlaybackRateButtonDataAttrs } from './playback-rate-button-data-attrs';
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { PlaybackRateRadioGroupProps } from './playback-rate-radio-group-core';
|
||||
import { PlaybackRateRadioGroupDataAttrs } from './playback-rate-radio-group-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { PopoverProps } from './popover-core';
|
||||
import { PopoverDataAttrs } from './popover-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import { PosterDataAttrs } from './poster-data-attrs';
|
||||
|
||||
export default defineComponent()({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { QualityRadioGroupProps } from './quality-radio-group-core';
|
||||
import { QualityRadioGroupDataAttrs } from './quality-radio-group-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { SeekButtonProps } from './seek-button-core';
|
||||
import { SeekButtonDataAttrs } from './seek-button-data-attrs';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import type { SeekIndicatorProps } from '../input-feedback/seek-indicator-core';
|
||||
import { SeekIndicatorDataAttrs } from '../input-feedback/seek-indicator-data-attrs';
|
||||
import { defineComponent } from '../manifest';
|
||||
|
||||
export default defineComponent<SeekIndicatorProps>()({
|
||||
name: 'SeekIndicator',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { SliderProps } from './slider-core';
|
||||
import { SliderDataAttrs } from './slider-data-attrs';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import type { StatusAnnouncerProps } from '../input-feedback/status-announcer-core';
|
||||
import { defineComponent } from '../manifest';
|
||||
|
||||
export default defineComponent<StatusAnnouncerProps>()({
|
||||
name: 'StatusAnnouncer',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import type { StatusIndicatorProps } from '../input-feedback/status-indicator-core';
|
||||
import { StatusIndicatorDataAttrs } from '../input-feedback/status-indicator-data-attrs';
|
||||
import { defineComponent } from '../manifest';
|
||||
|
||||
export default defineComponent<StatusIndicatorProps>()({
|
||||
name: 'StatusIndicator',
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/** @jsxImportSource ../../.. */
|
||||
|
||||
import { describe, it } from 'vitest';
|
||||
import { createComponent, Slot } from '../../../jsx-runtime';
|
||||
import { defineComponent } from '../manifest';
|
||||
|
||||
const PlayButton = createComponent(
|
||||
defineComponent()({
|
||||
name: 'PlayButton',
|
||||
})
|
||||
);
|
||||
|
||||
const Slider = createComponent(
|
||||
defineComponent<{ orientation?: 'horizontal' | 'vertical'; thumbAlignment?: 'center' | 'edge' }>()({
|
||||
name: 'Slider',
|
||||
parts: ['Root', 'Track', 'Fill', 'Thumb'] as const,
|
||||
})
|
||||
);
|
||||
|
||||
const Time = createComponent(
|
||||
defineComponent()({
|
||||
name: 'Time',
|
||||
parts: ['Value'] as const,
|
||||
partProps: {
|
||||
Value: {} as { type: 'current' | 'duration' },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('constrained JSX', () => {
|
||||
it('accepts a single component', () => {
|
||||
void (<PlayButton className="x" />);
|
||||
});
|
||||
|
||||
it('rejects invalid props on a single component', () => {
|
||||
// @ts-expect-error - className must be a string
|
||||
void (<PlayButton className={5} />);
|
||||
});
|
||||
|
||||
it('accepts compound parts inside their root', () => {
|
||||
void (
|
||||
<Slider.Root orientation="vertical" thumbAlignment="edge">
|
||||
<Slider.Track>
|
||||
<Slider.Fill />
|
||||
</Slider.Track>
|
||||
<Slider.Thumb />
|
||||
</Slider.Root>
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects invalid compound root props', () => {
|
||||
// @ts-expect-error - `bogus` is not a valid orientation
|
||||
void (<Slider.Root orientation="bogus" />);
|
||||
});
|
||||
|
||||
it('rejects invalid Time.Value props', () => {
|
||||
void (<Time.Value type="current" className="t" />);
|
||||
// @ts-expect-error - `bogus` not in Time type union
|
||||
void (<Time.Value type="bogus" />);
|
||||
});
|
||||
|
||||
it('accepts div and span as layout intrinsics', () => {
|
||||
void (
|
||||
<div className="row">
|
||||
<span className="label">hello</span>
|
||||
</div>
|
||||
);
|
||||
// @ts-expect-error - arbitrary HTML attributes (id) are not allowed on layout intrinsics
|
||||
void (<div id="foo" />);
|
||||
});
|
||||
|
||||
it('accepts slot primitives', () => {
|
||||
void (<Slot />);
|
||||
void (
|
||||
<Slot name="poster">
|
||||
<span className="fallback" />
|
||||
</Slot>
|
||||
);
|
||||
// @ts-expect-error - slot name must be a string
|
||||
void (<Slot name={5} />);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "../../..",
|
||||
"noEmit": true,
|
||||
"isolatedDeclarations": false,
|
||||
"composite": false,
|
||||
"incremental": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false
|
||||
},
|
||||
"include": ["**/*.test-d.tsx"]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { ThumbnailProps } from './thumbnail-core';
|
||||
import { ThumbnailDataAttrs } from './thumbnail-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { TimeSliderProps } from './time-slider-core';
|
||||
import { TimeSliderDataAttrs } from './time-slider-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { TimeProps } from './time-core';
|
||||
import { TimeDataAttrs } from './time-data-attrs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { TooltipProps } from './tooltip-core';
|
||||
import { TooltipDataAttrs } from './tooltip-data-attrs';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import type { VolumeIndicatorProps } from '../input-feedback/volume-indicator-core';
|
||||
import { VolumeIndicatorDataAttrs } from '../input-feedback/volume-indicator-data-attrs';
|
||||
import { defineComponent } from '../manifest';
|
||||
|
||||
export default defineComponent<VolumeIndicatorProps>()({
|
||||
name: 'VolumeIndicator',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent } from '@videojs/compiler';
|
||||
import { defineComponent } from '../manifest';
|
||||
import type { VolumeSliderProps } from './volume-slider-core';
|
||||
import { VolumeSliderDataAttrs } from './volume-slider-data-attrs';
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './jsx-runtime';
|
||||
export { jsx as jsxDEV } from './jsx-runtime';
|
||||
@@ -0,0 +1,113 @@
|
||||
import type { ComponentManifest, InferPartProps, InferParts, InferProps } from './core/ui/manifest';
|
||||
|
||||
export const VIDEOJS_NODE = Symbol.for('@videojs/node');
|
||||
|
||||
declare const EMPTY_PROPS_SYMBOL: unique symbol;
|
||||
|
||||
type EmptyProps = {
|
||||
readonly [EMPTY_PROPS_SYMBOL]?: never;
|
||||
};
|
||||
|
||||
export type ComponentType = string | Component<never> | typeof Fragment;
|
||||
|
||||
export interface ComponentNode {
|
||||
readonly [VIDEOJS_NODE]: true;
|
||||
readonly type: ComponentType;
|
||||
readonly props: Record<string, unknown>;
|
||||
readonly key: string | number | null;
|
||||
}
|
||||
|
||||
export interface BaseProps {
|
||||
className?: string | undefined;
|
||||
children?: unknown;
|
||||
}
|
||||
|
||||
export interface SlotProps {
|
||||
name?: string | undefined;
|
||||
children?: unknown;
|
||||
}
|
||||
|
||||
export interface Component<Props extends object> {
|
||||
(props: BaseProps & Props): ComponentNode;
|
||||
readonly $$component: { name: string; part: string | null };
|
||||
}
|
||||
|
||||
type PartComponentProps<M, K extends string> = K extends 'Root'
|
||||
? InferProps<M>
|
||||
: [NonNullable<InferPartProps<M, K>>] extends [never]
|
||||
? EmptyProps
|
||||
: NonNullable<InferPartProps<M, K>>;
|
||||
|
||||
type CompoundComponent<M> = {
|
||||
[K in InferParts<M> & string]: Component<PartComponentProps<M, K>>;
|
||||
};
|
||||
|
||||
export type CreateComponentResult<M> = [InferParts<M>] extends [never]
|
||||
? Component<InferProps<M>>
|
||||
: CompoundComponent<M>;
|
||||
|
||||
function makePart<Props extends object>(name: string, part: string | null): Component<Props> {
|
||||
const fn = (_props: BaseProps & Props): ComponentNode => {
|
||||
throw new Error(`@videojs/core: <${name}${part ? `.${part}` : ''}> can only be evaluated by the compiler.`);
|
||||
};
|
||||
|
||||
Object.assign(fn, { $$component: { name, part } });
|
||||
|
||||
return fn as Component<Props>;
|
||||
}
|
||||
|
||||
export const Slot = makePart<SlotProps>('Slot', null);
|
||||
|
||||
export function createComponent<
|
||||
M extends ComponentManifest<object, readonly string[], Partial<Record<string, object>>>,
|
||||
>(manifest: M): CreateComponentResult<M> {
|
||||
const parts = manifest.parts ?? [];
|
||||
|
||||
if (parts.length === 0) {
|
||||
return makePart(manifest.name, null) as CreateComponentResult<M>;
|
||||
}
|
||||
|
||||
const compound: Record<string, Component<never>> = {};
|
||||
|
||||
for (const part of parts) {
|
||||
compound[part] = makePart(manifest.name, part);
|
||||
}
|
||||
|
||||
return compound as CreateComponentResult<M>;
|
||||
}
|
||||
|
||||
function createNode(type: ComponentType, props: Record<string, unknown>, key?: string | number | null): ComponentNode {
|
||||
return {
|
||||
[VIDEOJS_NODE]: true,
|
||||
type,
|
||||
props,
|
||||
key: key ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export function jsx(type: ComponentType, props: Record<string, unknown>, key?: string | number | null): ComponentNode {
|
||||
return createNode(type, props, key);
|
||||
}
|
||||
|
||||
export function jsxs(type: ComponentType, props: Record<string, unknown>, key?: string | number | null): ComponentNode {
|
||||
return createNode(type, props, key);
|
||||
}
|
||||
|
||||
export const Fragment: unique symbol = Symbol.for('@videojs/fragment') as never;
|
||||
|
||||
export namespace JSX {
|
||||
export type Element = ComponentNode;
|
||||
|
||||
export interface ElementChildrenAttribute {
|
||||
children: Record<string, never>;
|
||||
}
|
||||
|
||||
export interface IntrinsicAttributes {
|
||||
key?: string | number | undefined;
|
||||
}
|
||||
|
||||
export interface IntrinsicElements {
|
||||
div: BaseProps;
|
||||
span: BaseProps;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user