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