diff --git a/packages/sandbox/src/compilerOptions.ts b/packages/sandbox/src/compilerOptions.ts index ec202423c15f..353b1d9fe2ae 100644 --- a/packages/sandbox/src/compilerOptions.ts +++ b/packages/sandbox/src/compilerOptions.ts @@ -10,6 +10,8 @@ type Monaco = typeof import("monaco-editor") export function getDefaultSandboxCompilerOptions(config: SandboxConfig, monaco: Monaco) { const useJavaScript = config.filetype === "js" const settings: CompilerOptions = { + strict: true, + noImplicitAny: true, strictNullChecks: !useJavaScript, strictFunctionTypes: true, @@ -60,24 +62,40 @@ export function getDefaultSandboxCompilerOptions(config: SandboxConfig, monaco: * Loop through all of the entries in the existing compiler options then compare them with the * query params and return an object which is the changed settings via the query params */ -export const getCompilerOptionsFromParams = (options: CompilerOptions, params: URLSearchParams): CompilerOptions => { - const urlDefaults = Object.entries(options).reduce((acc: any, [key, value]) => { - if (params.has(key)) { - const urlValue = params.get(key)! - - if (urlValue === "true") { - acc[key] = true - } else if (urlValue === "false") { - acc[key] = false - } else if (!isNaN(parseInt(urlValue, 10))) { - acc[key] = parseInt(urlValue, 10) +export const getCompilerOptionsFromParams = ( + playgroundDefaults: CompilerOptions, + ts: typeof import("typescript"), + params: URLSearchParams +): CompilerOptions => { + const returnedOptions: CompilerOptions = {} + + params.forEach((val, key) => { + // First use the defaults object to drop compiler flags which are already set to the default + if (playgroundDefaults[key]) { + let toSet = undefined + if (val === "true" && playgroundDefaults[key] !== true) { + toSet = true + } else if (val === "false" && playgroundDefaults[key] !== false) { + toSet = false + } else if (!isNaN(parseInt(val, 10)) && playgroundDefaults[key] !== parseInt(val, 10)) { + toSet = parseInt(val, 10) } - } - return acc - }, {}) + if (toSet !== undefined) returnedOptions[key] = toSet + } else { + // If that doesn't work, double check that the flag exists and allow it through + // @ts-ignore + const flagExists = ts.optionDeclarations.find(opt => opt.name === key) + if (flagExists) { + let realValue: number | boolean = true + if (val === "false") realValue = false + if (!isNaN(parseInt(val, 10))) realValue = parseInt(val, 10) + returnedOptions[key] = realValue + } + } + }) - return urlDefaults + return returnedOptions } // Can't set sandbox to be the right type because the param would contain this function diff --git a/packages/sandbox/src/index.ts b/packages/sandbox/src/index.ts index 5117a00cb90d..0723b71081aa 100644 --- a/packages/sandbox/src/index.ts +++ b/packages/sandbox/src/index.ts @@ -123,7 +123,7 @@ export const createTypeScriptSandbox = ( let compilerOptions: CompilerOptions if (!config.suppressAutomaticallyGettingCompilerFlags) { const params = new URLSearchParams(location.search) - let queryParamCompilerOptions = getCompilerOptionsFromParams(compilerDefaults, params) + let queryParamCompilerOptions = getCompilerOptionsFromParams(compilerDefaults, ts, params) if (Object.keys(queryParamCompilerOptions).length) config.logger.log("[Compiler] Found compiler options in query params: ", queryParamCompilerOptions) compilerOptions = { ...compilerDefaults, ...queryParamCompilerOptions } diff --git a/packages/sandbox/test/defaultCompilerOptions.test.ts b/packages/sandbox/test/defaultCompilerOptions.test.ts new file mode 100644 index 000000000000..c4cf6201479e --- /dev/null +++ b/packages/sandbox/test/defaultCompilerOptions.test.ts @@ -0,0 +1,59 @@ +import { getCompilerOptionsFromParams, getDefaultSandboxCompilerOptions } from "../src/compilerOptions" +import ts from "typescript" + +const fauxMonaco: any = { + languages: { + typescript: { + ModuleResolutionKind: ts.ModuleResolutionKind, + ScriptTarget: ts.ScriptTarget, + JsxEmit: ts.JsxEmit, + ModuleKind: ts.ModuleKind, + }, + }, +} + +describe(getCompilerOptionsFromParams, () => { + it("ignores compiler flags which are the same as the defaults", () => { + // noImplicitReturns=true is the default, and shouldnt be in the object + const params = new URLSearchParams("?noImplicitThis=false&noImplicitReturns=true#code/JYOw") + const defaults = getDefaultSandboxCompilerOptions({ filetype: "js" } as any, fauxMonaco) + + expect(getCompilerOptionsFromParams(defaults, ts, params)).toMatchInlineSnapshot(` + Object { + "noImplicitThis": false, + } + `) + }) + + it("ignores non-compiler flags", () => { + const params = new URLSearchParams("?asdasdasdasd=false") + const defaults = getDefaultSandboxCompilerOptions({ filetype: "js" } as any, fauxMonaco) + + expect(getCompilerOptionsFromParams(defaults, ts, params)).toMatchInlineSnapshot(`Object {}`) + }) + + it("handles mapped types like target et", () => { + const params = new URLSearchParams("?target=6") + const defaults = getDefaultSandboxCompilerOptions({ filetype: "js" } as any, fauxMonaco) + + expect(getCompilerOptionsFromParams(defaults, ts, params)).toMatchInlineSnapshot(` + Object { + "target": 6, + } + `) + }) + + it("handles settings options which haven't been given defaults in the monaco defaults", () => { + const search = "?ts=4.4.0-beta&exactOptionalPropertyTypes=true#code/JYOw" + const params = new URLSearchParams(search) + expect(params.has("exactOptionalPropertyTypes")).toBeTruthy() + + const defaults = getDefaultSandboxCompilerOptions({ filetype: "js" } as any, fauxMonaco) + + expect(getCompilerOptionsFromParams(defaults, ts, params)).toMatchInlineSnapshot(` + Object { + "exactOptionalPropertyTypes": true, + } + `) + }) +})