committed by
GitHub
9 changed files with 145 additions and 132 deletions
@ -0,0 +1,46 @@ |
|||||
|
/* |
||||
|
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
|
*/ |
||||
|
|
||||
|
export type ViewportSizePreset = |
||||
|
| 'iPhone X' |
||||
|
| 'Pixel 2' |
||||
|
| 'iPad' |
||||
|
| 'Moto G4' |
||||
|
| 'Surface Duo'; |
||||
|
|
||||
|
export type ViewportSize = |
||||
|
| ViewportSizePreset |
||||
|
| 'auto' |
||||
|
| {width: number; height: number}; |
||||
|
|
||||
|
export type ViewportOrientation = 'portrait' | 'landscape'; |
||||
|
|
||||
|
const VIEWPORT_SIZE_PRESET_MAP: Record< |
||||
|
ViewportSizePreset, |
||||
|
{x: number; y: number} |
||||
|
> = { |
||||
|
'iPhone X': {x: 375, y: 812}, |
||||
|
iPad: {x: 768, y: 1024}, |
||||
|
'Pixel 2': {x: 411, y: 731}, |
||||
|
'Moto G4': {x: 360, y: 640}, |
||||
|
'Surface Duo': {x: 540, y: 720}, |
||||
|
}; |
||||
|
|
||||
|
export const computeViewportSize = ( |
||||
|
viewport: ViewportSize, |
||||
|
orientation: ViewportOrientation |
||||
|
): {width?: number; height?: number} => { |
||||
|
if (viewport === 'auto') { |
||||
|
return {}; |
||||
|
} |
||||
|
|
||||
|
if (typeof viewport === 'string') { |
||||
|
const {x, y} = VIEWPORT_SIZE_PRESET_MAP[viewport]; |
||||
|
return orientation === 'portrait' |
||||
|
? {width: x, height: y} |
||||
|
: {width: y, height: x}; |
||||
|
} |
||||
|
|
||||
|
return viewport; |
||||
|
}; |
@ -0,0 +1,53 @@ |
|||||
|
/* |
||||
|
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
|
*/ |
||||
|
|
||||
|
import type {SandpackFile} from '@codesandbox/sandpack-react'; |
||||
|
|
||||
|
export const createFileMap = (codeSnippets: any) => { |
||||
|
return codeSnippets.reduce( |
||||
|
(result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => { |
||||
|
if (codeSnippet.props.mdxType !== 'pre') { |
||||
|
return result; |
||||
|
} |
||||
|
const {props} = codeSnippet.props.children; |
||||
|
let filePath; // path in the folder structure
|
||||
|
let fileHidden = false; // if the file is available as a tab
|
||||
|
let fileActive = false; // if the file tab is shown by default
|
||||
|
|
||||
|
if (props.metastring) { |
||||
|
const [name, ...params] = props.metastring.split(' '); |
||||
|
filePath = '/' + name; |
||||
|
if (params.includes('hidden')) { |
||||
|
fileHidden = true; |
||||
|
} |
||||
|
if (params.includes('active')) { |
||||
|
fileActive = true; |
||||
|
} |
||||
|
} else { |
||||
|
if (props.className === 'language-js') { |
||||
|
filePath = '/App.js'; |
||||
|
} else if (props.className === 'language-css') { |
||||
|
filePath = '/styles.css'; |
||||
|
} else { |
||||
|
throw new Error( |
||||
|
`Code block is missing a filename: ${props.children}` |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
if (result[filePath]) { |
||||
|
throw new Error( |
||||
|
`File ${filePath} was defined multiple times. Each file snippet should have a unique path name` |
||||
|
); |
||||
|
} |
||||
|
result[filePath] = { |
||||
|
code: props.children as string, |
||||
|
hidden: fileHidden, |
||||
|
active: fileActive, |
||||
|
}; |
||||
|
|
||||
|
return result; |
||||
|
}, |
||||
|
{} |
||||
|
); |
||||
|
}; |
@ -0,0 +1,35 @@ |
|||||
|
/* |
||||
|
* Copyright (c) Facebook, Inc. and its affiliates. |
||||
|
*/ |
||||
|
|
||||
|
// @ts-nocheck
|
||||
|
|
||||
|
import {useState, useEffect} from 'react'; |
||||
|
import {linter} from '@codemirror/lint'; |
||||
|
import type {Diagnostic} from '@codemirror/lint'; |
||||
|
import type {Text} from '@codemirror/text'; |
||||
|
import type {EditorView} from '@codemirror/view'; |
||||
|
|
||||
|
export type LintDiagnostic = { |
||||
|
line: number; |
||||
|
column: number; |
||||
|
severity: 'warning' | 'error'; |
||||
|
message: string; |
||||
|
}[]; |
||||
|
|
||||
|
export const useSandpackLint = () => { |
||||
|
const [lintErrors, setLintErrors] = useState<LintDiagnostic>([]); |
||||
|
|
||||
|
// TODO: ideally @codemirror/linter would be code-split too but I don't know how
|
||||
|
// because Sandpack seems to ignore updates to the "extensions" prop.
|
||||
|
const onLint = linter(async (props: EditorView) => { |
||||
|
const {runESLint} = await import('./runESLint'); |
||||
|
const editorState = props.state.doc; |
||||
|
let {errors, codeMirrorPayload} = runESLint(editorState); |
||||
|
// Only show errors from rules, not parsing errors etc
|
||||
|
setLintErrors(errors.filter((e) => !e.fatal)); |
||||
|
return codeMirrorPayload; |
||||
|
}); |
||||
|
|
||||
|
return {lintErrors, lintExtensions: [onLint]}; |
||||
|
}; |
@ -1,124 +0,0 @@ |
|||||
/* |
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. |
|
||||
*/ |
|
||||
import {useState} from 'react'; |
|
||||
import {lintDiagnostic} from './eslint-integration'; |
|
||||
import {linter} from '@codemirror/lint'; |
|
||||
import type {EditorView} from '@codemirror/view'; |
|
||||
import type {SandpackFile} from '@codesandbox/sandpack-react'; |
|
||||
export type ViewportSizePreset = |
|
||||
| 'iPhone X' |
|
||||
| 'Pixel 2' |
|
||||
| 'iPad' |
|
||||
| 'Moto G4' |
|
||||
| 'Surface Duo'; |
|
||||
|
|
||||
export type ViewportSize = |
|
||||
| ViewportSizePreset |
|
||||
| 'auto' |
|
||||
| {width: number; height: number}; |
|
||||
|
|
||||
export type ViewportOrientation = 'portrait' | 'landscape'; |
|
||||
|
|
||||
export const generateRandomId = (): string => |
|
||||
Math.floor(Math.random() * 10000).toString(); |
|
||||
|
|
||||
const VIEWPORT_SIZE_PRESET_MAP: Record< |
|
||||
ViewportSizePreset, |
|
||||
{x: number; y: number} |
|
||||
> = { |
|
||||
'iPhone X': {x: 375, y: 812}, |
|
||||
iPad: {x: 768, y: 1024}, |
|
||||
'Pixel 2': {x: 411, y: 731}, |
|
||||
'Moto G4': {x: 360, y: 640}, |
|
||||
'Surface Duo': {x: 540, y: 720}, |
|
||||
}; |
|
||||
|
|
||||
export const computeViewportSize = ( |
|
||||
viewport: ViewportSize, |
|
||||
orientation: ViewportOrientation |
|
||||
): {width?: number; height?: number} => { |
|
||||
if (viewport === 'auto') { |
|
||||
return {}; |
|
||||
} |
|
||||
|
|
||||
if (typeof viewport === 'string') { |
|
||||
const {x, y} = VIEWPORT_SIZE_PRESET_MAP[viewport]; |
|
||||
return orientation === 'portrait' |
|
||||
? {width: x, height: y} |
|
||||
: {width: y, height: x}; |
|
||||
} |
|
||||
|
|
||||
return viewport; |
|
||||
}; |
|
||||
|
|
||||
export const createFileMap = (codeSnippets: any) => { |
|
||||
return codeSnippets.reduce( |
|
||||
(result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => { |
|
||||
if (codeSnippet.props.mdxType !== 'pre') { |
|
||||
return result; |
|
||||
} |
|
||||
const {props} = codeSnippet.props.children; |
|
||||
let filePath; // path in the folder structure
|
|
||||
let fileHidden = false; // if the file is available as a tab
|
|
||||
let fileActive = false; // if the file tab is shown by default
|
|
||||
|
|
||||
if (props.metastring) { |
|
||||
const [name, ...params] = props.metastring.split(' '); |
|
||||
filePath = '/' + name; |
|
||||
if (params.includes('hidden')) { |
|
||||
fileHidden = true; |
|
||||
} |
|
||||
if (params.includes('active')) { |
|
||||
fileActive = true; |
|
||||
} |
|
||||
} else { |
|
||||
if (props.className === 'language-js') { |
|
||||
filePath = '/App.js'; |
|
||||
} else if (props.className === 'language-css') { |
|
||||
filePath = '/styles.css'; |
|
||||
} else { |
|
||||
throw new Error( |
|
||||
`Code block is missing a filename: ${props.children}` |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
if (result[filePath]) { |
|
||||
throw new Error( |
|
||||
`File ${filePath} was defined multiple times. Each file snippet should have a unique path name` |
|
||||
); |
|
||||
} |
|
||||
result[filePath] = { |
|
||||
code: props.children as string, |
|
||||
hidden: fileHidden, |
|
||||
active: fileActive, |
|
||||
}; |
|
||||
|
|
||||
return result; |
|
||||
}, |
|
||||
{} |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export type LintDiagnostic = { |
|
||||
line: number; |
|
||||
column: number; |
|
||||
severity: 'warning' | 'error'; |
|
||||
message: string; |
|
||||
}[]; |
|
||||
|
|
||||
export const useSandpackLint = () => { |
|
||||
const [lintErrors, setLintErrors] = useState<LintDiagnostic>([]); |
|
||||
|
|
||||
const onLint = linter((props: EditorView) => { |
|
||||
const editorState = props.state.doc; |
|
||||
return import('./eslint-integration').then((module) => { |
|
||||
let {errors} = module.lintDiagnostic(editorState); |
|
||||
// Only show errors from rules, not parsing errors etc
|
|
||||
setLintErrors(errors.filter((e) => !e.fatal)); |
|
||||
return module.lintDiagnostic(editorState).codeMirrorPayload; |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
return {lintErrors, onLint}; |
|
||||
}; |
|
Loading…
Reference in new issue