From b826382b26934b18d19b0d2be17c346d19c32f73 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Thu, 7 Sep 2023 13:13:09 -0700 Subject: [PATCH 1/7] Playground linters --- packages/playground/src/index.ts | 2 +- .../src/react/editor-command-bar.tsx | 13 ++-- packages/playground/src/react/hooks.ts | 15 +++++ packages/playground/src/react/playground.tsx | 15 +++-- .../src/react/settings/compiler-settings.tsx | 62 +++++++++++++++++++ .../emitter-options-form.tsx} | 56 ++--------------- .../src/react/settings/linter-form.tsx | 26 ++++++++ packages/playground/src/react/standalone.tsx | 4 +- packages/playground/src/react/types.ts | 8 ++- packages/playground/src/utils.ts | 13 ++-- 10 files changed, 144 insertions(+), 70 deletions(-) create mode 100644 packages/playground/src/react/settings/compiler-settings.tsx rename packages/playground/src/react/{output-settings.tsx => settings/emitter-options-form.tsx} (66%) create mode 100644 packages/playground/src/react/settings/linter-form.tsx diff --git a/packages/playground/src/index.ts b/packages/playground/src/index.ts index 1c21c3c09e..10a85b36b2 100644 --- a/packages/playground/src/index.ts +++ b/packages/playground/src/index.ts @@ -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"; diff --git a/packages/playground/src/react/editor-command-bar.tsx b/packages/playground/src/react/editor-command-bar.tsx index b8a76aab58..abc9523c38 100644 --- a/packages/playground/src/react/editor-command-bar.tsx +++ b/packages/playground/src/react/editor-command-bar.tsx @@ -13,15 +13,15 @@ import { Bug16Regular, Save16Regular, Settings24Regular } from "@fluentui/react- 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 { EmitterOptions, PlaygroundTspLibrary } from "./types.js"; export interface EditorCommandBarProps { documentationUrl?: string; saveCode: () => Promise | void; newIssue?: () => Promise | void; - emitters: string[]; + libraries: PlaygroundTspLibrary[]; selectedEmitter: string; onSelectedEmitterChange: (emitter: string) => void; emitterOptions: EmitterOptions; @@ -35,7 +35,7 @@ export const EditorCommandBar: FunctionComponent = ({ documentationUrl, saveCode, newIssue, - emitters, + libraries, selectedEmitter, onSelectedEmitterChange, emitterOptions, @@ -73,7 +73,7 @@ export const EditorCommandBar: FunctionComponent = ({ /> )} x.isEmitter).map((x) => x.name)} onSelectedEmitterChange={onSelectedEmitterChange} selectedEmitter={selectedEmitter} /> @@ -83,7 +83,8 @@ export const EditorCommandBar: FunctionComponent = ({ - ( return [currentValue, setValueOrCallOnChange] as any; } + +export function useAsyncMemo( + callback: () => Promise, + defaultValue: T, + deps?: React.DependencyList +): T { + const [value, setValue] = useState(defaultValue); + useEffect(() => { + callback() + .then(setValue) + // eslint-disable-next-line no-console + .catch(() => console.error("Failed to load async memo")); + }, deps); + return value; +} diff --git a/packages/playground/src/react/playground.tsx b/packages/playground/src/react/playground.tsx index 88027592c0..b39e67e16a 100644 --- a/packages/playground/src/react/playground.tsx +++ b/packages/playground/src/react/playground.tsx @@ -7,10 +7,11 @@ 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 { TypeSpecEditor } from "./typespec-editor.js"; @@ -21,8 +22,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; @@ -193,6 +194,12 @@ export const Playground: FunctionComponent = (props) => { [saveCode] ); + const libraries = useAsyncMemo( + async () => resolveLibraries(props.libraries), + [], + [props.libraries] + ); + return (
= (props) => { >
void; + // linterRuleSet: LinterRuleSet; + // linterRuleSetChanged: (options: LinterRuleSet) => void; +}; + +export const CompilerSettings: FunctionComponent = ({ + selectedEmitter, + libraries, + options, + optionsChanged, +}) => { + const library = useTypeSpecLibrary(selectedEmitter); + const linterRuleSet = {}; + const linterRuleSetChanged = () => {}; + return ( +
+

Settings

+ <>Emitter: {selectedEmitter} +

Options

+ {library && ( + + )} +

Linter

+ +
+ ); +}; + +function useTypeSpecLibrary(name: string): TypeSpecLibrary | undefined { + const [lib, setLib] = useState>(); + + useEffect(() => { + setLib(undefined); + import(/* @vite-ignore */ name) + .then((module) => { + if (module.$lib === undefined) { + // eslint-disable-next-line no-console + console.error(`Couldn't load library ${name} missing $lib export`); + } + setLib(module.$lib); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error("Failed to load library", name); + }); + }, [name]); + return lib; +} diff --git a/packages/playground/src/react/output-settings.tsx b/packages/playground/src/react/settings/emitter-options-form.tsx similarity index 66% rename from packages/playground/src/react/output-settings.tsx rename to packages/playground/src/react/settings/emitter-options-form.tsx index 2f07b25dff..d9276f9d23 100644 --- a/packages/playground/src/react/output-settings.tsx +++ b/packages/playground/src/react/settings/emitter-options-form.tsx @@ -10,61 +10,15 @@ import { useId, } from "@fluentui/react-components"; import { TypeSpecLibrary } from "@typespec/compiler"; -import { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react"; -import { EmitterOptions } from "./types.js"; +import { FunctionComponent, useCallback, useMemo } from "react"; +import { EmitterOptions } from "../types.js"; -export type OutputSettingsProps = { - selectedEmitter: string; - options: EmitterOptions; - optionsChanged: (options: EmitterOptions) => void; -}; - -export const OutputSettings: FunctionComponent = ({ - selectedEmitter, - options, - optionsChanged, -}) => { - const library = useTypeSpecLibrary(selectedEmitter); - return ( -
-

Settings

- <>Emitter: {selectedEmitter} -

Options

- {library && ( - - )} -
- ); -}; - -function useTypeSpecLibrary(name: string): TypeSpecLibrary | undefined { - const [lib, setLib] = useState>(); - - useEffect(() => { - setLib(undefined); - import(/* @vite-ignore */ name) - .then((module) => { - if (module.$lib === undefined) { - // eslint-disable-next-line no-console - console.error(`Couldn't load library ${name} missing $lib export`); - } - setLib(module.$lib); - }) - .catch((e) => { - // eslint-disable-next-line no-console - console.error("Failed to load library", name); - }); - }, [name]); - return lib; -} - -type EmitterOptionsFormProps = { +export interface EmitterOptionsFormProps { library: TypeSpecLibrary; options: EmitterOptions; optionsChanged: (options: EmitterOptions) => void; -}; - -const EmitterOptionsForm: FunctionComponent = ({ +} +export const EmitterOptionsForm: FunctionComponent = ({ library, options, optionsChanged, diff --git a/packages/playground/src/react/settings/linter-form.tsx b/packages/playground/src/react/settings/linter-form.tsx new file mode 100644 index 0000000000..e9f91a3bb8 --- /dev/null +++ b/packages/playground/src/react/settings/linter-form.tsx @@ -0,0 +1,26 @@ +import { LinterRuleSet } from "@typespec/compiler"; +import { FunctionComponent } from "react"; +import { PlaygroundTspLibrary } from "../types.js"; +import { Checkbox } from "@fluentui/react-components"; + +export interface LinterFormProps { + libraries: PlaygroundTspLibrary[]; + linterRuleSet: LinterRuleSet; + linterRuleSetChanged: (options: LinterRuleSet) => void; +} + +export const LinterForm: FunctionComponent = ({ libraries }) => { + const rulesets = libraries.flatMap((lib) => { + return Object.keys(lib.definition?.linter?.ruleSets ?? {}); + }); + if (rulesets.length === 0) { + return <>No ruleset available; + } + return ( + <> + {rulesets.map((ruleSet) => { + return ; + })} + + ); +}; diff --git a/packages/playground/src/react/standalone.tsx b/packages/playground/src/react/standalone.tsx index 8b9f595c33..1dda85ce4c 100644 --- a/packages/playground/src/react/standalone.tsx +++ b/packages/playground/src/react/standalone.tsx @@ -5,7 +5,6 @@ import { createBrowserHost } from "../browser-host.js"; import { registerMonacoDefaultWorkers } from "../monaco-worker.js"; import { registerMonacoLanguage } from "../services.js"; import { StateStorage, createUrlStateStorage } from "../state-storage.js"; -import { filterEmitters } from "../utils.js"; import { Playground, PlaygroundProps, PlaygroundSaveData } from "./playground.js"; export interface ReactPlaygroundConfig extends Partial { @@ -14,7 +13,6 @@ export interface ReactPlaygroundConfig extends Partial { export async function createReactPlayground(config: ReactPlaygroundConfig) { const host = await createBrowserHost(config.libraries); - const emitters = await filterEmitters(config.libraries); await registerMonacoLanguage(host); registerMonacoDefaultWorkers(); @@ -23,7 +21,7 @@ export async function createReactPlayground(config: ReactPlaygroundConfig) { const options: PlaygroundProps = { ...config, host, - emitters, + libraries: config.libraries, defaultContent: initialState.content, defaultEmitter: initialState.emitter ?? config.defaultEmitter, defaultEmitterOptions: initialState.options, diff --git a/packages/playground/src/react/types.ts b/packages/playground/src/react/types.ts index 74187de89e..af70ceb7a5 100644 --- a/packages/playground/src/react/types.ts +++ b/packages/playground/src/react/types.ts @@ -1,4 +1,4 @@ -import { Program } from "@typespec/compiler"; +import { Program, TypeSpecLibrary } from "@typespec/compiler"; import { ReactElement } from "react"; export type CompilationCrashed = { @@ -23,3 +23,9 @@ export interface ViewerProps { filename: string; content: string; } + +export interface PlaygroundTspLibrary { + name: string; + isEmitter: boolean; + definition?: TypeSpecLibrary; +} diff --git a/packages/playground/src/utils.ts b/packages/playground/src/utils.ts index 74ee47f1aa..a25488a9fb 100644 --- a/packages/playground/src/utils.ts +++ b/packages/playground/src/utils.ts @@ -1,11 +1,16 @@ import { importLibrary } from "./core.js"; +import { PlaygroundTspLibrary } from "./react/types.js"; /** * Filter emitters in given list of libraries - * @param libraries List of libraries that could include emitters + * @param names List of libraries that could include emitters * @returns Names of all the emitters */ -export async function filterEmitters(libraries: string[]): Promise { - const loaded = await Promise.all(libraries.map(async (x) => [x, await importLibrary(x)])); - return loaded.filter(([, x]) => (x as any).$lib?.emitter).map((x: any) => x[0]); +export async function resolveLibraries(names: string[]): Promise { + return await Promise.all( + names.map(async (x) => { + const lib: any = await importLibrary(x); + return { name: x, isEmitter: lib.$lib?.emitter }; + }) + ); } From 107b89254c8f280fdc4be690dc9ea29d7299c1e3 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Thu, 7 Sep 2023 13:34:02 -0700 Subject: [PATCH 2/7] Select linters --- packages/compiler/src/core/index.ts | 1 + .../src/react/editor-command-bar.tsx | 13 +++--- packages/playground/src/react/playground.tsx | 41 ++++++++----------- .../src/react/settings/compiler-settings.tsx | 39 +++++++++++++----- packages/playground/src/react/standalone.tsx | 2 +- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/packages/compiler/src/core/index.ts b/packages/compiler/src/core/index.ts index 5dde2a7fb2..069a13d230 100644 --- a/packages/compiler/src/core/index.ts +++ b/packages/compiler/src/core/index.ts @@ -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"; diff --git a/packages/playground/src/react/editor-command-bar.tsx b/packages/playground/src/react/editor-command-bar.tsx index abc9523c38..8c20f103e6 100644 --- a/packages/playground/src/react/editor-command-bar.tsx +++ b/packages/playground/src/react/editor-command-bar.tsx @@ -10,12 +10,13 @@ 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 { SamplesDropdown } from "./samples-dropdown.js"; import { CompilerSettings } from "./settings/compiler-settings.js"; -import { EmitterOptions, PlaygroundTspLibrary } from "./types.js"; +import { PlaygroundTspLibrary } from "./types.js"; export interface EditorCommandBarProps { documentationUrl?: string; @@ -24,8 +25,8 @@ export interface EditorCommandBarProps { libraries: PlaygroundTspLibrary[]; selectedEmitter: string; onSelectedEmitterChange: (emitter: string) => void; - emitterOptions: EmitterOptions; - onEmitterOptionsChange: (options: EmitterOptions) => void; + compilerOptions: CompilerOptions; + onCompilerOptionsChange: (options: CompilerOptions) => void; samples?: Record; selectedSampleName: string; @@ -38,8 +39,8 @@ export const EditorCommandBar: FunctionComponent = ({ libraries, selectedEmitter, onSelectedEmitterChange, - emitterOptions, - onEmitterOptionsChange, + compilerOptions: emitterOptions, + onCompilerOptionsChange, samples, selectedSampleName, onSelectedSampleNameChange, @@ -87,7 +88,7 @@ export const EditorCommandBar: FunctionComponent = ({ libraries={libraries} selectedEmitter={selectedEmitter} options={emitterOptions} - optionsChanged={onEmitterOptionsChange} + onOptionsChanged={onCompilerOptionsChange} /> diff --git a/packages/playground/src/react/playground.tsx b/packages/playground/src/react/playground.tsx index b39e67e16a..7df66fda06 100644 --- a/packages/playground/src/react/playground.tsx +++ b/packages/playground/src/react/playground.tsx @@ -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"; @@ -13,7 +14,7 @@ import { useMonacoModel } from "./editor.js"; import { Footer } from "./footer.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 { @@ -33,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; @@ -66,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; @@ -86,10 +87,10 @@ export const Playground: FunctionComponent = (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, @@ -108,7 +109,7 @@ export const Playground: FunctionComponent = (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) => ({ @@ -122,7 +123,7 @@ export const Playground: FunctionComponent = (props) => { } else { editor.setModelMarkers(typespecModel, "owner", []); } - }, [host, selectedEmitter, emitterOptions, typespecModel, setContent]); + }, [host, selectedEmitter, compilerOptions, typespecModel, setContent]); const updateTypeSpec = useCallback( (value: string) => { @@ -166,7 +167,7 @@ export const Playground: FunctionComponent = (props) => { onSave({ content: typespecModel.getValue(), emitter: selectedEmitter, - options: emitterOptions, + options: compilerOptions, sampleName: isSampleUntouched ? selectedSampleName : undefined, }); } @@ -174,7 +175,7 @@ export const Playground: FunctionComponent = (props) => { typespecModel, onSave, selectedEmitter, - emitterOptions, + compilerOptions, selectedSampleName, isSampleUntouched, ]); @@ -218,8 +219,8 @@ export const Playground: FunctionComponent = (props) => { libraries={libraries} selectedEmitter={selectedEmitter} onSelectedEmitterChange={onSelectedEmitterChange} - emitterOptions={emitterOptions} - onEmitterOptionsChange={onEmitterOptionsChange} + compilerOptions={compilerOptions} + onCompilerOptionsChange={onCompilerOptionsChange} samples={props.samples} selectedSampleName={selectedSampleName} onSelectedSampleNameChange={onSelectedSampleNameChange} @@ -254,22 +255,16 @@ async function compile( host: BrowserHost, content: string, selectedEmitter: string, - emittersOptions: Record> + options: CompilerOptions ): Promise { await host.writeFile("main.tsp", content); await emptyOutputDir(host); try { const typespecCompiler = await importTypeSpecCompiler(); const program = await typespecCompiler.compile(host, "main.tsp", { + ...options, outputDir: "tsp-output", emit: [selectedEmitter], - options: { - ...emittersOptions, - [selectedEmitter]: { - ...emittersOptions[selectedEmitter], - "emitter-output-dir": "tsp-output", - }, - }, }); const outputFiles = await findOutputFiles(host); return { program, outputFiles }; diff --git a/packages/playground/src/react/settings/compiler-settings.tsx b/packages/playground/src/react/settings/compiler-settings.tsx index f64685f1cc..51875e4eb5 100644 --- a/packages/playground/src/react/settings/compiler-settings.tsx +++ b/packages/playground/src/react/settings/compiler-settings.tsx @@ -1,5 +1,5 @@ -import { TypeSpecLibrary } from "@typespec/compiler"; -import { FunctionComponent, useEffect, useState } from "react"; +import { CompilerOptions, LinterRuleSet, TypeSpecLibrary } from "@typespec/compiler"; +import { FunctionComponent, useCallback, useEffect, useState } from "react"; import { EmitterOptions, PlaygroundTspLibrary } from "../types.js"; import { EmitterOptionsForm } from "./emitter-options-form.js"; import { LinterForm } from "./linter-form.js"; @@ -7,28 +7,47 @@ import { LinterForm } from "./linter-form.js"; export type CompilerSettingsProps = { libraries: PlaygroundTspLibrary[]; selectedEmitter: string; - options: EmitterOptions; - optionsChanged: (options: EmitterOptions) => void; - // linterRuleSet: LinterRuleSet; - // linterRuleSetChanged: (options: LinterRuleSet) => void; + options: CompilerOptions; + onOptionsChanged: (options: CompilerOptions) => void; }; export const CompilerSettings: FunctionComponent = ({ selectedEmitter, libraries, options, - optionsChanged, + onOptionsChanged, }) => { + const emitterOptionsChanged = useCallback( + (emitterOptions: EmitterOptions) => { + onOptionsChanged({ + ...options, + options: emitterOptions, + }); + }, + [options] + ); const library = useTypeSpecLibrary(selectedEmitter); - const linterRuleSet = {}; - const linterRuleSetChanged = () => {}; + const linterRuleSet = options.linterRuleSet ?? {}; + const linterRuleSetChanged = useCallback( + (linterRuleSet: LinterRuleSet) => { + onOptionsChanged({ + ...options, + linterRuleSet, + }); + }, + [options] + ); return (

Settings

<>Emitter: {selectedEmitter}

Options

{library && ( - + )}

Linter

{ stateStorage.save(value); From c4fecf8439955f9f5e0d6cca881043a12d6763bc Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Thu, 7 Sep 2023 13:39:15 -0700 Subject: [PATCH 3/7] Load from sample --- packages/playground-website/vite.config.ts | 12 ++++++++++-- packages/playground/src/react/playground.tsx | 3 +++ packages/playground/src/types.ts | 7 +++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/playground-website/vite.config.ts b/packages/playground-website/vite.config.ts index 744e63bf54..920f60f6db 100644 --- a/packages/playground-website/vite.config.ts +++ b/packages/playground-website/vite.config.ts @@ -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", diff --git a/packages/playground/src/react/playground.tsx b/packages/playground/src/react/playground.tsx index 7df66fda06..7cfc7fb72e 100644 --- a/packages/playground/src/react/playground.tsx +++ b/packages/playground/src/react/playground.tsx @@ -145,6 +145,9 @@ export const Playground: FunctionComponent = (props) => { if (config.preferredEmitter) { onSelectedEmitterChange(config.preferredEmitter); } + if(config.compilerOptions) { + onCompilerOptionsChange(config.compilerOptions) + } } } }, [updateTypeSpec, selectedSampleName]); diff --git a/packages/playground/src/types.ts b/packages/playground/src/types.ts index 647eea5981..9f99c9c28a 100644 --- a/packages/playground/src/types.ts +++ b/packages/playground/src/types.ts @@ -1,5 +1,12 @@ +import { CompilerOptions } from "@typespec/compiler"; + export interface PlaygroundSample { filename: string; preferredEmitter?: string; content: string; + + /** + * Compiler options for the sample. + */ + compilerOptions?: CompilerOptions; } From aa45bd5c4827d21bd0fecd1ff457152464f9bed4 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Thu, 7 Sep 2023 14:53:27 -0700 Subject: [PATCH 4/7] Fix --- packages/playground/src/react/playground.tsx | 1 + .../src/react/settings/compiler-settings.tsx | 2 +- .../src/react/settings/linter-form.tsx | 54 ++++++++++++++++--- packages/playground/src/utils.ts | 2 +- packages/playground/src/vite/index.ts | 8 ++- 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/packages/playground/src/react/playground.tsx b/packages/playground/src/react/playground.tsx index 7cfc7fb72e..9b283de54a 100644 --- a/packages/playground/src/react/playground.tsx +++ b/packages/playground/src/react/playground.tsx @@ -260,6 +260,7 @@ async function compile( selectedEmitter: string, options: CompilerOptions ): Promise { + console.log("OP", options) await host.writeFile("main.tsp", content); await emptyOutputDir(host); try { diff --git a/packages/playground/src/react/settings/compiler-settings.tsx b/packages/playground/src/react/settings/compiler-settings.tsx index 51875e4eb5..420aed574b 100644 --- a/packages/playground/src/react/settings/compiler-settings.tsx +++ b/packages/playground/src/react/settings/compiler-settings.tsx @@ -53,7 +53,7 @@ export const CompilerSettings: FunctionComponent = ({
); diff --git a/packages/playground/src/react/settings/linter-form.tsx b/packages/playground/src/react/settings/linter-form.tsx index e9f91a3bb8..738895820b 100644 --- a/packages/playground/src/react/settings/linter-form.tsx +++ b/packages/playground/src/react/settings/linter-form.tsx @@ -1,26 +1,66 @@ -import { LinterRuleSet } from "@typespec/compiler"; -import { FunctionComponent } from "react"; +import { Checkbox, CheckboxOnChangeData } from "@fluentui/react-components"; +import { LinterRuleSet, RuleRef } from "@typespec/compiler"; +import { FunctionComponent, useCallback } from "react"; import { PlaygroundTspLibrary } from "../types.js"; -import { Checkbox } from "@fluentui/react-components"; export interface LinterFormProps { libraries: PlaygroundTspLibrary[]; linterRuleSet: LinterRuleSet; - linterRuleSetChanged: (options: LinterRuleSet) => void; + onLinterRuleSetChanged: (options: LinterRuleSet) => void; } -export const LinterForm: FunctionComponent = ({ libraries }) => { +export const LinterForm: FunctionComponent = ({ + libraries, + linterRuleSet, + onLinterRuleSetChanged, +}) => { const rulesets = libraries.flatMap((lib) => { - return Object.keys(lib.definition?.linter?.ruleSets ?? {}); + return Object.keys(lib.definition?.linter?.ruleSets ?? {}).map( + (x) => `${lib.name}/${x}` + ) as RuleRef[]; }); if (rulesets.length === 0) { return <>No ruleset available; } + + const handleChange = (ruleSet: RuleRef, checked: boolean) => { + const ruleSets = linterRuleSet.extends ?? []; + + const updatedRuleSets = checked + ? [...ruleSets, ruleSet] + : ruleSets.filter((x) => x !== ruleSet); + + onLinterRuleSetChanged({ + extends: updatedRuleSets, + }); + }; return ( <> {rulesets.map((ruleSet) => { - return ; + return ( + + ); })} ); }; + +interface RuleSetCheckbox { + ruleSet: RuleRef; + checked?: boolean; + onChange(ruleSet: RuleRef, checked: boolean): void; +} +const RuleSetCheckbox = ({ ruleSet, checked, onChange }: RuleSetCheckbox) => { + const handleChange = useCallback( + (_: any, data: CheckboxOnChangeData) => { + onChange(ruleSet, !!data.checked); + }, + [ruleSet, checked, onChange] + ); + return ; +}; diff --git a/packages/playground/src/utils.ts b/packages/playground/src/utils.ts index a25488a9fb..e137870aa3 100644 --- a/packages/playground/src/utils.ts +++ b/packages/playground/src/utils.ts @@ -10,7 +10,7 @@ export async function resolveLibraries(names: string[]): Promise { const lib: any = await importLibrary(x); - return { name: x, isEmitter: lib.$lib?.emitter }; + return { name: x, isEmitter: lib.$lib?.emitter, definition: lib.$lib }; }) ); } diff --git a/packages/playground/src/vite/index.ts b/packages/playground/src/vite/index.ts index b547e1b7c2..3bd813c93f 100644 --- a/packages/playground/src/vite/index.ts +++ b/packages/playground/src/vite/index.ts @@ -80,7 +80,13 @@ function playgroundManifestPlugin(config: PlaygroundUserConfig): Plugin { preferredEmitter: ${ config.preferredEmitter ? JSON.stringify(config.preferredEmitter) : "undefined" }, - content: s${index} + content: s${index}, + ${ + config.compilerOptions + ? `compilerOptions: ${JSON.stringify(config.compilerOptions)},` + : "" + } + }, ` ), "}", From 8bf3dfa8a7b1c42458cc7cdcf7bfccc5f651a460 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Thu, 7 Sep 2023 15:00:39 -0700 Subject: [PATCH 5/7] expose --- .../compiler/playground-linters_2023-09-07-22-00.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@typespec/compiler/playground-linters_2023-09-07-22-00.json diff --git a/common/changes/@typespec/compiler/playground-linters_2023-09-07-22-00.json b/common/changes/@typespec/compiler/playground-linters_2023-09-07-22-00.json new file mode 100644 index 0000000000..d87aadf949 --- /dev/null +++ b/common/changes/@typespec/compiler/playground-linters_2023-09-07-22-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/compiler", + "comment": "Expose `CompilerOptions` TypeScript type", + "type": "none" + } + ], + "packageName": "@typespec/compiler" +} \ No newline at end of file From e1baea5d7b5ef245ca7ac87fd6a496806a35c066 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 8 Sep 2023 07:59:06 -0700 Subject: [PATCH 6/7] Fix --- packages/playground/src/react/playground.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/playground/src/react/playground.tsx b/packages/playground/src/react/playground.tsx index 9b283de54a..f4f1a9f2e8 100644 --- a/packages/playground/src/react/playground.tsx +++ b/packages/playground/src/react/playground.tsx @@ -145,8 +145,8 @@ export const Playground: FunctionComponent = (props) => { if (config.preferredEmitter) { onSelectedEmitterChange(config.preferredEmitter); } - if(config.compilerOptions) { - onCompilerOptionsChange(config.compilerOptions) + if (config.compilerOptions) { + onCompilerOptionsChange(config.compilerOptions); } } } @@ -260,13 +260,19 @@ async function compile( selectedEmitter: string, options: CompilerOptions ): Promise { - console.log("OP", options) await host.writeFile("main.tsp", content); await emptyOutputDir(host); try { const typespecCompiler = await importTypeSpecCompiler(); const program = await typespecCompiler.compile(host, "main.tsp", { ...options, + options: { + ...options.options, + [selectedEmitter]: { + ...options.options?.[selectedEmitter], + "emitter-output-dir": "tsp-output", + }, + }, outputDir: "tsp-output", emit: [selectedEmitter], }); From fdfbb398b1654e2abb2c12cd4c99f2c1d783e733 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Mon, 11 Sep 2023 10:40:33 -0700 Subject: [PATCH 7/7] update to new storage account name --- .github/workflows/tryit-comment.yml | 2 +- eng/pipelines/pr-tryit.yaml | 2 +- eng/scripts/create-tryit-comment.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tryit-comment.yml b/.github/workflows/tryit-comment.yml index 53985e4041..b94778efa7 100644 --- a/.github/workflows/tryit-comment.yml +++ b/.github/workflows/tryit-comment.yml @@ -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") }) diff --git a/eng/pipelines/pr-tryit.yaml b/eng/pipelines/pr-tryit.yaml index b7c813ad00..151ec88304 100644 --- a/eng/pipelines/pr-tryit.yaml +++ b/eng/pipelines/pr-tryit.yaml @@ -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 diff --git a/eng/scripts/create-tryit-comment.js b/eng/scripts/create-tryit-comment.js index 5762c709a9..ad4e87bd1b 100644 --- a/eng/scripts/create-tryit-comment.js +++ b/eng/scripts/create-tryit-comment.js @@ -44,7 +44,7 @@ async function main() { ``, `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); }