diff --git a/.changeset/strange-spies-train.md b/.changeset/strange-spies-train.md new file mode 100644 index 00000000..f5e545bb --- /dev/null +++ b/.changeset/strange-spies-train.md @@ -0,0 +1,5 @@ +--- +'playroom': patch +--- + +Migrate internals to Typescript. This moves all of the files under src/ to TypeScript. diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 3223b8fe..56b6eeae 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -4,6 +4,6 @@ "isolatedModules": false, "types": ["cypress"] }, - "include": ["**/*"], // override the include from the root tsconfig + "include": ["**/*", "../src/userDefinedModules.d.ts"], // override the include from the root tsconfig "exclude": [] // override the exclude from the root tsconfig } diff --git a/lib/makeWebpackConfig.js b/lib/makeWebpackConfig.js index 0843f0e5..e41b14de 100644 --- a/lib/makeWebpackConfig.js +++ b/lib/makeWebpackConfig.js @@ -10,9 +10,12 @@ const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const playroomPath = path.resolve(__dirname, '..'); + +// All of the paths containing files used internally by Playroom should be listed in this array const includePaths = [ path.resolve(playroomPath, 'lib'), path.resolve(playroomPath, 'src'), + path.resolve(playroomPath, 'utils'), ]; module.exports = async (playroomConfig, options) => { @@ -40,9 +43,9 @@ module.exports = async (playroomConfig, options) => { const ourConfig = { mode: options.production ? 'production' : 'development', entry: { - index: [require.resolve('../src/index.js')], - frame: [require.resolve('../src/frame.js')], - preview: [require.resolve('../src/preview.js')], + index: [require.resolve('../src/index.tsx')], + frame: [require.resolve('../src/frame.tsx')], + preview: [require.resolve('../src/preview.tsx')], }, output: { filename: '[name].[contenthash].js', diff --git a/package.json b/package.json index 54ce77d2..e8b8f540 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "playroom", "version": "0.34.1", "description": "Design with code, powered by your own component library", - "main": "utils/index.js", - "types": "utils/index.d.ts", + "main": "utils/index.ts", + "types": "utils/index.ts", "bin": { "playroom": "bin/cli.cjs" }, @@ -116,6 +116,7 @@ "@changesets/cli": "^2.25.2", "@octokit/rest": "^19.0.5", "@types/jest": "^29.2.4", + "@types/webpack-env": "^1.18.4", "concurrently": "^7.6.0", "cypress": "^12.0.2", "eslint": "^8.44.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90379a22..75fb0072 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,7 @@ specifiers: '@types/prettier': ^2.7.1 '@types/react': ^18.0.26 '@types/react-dom': ^18.0.9 + '@types/webpack-env': ^1.18.4 '@vanilla-extract/css': ^1.9.2 '@vanilla-extract/css-utils': ^0.1.3 '@vanilla-extract/sprinkles': ^1.5.1 @@ -135,6 +136,7 @@ devDependencies: '@changesets/cli': 2.25.2 '@octokit/rest': 19.0.5 '@types/jest': 29.2.4 + '@types/webpack-env': 1.18.4 concurrently: 7.6.0 cypress: 12.0.2 eslint: 8.44.0 @@ -2602,6 +2604,10 @@ packages: '@types/estree': 1.0.0 dev: false + /@types/webpack-env/1.18.4: + resolution: {integrity: sha512-I6e+9+HtWADAWeeJWDFQtdk4EVSAbj6Rtz4q8fJ7mSr1M0jzlFcs8/HZ+Xb5SHzVm1dxH7aUiI+A8kA8Gcrm0A==} + dev: true + /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: diff --git a/src/Playroom/CodeEditor/CodeMirror2.tsx b/src/Playroom/CodeEditor/CodeMirror2.tsx index e84fbbd1..a4ce23b2 100644 --- a/src/Playroom/CodeEditor/CodeMirror2.tsx +++ b/src/Playroom/CodeEditor/CodeMirror2.tsx @@ -12,7 +12,7 @@ /* eslint-disable */ import * as React from 'react'; -import * as codemirror from 'codemirror'; +import codemirror from 'codemirror'; declare let global: any; declare let require: any; @@ -21,10 +21,7 @@ const SERVER_RENDERED = typeof navigator === 'undefined' || (typeof global !== 'undefined' && global.PREVENT_CODEMIRROR_RENDER === true); -let cm: typeof codemirror.default; -if (!SERVER_RENDERED) { - cm = require('codemirror'); -} +let cm = codemirror; export interface IDefineModeOptions { fn: () => codemirror.Mode; diff --git a/src/Playroom/Frame.tsx b/src/Playroom/Frame.tsx index 32b0a4f1..81f2c477 100644 --- a/src/Playroom/Frame.tsx +++ b/src/Playroom/Frame.tsx @@ -1,19 +1,18 @@ -import type React from 'react'; -import type { ReactNode } from 'react'; +import type { ComponentType, ReactNode } from 'react'; import { useParams } from '../utils/params'; import CatchErrors from './CatchErrors/CatchErrors'; -// @ts-expect-error import RenderCode from './RenderCode/RenderCode'; interface FrameProps { themes: Record; - components: Array; - FrameComponent: React.ComponentType<{ + components: Record; + FrameComponent: ComponentType<{ themeName: string | null; theme: string; children?: ReactNode; }>; } + export default function Frame({ themes, components, diff --git a/src/Playroom/Frames/frameSrc.d.ts b/src/Playroom/Frames/frameSrc.d.ts deleted file mode 100644 index 89316787..00000000 --- a/src/Playroom/Frames/frameSrc.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -interface FrameParams { - code: string; - themeName: string; -} - -declare function frameSrc( - frameParams: FrameParams, - config: InternalPlayroomConfig -): string; - -export default frameSrc; diff --git a/src/Playroom/Frames/frameSrc.js b/src/Playroom/Frames/frameSrc.js deleted file mode 100644 index c08b96df..00000000 --- a/src/Playroom/Frames/frameSrc.js +++ /dev/null @@ -1,11 +0,0 @@ -// eslint-disable-next-line import/no-unresolved -const frameConfig = require('__PLAYROOM_ALIAS__FRAME_COMPONENT__'); - -const defaultFrameSrc = ({ code, themeName }, { baseUrl, paramType }) => - `${baseUrl}frame.html${ - paramType === 'hash' ? '#' : '' - }?themeName=${encodeURIComponent(themeName)}&code=${encodeURIComponent( - code - )}`; - -module.exports = frameConfig.frameSrc ? frameConfig.frameSrc : defaultFrameSrc; diff --git a/src/Playroom/Frames/frameSrc.ts b/src/Playroom/Frames/frameSrc.ts new file mode 100644 index 00000000..d815fd1f --- /dev/null +++ b/src/Playroom/Frames/frameSrc.ts @@ -0,0 +1,17 @@ +import type { FrameSrcHandler } from '../../internalTypes'; + +import frameConfig from '__PLAYROOM_ALIAS__FRAME_COMPONENT__'; + +const defaultFrameSrc: FrameSrcHandler = ( + { code, themeName }, + { baseUrl, paramType } +) => + `${baseUrl}frame.html${ + paramType === 'hash' ? '#' : '' + }?themeName=${encodeURIComponent(themeName)}&code=${encodeURIComponent( + code + )}`; + +export default (frameConfig.frameSrc + ? frameConfig.frameSrc + : defaultFrameSrc) as FrameSrcHandler; diff --git a/src/Playroom/Preview.tsx b/src/Playroom/Preview.tsx index 74a49240..2aabd12e 100644 --- a/src/Playroom/Preview.tsx +++ b/src/Playroom/Preview.tsx @@ -5,7 +5,6 @@ import { useParams } from '../utils/params'; import { compileJsx } from '../utils/compileJsx'; import SplashScreen from './SplashScreen/SplashScreen'; import CatchErrors from './CatchErrors/CatchErrors'; -// @ts-expect-error import RenderCode from './RenderCode/RenderCode'; import * as styles from './Preview.css'; diff --git a/src/Playroom/RenderCode/RenderCode.js b/src/Playroom/RenderCode/RenderCode.tsx similarity index 81% rename from src/Playroom/RenderCode/RenderCode.js rename to src/Playroom/RenderCode/RenderCode.tsx index 8d5da3cf..5051ba3b 100644 --- a/src/Playroom/RenderCode/RenderCode.js +++ b/src/Playroom/RenderCode/RenderCode.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { type ComponentType } from 'react'; import scopeEval from 'scope-eval'; // eslint-disable-next-line import/no-unresolved @@ -9,7 +9,13 @@ import { ReactFragmentPragma, } from '../../utils/compileJsx'; -export default function RenderCode({ code, scope }) { +export default function RenderCode({ + code, + scope, +}: { + code: string | undefined; + scope: Record; +}) { const userScope = { ...(useScope() ?? {}), ...scope, diff --git a/src/components.js b/src/components.js deleted file mode 100644 index 4a70be93..00000000 --- a/src/components.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable-next-line import/no-unresolved */ -module.exports = require('__PLAYROOM_ALIAS__COMPONENTS__'); diff --git a/src/components.ts b/src/components.ts new file mode 100644 index 00000000..0f6ec4ba --- /dev/null +++ b/src/components.ts @@ -0,0 +1,4 @@ +import * as components from '__PLAYROOM_ALIAS__COMPONENTS__'; + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +export default components as typeof import('__PLAYROOM_ALIAS__COMPONENTS__').default; diff --git a/src/frame.js b/src/frame.js deleted file mode 100644 index 470b81da..00000000 --- a/src/frame.js +++ /dev/null @@ -1,35 +0,0 @@ -import { renderElement } from './render'; -import Frame from './Playroom/Frame'; - -const outlet = document.createElement('div'); -document.body.appendChild(outlet); - -const renderFrame = ({ - themes = require('./themes'), - components = require('./components'), - FrameComponent = require('./frameComponent'), -} = {}) => { - renderElement( - , - outlet - ); -}; -renderFrame(); - -if (module.hot) { - module.hot.accept('./components', () => { - renderFrame({ components: require('./components') }); - }); - - module.hot.accept('./themes', () => { - renderFrame({ themes: require('./themes') }); - }); - - module.hot.accept('./frameComponent', () => { - renderFrame({ FrameComponent: require('./frameComponent') }); - }); -} diff --git a/src/frame.tsx b/src/frame.tsx new file mode 100644 index 00000000..b68cb22e --- /dev/null +++ b/src/frame.tsx @@ -0,0 +1,39 @@ +import { renderElement } from './render'; +import Frame from './Playroom/Frame'; +import { hmrAccept } from './utils/hmr'; +import playroomThemes from './themes'; +import playroomComponents from './components'; +import PlayroomFrameComponent from './frameComponent'; + +const outlet = document.createElement('div'); +document.body.appendChild(outlet); + +const renderFrame = ({ + themes = playroomThemes, + components = playroomComponents, + FrameComponent = PlayroomFrameComponent, +} = {}) => { + renderElement( + , + outlet + ); +}; +renderFrame(); + +hmrAccept((accept) => { + accept('./components', () => { + renderFrame({ components: playroomComponents }); + }); + + accept('./themes', () => { + renderFrame({ themes: playroomThemes }); + }); + + accept('./frameComponent', () => { + renderFrame({ FrameComponent: PlayroomFrameComponent }); + }); +}); diff --git a/src/frameComponent.js b/src/frameComponent.js deleted file mode 100644 index 5a364cc5..00000000 --- a/src/frameComponent.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable-next-line import/no-unresolved */ -const frameComponent = require('__PLAYROOM_ALIAS__FRAME_COMPONENT__'); - -module.exports = frameComponent.default || frameComponent; diff --git a/src/frameComponent.ts b/src/frameComponent.ts new file mode 100644 index 00000000..ba834552 --- /dev/null +++ b/src/frameComponent.ts @@ -0,0 +1,3 @@ +import frameComponent from '__PLAYROOM_ALIAS__FRAME_COMPONENT__'; + +export default frameComponent; diff --git a/src/images.d.ts b/src/images.d.ts new file mode 100644 index 00000000..0935dbbd --- /dev/null +++ b/src/images.d.ts @@ -0,0 +1,4 @@ +declare module '*.png' { + const value: any; + export default value; +} diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index 80a6683a..00000000 --- a/src/index.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -interface PlayroomConfig { - components: string; - outputPath: string; - title?: string; - themes?: string; - widths?: number[]; - snippets?: Snippet[]; - frameComponent?: string; - exampleCode?: string; - cwd?: string; - storageKey?: string; - webpackConfig?: () => void; - baseUrl?: string; - paramType: 'hash' | 'search'; - iframeSandbox?: string; - // eslint-disable-next-line @typescript-eslint/consistent-type-imports - reactDocgenTypescriptConfig?: import('react-docgen-typescript').ParserOptions; -} - -interface InternalPlayroomConfig extends PlayroomConfig { - cwd: string; - storageKey: string; - port: number; - openBrowser: boolean; -} - -interface Window { - __playroomConfig__: InternalPlayroomConfig; -} - -declare const __PLAYROOM_GLOBAL__CONFIG__: InternalPlayroomConfig; -declare const __PLAYROOM_GLOBAL__STATIC_TYPES__: any; diff --git a/src/index.js b/src/index.tsx similarity index 71% rename from src/index.js rename to src/index.tsx index 4bd28845..0ec4a064 100644 --- a/src/index.js +++ b/src/index.tsx @@ -4,6 +4,10 @@ import { StoreProvider } from './StoreContext/StoreContext'; import playroomConfig from './config'; import faviconPath from '../images/favicon.png'; import faviconInvertedPath from '../images/favicon-inverted.png'; +import playroomThemes from './themes'; +import playroomComponents from './components'; +import playroomSnippets from './snippets'; +import { hmrAccept } from './utils/hmr'; const polyfillIntersectionObserver = () => typeof window.IntersectionObserver !== 'undefined' @@ -26,9 +30,9 @@ polyfillIntersectionObserver().then(() => { } const renderPlayroom = ({ - themes = require('./themes'), - components = require('./components'), - snippets = require('./snippets'), + themes = playroomThemes, + components = playroomComponents, + snippets = playroomSnippets, } = {}) => { const themeNames = Object.keys(themes); @@ -43,11 +47,7 @@ polyfillIntersectionObserver().then(() => { components={filteredComponents} widths={widths} themes={themeNames} - snippets={ - typeof snippets.default !== 'undefined' - ? snippets.default - : snippets - } + snippets={snippets} /> , outlet @@ -55,17 +55,17 @@ polyfillIntersectionObserver().then(() => { }; renderPlayroom(); - if (module.hot) { - module.hot.accept('./components', () => { - renderPlayroom({ components: require('./components') }); + hmrAccept((accept) => { + accept('./components', () => { + renderPlayroom({ components: playroomComponents }); }); - module.hot.accept('./themes', () => { - renderPlayroom({ themes: require('./themes') }); + accept('./themes', () => { + renderPlayroom({ themes: playroomThemes }); }); - module.hot.accept('./snippets', () => { - renderPlayroom({ snippets: require('./snippets') }); + accept('./snippets', () => { + renderPlayroom({ snippets: playroomSnippets }); }); - } + }); }); diff --git a/src/internalTypes.d.ts b/src/internalTypes.d.ts new file mode 100644 index 00000000..a095d6a6 --- /dev/null +++ b/src/internalTypes.d.ts @@ -0,0 +1,36 @@ +// file with types that are not meant to be exposed as part of the public API + +import type { PlayroomConfig } from './types'; + +export interface InternalPlayroomConfig extends PlayroomConfig { + cwd: string; + storageKey: string; + port: number; + openBrowser: boolean; +} + +declare global { + declare const __PLAYROOM_GLOBAL__CONFIG__: InternalPlayroomConfig; + declare const __PLAYROOM_GLOBAL__STATIC_TYPES__: any; + + interface Window { + __playroomConfig__: InternalPlayroomConfig; + } +} + +export interface InternalPlayroomConfig extends PlayroomConfig { + cwd: string; + storageKey: string; + port: number; + openBrowser: boolean; +} + +export interface FrameParams { + code: string; + themeName: string; +} + +export type FrameSrcHandler = ( + frameParams: FrameParams, + config: InternalPlayroomConfig +) => string; diff --git a/src/intersection-observer.d.ts b/src/intersection-observer.d.ts new file mode 100644 index 00000000..bafd1669 --- /dev/null +++ b/src/intersection-observer.d.ts @@ -0,0 +1 @@ +declare module 'intersection-observer'; diff --git a/src/preview.js b/src/preview.js deleted file mode 100644 index efae4509..00000000 --- a/src/preview.js +++ /dev/null @@ -1,35 +0,0 @@ -import { renderElement } from './render'; -import Preview from './Playroom/Preview'; - -const outlet = document.createElement('div'); -document.body.appendChild(outlet); - -const renderPreview = ({ - themes = require('./themes'), - components = require('./components'), - FrameComponent = require('./frameComponent'), -} = {}) => { - renderElement( - , - outlet - ); -}; -renderPreview(); - -if (module.hot) { - module.hot.accept('./components', () => { - renderPreview({ components: require('./components') }); - }); - - module.hot.accept('./themes', () => { - renderPreview({ themes: require('./themes') }); - }); - - module.hot.accept('./frameComponent', () => { - renderPreview({ FrameComponent: require('./frameComponent') }); - }); -} diff --git a/src/preview.tsx b/src/preview.tsx new file mode 100644 index 00000000..ee2fc0ab --- /dev/null +++ b/src/preview.tsx @@ -0,0 +1,39 @@ +import { renderElement } from './render'; +import Preview from './Playroom/Preview'; +import { hmrAccept } from './utils/hmr'; +import playroomThemes from './themes'; +import playroomComponents from './components'; +import PlayroomFrameComponent from './frameComponent'; + +const outlet = document.createElement('div'); +document.body.appendChild(outlet); + +const renderPreview = ({ + themes = playroomThemes, + components = playroomComponents, + FrameComponent = PlayroomFrameComponent, +} = {}) => { + renderElement( + , + outlet + ); +}; +renderPreview(); + +hmrAccept((accept) => { + accept('./components', () => { + renderPreview({ components: playroomComponents }); + }); + + accept('./themes', () => { + renderPreview({ themes: playroomThemes }); + }); + + accept('./frameComponent', () => { + renderPreview({ FrameComponent: PlayroomFrameComponent }); + }); +}); diff --git a/src/render.js b/src/render.js deleted file mode 100644 index fe1e0355..00000000 --- a/src/render.js +++ /dev/null @@ -1,19 +0,0 @@ -import ReactDOM, { version as reactDomVersion } from 'react-dom'; - -// Uses the correct render API based on the available version of -// `react-dom`. This hack can be removed when support for older -// versions of React is removed. -const canUseNewReactRootApi = - reactDomVersion && - (reactDomVersion.startsWith('18') || reactDomVersion.startsWith('0.0.0')); - -export const renderElement = (node, outlet) => { - if (canUseNewReactRootApi) { - // eslint-disable-next-line import/no-unresolved - const { createRoot } = require('react-dom/client'); - const root = createRoot(outlet); - root.render(node); - } else { - ReactDOM.render(node, outlet); - } -}; diff --git a/src/render.ts b/src/render.ts new file mode 100644 index 00000000..54cfab36 --- /dev/null +++ b/src/render.ts @@ -0,0 +1,28 @@ +import type { ReactElement, ReactNode } from 'react'; +import ReactDOM, { version as reactDomVersion } from 'react-dom'; + +// Uses the correct render API based on the available version of +// `react-dom`. This hack can be removed when support for older +// versions of React is removed. +const canUseNewReactRootApi = + reactDomVersion && + (reactDomVersion.startsWith('18') || reactDomVersion.startsWith('0.0.0')); + +let __webpack_public_path__: string | undefined; + +export const renderElement = async (node: ReactNode, outlet: HTMLElement) => { + if (canUseNewReactRootApi) { + // webpack needs to know the public path when doing dynamic imports, + // otherwise the HTML chunk for the preview page, will end up importing + // react-dom/client from the wrong path + if (typeof __webpack_public_path__ !== 'undefined') { + __webpack_public_path__ = '../../../'; + } + const { createRoot } = await import('react-dom/client'); + const root = createRoot(outlet); + root.render(node); + } else { + // casting as ReactDOM.render requires a different type + ReactDOM.render(node as ReactElement, outlet); + } +}; diff --git a/src/scope-eval.d.ts b/src/scope-eval.d.ts new file mode 100644 index 00000000..3d0e0539 --- /dev/null +++ b/src/scope-eval.d.ts @@ -0,0 +1 @@ +declare module 'scope-eval'; diff --git a/src/snippets.js b/src/snippets.js deleted file mode 100644 index 2785a73e..00000000 --- a/src/snippets.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable-next-line import/no-unresolved */ -module.exports = require('__PLAYROOM_ALIAS__SNIPPETS__'); diff --git a/src/snippets.ts b/src/snippets.ts new file mode 100644 index 00000000..5415d761 --- /dev/null +++ b/src/snippets.ts @@ -0,0 +1,3 @@ +import snippets from '__PLAYROOM_ALIAS__SNIPPETS__'; + +export default snippets; diff --git a/src/themes.js b/src/themes.js deleted file mode 100644 index bf7dfe79..00000000 --- a/src/themes.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable-next-line import/no-unresolved */ -module.exports = require('__PLAYROOM_ALIAS__THEMES__'); diff --git a/src/themes.ts b/src/themes.ts new file mode 100644 index 00000000..1fcf3967 --- /dev/null +++ b/src/themes.ts @@ -0,0 +1,4 @@ +import * as themes from '__PLAYROOM_ALIAS__THEMES__'; + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +export default themes as typeof import('__PLAYROOM_ALIAS__THEMES__').default; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..1dd0152a --- /dev/null +++ b/src/types.ts @@ -0,0 +1,30 @@ +import type { ParserOptions } from 'react-docgen-typescript'; +import type { ParamType } from '../utils/index'; + +// all of the types in here get exposed as part of the public API + +export interface Snippet { + group: string; + name: string; + code: string; +} + +export interface PlayroomConfig { + components: string; + outputPath: string; + title?: string; + themes?: string; + widths?: number[]; + snippets?: Snippet[]; + frameComponent?: string; + exampleCode?: string; + cwd?: string; + storageKey?: string; + bundler: 'webpack' | 'vite'; + viteConfig?: () => Promise; + webpackConfig?: () => Promise; + baseUrl?: string; + paramType: ParamType; + iframeSandbox?: string; + reactDocgenTypescriptConfig?: ParserOptions; +} diff --git a/src/useScope.js b/src/useScope.js deleted file mode 100644 index 9afa1d90..00000000 --- a/src/useScope.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable-next-line import/no-unresolved */ -const useScope = require('__PLAYROOM_ALIAS__USE_SCOPE__'); - -module.exports = useScope.default || useScope; diff --git a/src/useScope.ts b/src/useScope.ts new file mode 100644 index 00000000..1fcf3967 --- /dev/null +++ b/src/useScope.ts @@ -0,0 +1,4 @@ +import * as themes from '__PLAYROOM_ALIAS__THEMES__'; + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +export default themes as typeof import('__PLAYROOM_ALIAS__THEMES__').default; diff --git a/src/userDefinedModules.d.ts b/src/userDefinedModules.d.ts new file mode 100644 index 00000000..01ed592a --- /dev/null +++ b/src/userDefinedModules.d.ts @@ -0,0 +1,28 @@ +declare module '__PLAYROOM_ALIAS__SNIPPETS__' { + import type { Snippet } from '../utils/index'; + const snippets: Snippet[]; + export default snippets; +} + +declare module '__PLAYROOM_ALIAS__THEMES__' { + const themes: Record; + export default themes; +} + +// the type for this module may not be what we want as we import it using '* as something' +// this means it won't end up actually being a record +declare module '__PLAYROOM_ALIAS__COMPONENTS__' { + const components: Record>; + export default components; +} + +declare module '__PLAYROOM_ALIAS__FRAME_COMPONENT__' { + import type { ComponentType, ComponentType } from 'react'; + const frameComponent: ComponentType & { frameSrc?: any }; + export default frameComponent; +} + +declare module '__PLAYROOM_ALIAS__USE_SCOPE__' { + const useScope: () => Record; + export default useScope; +} diff --git a/src/utils/hmr.ts b/src/utils/hmr.ts new file mode 100644 index 00000000..6872776a --- /dev/null +++ b/src/utils/hmr.ts @@ -0,0 +1,10 @@ +type AcceptHandler = (relativePath: string, handler: () => void) => void; + +export function hmrAccept(handler: (accept: AcceptHandler) => void) { + if ( + typeof module !== 'undefined' && + typeof module?.hot?.accept === 'function' + ) { + handler(module.hot.accept); + } +} diff --git a/tsconfig.json b/tsconfig.json index 6f01e5bd..aef89ae4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,6 @@ "lib": ["dom", "es2022"], "target": "es2022" }, - "include": ["src"], + "include": ["src", "utils"], "exclude": ["cypress", "node_modules"] } diff --git a/utils/index.d.ts b/utils/index.d.ts deleted file mode 100644 index f4ca437c..00000000 --- a/utils/index.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -interface Snippet { - group: string; - name: string; - code: string; -} - -type Snippets = Snippet[]; - -type ParamType = 'hash' | 'search'; - -interface CompressParamsOptions { - code?: string; - themes?: string[]; - widths?: number[]; - theme?: string; -} -export const compressParams: (options: CompressParamsOptions) => string; - -interface CreateUrlOptions { - baseUrl?: string; - code?: string; - themes?: string[]; - widths?: number[]; - paramType?: ParamType; -} - -export const createUrl: (options: CreateUrlOptions) => string; - -interface CreatePreviewUrlOptions { - baseUrl?: string; - code?: string; - theme?: string; - paramType?: ParamType; -} - -export const createPreviewUrl: (options: CreatePreviewUrlOptions) => string; diff --git a/utils/index.js b/utils/index.js deleted file mode 100644 index 43ce6f10..00000000 --- a/utils/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const lzString = require('lz-string'); - -const compressParams = ({ code, themes, widths, theme }) => { - const data = JSON.stringify({ - ...(code ? { code } : {}), - ...(themes ? { themes } : {}), - ...(widths ? { widths } : {}), - ...(theme ? { theme } : {}), - }); - - return lzString.compressToEncodedURIComponent(data); -}; - -const createUrl = ({ baseUrl, code, themes, widths, paramType = 'hash' }) => { - let path = ''; - - if (code || themes || widths) { - const compressedData = compressParams({ code, themes, widths }); - - path = `${paramType === 'hash' ? '#' : ''}?code=${compressedData}`; - } - - if (baseUrl) { - const trimmedBaseUrl = baseUrl.replace(/\/$/, ''); - - return `${trimmedBaseUrl}/${path}`; - } - - return path; -}; - -const createPreviewUrl = ({ baseUrl, code, theme, paramType = 'hash' }) => { - let path = ''; - - if (code || theme) { - const compressedData = compressParams({ code, theme }); - - path = `/preview/${paramType === 'hash' ? '#' : ''}?code=${compressedData}`; - } - - if (baseUrl) { - const trimmedBaseUrl = baseUrl.replace(/\/$/, ''); - - return `${trimmedBaseUrl}${path}`; - } - - return path; -}; - -module.exports = { - compressParams, - createUrl, - createPreviewUrl, -}; diff --git a/utils/index.ts b/utils/index.ts new file mode 100644 index 00000000..c545d847 --- /dev/null +++ b/utils/index.ts @@ -0,0 +1,93 @@ +import lzString from 'lz-string'; +import type { Snippet } from '../src/types'; + +export type { PlayroomConfig, Snippet } from '../src/types'; + +export type Snippets = Snippet[]; + +export type ParamType = 'hash' | 'search'; + +export interface CompressParamsOptions { + code?: string; + themes?: string[]; + widths?: number[]; + theme?: string; +} + +export const compressParams = ({ + code, + themes, + widths, + theme, +}: CompressParamsOptions): string => { + const data = JSON.stringify({ + ...(code ? { code } : {}), + ...(themes ? { themes } : {}), + ...(widths ? { widths } : {}), + ...(theme ? { theme } : {}), + }); + + return lzString.compressToEncodedURIComponent(data); +}; + +export interface CreateUrlOptions { + baseUrl?: string; + code?: string; + themes?: string[]; + widths?: number[]; + paramType?: ParamType; +} + +export const createUrl = ({ + baseUrl, + code, + themes, + widths, + paramType = 'hash', +}: CreateUrlOptions): string => { + let path = ''; + + if (code || themes || widths) { + const compressedData = compressParams({ code, themes, widths }); + + path = `${paramType === 'hash' ? '#' : ''}?code=${compressedData}`; + } + + if (baseUrl) { + const trimmedBaseUrl = baseUrl.replace(/\/$/, ''); + + return `${trimmedBaseUrl}/${path}`; + } + + return path; +}; + +export interface CreatePreviewUrlOptions { + baseUrl?: string; + code?: string; + theme?: string; + paramType?: ParamType; +} + +export const createPreviewUrl = ({ + baseUrl, + code, + theme, + paramType = 'hash', +}: CreatePreviewUrlOptions): string => { + let path = ''; + + if (code || theme) { + const compressedData = compressParams({ code, theme }); + + path = `/preview/${paramType === 'hash' ? '#' : ''}?code=${compressedData}`; + } + + if (baseUrl) { + const trimmedBaseUrl = baseUrl.replace(/\/$/, ''); + + return `${trimmedBaseUrl}${path}`; + } + + return path; +};