Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/tryit-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
`Changes in this PR will be published to the following url to try(check status of TypeSpec Pull Request Try It pipeline for publish status):`,
`Playground: https://cadlplayground.z22.web.core.windows.net/prs/${prNumber}/`,
"",
`Website: https://cadlwebsite.z1.web.core.windows.net/prs/${prNumber}/`,
`Website: https://tspwebsitepr.z5.web.core.windows.net/prs/${prNumber}/`,

].join("\n")
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "Expose `CompilerOptions` TypeScript type",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}
2 changes: 1 addition & 1 deletion eng/pipelines/pr-tryit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
inlineScript: |
az storage blob upload-batch \
--destination \$web \
--account-name "cadlwebsite" \
--account-name "tspwebsitepr" \
--destination-path $(TYPESPEC_WEBSITE_BASE_PATH) \
--source "./packages/website/build/" \
--overwrite
Expand Down
2 changes: 1 addition & 1 deletion eng/scripts/create-tryit-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ async function main() {
`<!-- ${TRY_ID_COMMENT_IDENTIFIER} -->`,
`You can try these changes at https://cadlplayground.z22.web.core.windows.net${folderName}/prs/${prNumber}/`,
"",
`Check the website changes at https://cadlwebsite.z1.web.core.windows.net${folderName}/prs/${prNumber}/`,
`Check the website changes at https://tspwebsitepr.z5.web.core.windows.net${folderName}/prs/${prNumber}/`,
].join("\n");
await writeComment(repo, prNumber, comment, ghAuth);
}
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export {
} from "./library.js";
export * from "./module-resolver.js";
export * from "./node-host.js";
export * from "./options.js";
export * from "./parser.js";
export * from "./path-utils.js";
export * from "./program.js";
Expand Down
12 changes: 10 additions & 2 deletions packages/playground-website/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ const config = definePlaygroundViteConfig({
filename: "samples/unions.tsp",
preferredEmitter: "@typespec/openapi3",
},
"HTTP service": { filename: "samples/http.tsp", preferredEmitter: "@typespec/openapi3" },
"REST framework": { filename: "samples/rest.tsp", preferredEmitter: "@typespec/openapi3" },
"HTTP service": {
filename: "samples/http.tsp",
preferredEmitter: "@typespec/openapi3",
compilerOptions: { linterRuleSet: { extends: ["@typespec/http/all"] } },
},
"REST framework": {
filename: "samples/rest.tsp",
preferredEmitter: "@typespec/openapi3",
compilerOptions: { linterRuleSet: { extends: ["@typespec/http/all"] } },
},
"Protobuf Kiosk": {
filename: "samples/kiosk.tsp",
preferredEmitter: "@typespec/protobuf",
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export { registerMonacoDefaultWorkers } from "./monaco-worker.js";
export { registerMonacoLanguage } from "./services.js";
export { createUrlStateStorage } from "./state-storage.js";
export { PlaygroundSample } from "./types.js";
export { filterEmitters } from "./utils.js";
export { resolveLibraries as filterEmitters } from "./utils.js";
24 changes: 13 additions & 11 deletions packages/playground/src/react/editor-command-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ import {
Tooltip,
} from "@fluentui/react-components";
import { Bug16Regular, Save16Regular, Settings24Regular } from "@fluentui/react-icons";
import { CompilerOptions } from "@typespec/compiler";
import { FunctionComponent } from "react";
import { PlaygroundSample } from "../types.js";
import { EmitterDropdown } from "./emitter-dropdown.js";
import { OutputSettings } from "./output-settings.js";
import { SamplesDropdown } from "./samples-dropdown.js";
import { EmitterOptions } from "./types.js";
import { CompilerSettings } from "./settings/compiler-settings.js";
import { PlaygroundTspLibrary } from "./types.js";

export interface EditorCommandBarProps {
documentationUrl?: string;
saveCode: () => Promise<void> | void;
newIssue?: () => Promise<void> | void;
emitters: string[];
libraries: PlaygroundTspLibrary[];
selectedEmitter: string;
onSelectedEmitterChange: (emitter: string) => void;
emitterOptions: EmitterOptions;
onEmitterOptionsChange: (options: EmitterOptions) => void;
compilerOptions: CompilerOptions;
onCompilerOptionsChange: (options: CompilerOptions) => void;

samples?: Record<string, PlaygroundSample>;
selectedSampleName: string;
Expand All @@ -35,11 +36,11 @@ export const EditorCommandBar: FunctionComponent<EditorCommandBarProps> = ({
documentationUrl,
saveCode,
newIssue,
emitters,
libraries,
selectedEmitter,
onSelectedEmitterChange,
emitterOptions,
onEmitterOptionsChange,
compilerOptions: emitterOptions,
onCompilerOptionsChange,
samples,
selectedSampleName,
onSelectedSampleNameChange,
Expand Down Expand Up @@ -73,7 +74,7 @@ export const EditorCommandBar: FunctionComponent<EditorCommandBarProps> = ({
/>
)}
<EmitterDropdown
emitters={emitters}
emitters={libraries.filter((x) => x.isEmitter).map((x) => x.name)}
onSelectedEmitterChange={onSelectedEmitterChange}
selectedEmitter={selectedEmitter}
/>
Expand All @@ -83,10 +84,11 @@ export const EditorCommandBar: FunctionComponent<EditorCommandBarProps> = ({
</DialogTrigger>
<DialogSurface>
<DialogBody>
<OutputSettings
<CompilerSettings
libraries={libraries}
selectedEmitter={selectedEmitter}
options={emitterOptions}
optionsChanged={onEmitterOptionsChange}
onOptionsChanged={onCompilerOptionsChange}
/>
</DialogBody>
</DialogSurface>
Expand Down
15 changes: 15 additions & 0 deletions packages/playground/src/react/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,18 @@ export function useControllableValue<TValue>(

return [currentValue, setValueOrCallOnChange] as any;
}

export function useAsyncMemo<T>(
callback: () => Promise<T>,
defaultValue: T,
deps?: React.DependencyList
): T {
const [value, setValue] = useState<T>(defaultValue);
useEffect(() => {
callback()
.then(setValue)
// eslint-disable-next-line no-console
.catch(() => console.error("Failed to load async memo"));
}, deps);
return value;
}
60 changes: 36 additions & 24 deletions packages/playground/src/react/playground.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CompilerOptions } from "@typespec/compiler";
import debounce from "debounce";
import { KeyCode, KeyMod, MarkerSeverity, Uri, editor } from "monaco-editor";
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
Expand All @@ -7,12 +8,13 @@ import { BrowserHost } from "../browser-host.js";
import { importTypeSpecCompiler } from "../core.js";
import { getMarkerLocation } from "../services.js";
import { PlaygroundSample } from "../types.js";
import { resolveLibraries } from "../utils.js";
import { EditorCommandBar } from "./editor-command-bar.js";
import { useMonacoModel } from "./editor.js";
import { Footer } from "./footer.js";
import { useControllableValue } from "./hooks.js";
import { useAsyncMemo, useControllableValue } from "./hooks.js";
import { OutputView } from "./output-view.js";
import { CompilationState, EmitterOptions, FileOutputViewer } from "./types.js";
import { CompilationState, FileOutputViewer } from "./types.js";
import { TypeSpecEditor } from "./typespec-editor.js";

export interface PlaygroundProps {
Expand All @@ -21,8 +23,8 @@ export interface PlaygroundProps {
/** Default emitter if leaving this unmanaged. */
defaultContent?: string;

/** List of available emitters */
emitters: string[];
/** List of available libraries */
libraries: string[];

/** Emitter to use */
emitter?: string;
Expand All @@ -32,11 +34,11 @@ export interface PlaygroundProps {
onEmitterChange?: (emitter: string) => void;

/** Emitter options */
emitterOptions?: EmitterOptions;
compilerOptions?: CompilerOptions;
/** Default emitter options if leaving this unmanaged. */
defaultEmitterOptions?: EmitterOptions;
defaultCompilerOptions?: CompilerOptions;
/** Callback when emitter options change */
onEmitterOptionsChange?: (emitter: EmitterOptions) => void;
onCompilerOptionsChange?: (emitter: CompilerOptions) => void;

/** Samples available */
samples?: Record<string, PlaygroundSample>;
Expand Down Expand Up @@ -65,7 +67,7 @@ export interface PlaygroundSaveData {
emitter: string;

/** Emitter options. */
options?: EmitterOptions;
options?: CompilerOptions;

/** If a sample is selected and the content hasn't changed since. */
sampleName?: string;
Expand All @@ -85,10 +87,10 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
props.defaultEmitter,
props.onEmitterChange
);
const [emitterOptions, onEmitterOptionsChange] = useControllableValue(
props.emitterOptions,
props.defaultEmitterOptions ?? {},
props.onEmitterOptionsChange
const [compilerOptions, onCompilerOptionsChange] = useControllableValue(
props.compilerOptions,
props.defaultCompilerOptions ?? {},
props.onCompilerOptionsChange
);
const [selectedSampleName, onSelectedSampleNameChange] = useControllableValue(
props.sampleName,
Expand All @@ -107,7 +109,7 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
setContent(content);
const typespecCompiler = await importTypeSpecCompiler();

const state = await compile(host, content, selectedEmitter, emitterOptions);
const state = await compile(host, content, selectedEmitter, compilerOptions);
setCompilationState(state);
if ("program" in state) {
const markers: editor.IMarkerData[] = state.program.diagnostics.map((diag) => ({
Expand All @@ -121,7 +123,7 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
} else {
editor.setModelMarkers(typespecModel, "owner", []);
}
}, [host, selectedEmitter, emitterOptions, typespecModel, setContent]);
}, [host, selectedEmitter, compilerOptions, typespecModel, setContent]);

const updateTypeSpec = useCallback(
(value: string) => {
Expand All @@ -143,6 +145,9 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
if (config.preferredEmitter) {
onSelectedEmitterChange(config.preferredEmitter);
}
if (config.compilerOptions) {
onCompilerOptionsChange(config.compilerOptions);
}
}
}
}, [updateTypeSpec, selectedSampleName]);
Expand All @@ -165,15 +170,15 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
onSave({
content: typespecModel.getValue(),
emitter: selectedEmitter,
options: emitterOptions,
options: compilerOptions,
sampleName: isSampleUntouched ? selectedSampleName : undefined,
});
}
}, [
typespecModel,
onSave,
selectedEmitter,
emitterOptions,
compilerOptions,
selectedSampleName,
isSampleUntouched,
]);
Expand All @@ -193,6 +198,12 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
[saveCode]
);

const libraries = useAsyncMemo(
async () => resolveLibraries(props.libraries),
[],
[props.libraries]
);

return (
<div
css={{
Expand All @@ -208,11 +219,11 @@ export const Playground: FunctionComponent<PlaygroundProps> = (props) => {
>
<div css={{ gridArea: "typespeceditor", width: "100%", height: "100%", overflow: "hidden" }}>
<EditorCommandBar
emitters={props.emitters}
libraries={libraries}
selectedEmitter={selectedEmitter}
onSelectedEmitterChange={onSelectedEmitterChange}
emitterOptions={emitterOptions}
onEmitterOptionsChange={onEmitterOptionsChange}
compilerOptions={compilerOptions}
onCompilerOptionsChange={onCompilerOptionsChange}
samples={props.samples}
selectedSampleName={selectedSampleName}
onSelectedSampleNameChange={onSelectedSampleNameChange}
Expand Down Expand Up @@ -247,22 +258,23 @@ async function compile(
host: BrowserHost,
content: string,
selectedEmitter: string,
emittersOptions: Record<string, Record<string, unknown>>
options: CompilerOptions
): Promise<CompilationState> {
await host.writeFile("main.tsp", content);
await emptyOutputDir(host);
try {
const typespecCompiler = await importTypeSpecCompiler();
const program = await typespecCompiler.compile(host, "main.tsp", {
outputDir: "tsp-output",
emit: [selectedEmitter],
...options,
options: {
...emittersOptions,
...options.options,
[selectedEmitter]: {
...emittersOptions[selectedEmitter],
...options.options?.[selectedEmitter],
"emitter-output-dir": "tsp-output",
},
},
outputDir: "tsp-output",
emit: [selectedEmitter],
});
const outputFiles = await findOutputFiles(host);
return { program, outputFiles };
Expand Down
Loading