Skip to content

Commit e22cb39

Browse files
Merge pull request #1541 from AxiomCodeAI/loop/ty-ts-jsx-tags
typescript: a JSX component tag is a call to the component
2 parents 7fb4ed6 + a055664 commit e22cb39

33 files changed

Lines changed: 407 additions & 61 deletions

‎graph/bundle/SCHEMA.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ THE GRAPH. One row per (site, resolved target). A site with N possible targets h
491491
| `DYNAMIC_IMPORT_CALL` | typescript | `import(…)`. |
492492
| `DECORATOR_CALL` | typescript | A decorator application `@d` / `@d(…)`. |
493493
| `OPTIONAL_CALL` | typescript | `f?.(…)`. |
494-
| `JSX_COMPONENT_CALL` | typescript | `<Component …/>` (reserved by the parser; emitted by nothing yet). |
494+
| `JSX_COMPONENT_CALL` | typescript | `<Component …/>` — a JSX element naming a component; `<div>` emits no site. |
495495
| `PROPERTY_READ` | typescript, javascript | Reading `obj.x` where `x` is a `get` accessor runs the getter (engine-authored). No written call; the site is the property-access expression. A compound assignment or `++` reads before it writes, so it carries this and PROPERTY_WRITE. |
496496
| `PROPERTY_WRITE` | typescript, javascript | Assigning `obj.x = v` where `x` is a `set` accessor runs the setter (engine-authored). No written call; the site is the property-access expression on the left. |
497497
| `FUNCTION_CALL` | javascript | `f(…)` — a bare callee, resolved by the binder. |

‎graph/bundle/schema.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ export const VOCAB: readonly VocabSpec[] = [
623623
{ table: 'call_edges', column: 'kind', value: 'DYNAMIC_IMPORT_CALL', languages: T, meaning: '`import(…)`.' },
624624
{ table: 'call_edges', column: 'kind', value: 'DECORATOR_CALL', languages: T, meaning: 'A decorator application `@d` / `@d(…)`.' },
625625
{ table: 'call_edges', column: 'kind', value: 'OPTIONAL_CALL', languages: T, meaning: '`f?.(…)`.' },
626-
{ table: 'call_edges', column: 'kind', value: 'JSX_COMPONENT_CALL', languages: T, meaning: '`<Component …/>` (reserved by the parser; emitted by nothing yet).' },
626+
{ table: 'call_edges', column: 'kind', value: 'JSX_COMPONENT_CALL', languages: T, meaning: '`<Component …/>` — a JSX element naming a component; `<div>` emits no site.' },
627627
{ table: 'call_edges', column: 'kind', value: 'PROPERTY_READ', languages: ['typescript', 'javascript'], meaning: 'Reading `obj.x` where `x` is a `get` accessor runs the getter (engine-authored). No written call; the site is the property-access expression. A compound assignment or `++` reads before it writes, so it carries this and PROPERTY_WRITE.' },
628628
{ table: 'call_edges', column: 'kind', value: 'PROPERTY_WRITE', languages: ['typescript', 'javascript'], meaning: 'Assigning `obj.x = v` where `x` is a `set` accessor runs the setter (engine-authored). No written call; the site is the property-access expression on the left.' },
629629
// — JavaScript (the parser's JsCallKind)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Avatar, { Badge, Field, lazy, Panel, UserCard, factory, ui } from "./parts";
2+
3+
// lazy() around a dynamic import: the component is the module's default export.
4+
const Settings = lazy(() => import("./settings"));
5+
6+
export function App(): string {
7+
return (
8+
<section>
9+
<UserCard id="1" />
10+
<Field label="name" />
11+
<Badge n={2} />
12+
<Avatar src="x" />
13+
<Panel title="t" />
14+
<Settings />
15+
<ui.Chip text="c" />
16+
<div>plain</div>
17+
</section>
18+
);
19+
}
20+
21+
// CONTROL: the factory product is called, never its argument.
22+
export function useFactory(): unknown {
23+
return factory();
24+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// The shapes a component is DECLARED in, each rendered from app.tsx as a JSX tag.
2+
// `<X/>` is an invocation of X whatever X is bound to: a function, an arrow held by
3+
// a const, a default export, a class, or a wrapper's result around one of those.
4+
//
5+
// No React dependency: the wrappers are declared here with the signatures that matter.
6+
declare global {
7+
namespace JSX {
8+
type Element = string;
9+
interface ElementAttributesProperty {
10+
props: unknown;
11+
}
12+
interface IntrinsicElements {
13+
[name: string]: unknown;
14+
}
15+
}
16+
}
17+
18+
export type Fc<P> = (props: P) => string;
19+
export declare function memo<P>(component: Fc<P>): Fc<P>;
20+
export declare function forwardRef<P>(render: (props: P, ref: unknown) => string): Fc<P>;
21+
export declare function lazy<P>(load: () => Promise<{ default: Fc<P> }>): Fc<P>;
22+
export declare function makeFactory<T>(build: () => T): () => T;
23+
24+
export function formatName(n: string): string {
25+
return n.toUpperCase();
26+
}
27+
28+
export function trackClick(id: string): string {
29+
return id;
30+
}
31+
32+
// memo() around a NAMED function expression.
33+
export const UserCard = memo(function UserCard(p: { id: string }): string {
34+
return formatName(p.id);
35+
});
36+
37+
// forwardRef() around a function declared elsewhere, passed by name.
38+
function FieldImpl(p: { label: string }, ref: unknown): string {
39+
return trackClick(p.label) + String(ref);
40+
}
41+
export const Field = forwardRef(FieldImpl);
42+
43+
// An arrow held by a typed const.
44+
export const Badge: Fc<{ n: number }> = (p) => String(p.n);
45+
46+
// A default export.
47+
export default function Avatar(p: { src: string }): string {
48+
return p.src;
49+
}
50+
51+
// A class component: the constructor is the nameable target.
52+
export class Panel {
53+
props: { title: string };
54+
constructor(props: { title: string }) {
55+
this.props = props;
56+
}
57+
render(): string {
58+
return this.props.title;
59+
}
60+
}
61+
62+
export const ui = {
63+
Chip(p: { text: string }): string {
64+
return p.text;
65+
},
66+
};
67+
68+
// CONTROL: a non-JSX wrapper. A factory's argument 0 is not what its product runs
69+
// when called, so `factory()` must not become a call to buildDefault.
70+
export function buildDefault(): number {
71+
return 1;
72+
}
73+
export const factory = makeFactory(buildDefault);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function saveSettings(): number {
2+
return 1;
3+
}
4+
5+
export default function Settings(): string {
6+
return String(saveSettings());
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
ambiguous_unknown CONSTRUCTOR_CALL components#LibApp() @L54 -> -
2+
ambiguous_unknown JSX_COMPONENT_CALL components#LibApp() @L53 -> -
23
ambiguous_unknown METHOD_CALL components#LibApp() @L54 -> -
34
known_edge CONSTRUCTOR_CALL components#App() @L44 -> Widget#<new>()
5+
known_edge JSX_COMPONENT_CALL components#App() @L38 -> components#Panel(Props)
6+
known_edge JSX_COMPONENT_CALL components#App() @L40 -> components#Panel(Props)
7+
known_edge JSX_COMPONENT_CALL components#App() @L41 -> components#Badge()
48
known_edge METHOD_CALL components#App() @L45 -> Widget#render()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
── entry_point (5) ──
2+
exported_from_entry_module components#App components.tsx:37
3+
exported_from_entry_module components#Badge components.tsx:27
4+
exported_from_entry_module components#LibApp components.tsx:51
5+
exported_from_entry_module components#Panel components.tsx:23
6+
unimported_module components#<module> components.tsx:1
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
# Accepted gaps for 10-jsx-component-calls — each line is an edge the TypeScript compiler
22
# resolves and this engine does not. A NEW missing edge fails the suite;
33
# a line here that STARTS working also fails, so the debt cannot rot.
4-
components#App() -> components#Badge()
5-
components#App() -> components#Panel(Props)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
boundary_lib CONSTRUCTOR_CALL components#LibApp() @L54 -> Theme#<new>()
2+
boundary_lib JSX_COMPONENT_CALL components#LibApp() @L53 -> ui#Card(CardProps)
23
boundary_lib METHOD_CALL components#LibApp() @L54 -> Theme#color()
34
known_edge CONSTRUCTOR_CALL components#App() @L44 -> Widget#<new>()
5+
known_edge JSX_COMPONENT_CALL components#App() @L38 -> components#Panel(Props)
6+
known_edge JSX_COMPONENT_CALL components#App() @L40 -> components#Panel(Props)
7+
known_edge JSX_COMPONENT_CALL components#App() @L41 -> components#Badge()
48
known_edge METHOD_CALL components#App() @L45 -> Widget#render()
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
# Accepted gaps for 10-jsx-component-calls — each line is an edge the TypeScript compiler
22
# resolves and this engine does not. A NEW missing edge fails the suite;
33
# a line here that STARTS working also fails, so the debt cannot rot.
4-
components#App() -> components#Badge()
5-
components#App() -> components#Panel(Props)
6-
components#LibApp() -> ui#Card(CardProps)

0 commit comments

Comments
 (0)