Skip to content

Commit db7d4ca

Browse files
committed
WIP: getting namespace as props not working yet
1 parent 55a5e05 commit db7d4ca

File tree

15 files changed

+201
-143
lines changed

15 files changed

+201
-143
lines changed
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
export * from "./source-file/source-file.jsx";
2-
export * from "./namespace/block.jsx";
3-
export * from "./namespace/file-level.jsx";
2+
export * from "./namespace/namespace.jsx";
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Scope } from "@alloy-js/core";
2+
import { Children } from "@alloy-js/core/jsx-runtime";
3+
import { NamespaceContext } from "../contexts/namespace.js";
4+
import { createTypeSpecNamespaceScope } from "../scopes/namespace.js";
5+
import { NamespaceSymbol } from "../symbols/namespace.js";
6+
7+
export interface NamespaceScopProps {
8+
symbol: NamespaceSymbol;
9+
children: Children;
10+
}
11+
12+
export function NamespaceScope(props: NamespaceScopProps) {
13+
const scope = createTypeSpecNamespaceScope(props.symbol);
14+
return (
15+
<NamespaceContext.Provider value={{ symbol: props.symbol }}>
16+
<Scope value={scope}>{props.children}</Scope>
17+
</NamespaceContext.Provider>
18+
);
19+
}
20+
21+
export interface NamespaceScopesProps {
22+
symbol: NamespaceSymbol;
23+
children: Children;
24+
}
25+
26+
export function NamespaceScopes(props: NamespaceScopesProps) {
27+
function wrapWithScope(symbol: NamespaceSymbol, children: Children) {
28+
const scopeChildren = (
29+
<NamespaceScope symbol={symbol}>{children}</NamespaceScope>
30+
);
31+
32+
if (symbol.enclosingNamespace) {
33+
return wrapWithScope(symbol.enclosingNamespace, scopeChildren);
34+
}
35+
36+
return scopeChildren;
37+
}
38+
39+
return (
40+
<NamespaceContext.Provider value={{ symbol: props.symbol }}>
41+
{wrapWithScope(props.symbol, props.children)}
42+
</NamespaceContext.Provider>
43+
);
44+
}

packages/typespec/src/components/namespace/block.test.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { expect, it } from "vitest";
22
import { Output } from "@alloy-js/core";
33
import { SourceFile } from "#components/source-file/source-file.jsx";
4-
import { Namespace } from "./block.jsx";
4+
import { Namespace } from "./namespace.jsx";
55
import { d } from "@alloy-js/core/testing";
6+
import { createNamespaceSymbol } from "../../symbols/factories.js";
67

78
it("renders a namespace with contents", () => {
89
expect(
@@ -43,9 +44,10 @@ it("renders nested block namespaces", () => {
4344
});
4445

4546
it("renders namespaces when a file level namespace is present", () => {
47+
const parentNamespace = createNamespaceSymbol("File.Level");
4648
expect(
4749
<Output>
48-
<SourceFile path="main.tsp" namespace="File.Level">
50+
<SourceFile path="main.tsp" namespace={parentNamespace}>
4951
<Namespace name="My.Namespace">
5052
Contents!
5153
</Namespace>

packages/typespec/src/components/namespace/block.tsx

Lines changed: 0 additions & 35 deletions
This file was deleted.

packages/typespec/src/components/namespace/file-level.tsx

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useNamespaceContext } from "../../contexts/namespace.js";
2+
import { NamespaceSymbol } from "../../symbols/namespace.js";
3+
4+
export interface NamespaceNameProps {
5+
symbol: NamespaceSymbol;
6+
7+
/** If it should print relative to the parent context */
8+
relative?: boolean;
9+
}
10+
11+
export function NamespaceName(props: NamespaceNameProps) {
12+
const names = [props.symbol.name];
13+
const parent = props.relative ? useNamespaceContext()?.symbol : undefined;
14+
15+
let current = props.symbol.ownerSymbol;
16+
while (current) {
17+
if (
18+
current === parent ||
19+
!(current instanceof NamespaceSymbol) ||
20+
current.isGlobal
21+
) {
22+
break;
23+
}
24+
names.unshift(current.name);
25+
current = current.ownerSymbol;
26+
}
27+
28+
return names.join(".");
29+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Block, Namekey, Refkey } from "@alloy-js/core";
2+
import { Children } from "@alloy-js/core/jsx-runtime";
3+
import { useSourceFileScope } from "../../scopes/source-file.js";
4+
import { createNamespaceSymbol } from "../../symbols/factories.js";
5+
import { NamespaceContext } from "../../contexts/namespace.js";
6+
import { NamespaceName } from "./namespace-name.jsx";
7+
import { NamespaceScope } from "#components/namespace-scopes.jsx";
8+
9+
10+
export interface NamespaceProps {
11+
name: string | Namekey | (string | Namekey)[];
12+
refkey?: Refkey | Refkey[];
13+
children?: Children;
14+
}
15+
16+
export function Namespace(props: NamespaceProps) {
17+
const namespaceSymbol = createNamespaceSymbol(props.name, {
18+
refkeys: props.refkey,
19+
});
20+
const sfScope = useSourceFileScope();
21+
22+
if (!sfScope) {
23+
return (<NamespaceContext.Provider value={{ symbol: namespaceSymbol }}>
24+
{props.children}
25+
</NamespaceContext.Provider>);
26+
} else {
27+
sfScope.hasBlockNamespace = true;
28+
return (
29+
<>
30+
namespace <NamespaceName symbol={namespaceSymbol} relative />{" "}
31+
<Block>
32+
<NamespaceContext.Provider value={{ symbol: namespaceSymbol }}>
33+
<NamespaceScope symbol={namespaceSymbol}>
34+
{props.children}
35+
</NamespaceScope>
36+
</NamespaceContext.Provider>
37+
</Block>
38+
</>
39+
);
40+
}
41+
}

packages/typespec/src/components/source-file/source-file.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect, it } from "vitest";
22
import { Output } from "@alloy-js/core";
33
import { SourceFile } from "./source-file.jsx";
44
import { SourceDirectory } from "@alloy-js/core";
5+
import { Namespace } from "#components/namespace/namespace.jsx";
56

67
it("defines multiple directories with unique source files", () => {
78
expect(
@@ -22,7 +23,9 @@ it("defines multiple directories with unique source files", () => {
2223
it("declares a file level namespace when one is provided", () => {
2324
expect(
2425
<Output>
25-
<SourceFile namespace="My.Namespace" path="main.tsp" />
26+
<Namespace name="My.Namespace">
27+
<SourceFile path="main.tsp" />
28+
</Namespace>
2629
</Output>
2730
).toRenderTo({
2831
"main.tsp": `namespace My.Namespace;\n\n\n`, // why do we need to do this for this assertion to pass?

packages/typespec/src/components/source-file/source-file.tsx

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { Children } from "@alloy-js/core/jsx-runtime";
22
import { SourceFileScope } from "../../scopes/source-file.js";
3-
import { SourceFile as CoreSourceFile, Scope } from "@alloy-js/core";
3+
import { Block, computed, SourceFile as CoreSourceFile, Scope, useBinder } from "@alloy-js/core";
44
import {
55
useTypeSpecFormatOptions
66
} from "../../contexts/format-options.js";
77
import { Reference } from "../Reference.jsx";
8-
import { useDirectoryScope } from "../../scopes/contexts.js";
9-
import { FileLevelNamespace } from "#components/namespace/file-level.jsx";
8+
import { useNamespaceContext } from "../../contexts/namespace.js";
9+
import { getGlobalNamespace } from "../../contexts/global-namespace.js";
10+
import { NamespaceScopes } from "#components/namespace-scopes.jsx";
11+
import { NamespaceName } from "#components/namespace/namespace-name.jsx";
12+
import { NamespaceSymbol } from "../../symbols/namespace.js";
1013

1114
export interface SourceFileProps {
1215
path: string;
1316

1417
/** If present, it defines a file-level namespace (if not present, it uses the global namespace) */
15-
namespace?: string;
18+
namespace?: NamespaceSymbol;
1619

1720
children?: Children;
1821

@@ -30,8 +33,15 @@ export interface SourceFileProps {
3033
};
3134

3235
export function SourceFile(props: SourceFileProps) {
33-
const parent = useDirectoryScope();
34-
const sourceFileScope = new SourceFileScope(props.path, parent);
36+
const sourceFileScope = new SourceFileScope(props.path);
37+
38+
const nsContext = useNamespaceContext();
39+
const parentNs = props.namespace ?? getGlobalNamespace(useBinder());
40+
const nsSymbol = nsContext ? nsContext.symbol : parentNs;
41+
42+
const content = computed(() => (
43+
<NamespaceScopes symbol={nsSymbol}>{props.children}</NamespaceScopes>
44+
));
3545

3646
const options = useTypeSpecFormatOptions();
3747

@@ -43,9 +53,24 @@ export function SourceFile(props: SourceFileProps) {
4353
{...options}
4454
>
4555
<Scope value={sourceFileScope}>
46-
<FileLevelNamespace name={props.namespace} isGlobal={props.namespace === undefined}>
47-
{props.children}
48-
</FileLevelNamespace>
56+
{nsSymbol === parentNs ?
57+
content
58+
: <>
59+
namespace <NamespaceName symbol={nsSymbol} />
60+
{sourceFileScope.hasBlockNamespace ?
61+
<>
62+
{" "}
63+
<Block>{content}</Block>
64+
</>
65+
:<>
66+
;<hbr />
67+
<hbr />
68+
{content}
69+
</>
70+
71+
}
72+
</>
73+
}
4974
</Scope>
5075
</CoreSourceFile>
5176
);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ComponentContext, createContext, useContext } from "@alloy-js/core";
2+
import { NamespaceSymbol } from "../symbols/namespace.js";
3+
4+
export interface NamespaceContext {
5+
symbol: NamespaceSymbol;
6+
}
7+
8+
export const NamespaceContext: ComponentContext<NamespaceContext> =
9+
createContext();
10+
11+
export function useNamespaceContext() {
12+
return useContext(NamespaceContext);
13+
}

0 commit comments

Comments
 (0)