Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/website-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,39 @@ jobs:

- name: Run website tests
run: pnpm exec turbo run test --filter=site

astro-check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install

- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-

- name: Build packages
run: pnpm build:packages

- name: Generate site content
run: cd site && pnpm api-docs && pnpm ejected-skins

- name: Astro check
run: cd site && pnpm astro check --minimumSeverity warning
1 change: 1 addition & 0 deletions site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export default defineConfig({
// SVG → React component transform. We use SVGR instead of Astro's
// experimental svg feature because: (1) React islands need React
// components, and (2) SVGR runs SVGO for automatic SVG optimization.
// @ts-expect-error — Astro 5 uses Vite 6 types, but these plugins ship Vite 8 types. Compatible at runtime.
plugins: [tailwindcss(), svgr()],
optimizeDeps: {
exclude: ['@videojs/react', '@videojs/html'],
Expand Down
2 changes: 1 addition & 1 deletion site/netlify/edge-functions/markdown-negotiation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Config, Context } from '@netlify/edge-functions';

export default async (request: Request, context: Context) => {
export default async (request: Request, _context: Context) => {
const url = new URL(request.url);
const path = url.pathname.replace(/\/$/, '');

Expand Down
1 change: 1 addition & 0 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/jsdom": "^28.0.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.1",
"@types/turndown": "^5.0.6",
Expand Down
4 changes: 2 additions & 2 deletions site/scripts/api-docs-builder/src/core-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function extractDefaultProps(

const defaultProps: Record<string, string> = {};

function visit(node: ts.Node) {
const visit = (node: ts.Node) => {
// Look for class declaration
if (ts.isClassDeclaration(node) && node.name?.text === `${componentName}Core`) {
for (const member of node.members) {
Expand Down Expand Up @@ -103,7 +103,7 @@ export function extractDefaultProps(
}

ts.forEachChild(node, visit);
}
};

visit(sourceFile);

Expand Down
4 changes: 2 additions & 2 deletions site/scripts/api-docs-builder/src/css-vars-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function extractCSSVars(filePath: string, program: ts.Program, componentN
const vars: Array<{ name: string; description: string }> = [];
const expectedName = `${componentName}CSSVars`;

function visit(node: ts.Node) {
const visit = (node: ts.Node) => {
if (ts.isVariableStatement(node)) {
for (const decl of node.declarationList.declarations) {
if (!ts.isIdentifier(decl.name) || decl.name.text !== expectedName || !decl.initializer) {
Expand Down Expand Up @@ -74,7 +74,7 @@ export function extractCSSVars(filePath: string, program: ts.Program, componentN
}

ts.forEachChild(node, visit);
}
};

visit(sourceFile);

Expand Down
4 changes: 2 additions & 2 deletions site/scripts/api-docs-builder/src/data-attrs-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function extractDataAttrs(
// Common naming patterns for data attributes exports
const possibleNames = [`${componentName}DataAttrs`, `${componentName}DataAttributes`];

function visit(node: ts.Node) {
const visit = (node: ts.Node) => {
// Look for variable declaration like: export const PlayButtonDataAttrs = { ... }
if (ts.isVariableStatement(node)) {
for (const decl of node.declarationList.declarations) {
Expand Down Expand Up @@ -136,7 +136,7 @@ export function extractDataAttrs(
}

ts.forEachChild(node, visit);
}
};

visit(sourceFile);

Expand Down
2 changes: 1 addition & 1 deletion site/scripts/api-docs-builder/src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function formatProperties(props: tae.PropertyNode[], allExports?: tae.Exp

const entry: PropDef = { type: abbreviated ?? expandedType };
if (abbreviated && expandedType !== abbreviated) entry.detailedType = expandedType;
if (prop.documentation?.defaultValue !== undefined) entry.default = prop.documentation.defaultValue;
if (prop.documentation?.defaultValue !== undefined) entry.default = String(prop.documentation.defaultValue);
if (!prop.optional) entry.required = true;
if (prop.documentation?.description !== undefined) entry.description = prop.documentation.description;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ interface HtmlPlayerOptions {
}

/** Create an HTML player instance. */
export function createPlayer(options: HtmlPlayerOptions): HtmlPlayerInstance {
export function createPlayer(_options: HtmlPlayerOptions): HtmlPlayerInstance {
return {} as HtmlPlayerInstance;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ interface StoreState {
/** Access the player store or select state from it. */
export function usePlayer(): PlayerStore;
export function usePlayer<R>(selector: (state: StoreState) => R): R;
export function usePlayer<R>(selector?: (state: StoreState) => R): PlayerStore | R {
export function usePlayer<R>(_selector?: (state: StoreState) => R): PlayerStore | R {
return {} as PlayerStore | R;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ interface PlayerOptions {
}

/** Create a React player instance. */
export function createPlayer(options?: PlayerOptions): PlayerInstance {
export function createPlayer(_options?: PlayerOptions): PlayerInstance {
return {} as PlayerInstance;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class SnapshotController<S, R = S> implements ReactiveController {
* @param state - The store to snapshot.
*/
constructor(host: ReactiveControllerHost, state: Store<S>);
constructor(host: ReactiveControllerHost, state: Store<S>, selector?: (state: S) => R) {
constructor(host: ReactiveControllerHost, _state: Store<S>, _selector?: (state: S) => R) {
host.addController(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ interface Store<S> {
/** Subscribe to a store. */
export function useStore<S>(store: Store<S>): S;
export function useStore<S, R>(store: Store<S>, selector: (state: S) => R): R;
export function useStore<S, R>(store: Store<S>, selector?: (state: S) => R): S | R {
export function useStore<S, R>(_store: Store<S>, _selector?: (state: S) => R): S | R {
return {} as S | R;
}
23 changes: 13 additions & 10 deletions site/scripts/api-docs-builder/src/util-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,15 @@ function extractPublicMembers(
for (const member of classDecl.members) {
// Skip private, protected, static, constructor
if (
member.modifiers?.some(
(m) =>
m.kind === ts.SyntaxKind.PrivateKeyword ||
m.kind === ts.SyntaxKind.ProtectedKeyword ||
m.kind === ts.SyntaxKind.StaticKeyword
)
ts.canHaveModifiers(member) &&
ts
.getModifiers(member)
?.some(
(m: ts.ModifierLike) =>
m.kind === ts.SyntaxKind.PrivateKeyword ||
m.kind === ts.SyntaxKind.ProtectedKeyword ||
m.kind === ts.SyntaxKind.StaticKeyword
)
)
continue;

Expand Down Expand Up @@ -502,7 +505,7 @@ function getNodeJSDoc(node: ts.Node): string | undefined {
if (typeof doc.comment === 'string') return doc.comment;

// Handle JSDocComment array
return doc.comment.map((c: ts.JSDocText | ts.JSDocLink) => ('text' in c ? c.text : '')).join('');
return doc.comment.map((c: ts.JSDocComment) => ('text' in c ? c.text : '')).join('');
}

function getJSDocParamDescription(node: ts.Node, paramName: string): string | undefined {
Expand All @@ -517,7 +520,7 @@ function getJSDocParamDescription(node: ts.Node, paramName: string): string | un
const raw =
typeof tag.comment === 'string'
? tag.comment
: tag.comment.map((c: ts.JSDocText | ts.JSDocLink) => ('text' in c ? c.text : '')).join('');
: tag.comment.map((c: ts.JSDocComment) => ('text' in c ? c.text : '')).join('');
return raw.replace(/^\s*-\s+/, '');
}
}
Expand Down Expand Up @@ -550,7 +553,7 @@ function getJSDocTagValue(node: ts.Node, tagName: string): string | undefined {
if (!tag.comment) return undefined;
if (typeof tag.comment === 'string') return tag.comment.trim();
return tag.comment
.map((c: ts.JSDocText | ts.JSDocLink) => ('text' in c ? c.text : ''))
.map((c: ts.JSDocComment) => ('text' in c ? c.text : ''))
.join('')
.trim();
}
Expand Down Expand Up @@ -947,7 +950,7 @@ function discoverUtilExports(monorepoRoot: string, program: ts.Program): UtilEnt
for (const modulePath of modulesToScan) {
if (!fs.existsSync(modulePath)) continue;

let ast: tae.Module;
let ast: tae.ModuleNode;
try {
ast = tae.parseFromProgram(modulePath, program);
} catch {
Expand Down
7 changes: 0 additions & 7 deletions site/scripts/build-ejected-skins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,6 @@ function getStatementName(statement: ts.Statement): string | null {
return null;
}

function parseNames(raw: string): string[] {
return raw
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}

function isRelativeImport(specifier: string): boolean {
return specifier.startsWith('./') || specifier.startsWith('../');
}
Expand Down
3 changes: 3 additions & 0 deletions site/src/components/JsonLd.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ interface Props {
const { schema } = Astro.props;
---

{
/* astro(4000) is expected — JSON-LD is static data that should be inline (withastro/astro#3544) */
}
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
3 changes: 1 addition & 2 deletions site/src/components/NavBar/NavBar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import GetStartedLink from './GetStartedLink';
import MobileNav from './MobileNav';

export interface Props {
dark?: boolean;
class?: string;
}

Expand All @@ -29,7 +28,7 @@ const navLinks: NavLink[] = [
const currentPath = Astro.url.pathname;
const isDocsActive = currentPath.startsWith('/docs');
const otherNavLinks = navLinks.filter((link) => link.href !== '/docs');
const { class: className, dark = false } = Astro.props;
const { class: className } = Astro.props;
---

<nav
Expand Down
3 changes: 3 additions & 0 deletions site/src/components/ThemeInit.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { THEME_KEY } from '@/consts';
---

{/* Dark mode initialization script - runs before paint to prevent FOUC */}
{
/* ts(2570) on localStorageKey is expected — astro check can't see define:vars injections (withastro/language-tools#711) */
}
<script is:inline define:vars={{ localStorageKey: THEME_KEY }}>
if (
typeof localStorage !== "undefined" &&
Expand Down
2 changes: 2 additions & 0 deletions site/src/components/blog/AuthorSocialLinks.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Github, Linkedin, Twitter are deprecated brand icons (lucide-icons/lucide#670).
// Lucide will remove them in v1.0 — replace with simple-icons or custom SVGs then.
import { AtSign, Github, Globe, Linkedin, Twitter } from 'lucide-react';
import { Fragment } from 'react';
import { Tooltip, TooltipProvider } from '../Tooltip';
Expand Down
4 changes: 1 addition & 3 deletions site/src/components/blog/BlogPostCard.astro
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
---

import { Image } from 'astro:assets';
import type { CollectionEntry } from 'astro:content';
import FormattedDate from '@/components/FormattedDate.astro';

interface Props {
post: CollectionEntry<'blog'>;
authors: CollectionEntry<'authors'>[];
asLink?: boolean;
}

const { post, authors, asLink } = Astro.props;
const { post, authors } = Astro.props;
---

<article>
Expand Down
2 changes: 1 addition & 1 deletion site/src/components/docs/api-reference/DetailRow.astro
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface Props {
colspan: number;
}

const { id, name, type, attributeName, detailedType, description, colspan } = Astro.props;
const { id, name, attributeName, detailedType, description, colspan } = Astro.props;

const hasDetail = Boolean(attributeName || detailedType || description);
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class PlayToggle extends MediaElement {
isDisabled: () => !this.#player.value,
});

applyElementProps(this, buttonProps, this.#disconnect.signal);
applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
}

override disconnectedCallback(): void {
Expand All @@ -46,8 +46,8 @@ class PlayToggle extends MediaElement {
this.#disconnect = null;
}

protected override update(): void {
super.update();
protected override update(changed: Map<string, unknown>): void {
super.update(changed);
const state = this.#player.value;
if (!state) return;
applyStateDataAttrs(this, state, { paused: 'data-paused', ended: 'data-ended' });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { applyElementProps, createButton, createPlayer, MediaElement } from '@videojs/html';
import {
applyElementProps,
createButton,
createPlayer,
MediaElement,
selectPlayback,
selectTime,
selectVolume,
} from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';
import '@videojs/html/media/container';

Expand Down Expand Up @@ -28,7 +36,7 @@ class PlayerActions extends MediaElement {

const bind = (el: HTMLElement, action: () => void) => {
const props = createButton({ onActivate: action, isDisabled: () => !this.#player.value });
applyElementProps(el, props, signal);
applyElementProps(el, props, { signal });
};

bind(playBtn, () => this.#player.value?.play());
Expand All @@ -46,20 +54,20 @@ class PlayerActions extends MediaElement {
class PlayerState extends MediaElement {
static readonly tagName = 'demo-ctrl-state';

readonly #state = new PlayerController(this, context, (s) => ({
paused: s.paused,
currentTime: s.currentTime,
volume: s.volume,
}));
readonly #playback = new PlayerController(this, context, selectPlayback);
readonly #time = new PlayerController(this, context, selectTime);
readonly #volume = new PlayerController(this, context, selectVolume);

protected override update(): void {
super.update();
const state = this.#state.value;
if (!state) return;
protected override update(changed: Map<string, unknown>): void {
super.update(changed);
const playback = this.#playback.value;
const time = this.#time.value;
const volume = this.#volume.value;
if (!playback) return;

const el = this.querySelector('.text');
if (el) {
el.textContent = `Paused: ${state.paused ? 'Yes' : 'No'} | Time: ${state.currentTime.toFixed(1)}s | Volume: ${Math.round(state.volume * 100)}%`;
el.textContent = `Paused: ${playback.paused ? 'Yes' : 'No'} | Time: ${(time?.currentTime ?? 0).toFixed(1)}s | Volume: ${Math.round((volume?.volume ?? 0) * 100)}%`;
}
}
}
Expand Down
Loading
Loading