diff --git a/compiler/apps/playground/__tests__/e2e/page.spec.ts b/compiler/apps/playground/__tests__/e2e/page.spec.ts index 1a7bf1def137..5994a124e98e 100644 --- a/compiler/apps/playground/__tests__/e2e/page.spec.ts +++ b/compiler/apps/playground/__tests__/e2e/page.spec.ts @@ -313,6 +313,27 @@ test('error is displayed when source has syntax error', async ({page}) => { ); }); +test('typescript `as const` does not produce a JS-service error squiggle', async ({ + page, +}) => { + const store: Store = { + source: ["let noError = '' as const;", 'let error = '].join('\n'), + config: defaultConfig, + showInternals: false, + }; + const hash = encodeStore(store); + await page.goto(`/#${hash}`, {waitUntil: 'networkidle'}); + await page.waitForFunction(isMonacoLoaded); + + const editorInput = page.locator('.monaco-editor-input'); + await expect(editorInput).toBeVisible(); + await editorInput.click(); + + await expect(page.locator('.squiggly-error')).toHaveCount(1, { + timeout: 10_000, + }); +}); + TEST_CASE_INPUTS.forEach((t, idx) => test(`playground compiles: ${t.name}`, async ({page}) => { const store: Store = { diff --git a/compiler/apps/playground/components/Editor/Input.tsx b/compiler/apps/playground/components/Editor/Input.tsx index 8c37b12975b8..422f81143cf0 100644 --- a/compiler/apps/playground/components/Editor/Input.tsx +++ b/compiler/apps/playground/components/Editor/Input.tsx @@ -39,10 +39,14 @@ export default function Input({errors, language}: Props): JSX.Element { const store = useStore(); const dispatchStore = useStoreDispatch(); + const isTypeScript = language === 'typescript'; + const editorPath = isTypeScript ? 'index.tsx' : 'index.js'; + const editorLanguage = isTypeScript ? 'typescript' : 'javascript'; + // Set tab width to 2 spaces for the selected input file. useEffect(() => { if (!monaco) return; - const uri = monaco.Uri.parse(`file:///index.js`); + const uri = monaco.Uri.parse(`file:///${editorPath}`); const model = monaco.editor.getModel(uri); invariant(model, 'Model must exist for the selected input file.'); renderReactCompilerMarkers({ @@ -51,13 +55,14 @@ export default function Input({errors, language}: Props): JSX.Element { details: errors, source: store.source, }); - }, [monaco, errors, store.source]); + }, [monaco, errors, store.source, editorPath]); useEffect(() => { /** - * Ignore "can only be used in TypeScript files." errors, since - * we want to support syntax highlighting for Flow (*.js) files - * and Flow is not a built-in language. + * For Flow (*.js) files, suppress the "can only be used in TypeScript + * files." diagnostics emitted by Monaco's JS service so Flow type syntax + * doesn't squiggle. TS sources use a *.tsx model instead, which lets + * Monaco's TS service parse TS syntax natively without these diagnostics. */ if (!monaco) return; monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ @@ -81,6 +86,10 @@ export default function Input({errors, language}: Props): JSX.Element { // Monaco can't validate Flow component syntax noSyntaxValidation: language === 'flow', }); + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: false, + }); }, [monaco, language]); const handleChange: (value: string | undefined) => void = async value => { @@ -141,13 +150,8 @@ export default function Input({errors, language}: Props): JSX.Element { const editorContent = (