diff --git a/.changeset/rare-gifts-sip.md b/.changeset/rare-gifts-sip.md new file mode 100644 index 0000000..8f5ab16 --- /dev/null +++ b/.changeset/rare-gifts-sip.md @@ -0,0 +1,9 @@ +--- +"react-github-permalink": minor +--- + +Switch style provider from HLJS to Prism. (Prism supports TSX). + +Auto detect language based on file extension. + +Adds a `language` prop to override any auto-detection behaviour. diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..b4044eb --- /dev/null +++ b/.env.template @@ -0,0 +1 @@ +STORYBOOK_GITHUB_TOKEN=${GITHUB_TOKEN_READONLY} diff --git a/.gitignore b/.gitignore index ee8face..ccbe188 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ .pnp.js .yarn/install-state.gz +.env + # testing /coverage diff --git a/.storybook/preview.ts b/.storybook/preview.ts deleted file mode 100644 index f9f6cbe..0000000 --- a/.storybook/preview.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Preview } from "@storybook/react"; -import './global.css'; - -const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, - }, - }, -}; - -export default preview; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 0000000..edde195 --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,22 @@ +import type { Preview } from "@storybook/react"; +import './global.css'; +import { GithubPermalinkProvider } from "../src/library/config/GithubPermalinkContext"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, + decorators: [(Story) => ( +
+ + + +
)] +}; + +export default preview; diff --git a/README.md b/README.md index fb1b2b5..5fcbec7 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,15 @@ I highly rate the [`vscode-copy-github-permalink` plugin](https://marketplace.vi https://codesandbox.io/s/exciting-nova-js5zlk?file=/src/App.js +## Language Support + +Langauge is naively auto detected based on file extension. See [logic here for all auto-detected languages](https://github.com/dwjohnston/react-github-permalink/pull/73/files#diff-b6feb43e40d6eae1cba733450d691be8f83a1a50ecbff1b890cd343b2039ece1). + +If this does not suit you, you can override the autodetected langauage with the `language` prop. + + + + ## RSC Compatibility / Three modes of operation This package is compatible with Next 13+ and the components can be used as RSCs if you wish. diff --git a/sample_files/sample.dockerfile b/sample_files/sample.dockerfile new file mode 100644 index 0000000..b95a456 --- /dev/null +++ b/sample_files/sample.dockerfile @@ -0,0 +1,20 @@ +# Use an official Node.js runtime as a parent image +FROM node:18-alpine + +# Set the working directory +WORKDIR /app + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the application code +COPY . . + +# Expose the app port +EXPOSE 3000 + +# Start the application +CMD ["npm", "start"] \ No newline at end of file diff --git a/sample_files/sample.html b/sample_files/sample.html new file mode 100644 index 0000000..9230168 --- /dev/null +++ b/sample_files/sample.html @@ -0,0 +1,19 @@ + + + + + + Sample HTML Page + + + +

Welcome to the Sample HTML Page

+

This is a simple HTML file for demonstration purposes.

+ + + + \ No newline at end of file diff --git a/sample_files/sample.xml b/sample_files/sample.xml new file mode 100644 index 0000000..674eac8 --- /dev/null +++ b/sample_files/sample.xml @@ -0,0 +1,15 @@ + + + + Introduction to XML + Jane Doe + 2022 + Technology + + + Learning React + John Smith + 2023 + Programming + + \ No newline at end of file diff --git a/sample_files/sample1.tsx b/sample_files/sample1.tsx new file mode 100644 index 0000000..5a79915 --- /dev/null +++ b/sample_files/sample1.tsx @@ -0,0 +1,11 @@ +function Foo(props: { name: string }) { + return
{props.name}
+ +} + +export function Demo() { + return
+ + Hello world! +
+} \ No newline at end of file diff --git a/src/library/GithubPermalink/GithubPermalink.stories.tsx b/src/library/GithubPermalink/GithubPermalink.stories.tsx index e2a07fe..f2820aa 100644 --- a/src/library/GithubPermalink/GithubPermalink.stories.tsx +++ b/src/library/GithubPermalink/GithubPermalink.stories.tsx @@ -23,6 +23,33 @@ export const Primary: Story = { ), }; +export const DifferentLanguages: Story = { + render: () => (
+

Go

+ + +

JavaScript

+ + +

SQL

+ + +

TSX

+ + +

Docker file

+ + +

XML

+ + +

HTML

+ + +
+ ), +}; + export const WithBackground: Story = { render: () => (
diff --git a/src/library/GithubPermalink/GithubPermalink.tsx b/src/library/GithubPermalink/GithubPermalink.tsx index 2a7c5b2..351d46b 100644 --- a/src/library/GithubPermalink/GithubPermalink.tsx +++ b/src/library/GithubPermalink/GithubPermalink.tsx @@ -1,11 +1,10 @@ "use client" -import {useContext, useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { GithubPermalinkDataResponse, GithubPermalinkContext } from "../config/GithubPermalinkContext"; -import { useMediaQuery } from "react-responsive"; import { GithubPermalinkBase, GithubPermalinkBaseProps } from "./GithubPermalinkBase"; -type GithubPermalinkProps = Omit & {permalink: string}; +type GithubPermalinkProps = Omit & { permalink: string }; export function GithubPermalink(props: GithubPermalinkProps) { const { permalink } = props; @@ -28,7 +27,7 @@ export function GithubPermalink(props: GithubPermalinkProps) { } - return + return } diff --git a/src/library/GithubPermalink/GithubPermalinkBase.tsx b/src/library/GithubPermalink/GithubPermalinkBase.tsx index 2529e0a..7036ebc 100644 --- a/src/library/GithubPermalink/GithubPermalinkBase.tsx +++ b/src/library/GithubPermalink/GithubPermalinkBase.tsx @@ -4,8 +4,9 @@ import { GithubSvg } from "../GithubSvg/GithubSvg"; import { PropsWithChildren } from "react"; 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"; +import { AvailableLanguagesPrism } from "../SyntaxHighlight/availableLanguagesPrism"; export type GithubPermalinkBaseProps = { className?: string; @@ -13,19 +14,21 @@ export type GithubPermalinkBaseProps = { excludeLines?: Array<[from: number, to: number]>; excludeText?: string; data: GithubPermalinkDataResponse; + language?: AvailableLanguagesPrism; } export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { - const { data, permalink, excludeLines, excludeText = "" } = props; + const { data, permalink, excludeLines, excludeText = "", } = props; if (data.status === "ok") { const formatedLineExclusions = formatForLineExclusions(data, excludeLines); + const language = props.language ?? getLanguageFromPath(data.path); const clipboard = formatedLineExclusions.reduce((acc, cur) => { if (cur.isExclude) { @@ -41,11 +44,11 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { {formatedLineExclusions.map((v) => { if (v.isExclude) { - return + return } - return + return })} diff --git a/src/library/GithubPermalink/github-permalink.css b/src/library/GithubPermalink/github-permalink.css index 5229982..0e6e0ec 100644 --- a/src/library/GithubPermalink/github-permalink.css +++ b/src/library/GithubPermalink/github-permalink.css @@ -1,25 +1,23 @@ - - .rgp-base { - --rgp-color-bg-stark: white; + --rgp-color-bg-stark: white; --rgp-color-text-stark: rgb(31, 35, 40); - + --rgp-color-bg-frame: rgb(246, 248, 250); --rgp-color-text-frame: rgb(101, 109, 118); - - --rgp-color-border: rgb(208, 215, 222); + + --rgp-color-border: rgb(208, 215, 222); --rgp-color-issuenumber: rgb(101, 109, 118); - --rgp-color-file-link: rgb(9, 105, 218); + --rgp-color-file-link: rgb(9, 105, 218); --rgp-color-commit-link: rgb(31, 35, 40); - --rgp-color-status-foreground: white; + --rgp-color-status-foreground: white; --rgp-color-status-open: rgb(31, 136, 61); --rgp-color-status-closed: rgb(130, 80, 223); --rgp-color-status-error: red; - --rgp-color-reaction-foreground: #0969da; + --rgp-color-reaction-foreground: #0969da; --rgp-color-reaction-background: #ddf4ff; --rgp-color-button-text: #59636e; @@ -44,43 +42,43 @@ .rgp-base { - --rgp-color-bg-stark: rgb(29, 31, 33); - --rgp-color-text-stark: rgb(230, 237, 243); + --rgp-color-bg-stark: rgb(29, 31, 33); + --rgp-color-text-stark: rgb(230, 237, 243); - - --rgp-color-bg-frame: rgb(22, 27, 34); - --rgp-color-text-frame: rgb(141, 150, 160); - - --rgp-color-border: rgb(48, 54, 61); - - --rgp-color-issuenumber: rgb(101, 109, 118); - --rgp-color-file-link: rgb(68, 147, 248); - --rgp-color-commit-link: rgb(230, 237, 243); - --rgp-color-status-foreground: white; - --rgp-color-status-open: #238636; - --rgp-color-status-closed: rgb(130, 80, 223); - --rgp-color-status-error: red; + --rgp-color-bg-frame: rgb(22, 27, 34); + --rgp-color-text-frame: rgb(141, 150, 160); + + --rgp-color-border: rgb(48, 54, 61); + + --rgp-color-issuenumber: rgb(101, 109, 118); + --rgp-color-file-link: rgb(68, 147, 248); + --rgp-color-commit-link: rgb(230, 237, 243); + + --rgp-color-status-foreground: white; + --rgp-color-status-open: #238636; + --rgp-color-status-closed: rgb(130, 80, 223); + --rgp-color-status-error: red; + + --rgp-color-reaction-foreground: #4493f8; + --rgp-color-reaction-background: #388bfd33; + - --rgp-color-reaction-foreground: #4493f8; - --rgp-color-reaction-background: #388bfd33; + --rgp-color-button-text: #9198a1; + --rgp-color-button-background: #212830; + --rgp-color-button-background-hover: #262c36; - - --rgp-color-button-text: #9198a1; - --rgp-color-button-background: #212830; - --rgp-color-button-background-hover: #262c36; + --rgp-color-button-border: #3d444d; - --rgp-color-button-border: #3d444d; - } } .react-github-issue-link-inline { - color: var(--rgp-color-reaction-forergound); - border: solid 1px var(--rgp-color-text-frame); + color: var(--rgp-color-reaction-forergound); + border: solid 1px var(--rgp-color-text-frame); border-radius: 2px; - background-color: var(--rgp-color-bg-stark); + background-color: var(--rgp-color-bg-stark); font-family: monospace; padding: 0 0.25em; margin: 0 0.25em; @@ -88,28 +86,33 @@ .github-logo { - position: relative; + position: relative; top: 0.1em; - width: 0.9em; - height: 0.9em; + width: 0.9em; + height: 0.9em; } } .rgp-base { - pre + .hide-line-numbers { + &>pre { + /* react-style-highlighter is adding margin via style attribute*/ + margin: 0 !important; + } + + pre+.hide-line-numbers { margin-top: -1em; } - .hide-line-numbers + pre { + .hide-line-numbers+pre { margin-top: 0em; } .hide-line-numbers { - margin:0; + margin: 0; } - + .hide-line-numbers .linenumber { visibility: hidden; } @@ -119,19 +122,20 @@ -svg.github-logo{ +svg.github-logo { fill: var(--rgp-color-commit-link); } .react-github-issuelink .react-github-issuelink-body { font-size: 16px; color: var(--rgp-color-text-stark); - display: flex; - flex-flow: row nowrap; + display: flex; + flex-flow: row nowrap; justify-content: space-between; align-items: center; text-decoration: none; } + .react-github-issuelink-body p { margin: 0; } @@ -143,7 +147,7 @@ svg.github-logo{ } .react-github-issuelink a:hover .react-github-issuelink-title, - .react-github-issuelink a:hover .react-github-issuelink-number { +.react-github-issuelink a:hover .react-github-issuelink-number { text-decoration: underline; } @@ -153,7 +157,7 @@ svg.github-logo{ } .react-github-issuelink-repo { - font-size: 12px; + font-size: 12px; font-weight: bold; display: flex; flex-flow: row nowrap; @@ -162,18 +166,18 @@ svg.github-logo{ text-decoration: underline; } -.react-github-issuelink-status { - color: var(--rgp-color-status-foreground); - fill: var(--rgp-color-status-foreground); +.react-github-issuelink-status { + color: var(--rgp-color-status-foreground); + fill: var(--rgp-color-status-foreground); font-size: 0.75em; - padding: 0 10px; - border-radius: 2em; + padding: 0 10px; + border-radius: 2em; font-weight: 500; - line-height: 24px; + line-height: 24px; - display: flex; - flex-flow: row nowrap; - align-items: center; + display: flex; + flex-flow: row nowrap; + align-items: center; justify-content: space-between; gap: 0.6em; @@ -191,26 +195,28 @@ svg.github-logo{ padding: 0.5em; } -.react-github-permalink, .react-github-issuelink { +.react-github-permalink, +.react-github-issuelink { border: solid 1px var(--rgp-color-border); margin: 0.5em; font-size: 12px; - border-radius: 4px; + border-radius: 4px; text-align: left; background-color: var(--rgp-color-bg-stark); } -.react-github-permalink, .react-github-permalink .header { +.react-github-permalink, +.react-github-permalink .header { border-color: var(--rgp-color-border); - border-style: solid; - border-width: 1px; + border-style: solid; + border-width: 1px; } .react-github-permalink .header { - display: flex; + display: flex; flex-flow: row nowrap; gap: 0.75em; align-items: center; @@ -218,113 +224,116 @@ svg.github-logo{ .copy-button-container { - .tooltip-container{ - position: relative; - anchor-name: --tooltip-anchor; + .tooltip-container { + position: relative; + anchor-name: --tooltip-anchor; + } + + .tooltip-content { + + position: absolute; + top: -14px; + left: -26px; + background-color: var(--rgp-color-tooltip-bg); + + transition-property: opacity; + transition-duration: 0.25s; + opacity: 1; + + @starting-style { + opacity: 0; } - .tooltip-content { + @supports(position-anchor: --tooltip-anchor) { + position: fixed; + position-anchor: --tooltip-anchor; + top: unset; + left: unset; + bottom: anchor(top); + justify-self: anchor-center; + margin: 3px; - position: absolute; - top: -14px; - left: -26px; - background-color: var(--rgp-color-tooltip-bg); + } - transition-property: opacity; - transition-duration: 0.25s; - opacity: 1; - @starting-style { - opacity: 0; - } + height: 12px; - @supports(position-anchor: --tooltip-anchor){ - position: fixed; - position-anchor: --tooltip-anchor; - top:unset; - left: unset; - bottom: anchor(top); - justify-self: anchor-center; - margin: 3px; - } + white-space: nowrap; + border-radius: 2px; + color: var(--rgp-color-text-stark); + box-shadow: 2px 2px 2px var(--rgp-color-bg-stark); - height: 12px; - - - white-space: nowrap; - border-radius: 2px; + font-size: 11px; + padding: 0.5em 0.75em; + } - color: var(--rgp-color-text-stark); - box-shadow: 2px 2px 2px var(--rgp-color-bg-stark); + .tooltip-target[popover] { + overlay: none !important; + display: none; + } - font-size: 11px; - padding: 0.5em 0.75em; - } + .tooltip-target[popover]:popover-open+.tooltip-content { + display: block; + } - .tooltip-target[popover] { - overlay: none !important; - display: none; - } + .tooltip-target[popover]+.tooltip-content { + display: none; + } - .tooltip-target[popover]:popover-open + .tooltip-content { - display: block; + + + button { + all: unset; + /* Remove default styling */ + appearance: none; + /* Remove specific styling across different browsers */ + border: none; + /* Remove any border */ + background: none; + /* Remove background */ + padding: 0; + /* Remove default padding */ + margin: 0; + /* Remove default margin */ + box-shadow: none; + /* Remove box shadow */ + text-align: inherit; + /* Inherit text alignment from parent */ + font: inherit; + /* Use parent's font settings */ + color: inherit; + /* Use parent's text color */ + cursor: pointer; + /* Set pointer cursor for better UX */ + background-color: var(--rgp-color-button-background); + border: solid 1px var(--rgp-color-button-border); + padding: 0.5em; + border-radius: 0.25em; + + + color: var(--rgp-color-button-text); + + &.clicked { + color: var(--rgp-color-button-text-success); } - .tooltip-target[popover] + .tooltip-content { - display: none; + svg { + fill: currentColor; } + &:hover { + background-color: var(--rgp-color-button-background-hover); + } + } - button { - all: unset; - /* Remove default styling */ - appearance: none; - /* Remove specific styling across different browsers */ - border: none; - /* Remove any border */ - background: none; - /* Remove background */ - padding: 0; - /* Remove default padding */ - margin: 0; - /* Remove default margin */ - box-shadow: none; - /* Remove box shadow */ - text-align: inherit; - /* Inherit text alignment from parent */ - font: inherit; - /* Use parent's font settings */ - color: inherit; - /* Use parent's text color */ - cursor: pointer; - /* Set pointer cursor for better UX */ - background-color: var(--rgp-color-button-background); - border: solid 1px var(--rgp-color-button-border); - padding: 0.5em; - border-radius: 0.25em; - - - color: var(--rgp-color-button-text); - - &.clicked { - color: var(--rgp-color-button-text-success); - } - svg { - fill: currentColor; - } - &:hover { - background-color: var(--rgp-color-button-background-hover); - } - - } - } -.react-github-permalink .header, .react-github-issuelink .header{ - border-width: 0 0 1px 0; +.react-github-permalink .header, +.react-github-issuelink .header { + border-width: 0 0 1px 0; font-family: "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif; } @@ -337,7 +346,7 @@ svg.github-logo{ } .react-github-permalink a.commit-link { - color: var(--rgp-color-commit-link); + color: var(--rgp-color-commit-link); text-decoration: underline; } @@ -351,10 +360,10 @@ svg.github-logo{ .react-github-permalink .header { background-color: var(--rgp-color-bg-frame); - padding: 8px; + padding: 8px; .link-wrapper { - min-width: 0; + min-width: 0; overflow: hidden; flex: 1 1 auto; @@ -369,28 +378,29 @@ svg.github-logo{ */ text-overflow: ellipsis; overflow: hidden; - display: block; + display: block; } } } -.react-github-permalink .error, .react-github-issuelink .error { - font-weight: bold; +.react-github-permalink .error, +.react-github-issuelink .error { + font-weight: bold; color: var(--rgp-color-status-error); margin-left: 1em; } .reaction-bar { - font-size: 0.8em; + font-size: 0.8em; margin-top: 0.5em; } .reaction-value { - color: var(--rgp-color-reaction-foreground); - background-color: var(--rgp-color-reaction-background); - border: solid 1px var(--rgp-color-reaction-foreground); + color: var(--rgp-color-reaction-foreground); + background-color: var(--rgp-color-reaction-background); + border: solid 1px var(--rgp-color-reaction-foreground); border-radius: 100px; display: inline-block; margin-right: 0.75em; @@ -399,4 +409,4 @@ svg.github-logo{ span { margin: 0 2px; } -} +} \ No newline at end of file diff --git a/src/library/SyntaxHighlight/SyntaxHighlight.tsx b/src/library/SyntaxHighlight/SyntaxHighlight.tsx index c2ebe3f..5a1a8de 100644 --- a/src/library/SyntaxHighlight/SyntaxHighlight.tsx +++ b/src/library/SyntaxHighlight/SyntaxHighlight.tsx @@ -1,8 +1,10 @@ "use client"; -import ReactSyntaxHighlighter from "react-syntax-highlighter/dist/esm/default-highlight"; -import { github, tomorrowNight } from 'react-syntax-highlighter/dist/cjs/styles/hljs'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { useMediaQuery } from "react-responsive"; import { useEffect, useState } from "react"; +import { AvailableLanguagesPrism } from './availableLanguagesPrism'; +import language from 'react-syntax-highlighter/dist/esm/languages/hljs/1c'; const noTheme = { "hljs": { @@ -125,10 +127,11 @@ const noTheme = { export function SyntaxHighlight(props: { text: string; startingLineNumber?: number; - className?: string; + className?: string; + language?: AvailableLanguagesPrism }) { - const { startingLineNumber, text, className } = props; + const { startingLineNumber, text, className, language } = props; const isDarkMode = useMediaQuery({ query: "(prefers-color-scheme: dark)" }) @@ -141,6 +144,6 @@ export function SyntaxHighlight(props: { }, []) - return {text} + return {text} } diff --git a/src/library/SyntaxHighlight/availableLanguagesPrism.ts b/src/library/SyntaxHighlight/availableLanguagesPrism.ts new file mode 100644 index 0000000..f302403 --- /dev/null +++ b/src/library/SyntaxHighlight/availableLanguagesPrism.ts @@ -0,0 +1,282 @@ +export type AvailableLanguagesPrism = + | "abap" + | "abnf" + | "actionscript" + | "ada" + | "agda" + | "al" + | "antlr4" + | "apacheconf" + | "apex" + | "apl" + | "applescript" + | "aql" + | "arduino" + | "arff" + | "asciidoc" + | "asm6502" + | "asmatmel" + | "aspnet" + | "autohotkey" + | "autoit" + | "avisynth" + | "avroIdl" // avro-idl + | "bash" + | "basic" + | "batch" + | "bbcode" + | "bicep" + | "birb" + | "bison" + | "bnf" + | "brainfuck" + | "brightscript" + | "bro" + | "bsl" + | "c" + | "cfscript" + | "chaiscript" + | "cil" + | "clike" + | "clojure" + | "cmake" + | "cobol" + | "coffeescript" + | "concurnas" + | "coq" + | "cpp" + | "crystal" + | "csharp" + | "cshtml" + | "csp" + | "cssExtras" // css-extras + | "css" + | "csv" + | "cypher" + | "d" + | "dart" + | "dataweave" + | "dax" + | "dhall" + | "diff" + | "django" + | "dnsZoneFile" // dns-zone-file + | "docker" + | "dot" + | "ebnf" + | "editorconfig" + | "eiffel" + | "ejs" + | "elixir" + | "elm" + | "erb" + | "erlang" + | "etlua" + | "excelFormula" // excel-formula + | "factor" + | "falselang" // false + | "firestoreSecurityRules" // firestore-security-rules + | "flow" + | "fortran" + | "fsharp" + | "ftl" + | "gap" + | "gcode" + | "gdscript" + | "gedcom" + | "gherkin" + | "git" + | "glsl" + | "gml" + | "gn" + | "goModule" // go-module + | "go" + | "graphql" + | "groovy" + | "haml" + | "handlebars" + | "haskell" + | "haxe" + | "hcl" + | "hlsl" + | "hoon" + | "hpkp" + | "hsts" + | "http" + | "ichigojam" + | "icon" + | "icuMessageFormat" // icu-message-format + | "idris" + | "iecst" + | "ignore" + | "inform7" + | "ini" + | "io" + | "j" + | "java" + | "javadoc" + | "javadoclike" + | "javascript" + | "javastacktrace" + | "jexl" + | "jolie" + | "jq" + | "jsExtras" // js-extras + | "jsTemplates" // js-templates + | "jsdoc" + | "json" + | "json5" + | "jsonp" + | "jsstacktrace" + | "jsx" + | "julia" + | "keepalived" + | "keyman" + | "kotlin" + | "kumir" + | "kusto" + | "latex" + | "latte" + | "less" + | "lilypond" + | "liquid" + | "lisp" + | "livescript" + | "llvm" + | "log" + | "lolcode" + | "lua" + | "magma" + | "makefile" + | "markdown" + | "markupTemplating" // markup-templating + | "markup" + | "matlab" + | "maxscript" + | "mel" + | "mermaid" + | "mizar" + | "mongodb" + | "monkey" + | "moonscript" + | "n1ql" + | "n4js" + | "nand2tetrisHdl" // nand2tetris-hdl + | "naniscript" + | "nasm" + | "neon" + | "nevod" + | "nginx" + | "nim" + | "nix" + | "nsis" + | "objectivec" + | "ocaml" + | "opencl" + | "openqasm" + | "oz" + | "parigp" + | "parser" + | "pascal" + | "pascaligo" + | "pcaxis" + | "peoplecode" + | "perl" + | "phpExtras" // php-extras + | "php" + | "phpdoc" + | "plsql" + | "powerquery" + | "powershell" + | "processing" + | "prolog" + | "promql" + | "properties" + | "protobuf" + | "psl" + | "pug" + | "puppet" + | "pure" + | "purebasic" + | "purescript" + | "python" + | "q" + | "qml" + | "qore" + | "qsharp" + | "r" + | "racket" + | "reason" + | "regex" + | "rego" + | "renpy" + | "rest" + | "rip" + | "roboconf" + | "robotframework" + | "ruby" + | "rust" + | "sas" + | "sass" + | "scala" + | "scheme" + | "scss" + | "shellSession" // shell-session + | "smali" + | "smalltalk" + | "smarty" + | "sml" + | "solidity" + | "solutionFile" // solution-file + | "soy" + | "sparql" + | "splunkSpl" // splunk-spl + | "sqf" + | "sql" + | "squirrel" + | "stan" + | "stylus" + | "swift" + | "systemd" + | "t4Cs" // t4-cs + | "t4Templating" // t4-templating + | "t4Vb" // t4-vb + | "tap" + | "tcl" + | "textile" + | "toml" + | "tremor" + | "tsx" + | "tt2" + | "turtle" + | "twig" + | "typescript" + | "typoscript" + | "unrealscript" + | "uorazor" + | "uri" + | "v" + | "vala" + | "vbnet" + | "velocity" + | "verilog" + | "vhdl" + | "vim" + | "visualBasic" // visual-basic + | "warpscript" + | "wasm" + | "webIdl" // web-idl + | "wiki" + | "wolfram" + | "wren" + | "xeora" + | "xmlDoc" // xml-doc + | "xojo" + | "xquery" + | "yaml" + | "yang" + | "zig" + | "xml" + | "html" + | "dockerfile" + | "text"; \ No newline at end of file diff --git a/src/library/utils/getLanguageFromPath.test.ts b/src/library/utils/getLanguageFromPath.test.ts new file mode 100644 index 0000000..e7165c8 --- /dev/null +++ b/src/library/utils/getLanguageFromPath.test.ts @@ -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('text'); + }); + + it('should return javascript as fallback for files without extension', () => { + expect(getLanguageFromPath('asdf')).toBe('text'); + }); + + 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'); + }); +}); \ No newline at end of file diff --git a/src/library/utils/getLanguageFromPath.ts b/src/library/utils/getLanguageFromPath.ts new file mode 100644 index 0000000..4afca5f --- /dev/null +++ b/src/library/utils/getLanguageFromPath.ts @@ -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 'text'; // fallback to javascript for unknown extensions + } +} \ No newline at end of file