Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Fix syntax highlighting for React/TypeScript files by adding language…
… detection

Co-authored-by: dwjohnston <2467377+dwjohnston@users.noreply.github.com>
  • Loading branch information
Copilot and dwjohnston committed Sep 26, 2025
commit a542435fd5b96580182a885dc93568a391a799f9
6 changes: 4 additions & 2 deletions src/library/GithubPermalink/GithubPermalinkBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SyntaxHighlight } from "../SyntaxHighlight/SyntaxHighlight";
import { formatForLineExclusions } from "./formatLineExclusions";
import { CopySvg } from "../images/CopySvg";
import { CopyButton } from "../common/CopyButton/CopyButton";
import { getLanguageFromPath } from "../utils/getLanguageFromPath";

export type GithubPermalinkBaseProps = {
className?: string;
Expand All @@ -26,6 +27,7 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) {
if (data.status === "ok") {

const formatedLineExclusions = formatForLineExclusions(data, excludeLines);
const language = getLanguageFromPath(data.path);

const clipboard = formatedLineExclusions.reduce((acc, cur) => {
if (cur.isExclude) {
Expand All @@ -41,11 +43,11 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) {

{formatedLineExclusions.map((v) => {
if (v.isExclude) {
return <SyntaxHighlight className="hide-line-numbers" text={excludeText} startingLineNumber={v.from} key={v.from}/>
return <SyntaxHighlight className="hide-line-numbers" text={excludeText} startingLineNumber={v.from} language={language} key={v.from}/>

}

return <SyntaxHighlight text={v.lines.join("\n")} startingLineNumber={v.from} key={v.from}/>
return <SyntaxHighlight text={v.lines.join("\n")} startingLineNumber={v.from} language={language} key={v.from}/>

})}

Expand Down
2 changes: 1 addition & 1 deletion src/library/SyntaxHighlight/SyntaxHighlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function SyntaxHighlight(props: {
language?: AvailableLanguagesPrism
}) {

const { startingLineNumber, text, className } = props;
const { startingLineNumber, text, className, language = "javascript" } = props;

const isDarkMode = useMediaQuery({ query: "(prefers-color-scheme: dark)" })

Expand Down
69 changes: 69 additions & 0 deletions src/library/utils/getLanguageFromPath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { describe, it, expect } from 'vitest';
import { getLanguageFromPath } from './getLanguageFromPath';

describe('getLanguageFromPath', () => {
it('should return typescript for .tsx files', () => {
expect(getLanguageFromPath('ReactRenders3.tsx')).toBe('typescript');
});

it('should return typescript for .ts files', () => {
expect(getLanguageFromPath('component.ts')).toBe('typescript');
});

it('should return javascript for .js files', () => {
expect(getLanguageFromPath('script.js')).toBe('javascript');
});

it('should return javascript for .jsx files', () => {
expect(getLanguageFromPath('component.jsx')).toBe('javascript');
});

it('should return python for .py files', () => {
expect(getLanguageFromPath('script.py')).toBe('python');
});

it('should return go for .go files', () => {
expect(getLanguageFromPath('main.go')).toBe('go');
});

it('should return html for .html files', () => {
expect(getLanguageFromPath('index.html')).toBe('html');
});

it('should return css for .css files', () => {
expect(getLanguageFromPath('styles.css')).toBe('css');
});

it('should return json for .json files', () => {
expect(getLanguageFromPath('package.json')).toBe('json');
});

it('should return markdown for .md files', () => {
expect(getLanguageFromPath('README.md')).toBe('markdown');
});

it('should return yaml for .yaml files', () => {
expect(getLanguageFromPath('config.yaml')).toBe('yaml');
});

it('should return yaml for .yml files', () => {
expect(getLanguageFromPath('config.yml')).toBe('yaml');
});

it('should return javascript as fallback for unknown extensions', () => {
expect(getLanguageFromPath('file.unknown')).toBe('javascript');
});

it('should return javascript as fallback for files without extension', () => {
expect(getLanguageFromPath('README')).toBe('javascript');
});

it('should handle paths with multiple dots', () => {
expect(getLanguageFromPath('src/components/MyComponent.tsx')).toBe('typescript');
});

it('should handle case insensitive extensions', () => {
expect(getLanguageFromPath('Component.TSX')).toBe('typescript');
expect(getLanguageFromPath('Script.JS')).toBe('javascript');
});
});
90 changes: 90 additions & 0 deletions src/library/utils/getLanguageFromPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { AvailableLanguagesPrism } from "../SyntaxHighlight/availableLanguagesPrism";

/**
* Maps file extensions to syntax highlighting languages supported by react-syntax-highlighter
*/
export function getLanguageFromPath(filePath: string): AvailableLanguagesPrism {
const extension = filePath.split('.').pop()?.toLowerCase();

switch (extension) {
case 'js':
case 'jsx':
case 'mjs':
case 'cjs':
return 'javascript';
case 'ts':
case 'tsx':
return 'typescript';
case 'py':
case 'python':
return 'python';
case 'java':
return 'java';
case 'cpp':
case 'cc':
case 'cxx':
case 'c++':
return 'cpp';
case 'c':
case 'h':
return 'c';
case 'cs':
return 'csharp';
case 'php':
return 'php';
case 'rb':
case 'ruby':
return 'ruby';
case 'go':
return 'go';
case 'rs':
return 'rust';
case 'swift':
return 'swift';
case 'kt':
case 'kts':
return 'kotlin';
case 'scala':
return 'scala';
case 'sh':
case 'bash':
return 'bash';
case 'ps1':
return 'powershell';
case 'sql':
return 'sql';
case 'html':
case 'htm':
return 'html';
case 'css':
return 'css';
case 'scss':
case 'sass':
return 'scss';
case 'json':
return 'json';
case 'xml':
return 'xml';
case 'yaml':
case 'yml':
return 'yaml';
case 'md':
case 'markdown':
return 'markdown';
case 'dockerfile':
return 'dockerfile';
case 'r':
return 'r';
case 'dart':
return 'dart';
case 'lua':
return 'lua';
case 'perl':
case 'pl':
return 'perl';
case 'vim':
return 'vim';
default:
return 'javascript'; // fallback to javascript for unknown extensions
}
}