Skip to content
Merged
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
85 changes: 81 additions & 4 deletions src/components/MDX/Sandpack/createFileMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,66 @@ export const AppJSPath = `/src/App.js`;
export const StylesCSSPath = `/src/styles.css`;
export const SUPPORTED_FILES = [AppJSPath, StylesCSSPath];

/**
* Tokenize meta attributes while ignoring brace-wrapped metadata (e.g. {expectedErrors: …}).
*/
function splitMeta(meta: string): string[] {
const tokens: string[] = [];
let current = '';
let depth = 0;
const trimmed = meta.trim();

for (let ii = 0; ii < trimmed.length; ii++) {
const char = trimmed[ii];

if (char === '{') {
if (depth === 0 && current) {
tokens.push(current);
current = '';
}
depth += 1;
continue;
}

if (char === '}') {
if (depth > 0) {
depth -= 1;
}
if (depth === 0) {
current = '';
}
if (depth < 0) {
throw new Error(`Unexpected closing brace in meta: ${meta}`);
}
continue;
}

if (depth > 0) {
continue;
}

if (/\s/.test(char)) {
if (current) {
tokens.push(current);
current = '';
}
continue;
}

current += char;
}

if (current) {
tokens.push(current);
}

if (depth !== 0) {
throw new Error(`Unclosed brace in meta: ${meta}`);
}

return tokens;
}

export const createFileMap = (codeSnippets: any) => {
return codeSnippets.reduce(
(result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => {
Expand All @@ -37,12 +97,17 @@ export const createFileMap = (codeSnippets: any) => {
let fileActive = false; // if the file tab is shown by default

if (props.meta) {
const [name, ...params] = props.meta.split(' ');
filePath = '/' + name;
if (params.includes('hidden')) {
const tokens = splitMeta(props.meta);
const name = tokens.find(
(token) => token.includes('/') || token.includes('.')
);
if (name) {
filePath = name.startsWith('/') ? name : `/${name}`;
}
if (tokens.includes('hidden')) {
fileHidden = true;
}
if (params.includes('active')) {
if (tokens.includes('active')) {
fileActive = true;
}
} else {
Expand All @@ -57,6 +122,18 @@ export const createFileMap = (codeSnippets: any) => {
}
}

if (!filePath) {
if (props.className === 'language-js') {
filePath = AppJSPath;
} else if (props.className === 'language-css') {
filePath = StylesCSSPath;
} 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`
Expand Down
Loading