feat(packages): add mux media with src parsing, structured source, and storyboards (#1850)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Wesley Luyten
2026-07-27 16:55:24 -07:00
committed by GitHub
co-authored by Claude
parent fd4d2662ea
commit 409e7ef225
29 changed files with 1519 additions and 41 deletions
@@ -247,8 +247,13 @@ function parseCustomMediaElementCall(
const extendsClause = node.heritageClauses.find((h) => h.token === ts.SyntaxKind.ExtendsKeyword);
if (!extendsClause || extendsClause.types.length === 0) return;
const extendsExpr = extendsClause.types[0]!.expression;
findCustomMediaElement(extendsExpr);
const extendsExpr = unwrapExpression(extendsClause.types[0]!.expression);
// Handle hoisted bases: `const Base = Mixin(CustomMediaElement(...)); class X extends (Base as ...)`
findCustomMediaElement(
ts.isIdentifier(extendsExpr)
? (resolveLocalInitializer(sourceFile, extendsExpr.text) ?? extendsExpr)
: extendsExpr
);
});
// Media implementations may put template behavior on a local base class
@@ -380,6 +385,19 @@ function unwrapExpression(expr: ts.Expression): ts.Expression {
return expr;
}
/** Resolve a same-file `const <name> = <initializer>` declaration to its initializer. */
function resolveLocalInitializer(sourceFile: ts.SourceFile, name: string): ts.Expression | undefined {
for (const statement of sourceFile.statements) {
if (!ts.isVariableStatement(statement)) continue;
for (const declaration of statement.declarationList.declarations) {
if (ts.isIdentifier(declaration.name) && declaration.name.text === name && declaration.initializer) {
return unwrapExpression(declaration.initializer);
}
}
}
return undefined;
}
/**
* Process an `extends` expression — either an `Identifier` (regular class
* inheritance) or a `CallExpression` (mixin chain like `OuterMixin(InnerMixin(Base))`).
@@ -83,6 +83,9 @@
* (ExtendingHost extends ComplexHost). Builder must
* walk the extends chain to include inherited properties.
* Child overrides (debug) replace parent definitions.
* Also exercises a hoisted-const base with an `as` cast
* (`extends (Base as typeof Base)`) the builder must
* unwrap the cast and resolve the local const initializer.
* container.ts Exclusion case. Not a media element re-exports an
* existing class instead of declaring one inline.
* background-video.ts Exclusion case. Uses MediaAttachMixin(HTMLElement)
@@ -1,7 +1,8 @@
/**
* Mock extending media element mirrors MuxVideo.
*
* Exercises: media element with a host that inherits from another host.
* Exercises: media element with a host that inherits from another host,
* plus a hoisted-const base with an `as` cast in the extends clause.
*/
import { CustomMediaElement } from '../../../../core/src/dom/media/custom-media-element';
import { ExtendingHost } from '../../../../core/src/dom/media/extending';
@@ -10,4 +11,6 @@ function MediaAttachMixin(base: any) {
return base;
}
export class ExtendingVideo extends MediaAttachMixin(CustomMediaElement('video', ExtendingHost)) {}
const ExtendingVideoBase = MediaAttachMixin(CustomMediaElement('video', ExtendingHost));
export class ExtendingVideo extends (ExtendingVideoBase as typeof ExtendingVideoBase) {}