Skip to content
Open
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
29 changes: 15 additions & 14 deletions compiler/apps/playground/components/Editor/ConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/

import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
import type {editor} from 'monaco-editor';
import MonacoEditor, { loader, type Monaco } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';
import * as monaco from 'monaco-editor';
import React, {
useState,
Expand All @@ -15,16 +15,16 @@ import React, {
unstable_addTransitionType as addTransitionType,
startTransition,
} from 'react';
import {Resizable} from 're-resizable';
import {useStore, useStoreDispatch} from '../StoreContext';
import {monacoConfigOptions} from './monacoOptions';
import {IconChevron} from '../Icons/IconChevron';
import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
import { Resizable } from 're-resizable';
import { useStore, useStoreDispatch } from '../StoreContext';
import { monacoConfigOptions } from './monacoOptions';
import { IconChevron } from '../Icons/IconChevron';
import { CONFIG_PANEL_TRANSITION } from '../../lib/transitionTypes';

// @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';

loader.config({monaco});
loader.config({ monaco });

export default function ConfigEditor({
formattedAppliedConfig,
Expand All @@ -33,7 +33,7 @@ export default function ConfigEditor({
}): React.ReactElement {
const [isExpanded, setIsExpanded] = useState(false);

// TODO: Add back <Activity> after upgrading next.js
// TODO: Addhbn back <Activity> after upgrading next.js
return (
<>
<div
Expand Down Expand Up @@ -80,7 +80,8 @@ function ExpandedEditor({
}): React.ReactElement {
const store = useStore();
const dispatchStore = useStoreDispatch();
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
// const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const handleChange: (value: string | undefined) => void = (
value: string | undefined,
Expand Down Expand Up @@ -126,14 +127,14 @@ function ExpandedEditor({

return (
<ViewTransition
update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
update={{ [CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none' }}>
{/* enter={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}
exit={{[CONFIG_PANEL_TRANSITION]: 'slide-out', default: 'none'}}> */}
<Resizable
minWidth={300}
maxWidth={600}
defaultSize={{width: 350}}
enable={{right: true, bottom: false}}>
defaultSize={{ width: 350 }}
enable={{ right: true, bottom: false }}>
<div className="bg-blue-10 relative h-full flex flex-col !h-[calc(100vh_-_3.5rem)] border border-gray-300">
<div
className="absolute w-8 h-16 bg-blue-10 rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-l-0 border-gray-300"
Expand Down Expand Up @@ -202,7 +203,7 @@ function CollapsedEditor({
return (
<div
className="w-4 !h-[calc(100vh_-_3.5rem)]"
style={{position: 'relative'}}>
style={{ position: 'relative' }}>
<div
className="absolute w-10 h-16 bg-blue-10 hover:translate-x-2 transition-transform rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-gray-300"
title="Expand config editor"
Expand Down
35 changes: 29 additions & 6 deletions compiler/apps/playground/lib/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from 'lz-string';
import {defaultStore, defaultConfig} from '../defaultStore';
import { defaultStore, defaultConfig } from '../defaultStore';

/**
* Global Store for Playground
Expand All @@ -23,8 +23,18 @@ export interface Store {
export function encodeStore(store: Store): string {
return compressToEncodedURIComponent(JSON.stringify(store));
}
export function decodeStore(hash: string): any {
return JSON.parse(decompressFromEncodedURIComponent(hash));
export function decodeStore(hash: string): unknown {
// The URL hash and localStorage are user-controlled inputs. Be resilient to
// malformed/corrupt values so the playground doesn't hard-crash on load.
const decompressed = decompressFromEncodedURIComponent(hash);
if (decompressed == null) {
return null;
}
try {
return JSON.parse(decompressed);
} catch {
return null;
}
}

/**
Expand All @@ -41,11 +51,18 @@ export function saveStore(store: Store): void {
* - it has a `source` property and is a string
*/
function isValidStore(raw: unknown): raw is Store {
// Validate the required shape while keeping backwards compatibility with
// older stored payloads.
return (
raw != null &&
typeof raw == 'object' &&
'source' in raw &&
typeof raw['source'] === 'string'
// typeof raw['source'] === 'string'
typeof (raw as Record<string, unknown>)['source'] === 'string' &&
(!('config' in raw) ||
typeof (raw as Record<string, unknown>)['config'] === 'string') &&
(!('showInternals' in raw) ||
typeof (raw as Record<string, unknown>)['showInternals'] === 'boolean')
);
}

Expand All @@ -64,10 +81,16 @@ export function initStoreFromUrlOrLocalStorage(): Store {
*/
if (!encodedSource) return defaultStore;

const raw: any = decodeStore(encodedSource);

// const raw: any = decodeStore(encodedSource);
const raw = decodeStore(encodedSource);
invariant(isValidStore(raw), 'Invalid Store');

// If the decoded data is invalid, treat it as if there was no saved store.
// This avoids breaking the playground when someone shares a malformed URL.
if (!isValidStore(raw)) {
return defaultStore;
}

// Make sure all properties are populated
return {
source: raw.source,
Expand Down