├── .changeset
└── config.json
├── .eslintrc
├── .github
├── actions
│ └── setup
│ │ └── action.yml
└── workflows
│ ├── release.yml
│ └── static-analysis.yml
├── .gitignore
├── .npmignore
├── .prettierrc
├── .readme
├── basic.png
└── line-numbers.png
├── LICENSE
├── README.md
├── package.json
├── packages
├── demo
│ ├── .gitignore
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ ├── nearform-icon.svg
│ │ └── nearform-logo-white.svg
│ ├── src
│ │ ├── App.tsx
│ │ ├── app.module.css
│ │ ├── index.css
│ │ ├── main.tsx
│ │ ├── sample-code.ts
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── generate-prism-languages
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
└── prism-react-renderer
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ ├── components
│ │ ├── __tests__
│ │ │ ├── Highlight.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── Highlight.test.tsx.snap
│ │ ├── highlight.ts
│ │ ├── useGetLineProps.ts
│ │ ├── useGetTokenProps.ts
│ │ └── useTokenize.ts
│ ├── index.ts
│ ├── themes
│ │ ├── dracula.ts
│ │ ├── duotoneDark.ts
│ │ ├── duotoneLight.ts
│ │ ├── github.ts
│ │ ├── gruvboxMaterialDark.ts
│ │ ├── gruvboxMaterialLight.ts
│ │ ├── index.ts
│ │ ├── jettwaveDark.ts
│ │ ├── jettwaveLight.ts
│ │ ├── nightOwl.ts
│ │ ├── nightOwlLight.ts
│ │ ├── oceanicNext.ts
│ │ ├── okaidia.ts
│ │ ├── oneDark.ts
│ │ ├── oneLight.ts
│ │ ├── palenight.ts
│ │ ├── shadesOfPurple.ts
│ │ ├── synthwave84.ts
│ │ ├── ultramin.ts
│ │ ├── vsDark.ts
│ │ └── vsLight.ts
│ ├── types.ts
│ └── utils
│ │ ├── __tests__
│ │ ├── normalizeTokens.test.ts
│ │ └── themeToDict.test.ts
│ │ ├── normalizeTokens.ts
│ │ └── themeToDict.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── vitest.config.ts
├── patches
└── prismjs@1.29.0.patch
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
3 | "changelog": [
4 | "@svitejs/changesets-changelog-github-compact",
5 | {
6 | "repo": "FormidableLabs/prism-react-renderer"
7 | }
8 | ],
9 | "access": "public",
10 | "baseBranch": "master"
11 | }
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "prettier",
5 | "plugin:@typescript-eslint/recommended",
6 | "plugin:prettier/recommended",
7 | "plugin:cypress/recommended",
8 | "plugin:react/recommended",
9 | "plugin:react/jsx-runtime"
10 | ],
11 | "env": {
12 | "node": true,
13 | "browser": true,
14 | "jest": true
15 | },
16 | "settings": {
17 | "react": {
18 | "version": "detect"
19 | }
20 | },
21 | "globals": {
22 | "page": true
23 | },
24 | "ignorePatterns": ["prism-langs.ts", "**/dist/**/*", "**/build/**/*", "**/node_modules/**/*", "**/public/**/*", "**/.docusaurus/**/*"],
25 | "parser": "@typescript-eslint/parser",
26 | "parserOptions": {
27 | "ecmaVersion": 12,
28 | "ecmaFeatures": {
29 | "jsx": true
30 | }
31 | },
32 | "plugins": ["react", "react-hooks", "prettier", "@typescript-eslint"],
33 | "rules": {
34 | "max-params": 0,
35 | "max-statements": 0,
36 | "react/react-in-jsx-scope": "off",
37 | "react/jsx-uses-react": "off",
38 | "no-invalid-this": 0,
39 | "no-magic-numbers": 0,
40 | "no-unused-expressions": 0,
41 | "prettier/prettier": "error",
42 | "react/no-multi-comp": 0,
43 | "react/prefer-es6-class": 0,
44 | "react/prop-types": 0,
45 | "react/sort-prop-types": 0,
46 | "react/no-string-refs": 0,
47 | "@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }],
48 | "react/no-did-update-set-state": 0,
49 | "react-hooks/rules-of-hooks": "error",
50 | "react-hooks/exhaustive-deps": "error",
51 | "@typescript-eslint/no-var-requires": 0
52 | },
53 | }
54 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup
2 | description: Setup Build Step
3 | inputs:
4 | node-version:
5 | required: true
6 | default: '18.x'
7 |
8 | runs:
9 | using: "composite"
10 | steps:
11 | - uses: pnpm/action-setup@v3
12 | with:
13 | version: 8.2.0
14 |
15 | - name: Use Node.js
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version: ${{ inputs.node-version }}
19 | cache: 'pnpm'
20 |
21 | - name: Install dependencies
22 | shell: bash
23 | run: pnpm install
24 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release Workflow
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | id-token: write
14 | issues: write
15 | repository-projects: write
16 | deployments: write
17 | packages: write
18 | pull-requests: write
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: ./.github/actions/setup
23 |
24 | - name: Build packages
25 | run: pnpm run build
26 |
27 | - name: PR or Publish
28 | id: changesets
29 | uses: changesets/action@v1
30 | with:
31 | version: pnpm run version
32 | publish: pnpm run changeset publish
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/static-analysis.yml:
--------------------------------------------------------------------------------
1 | name: Static Analysis
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | static-analysis:
13 | name: 'Lint and Type-check'
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | - uses: ./.github/actions/setup
19 |
20 | - name: Build library
21 | run: pnpm run build
22 |
23 | - name: Type Check
24 | run: pnpm run typecheck
25 |
26 | - name: Lint
27 | run: pnpm run lint
28 |
29 | - name: Generate language definitions
30 | run: pnpm run build:languages
31 |
32 | - name: Unit Test
33 | run: pnpm run test
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | .vscode
5 | sandbox/node_modules
6 | *.log
7 | prism-langs.ts
8 | .idea
9 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .vscode
4 | .babelrc
5 | *.log
6 | !./prism
7 | !./package.json
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "arrowParens": "avoid",
4 | "xmlWhitespaceSensitivity": "ignore",
5 | "proseWrap": "always",
6 | "singleQuote": false
7 | }
8 |
--------------------------------------------------------------------------------
/.readme/basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FormidableLabs/prism-react-renderer/e1c83a468b05df7f452b3ad7e4ae5ab874574d4e/.readme/basic.png
--------------------------------------------------------------------------------
/.readme/line-numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FormidableLabs/prism-react-renderer/e1c83a468b05df7f452b3ad7e4ae5ab874574d4e/.readme/line-numbers.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Nearform
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | A lean Prism highlighter component for React 7 |
8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 | Comes with everything to render Prismjs syntax highlighted code directly in React & React Native! 19 |
20 | 21 | ## Introduction 22 | 23 | Prism React Renderer powers syntax highlighting in the amazing [Docusaurus](https://docusaurus.io/) framework and many others. 24 | 25 | This library tokenises code using Prism and provides a small render-props-driven 26 | component to quickly render it out into React. This is why it even works with 27 | React Native! It's bundled with a modified version of Prism that won't pollute 28 | the global namespace and comes with 29 | [a couple of common language syntaxes](./packages/generate-prism-languages/index.ts#L9-L23). 30 | 31 | _(There's also an [escape-hatch](https://github.com/FormidableLabs/prism-react-renderer#prism) to use your own Prism setup, just in case)_ 32 | 33 | It also comes with its own [VSCode-like theming format](#theming), which means by default 34 | you can easily drop in different themes, use the ones this library ships with, or 35 | create new ones programmatically on the fly. 36 | 37 | _(If you just want to use your Prism CSS-file themes, that's also no problem)_ 38 | 39 | ## Table of Contents 40 | 41 | 42 | 43 | - [Installation](#installation) 44 | - [Usage](#usage) 45 | - [Custom Language Support](#custom-language-support) 46 | - [Basic Props](#basic-props) 47 | - [children](#children) 48 | - [language](#language) 49 | - [code](#code) 50 | - [Advanced Props](#advanced-props) 51 | - [theme](#theme) 52 | - [prism](#prism) 53 | - [Children Function](#children-function) 54 | - [state](#state) 55 | - [prop getters](#prop-getters) 56 | - [`getLineProps`](#getlineprops) 57 | - [`getTokenProps`](#gettokenprops) 58 | - [Utility Functions](#utility-functions) 59 | - [`useTokenize`](#usetokenize) 60 | - [`normalizeTokens`](#normalizetokens) 61 | - [Theming](#theming) 62 | - [Using a built-in theme](#using-a-built-in-theme) 63 | - [Providing a CSS based theme](#providing-a-css-based-theme) 64 | - [Upgrade](#upgrade) 65 | - [Change module imports](#change-module-imports) 66 | - [Change theme imports](#change-theme-imports) 67 | - [Check language support](#check-language-support) 68 | - [Add language component](#add-language-component) 69 | - [Development](#development) 70 | - [Local Demo](#local-demo) 71 | - [LICENSE](#license) 72 | - [Maintenance Status](#maintenance-status) 73 | 74 | 75 | 76 | ## Installation 77 | 78 | This module is distributed via npm which is bundled with node and 79 | should be installed as one of your project's `dependencies`: 80 | 81 | ```sh 82 | # npm 83 | npm install --save prism-react-renderer 84 | # yarn 85 | yarn add prism-react-renderer 86 | # pnpm 87 | pnpm add prism-react-renderer 88 | ``` 89 | 90 | > Prism React Renderer has a peer dependency on `react` 91 | 92 | ### Usage 93 | 94 | Prism React Renderer has a named export for the `Price: {item.price}
108 |Quantity: {item.quantity}
109 |122 | {tokens.map((line, i) => ( 123 |131 | )} 132 |124 | {i + 1} 125 | {line.map((token, key) => ( 126 | 127 | ))} 128 |129 | ))} 130 |
{/* more jsx here */}229 | )} 230 |
` | 245 | 246 | A "Token" is an object that represents a piece of content for Prism. It has a `types` property, which is an array 247 | of types that indicate the purpose and styling of a piece of text, and a `content` property, which is the actual 248 | text. 249 | 250 | You'd typically iterate over `tokens`, rendering each line, and iterate over its items, rendering out each token, which is a piece of 251 | this line. 252 | 253 | ### prop getters 254 | 255 | > See 256 | > [Kent C. Dodds' blog post about prop getters](https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf) 257 | 258 | These functions are used to apply props to the elements that you render. This 259 | gives you maximum flexibility to render what, when, and wherever you like. 260 | 261 | You'd typically call these functions with some dictated input and add on all other 262 | props that it should pass through. It'll correctly override and modify the props 263 | that it returns to you, so passing props to it instead of adding them directly is 264 | advisable. 265 | 266 | | property | type | description | 267 | | --------------- | -------------- | ----------------------------------------------------------------------------------------------------- | 268 | | `getLineProps` | `function({})` | returns the props you should apply to any list of tokens, i.e. the element that contains your tokens. | 269 | | `getTokenProps` | `function({})` | returns the props you should apply to the elements displaying tokens that you render. | 270 | 271 | #### `getLineProps` 272 | 273 | You need to add a `line` property (type: `Token[]`) to the object you're passing to 274 | `getLineProps`. 275 | 276 | This getter will return you props to spread onto your line elements (typically `s`). 277 | 278 | It will typically return a `className` (if you pass one it'll be appended), `children`, 279 | `style` (if you pass one it'll be merged). It also passes on all other props you pass 280 | to the input. 281 | 282 | The `className` will always contain `.token-line`. 283 | 284 | #### `getTokenProps` 285 | 286 | You need to add a `token` property (type: `Token`) to the object you're passing to 287 | `getTokenProps`. 288 | 289 | This getter will return you props to spread onto your token elements (typically `s`). 290 | 291 | It will typically return a `className` (if you pass one it'll be appended), `children`, 292 | `style` (if you pass one it'll be merged). It also passes on all other props you pass 293 | to the input. 294 | 295 | The `className` will always contain `.token`. This also provides full compatibility with 296 | your old Prism CSS-file themes. 297 | 298 | ## Utility Functions 299 | 300 | ### `useTokenize` 301 | 302 | > `(options: TokenizeOptions) => Token[][]` 303 | 304 | ```ts 305 | type TokenizeOptions = { 306 | prism: PrismLib 307 | code: string 308 | grammar?: PrismGrammar 309 | language: Language 310 | } 311 | 312 | ``` 313 | 314 | This is a React hook that tokenizes code using Prism. It returns an array of tokens that can be rendered using the built-in `` component or your own custom component. It uses `normalizeTokens` internally to convert the tokens into a shape that can be rendered. 315 | 316 | - `prism: PrismLib`: the Prism library to use for tokenization. This can be the vendored version of Prism that is included with `prism-react-renderer` or a custom version of Prism that you have configured. 317 | 318 | - `code: string`: a string containing the code to tokenize. 319 | - `grammar?: PrismGrammar`: a Prism grammar object to use for tokenization. If this is omitted, the tokens will just be normalized. A grammar can be obtained from `Prism.languages` or by importing a language from `prismjs/components/`. 320 | - `language: Language`: the language to use for tokenization. This should be a language that Prism supports. 321 | 322 | ### `normalizeTokens` 323 | 324 | > `(tokens: (PrismToken | string)[]) => Token[][]` 325 | 326 | Takes an array of Prism’s tokens and groups them by line, converting strings into tokens. Tokens can become recursive in some cases which means that their types are concatenated. Plain-string tokens however are always of type `plain`. 327 | 328 | - `PrismToken` is an internal alias for `Token` exported by `prismjs` and is defined [here](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/prismjs/index.d.ts#L347). 329 | 330 | - `Token` is an internal object that represents a slice of tokenized content for Prism with three properties: 331 | - `types: string[]`: an array of types that indicate the purpose and styling of a piece of text 332 | - `content: string`: the content of the token 333 | - `empty: boolean`: a flag indicating whether the token is empty or not. 334 | 335 | ## Theming 336 | 337 | ### Using a built-in theme 338 | 339 | The `defaultProps` you'd typically apply in a basic use-case, contain a default theme. 340 | This theme is [vsDark](./packages/prism-react-renderer/src/themes/vsDark.ts). 341 | 342 | ```jsx 343 | import { Highlight, themes } from 'prism-react-renderer'; 344 | 345 | 346 | ``` 347 | 348 | These themes are JSON-based and are heavily inspired by VSCode's theme format. 349 | 350 | ```ts 351 | export type PrismTheme = { 352 | plain: PrismThemeEntry 353 | styles: Array<{ 354 | types: string[] 355 | style: PrismThemeEntry 356 | languages?: Language[] 357 | }> 358 | } 359 | ``` 360 | 361 | The `plain` property provides a base style-object. This style object is directly used 362 | in the `style` props that you'll receive from the prop getters, if a `theme` prop has 363 | been passed to ` `. 364 | 365 | The `styles` property contains an array of definitions. Each definition contains a `style` 366 | property, that is also just a style object. These styles are limited by the `types` 367 | and `languages` properties. 368 | 369 | The `types` properties is an array of token types that Prism outputs. The `languages` 370 | property limits styles to highlighted languages. 371 | 372 | When converting a Prism CSS theme it's mostly just necessary to use classes as 373 | `types` and convert the declarations to object-style-syntax and put them on `style`. 374 | 375 | ### Providing a CSS based theme 376 | 377 | In order to use a CSS based theme like the ones from [PrismJS](https://github.com/PrismJS/prism-themes), you need to disable the built in theme. 378 | 379 | ```ts 380 | const emptyTheme = { plain: {}, styles: [] }; 381 | 382 | 383 | ``` 384 | 385 | ## Upgrade 386 | 387 | If you are migrating from v1.x to v2.x, follow these steps 388 | 389 | ### Change module imports 390 | 391 | ```diff 392 | - import Highlight, { defaultProps } from "prism-react-renderer"; 393 | + import { Highlight } from "prism-react-renderer" 394 | 395 | const Content = ( 396 | - 397 | + 398 | ``` 399 | 400 | ### Change theme imports 401 | 402 | ```diff 403 | - const theme = require('prism-react-renderer/themes/github') 404 | + const theme = require('prism-react-renderer').themes.github 405 | ``` 406 | 407 | ### Check language support 408 | 409 | > By default prism-react-renderer only includes a base set of languages that Prism supports. Depending on your app's build system you may need to await the import or use require to ensure window.Prism exists before importing the custom languages. 410 | 411 | See: https://github.com/FormidableLabs/prism-react-renderer#custom-language-support 412 | 413 | Install prismjs (if not available yet): 414 | 415 | ``` 416 | # npm 417 | npm install --save prismjs 418 | # yarn 419 | yarn add prismjs 420 | # pnpm 421 | pnpm add prismjs 422 | ``` 423 | 424 | ### Add language component 425 | 426 | If the language is not already bundled in the above, you can add additional languages with the following code: 427 | 428 | ``` 429 | import { Highlight, Prism } from "prism-react-renderer"; 430 | 431 | (typeof global !== "undefined" ? global : window).Prism = Prism 432 | await import("prismjs/components/prism-applescript") 433 | /** or **/ 434 | require("prismjs/components/prism-applescript") 435 | ``` 436 | 437 | ## Development 438 | 439 | Local development setup can be run with the following commands running Node 18.x. This project uses corepack to specify its package manager version and you should have it enabled locally using `corepack enable`. 440 | 441 | ``` 442 | $ pnpm install 443 | $ pnpm build 444 | $ pnpm test 445 | ``` 446 | 447 | ### Local Demo 448 | 449 | To run the local demo, ensure you have first run `pnpm build`, then run `pnpm start:demo` and open the provided URL to the demo site in your terminal. 450 | 451 | ``` 452 | $ pnpm build 453 | $ pnpm start:demo 454 | ``` 455 | 456 | The workspace projects are linked, so changes can be hot reloaded during development by running multiple terminals 457 | 458 | ``` 459 | // terminal 1 460 | $ pnpm build:watch 461 | ``` 462 | 463 | ``` 464 | // terminal 2 465 | $ pnpm start:demo 466 | ``` 467 | 468 | ## LICENSE 469 | 470 | MIT 471 | 472 | ## Maintenance Status 473 | 474 | **Active:** Nearform is actively working on this project, and we expect to continue work for the foreseeable future. Bug reports, feature requests and pull requests are welcome. 475 | 476 | [maintenance-image]: https://img.shields.io/badge/maintenance-active-green.svg 477 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prism-react-renderer-monorepo", 3 | "sideEffects": false, 4 | "private": true, 5 | "repository": "https://github.com/FormidableLabs/prism-react-renderer", 6 | "scripts": { 7 | "lint": "eslint packages", 8 | "test": "pnpm run -r test", 9 | "typecheck": "pnpm run -r typecheck", 10 | "check:ci": "pnpm run typecheck && pnpm run lint && pnpm run test", 11 | "start:demo": "pnpm --filter demo dev", 12 | "build": "pnpm --sequential --filter generate-prism-languages --filter prism-react-renderer build", 13 | "build:languages": "pnpm --filter generate-prism-languages build", 14 | "build:watch": "pnpm --filter generate-prism-languages build && pnpm --filter prism-react-renderer build:watch", 15 | "changeset": "changeset", 16 | "version": "pnpm changeset version" 17 | }, 18 | "devDependencies": { 19 | "@babel/cli": "^7.10.4", 20 | "@babel/core": "^7.21.4", 21 | "@babel/plugin-proposal-object-rest-spread": "^7.10.4", 22 | "@babel/plugin-transform-object-assign": "^7.10.4", 23 | "@babel/plugin-transform-runtime": "^7.10.4", 24 | "@babel/preset-env": "^7.10.4", 25 | "@babel/preset-react": "^7.10.4", 26 | "@babel/preset-typescript": "^7.16.0", 27 | "@changesets/cli": "^2.26.0", 28 | "@svitejs/changesets-changelog-github-compact": "^1.1.0", 29 | "@testing-library/react": "^11.2.5", 30 | "@types/react": "^18.0.35", 31 | "@typescript-eslint/eslint-plugin": "^5.58.0", 32 | "@typescript-eslint/parser": "^5.58.0", 33 | "babel-jest": "^26.6.3", 34 | "babel-plugin-macros": "^3.0.1", 35 | "codegen.macro": "^4.1.0", 36 | "eslint": "^8.14.0", 37 | "eslint-config-prettier": "^8.5.0", 38 | "eslint-plugin-cypress": "^2.12.1", 39 | "eslint-plugin-prettier": "^4.2.1", 40 | "eslint-plugin-react": "^7.29.4", 41 | "eslint-plugin-react-hooks": "^4.5.0", 42 | "globby": "^11.0.2", 43 | "prettier": "^2.8.7", 44 | "prismjs": "1.29.0", 45 | "react": "^18.2.0", 46 | "react-dom": "^18.2.0", 47 | "typescript": "^5.0.4" 48 | }, 49 | "pnpm": { 50 | "patchedDependencies": { 51 | "prismjs@1.29.0": "patches/prismjs@1.29.0.patch" 52 | } 53 | }, 54 | "packageManager": "pnpm@8.2.0+sha512.7f6fda6e5e86c9cc4b815650b56036cc124a31772fd8bf3a1c6470278aa74b4da05732e0b457a00b6e6a58a16d52e9c263be06530c6ad80ef2180244c8eb8262" 55 | } 56 | -------------------------------------------------------------------------------- /packages/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Prism React Renderer Demo 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "clsx": "^1.2.1", 13 | "formidable-oss-badges": "^1.1.0", 14 | "prism-react-renderer": "*", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^18.15.11", 20 | "@types/prismjs": "^1.26.0", 21 | "@types/react": "^18.0.28", 22 | "@types/react-dom": "^18.0.11", 23 | "@vitejs/plugin-react": "^3.1.0", 24 | "typescript": "^4.9.3", 25 | "vite": "^4.2.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/demo/public/nearform-icon.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /packages/demo/public/nearform-logo-white.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /packages/demo/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Highlight, Prism, themes } from "prism-react-renderer" 2 | import styles from "./app.module.css" 3 | import clsx from "clsx" 4 | import { ProjectBadge } from "formidable-oss-badges" 5 | import { useState } from "react" 6 | import { sampleCode } from "./sample-code" 7 | 8 | // Example of importing a custom language directly from Prism 9 | ;(typeof global !== "undefined" ? global : window).Prism = Prism 10 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 11 | // @ts-ignore 12 | await import("prismjs/components/prism-applescript") 13 | 14 | type SampleCodeType = keyof typeof sampleCode 15 | type ThemeType = keyof typeof themes 16 | 17 | function App() { 18 | const [activeSampleCodeType, setActiveSampleCodeType] = 19 | useState("TypeScript with React") 20 | const [activeThemeName, setActiveThemeName] = useState ("oneDark") 21 | 22 | const activeSampleCode = sampleCode[activeSampleCodeType] 23 | const activeTheme = themes[activeThemeName] 24 | 25 | return ( 26 | 27 |84 | ) 85 | } 86 | 87 | export default App 88 | -------------------------------------------------------------------------------- /packages/demo/src/app.module.css: -------------------------------------------------------------------------------- 1 | .headerBar { 2 | display: flex; 3 | justify-content: center; 4 | margin: 2rem auto; 5 | } 6 | 7 | .brandLogo { 8 | width: 200px; 9 | margin-right: 1rem; 10 | } 11 | 12 | .ossBadge { 13 | width: 125px; 14 | } 15 | 16 | .languageSelect { 17 | font-size: 1rem; 18 | padding: 0.25rem; 19 | } 20 | 21 | .wrapper { 22 | text-align: center; 23 | width: 70vw; 24 | padding: 0 15vw; 25 | } 26 | 27 | .line { 28 | text-align: left; 29 | margin: 1em 0; 30 | padding: 0.5em; 31 | line-height: 1.3; 32 | font-size: 1.1rem; 33 | font-family: "JetBrains Mono", monospace; 34 | overflow-x: scroll; 35 | } 36 | 37 | .lineNumber { 38 | display: inline-block; 39 | width: 2em; 40 | user-select: none; 41 | opacity: 0.3; 42 | } 43 | -------------------------------------------------------------------------------- /packages/demo/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | body { 18 | margin: 0; 19 | display: flex; 20 | place-items: center; 21 | width: 100%; 22 | } 23 | 24 | @media (prefers-color-scheme: light) { 25 | :root { 26 | color: #213547; 27 | background-color: #ffffff; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/demo/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | import "./index.css" 5 | 6 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 7 |28 |40 | 53 | 54 | 65 |33 |
39 | 70 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 71 | 83 |72 | {tokens.map((line, i) => ( 73 |81 | )} 82 |74 | {i + 1} 75 | {line.map((token, key) => ( 76 | 77 | ))} 78 |79 | ))} 80 |8 | 10 | ) 11 | -------------------------------------------------------------------------------- /packages/demo/src/sample-code.ts: -------------------------------------------------------------------------------- 1 | export const sampleCode = { 2 | ["TypeScript with React"]: { 3 | language: "tsx", 4 | code: ` 5 | import React from 'react'; 6 | 7 | interface GroceryItemProps { 8 | item: { 9 | name: string; 10 | price: number; 11 | quantity: number; 12 | } 13 | } 14 | 15 | const GroceryItem: React.FC9 | = ({ item }) => { 16 | return ( 17 | 18 |22 | ); 23 | } 24 | 25 | export default GroceryItem; 26 | `, 27 | }, 28 | 29 | ["JavaScript"]: { 30 | language: "javascript", 31 | code: ` 32 | const GroceryItem = new Proxy({}, { 33 | set(target, prop, value) { 34 | if (prop === 'name' && typeof value !== 'string') { 35 | throw new TypeError('Name must be a string'); 36 | } 37 | if (prop === 'price' && typeof value !== 'number') { 38 | throw new TypeError('Price must be a number'); 39 | } 40 | if (prop === 'quantity' && typeof value !== 'number') { 41 | throw new TypeError('Quantity must be a number'); 42 | } 43 | target[prop] = value; 44 | return true; 45 | } 46 | }); 47 | `, 48 | }, 49 | 50 | ["Objective-C"]: { 51 | language: "objectivec", 52 | code: ` 53 | @interface GroceryItem : NSObject 54 | 55 | @property (nonatomic, strong) NSString *name; 56 | @property (nonatomic, assign) float price; 57 | @property (nonatomic, assign) NSInteger quantity; 58 | 59 | - (instancetype) initWithName: (NSString *)name 60 | price: (float)price 61 | quantity: (NSInteger)quantity; 62 | 63 | @end 64 | 65 | @implementation GroceryItem 66 | 67 | - (instancetype) initWithName: (NSString *)name 68 | price: (float)price 69 | quantity: (NSInteger)quantity { 70 | self = [super init]; 71 | if (self) { 72 | _name = name; 73 | _price = price; 74 | _quantity = quantity; 75 | } 76 | return self; 77 | } 78 | 79 | @end 80 | `, 81 | }, 82 | 83 | ["HTML"]: { 84 | language: "html", 85 | code: ` 86 | 87 | 88 | 89 | 90 |{item.name}
19 |Price: {item.price}
20 |Quantity: {item.quantity}
21 |Nearform 91 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | `, 102 | }, 103 | 104 | ["AppleScript"]: { 105 | language: "applescript", 106 | code: ` 107 | display alert "Do you wish to buy groceries?" buttons {"No", "Yes"} 108 | set theAnswer to button returned of the result 109 | if theAnswer is "Yes" then 110 | beep 5 111 | end if 112 | `, 113 | }, 114 | 115 | ["Python"]: { 116 | language: "python", 117 | code: ` 118 | from sklearn.datasets import load_iris 119 | from sklearn.model_selection import train_test_split 120 | from sklearn.tree import DecisionTreeClassifier 121 | from sklearn.metrics import accuracy_score, classification_report 122 | 123 | iris = load_iris() 124 | X = iris.data 125 | y = iris.target 126 | 127 | X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) 128 | 129 | clf = DecisionTreeClassifier(random_state=42) 130 | clf.fit(X_train, y_train) 131 | 132 | y_pred = clf.predict(X_test) 133 | 134 | accuracy = accuracy_score(y_test, y_pred) 135 | report = classification_report(y_test, y_pred) 136 | print(f"Accuracy: {accuracy}") 137 | print("Classification Report:\\n", report) 138 | print("Feature Importances:", clf.feature_importances_) 139 | `, 140 | }, 141 | 142 | ["Rust"]: { 143 | language: "rust", 144 | code: ` 145 | use serde::{Deserialize, Serialize}; 146 | use reqwest::Error as ReqwestError; 147 | 148 | #[derive(Debug, Serialize, Deserialize)] 149 | struct GroceryItem { 150 | name: String, 151 | price: f32, 152 | quantity: i32, 153 | } 154 | 155 | impl GroceryItem { 156 | async fn fetch_all() -> Result, ReqwestError> { 157 | let response = reqwest::get("https://example.com/grocery_items") 158 | .await? 159 | .json:: >() 160 | .await?; 161 | Ok(response) 162 | } 163 | 164 | async fn fetch_by_name(name: &str) -> Result { 165 | let response = reqwest::get(&format!("https://example.com/grocery_items/{}", name)) 166 | .await? 167 | .json:: () 168 | .await?; 169 | Ok(response) 170 | } 171 | 172 | async fn create(&self) -> Result<(), ReqwestError> { 173 | let client = reqwest::Client::new(); 174 | let response = client.post("https://example.com/grocery_items") 175 | .json(self) 176 | .send() 177 | .await?; 178 | if !response.status().is_success() { 179 | return Err(ReqwestError::from(response.status())); 180 | } 181 | Ok(()) 182 | } 183 | 184 | async fn update(&self, name: &str) -> Result<(), ReqwestError> { 185 | let client = reqwest::Client::new(); 186 | let response = client.put(&format!("https://example.com/grocery_items/{}", name)) 187 | .json(self) 188 | .send() 189 | .await?; 190 | if !response.status().is_success() { 191 | return Err(ReqwestError::from(response.status())); 192 | } 193 | Ok(()) 194 | } 195 | 196 | async fn delete(name: &str) -> Result<(), ReqwestError> { 197 | let client = reqwest::Client::new(); 198 | let response = client.delete(&format!("https://example.com/grocery_items/{}", name)) 199 | .send() 200 | .await?; 201 | if !response.status().is_success() { 202 | return Err(ReqwestError::from(response.status())); 203 | } 204 | Ok(()) 205 | } 206 | } 207 | `, 208 | }, 209 | ["JSON"]: { 210 | language: "json", 211 | code: ` 212 | { 213 | "id": 1, 214 | "name": "John Doe", 215 | "email": "johndoe@example.com", 216 | "address": { 217 | "street": "123 Main St", 218 | "city": "San Diego", 219 | "state": "CA", 220 | "zip": "12345" 221 | }, 222 | "phoneNumbers": [ 223 | { 224 | "type": "home", 225 | "number": "555-123-4567" 226 | }, 227 | { 228 | "type": "work", 229 | "number": "555-901-2345" 230 | } 231 | ], 232 | "interests": ["reading", "hiking", "coding"] 233 | } 234 | `, 235 | }, 236 | } 237 | -------------------------------------------------------------------------------- /packages/demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/demo/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /packages/generate-prism-languages/index.ts: -------------------------------------------------------------------------------- 1 | import flowRight from "lodash.flowright" 2 | import pc from "picocolors" 3 | import { readFile, writeFile, access } from "node:fs/promises" 4 | import { constants } from "node:fs" 5 | import { join, dirname } from "node:path" 6 | import { languages as prismLanguages } from "prismjs/components" 7 | import uglify from "uglify-js" 8 | 9 | export const languagesToBundle = [ 10 | "markup", 11 | "jsx", 12 | "tsx", 13 | "swift", 14 | "kotlin", 15 | "objectivec", 16 | "js-extras", 17 | "reason", 18 | "rust", 19 | "graphql", 20 | "yaml", 21 | "go", 22 | "cpp", 23 | "markdown", 24 | "python", 25 | "json", 26 | ] 27 | 28 | /** 29 | * We need to disable typechecking on this generated file as it's just concatenating JS code 30 | * that starts off assuming Prism lives in global scope. We also need to provide Prism as that 31 | * gets passed into an iffe preventing us from needing to use global scope. 32 | */ 33 | const header = `// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport * as Prism from "prismjs";\nexport { Prism };` 34 | const prismPath = dirname(require.resolve("prismjs")) 35 | 36 | const readLanguageFile = async (language: string): Promise => { 37 | const pathToLanguage = join(prismPath, `components/prism-${language}.js`) 38 | await access(pathToLanguage, constants.R_OK) 39 | const buffer = await readFile(pathToLanguage, { encoding: "utf-8" }) 40 | return buffer.toString() 41 | } 42 | 43 | const strArrayFromUnknown = (input: unknown) => (array: string[]) => { 44 | if (typeof input === "string") array.push(input) 45 | else if (Array.isArray(input)) array = array.concat(input) 46 | return array 47 | } 48 | 49 | const main = async () => { 50 | let output = "" 51 | const bundledLanguages = new Set () 52 | const orderBundled = new Set () 53 | const outputPath = join( 54 | __dirname, 55 | "../prism-react-renderer/src/prism-langs.ts" 56 | ) 57 | 58 | const addLanguageToOutput = async (language?: string) => { 59 | if (bundledLanguages.has(language)) { 60 | return 61 | } 62 | if (language == null || prismLanguages[language] == null) { 63 | return 64 | } 65 | bundledLanguages.add(language) 66 | 67 | /** 68 | * We need to ensure any language dependencies are bundled first 69 | */ 70 | const prismLang = prismLanguages[language] 71 | const deps = flowRight( 72 | strArrayFromUnknown(prismLang.require), 73 | strArrayFromUnknown(prismLang.optional) 74 | )([]) 75 | const peerDeps = strArrayFromUnknown(prismLang.peerDependencies)([]) 76 | 77 | for await (const language of deps) { 78 | await addLanguageToOutput(language) 79 | } 80 | 81 | output += await readLanguageFile(language) 82 | orderBundled.add(language) 83 | 84 | for await (const language of peerDeps) { 85 | await addLanguageToOutput(language) 86 | } 87 | } 88 | 89 | for await (const language of languagesToBundle) { 90 | await addLanguageToOutput(language) 91 | } 92 | 93 | console.info(pc.bold(pc.bgYellow(pc.black("Prism React Renderer"))), "\n") 94 | console.info( 95 | pc.bgBlue(`Generated TypeScript output at:`), 96 | pc.cyan(outputPath) 97 | ) 98 | console.info( 99 | pc.bgGreen(`Included language definitions in the following order:`), 100 | Array.from(orderBundled.values()).join(", ") 101 | ) 102 | 103 | await writeFile(outputPath, header + uglify.minify(output).code) 104 | } 105 | 106 | main() 107 | -------------------------------------------------------------------------------- /packages/generate-prism-languages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generate-prism-languages", 3 | "private": true, 4 | "scripts": { 5 | "build": "ts-node ./index.ts", 6 | "typecheck": "tsc --noEmit" 7 | }, 8 | "peerDependencies": { 9 | "react": ">=16.0.0" 10 | }, 11 | "devDependencies": { 12 | "@types/lodash.flowright": "^3.5.7", 13 | "@types/node": "^18.15.11", 14 | "@types/prismjs": "^1.26.0", 15 | "@types/uglify-js": "^3.17.1", 16 | "picocolors": "^1.0.0", 17 | "prismjs": "*", 18 | "ts-node": "^10.9.1", 19 | "tslib": "^2.5.0", 20 | "typescript": "*", 21 | "uglify-js": "^3.17.4", 22 | "lodash.flowright": "^3.5.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/generate-prism-languages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | } 5 | } -------------------------------------------------------------------------------- /packages/prism-react-renderer/.gitignore: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # prism-react-renderer 2 | 3 | ## 2.4.1 4 | 5 | ### Patch Changes 6 | 7 | - Remove client side hooks 8 | ([#252](https://github.com/FormidableLabs/prism-react-renderer/pull/252)) 9 | 10 | ## 2.4.0 11 | 12 | ### Minor Changes 13 | 14 | - 'Add JSON as default supported language' 15 | ([#247](https://github.com/FormidableLabs/prism-react-renderer/pull/247)) 16 | 17 | ## 2.3.1 18 | 19 | ### Patch Changes 20 | 21 | - Export `normalizeTokens` and `useTokenize` utility functions. 22 | ([#237](https://github.com/FormidableLabs/prism-react-renderer/pull/237)) 23 | 24 | ## 2.3.0 25 | 26 | ### Minor Changes 27 | 28 | - Upgrade clsx to v2.0.0. 29 | ([#232](https://github.com/FormidableLabs/prism-react-renderer/pull/232)) 30 | 31 | ## 2.2.0 32 | 33 | ### Minor Changes 34 | 35 | - Add Python support. 36 | ([#226](https://github.com/FormidableLabs/prism-react-renderer/pull/226)) 37 | 38 | ### Patch Changes 39 | 40 | - Fix html language preset by using markup instead. 41 | ([#230](https://github.com/FormidableLabs/prism-react-renderer/pull/230)) 42 | 43 | ## 2.1.0 44 | 45 | ### Minor Changes 46 | 47 | - Added oneDark and oneLight themes 48 | ([#224](https://github.com/FormidableLabs/prism-react-renderer/pull/224)) 49 | 50 | ## 2.0.6 51 | 52 | ### Patch Changes 53 | 54 | - Export all types from package 55 | ([#216](https://github.com/FormidableLabs/prism-react-renderer/pull/216)) 56 | 57 | ## 2.0.5 58 | 59 | ### Patch Changes 60 | 61 | - Fixed bug where an undefined theme would cause a runtime error. 62 | ([#213](https://github.com/FormidableLabs/prism-react-renderer/pull/213)) 63 | 64 | ## 2.0.4 65 | 66 | ### Patch Changes 67 | 68 | - Fix types for Prism library. 69 | ([#204](https://github.com/FormidableLabs/prism-react-renderer/pull/204)) 70 | 71 | ## 2.0.3 72 | 73 | ### Patch Changes 74 | 75 | - Add package README 76 | ([#200](https://github.com/FormidableLabs/prism-react-renderer/pull/200)) 77 | 78 | ## 2.0.2 79 | 80 | ### Patch Changes 81 | 82 | - Add publish provenance 83 | ([#198](https://github.com/FormidableLabs/prism-react-renderer/pull/198)) 84 | 85 | ## 2.0.1 86 | 87 | ### Patch Changes 88 | 89 | - Fix inclusion of @types dependency for prismjs 90 | ([#196](https://github.com/FormidableLabs/prism-react-renderer/pull/196)) 91 | 92 | ## 2.0.0 93 | 94 | ### Major Changes 95 | 96 | - v2 release with updated API 97 | ([#191](https://github.com/FormidableLabs/prism-react-renderer/pull/191)) 98 | 99 | ### Minor Changes 100 | 101 | - Added 2 new styles for the code viewer. light & dark mode themes using only 102 | ([#192](https://github.com/FormidableLabs/prism-react-renderer/pull/192)) 103 | colors found on tailwindCSS. 104 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prism-react-renderer", 3 | "version": "2.4.1", 4 | "description": "Renders highlighted Prism output using React", 5 | "sideEffects": true, 6 | "main": "dist/index.js", 7 | "module": "dist/index.mjs", 8 | "types": "dist/index.d.ts", 9 | "license": "MIT", 10 | "repository": "https://github.com/FormidableLabs/prism-react-renderer", 11 | "files": [ 12 | "index.d.ts", 13 | "dist" 14 | ], 15 | "scripts": { 16 | "build": "tsup", 17 | "build:watch": "tsup --watch", 18 | "test": "vitest", 19 | "typecheck": "tsc --noEmit", 20 | "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", 21 | "format": "prettier --write 'src/**/*.js'", 22 | "prepublishOnly": "cp ../../README.md ./README.md" 23 | }, 24 | "peerDependencies": { 25 | "react": ">=16.0.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "*", 29 | "@rollup/plugin-babel": "^6.0.3", 30 | "@rollup/plugin-buble": "^0.21.3", 31 | "@rollup/plugin-commonjs": "^19.0.2", 32 | "@rollup/plugin-json": "^6.0.0", 33 | "@rollup/plugin-node-resolve": "^11.2.0", 34 | "@rollup/plugin-typescript": "^11.1.0", 35 | "@testing-library/react": "^14.0.0", 36 | "@types/jest": "^29.5.0", 37 | "@types/node": "^18.15.11", 38 | "@vitejs/plugin-react": "^3.1.0", 39 | "babel-plugin-codegen": "^4.1.5", 40 | "happy-dom": "^9.7.1", 41 | "prismjs": "*", 42 | "react": "*", 43 | "react-dom": "*", 44 | "rollup": "^2.39.0", 45 | "ts-node": "^10.9.1", 46 | "tslib": "^2.5.0", 47 | "tsup": "^6.7.0", 48 | "typescript": "*", 49 | "vite": "^4.2.0", 50 | "vitest": "^0.30.1" 51 | }, 52 | "dependencies": { 53 | "@types/prismjs": "^1.26.0", 54 | "clsx": "^2.0.0" 55 | }, 56 | "publishConfig": { 57 | "provenance": true 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/__tests__/Highlight.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, cleanup } from "@testing-library/react" 2 | import { Highlight } from "../../index" 3 | import { expect } from "vitest" 4 | 5 | const exampleCode = ` 6 | (function someDemo() { 7 | var test = "Hello World!"; 8 | console.log(test); 9 | })(); 10 | 11 | return () => ; 12 | `.trim() 13 | describe(" ", () => { 14 | afterEach(cleanup) 15 | describe("snapshots", () => { 16 | it("renders correctly", () => { 17 | const { container } = render( 18 | 19 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 20 | 41 | ) 42 | expect(container).toMatchSnapshot() 43 | }) 44 | it("renders unsupported languages correctly", () => { 45 | const { container } = render( 46 |21 | {tokens.map((line, i) => ( 22 |39 | )} 40 |28 | {line.map((token, key) => ( 29 | 35 | ))} 36 |37 | ))} 38 |53 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 54 | 75 | ) 76 | expect(container).toMatchSnapshot() 77 | }) 78 | it("renders a default theme when no theme is passed", () => { 79 | const { container } = render( 80 |55 | {tokens.map((line, i) => ( 56 |73 | )} 74 |62 | {line.map((token, key) => ( 63 | 69 | ))} 70 |71 | ))} 72 |81 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 82 | 103 | ) 104 | expect(container.innerHTML.includes("style")).toBeTruthy() 105 | }) 106 | }) 107 | describe("getLineProps", () => { 108 | it("transforms lineProps inputs correctly", () => { 109 | const input = { 110 | style: { 111 | cursor: "pointer", 112 | }, 113 | className: "line-class", 114 | line: [ 115 | { 116 | types: ["punctuation"], 117 | content: "!", 118 | }, 119 | ], 120 | restPropsTest: true, 121 | } 122 | render( 123 |83 | {tokens.map((line, i) => ( 84 |101 | )} 102 |90 | {line.map((token, key) => ( 91 | 97 | ))} 98 |99 | ))} 100 |124 | {({ getLineProps }) => { 125 | const output = getLineProps(input) 126 | expect(output).toEqual({ 127 | style: { 128 | cursor: "pointer", 129 | color: expect.any(String), 130 | }, 131 | className: "token-line line-class", 132 | restPropsTest: true, 133 | }) 134 | return 135 | }} 136 | 137 | ) 138 | }) 139 | }) 140 | describe("getTokenProps", () => { 141 | it("transforms tokenProps inputs correctly", () => { 142 | const input = { 143 | style: { 144 | cursor: "pointer", 145 | }, 146 | className: "token-class", 147 | token: { 148 | types: ["keyword"], 149 | content: "function", 150 | }, 151 | restPropsTest: true, 152 | } 153 | render( 154 |155 | {({ getTokenProps }) => { 156 | const output = getTokenProps(input) 157 | expect(output).toEqual({ 158 | style: { 159 | cursor: "pointer", 160 | color: expect.any(String), 161 | }, 162 | className: "token keyword token-class", 163 | restPropsTest: true, 164 | children: "function", 165 | }) 166 | return 167 | }} 168 | 169 | ) 170 | }) 171 | it("transforms constructor token style correctly", () => { 172 | // From https://github.com/FormidableLabs/prism-react-renderer/issues/11 173 | render( 174 |175 | {({ tokens, getTokenProps }) => { 176 | const line = tokens[0] 177 | const token = line[2] 178 | const output = getTokenProps({ 179 | token, 180 | }) 181 | expect(typeof output.style).not.toBe("function") 182 | return 183 | }} 184 | 185 | ) 186 | }) 187 | }) 188 | }) 189 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/__tests__/__snapshots__/Highlight.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`> snapshots > renders correctly 1`] = ` 4 | 5 |296 | `; 297 | 298 | exports[`9 |295 |13 | 17 | ( 18 | 19 | 23 | function 24 | 25 | 28 | 29 | 30 | 34 | someDemo 35 | 36 | 40 | ( 41 | 42 | 46 | ) 47 | 48 | 51 | 52 | 53 | 57 | { 58 | 59 | 62 |63 |67 | 70 | 71 | 72 | 76 | var 77 | 78 | 81 | test 82 | 83 | 87 | = 88 | 89 | 92 | 93 | 94 | 98 | "Hello World!" 99 | 100 | 104 | ; 105 | 106 | 109 |110 |114 | 117 | 118 | 119 | 123 | console 124 | 125 | 129 | . 130 | 131 | 135 | log 136 | 137 | 141 | ( 142 | 143 | 146 | test 147 | 148 | 152 | ) 153 | 154 | 158 | ; 159 | 160 | 163 |164 |168 | 171 | 175 | } 176 | 177 | 181 | ) 182 | 183 | 187 | ( 188 | 189 | 193 | ) 194 | 195 | 199 | ; 200 | 201 | 204 |205 |209 | 213 | 214 | 215 | 216 |217 |221 | 224 | 228 | return 229 | 230 | 233 | 234 | 235 | 239 | ( 240 | 241 | 245 | ) 246 | 247 | 250 | 251 | 252 | 256 | => 257 | 258 | 261 | 262 | 263 | 267 | < 268 | 269 | 273 | App 274 | 275 | 279 | 280 | 281 | 285 | /> 286 | 287 | 291 | ; 292 | 293 |294 |> snapshots > renders unsupported languages correctly 1`] = ` 299 | 300 |368 | `; 369 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/highlight.ts: -------------------------------------------------------------------------------- 1 | import { InternalHighlightProps } from "../types" 2 | import { useGetLineProps } from "./useGetLineProps" 3 | import { useGetTokenProps } from "./useGetTokenProps" 4 | import { useTokenize } from "./useTokenize" 5 | import themeToDict from "../utils/themeToDict" 6 | 7 | export const Highlight = ({ 8 | children, 9 | language: _language, 10 | code, 11 | theme, 12 | prism, 13 | }: InternalHighlightProps) => { 14 | const language = _language.toLowerCase() 15 | const themeDictionary = themeToDict(theme, language) 16 | const getLineProps = useGetLineProps(themeDictionary) 17 | const getTokenProps = useGetTokenProps(themeDictionary) 18 | const grammar = prism.languages[language] 19 | const tokens = useTokenize({ prism, language, code, grammar }) 20 | 21 | return children({ 22 | tokens, 23 | className: `prism-code language-${language}`, 24 | style: themeDictionary != null ? themeDictionary.root : {}, 25 | getLineProps, 26 | getTokenProps, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/useGetLineProps.ts: -------------------------------------------------------------------------------- 1 | import { ThemeDict } from "../utils/themeToDict" 2 | import { useCallback } from "react" 3 | import { LineInputProps, LineOutputProps } from "../types" 4 | import clsx from "clsx" 5 | 6 | export const useGetLineProps = (themeDictionary?: ThemeDict) => 7 | useCallback( 8 | ({ className, style, line, ...rest }: LineInputProps) => { 9 | const output: LineOutputProps = { 10 | ...rest, 11 | className: clsx("token-line", className), 12 | } 13 | 14 | if (typeof themeDictionary === "object" && "plain" in themeDictionary) 15 | output.style = themeDictionary.plain 16 | 17 | if (typeof style === "object") 18 | output.style = { ...(output.style || {}), ...style } 19 | 20 | return output 21 | }, 22 | [themeDictionary] 23 | ) 24 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/useGetTokenProps.ts: -------------------------------------------------------------------------------- 1 | import { ThemeDict } from "../utils/themeToDict" 2 | import { CSSProperties, useCallback } from "react" 3 | import { Token, TokenInputProps, TokenOutputProps } from "../types" 4 | import clsx from "clsx" 5 | 6 | export const useGetTokenProps = (themeDictionary?: ThemeDict) => { 7 | const styleForToken = useCallback( 8 | ({ types, empty }: Token) => { 9 | if (themeDictionary == null) return undefined 10 | else if (types.length === 1 && types[0] === "plain") { 11 | return empty != null ? { display: "inline-block" } : undefined 12 | } else if (types.length === 1 && empty != null) { 13 | return themeDictionary[types[0]] 14 | } 15 | 16 | return Object.assign( 17 | empty != null ? { display: "inline-block" } : {}, 18 | ...types.map(type => themeDictionary[type]) 19 | ) satisfies CSSProperties 20 | }, 21 | [themeDictionary] 22 | ) 23 | 24 | return useCallback( 25 | ({ token, className, style, ...rest }: TokenInputProps) => { 26 | const output: TokenOutputProps = { 27 | ...rest, 28 | className: clsx("token", ...token.types, className), 29 | children: token.content, 30 | style: styleForToken(token), 31 | } 32 | 33 | if (style != null) { 34 | output.style = { 35 | ...(output.style || {}), 36 | ...style, 37 | } 38 | } 39 | 40 | return output 41 | }, 42 | [styleForToken] 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/components/useTokenize.ts: -------------------------------------------------------------------------------- 1 | import { EnvConfig, Language, PrismGrammar, PrismLib } from "../types" 2 | import normalizeTokens from "../utils/normalizeTokens" 3 | import { useMemo } from "react" 4 | 5 | type Options = { 6 | prism: PrismLib 7 | code: string 8 | grammar?: PrismGrammar 9 | language: Language 10 | } 11 | 12 | export const useTokenize = ({ prism, code, grammar, language }: Options) => { 13 | return useMemo(() => { 14 | if (grammar == null) return normalizeTokens([code]) 15 | 16 | const prismConfig: EnvConfig = { 17 | code, 18 | grammar, 19 | language, 20 | tokens: [], 21 | } 22 | 23 | prism.hooks.run("before-tokenize", prismConfig) 24 | prismConfig.tokens = prism.tokenize(code, grammar) 25 | prism.hooks.run("after-tokenize", prismConfig) 26 | return normalizeTokens(prismConfig.tokens) 27 | }, [ 28 | code, 29 | grammar, 30 | language, 31 | // prism is a stable import 32 | prism, 33 | ]) 34 | } 35 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Prism } from "./prism-langs" 2 | import * as themes from "./themes" 3 | import { createElement } from "react" 4 | import { Highlight as InternalHighlight } from "./components/highlight" 5 | import { HighlightProps, PrismLib } from "./types" 6 | import normalizeTokens from "./utils/normalizeTokens" 7 | import { useTokenize } from "./components/useTokenize" 8 | export * from "./types" 9 | 10 | /** 11 | * Prism React Renderer requires this specific instance 12 | * of Prism provided to ensure the languages are correctly loaded 13 | */ 14 | const Highlight = (props: HighlightProps) => 15 | createElement(InternalHighlight, { 16 | ...props, 17 | prism: props.prism || (Prism as PrismLib), 18 | theme: props.theme || themes.vsDark, 19 | code: props.code, 20 | language: props.language, 21 | }) 22 | 23 | export { Highlight, Prism, themes, normalizeTokens, useTokenize } 24 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/dracula.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/dracula/visual-studio-code 2 | // Converted automatically using ./tools/themeFromVsCode 3 | import type { PrismTheme } from "../types" 4 | const theme: PrismTheme = { 5 | plain: { 6 | color: "#F8F8F2", 7 | backgroundColor: "#282A36", 8 | }, 9 | styles: [ 10 | { 11 | types: ["prolog", "constant", "builtin"], 12 | style: { 13 | color: "rgb(189, 147, 249)", 14 | }, 15 | }, 16 | { 17 | types: ["inserted", "function"], 18 | style: { 19 | color: "rgb(80, 250, 123)", 20 | }, 21 | }, 22 | { 23 | types: ["deleted"], 24 | style: { 25 | color: "rgb(255, 85, 85)", 26 | }, 27 | }, 28 | { 29 | types: ["changed"], 30 | style: { 31 | color: "rgb(255, 184, 108)", 32 | }, 33 | }, 34 | { 35 | types: ["punctuation", "symbol"], 36 | style: { 37 | color: "rgb(248, 248, 242)", 38 | }, 39 | }, 40 | { 41 | types: ["string", "char", "tag", "selector"], 42 | style: { 43 | color: "rgb(255, 121, 198)", 44 | }, 45 | }, 46 | { 47 | types: ["keyword", "variable"], 48 | style: { 49 | color: "rgb(189, 147, 249)", 50 | fontStyle: "italic", 51 | }, 52 | }, 53 | { 54 | types: ["comment"], 55 | style: { 56 | color: "rgb(98, 114, 164)", 57 | }, 58 | }, 59 | { 60 | types: ["attr-name"], 61 | style: { 62 | color: "rgb(241, 250, 140)", 63 | }, 64 | }, 65 | ], 66 | } 67 | export default theme 68 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/duotoneDark.ts: -------------------------------------------------------------------------------- 1 | // Duotone Dark 2 | // Author: Simurai, adapted from DuoTone themes for Atom (http://simurai.com/projects/2016/01/01/duotone-themes) 3 | // Conversion: Bram de Haan (http://atelierbram.github.io/Base2Tone-prism/output/prism/prism-base2tone-evening-dark.css) 4 | // Generated with Base16 Builder (https://github.com/base16-builder/base16-builder) 5 | import type { PrismTheme } from "../types" 6 | const theme: PrismTheme = { 7 | plain: { 8 | backgroundColor: "#2a2734", 9 | color: "#9a86fd", 10 | }, 11 | styles: [ 12 | { 13 | types: ["comment", "prolog", "doctype", "cdata", "punctuation"], 14 | style: { 15 | color: "#6c6783", 16 | }, 17 | }, 18 | { 19 | types: ["namespace"], 20 | style: { 21 | opacity: 0.7, 22 | }, 23 | }, 24 | { 25 | types: ["tag", "operator", "number"], 26 | style: { 27 | color: "#e09142", 28 | }, 29 | }, 30 | { 31 | types: ["property", "function"], 32 | style: { 33 | color: "#9a86fd", 34 | }, 35 | }, 36 | { 37 | types: ["tag-id", "selector", "atrule-id"], 38 | style: { 39 | color: "#eeebff", 40 | }, 41 | }, 42 | { 43 | types: ["attr-name"], 44 | style: { 45 | color: "#c4b9fe", 46 | }, 47 | }, 48 | { 49 | types: [ 50 | "boolean", 51 | "string", 52 | "entity", 53 | "url", 54 | "attr-value", 55 | "keyword", 56 | "control", 57 | "directive", 58 | "unit", 59 | "statement", 60 | "regex", 61 | "atrule", 62 | "placeholder", 63 | "variable", 64 | ], 65 | style: { 66 | color: "#ffcc99", 67 | }, 68 | }, 69 | { 70 | types: ["deleted"], 71 | style: { 72 | textDecorationLine: "line-through", 73 | }, 74 | }, 75 | { 76 | types: ["inserted"], 77 | style: { 78 | textDecorationLine: "underline", 79 | }, 80 | }, 81 | { 82 | types: ["italic"], 83 | style: { 84 | fontStyle: "italic", 85 | }, 86 | }, 87 | { 88 | types: ["important", "bold"], 89 | style: { 90 | fontWeight: "bold", 91 | }, 92 | }, 93 | { 94 | types: ["important"], 95 | style: { 96 | color: "#c4b9fe", 97 | }, 98 | }, 99 | ], 100 | } 101 | export default theme 102 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/duotoneLight.ts: -------------------------------------------------------------------------------- 1 | // Duotone Light 2 | // Author: Simurai, adapted from DuoTone themes for Atom (http://simurai.com/projects/2016/01/01/duotone-themes) 3 | // Conversion: Bram de Haan (http://atelierbram.github.io/Base2Tone-prism/output/prism/prism-base2tone-evening-dark.css) 4 | // Generated with Base16 Builder (https://github.com/base16-builder/base16-builder) 5 | import type { PrismTheme } from "../types" 6 | const theme: PrismTheme = { 7 | plain: { 8 | backgroundColor: "#faf8f5", 9 | color: "#728fcb", 10 | }, 11 | styles: [ 12 | { 13 | types: ["comment", "prolog", "doctype", "cdata", "punctuation"], 14 | style: { 15 | color: "#b6ad9a", 16 | }, 17 | }, 18 | { 19 | types: ["namespace"], 20 | style: { 21 | opacity: 0.7, 22 | }, 23 | }, 24 | { 25 | types: ["tag", "operator", "number"], 26 | style: { 27 | color: "#063289", 28 | }, 29 | }, 30 | { 31 | types: ["property", "function"], 32 | style: { 33 | color: "#b29762", 34 | }, 35 | }, 36 | { 37 | types: ["tag-id", "selector", "atrule-id"], 38 | style: { 39 | color: "#2d2006", 40 | }, 41 | }, 42 | { 43 | types: ["attr-name"], 44 | style: { 45 | color: "#896724", 46 | }, 47 | }, 48 | { 49 | types: [ 50 | "boolean", 51 | "string", 52 | "entity", 53 | "url", 54 | "attr-value", 55 | "keyword", 56 | "control", 57 | "directive", 58 | "unit", 59 | "statement", 60 | "regex", 61 | "atrule", 62 | ], 63 | style: { 64 | color: "#728fcb", 65 | }, 66 | }, 67 | { 68 | types: ["placeholder", "variable"], 69 | style: { 70 | color: "#93abdc", 71 | }, 72 | }, 73 | { 74 | types: ["deleted"], 75 | style: { 76 | textDecorationLine: "line-through", 77 | }, 78 | }, 79 | { 80 | types: ["inserted"], 81 | style: { 82 | textDecorationLine: "underline", 83 | }, 84 | }, 85 | { 86 | types: ["italic"], 87 | style: { 88 | fontStyle: "italic", 89 | }, 90 | }, 91 | { 92 | types: ["important", "bold"], 93 | style: { 94 | fontWeight: "bold", 95 | }, 96 | }, 97 | { 98 | types: ["important"], 99 | style: { 100 | color: "#896724", 101 | }, 102 | }, 103 | ], 104 | } 105 | export default theme 106 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/github.ts: -------------------------------------------------------------------------------- 1 | // Original: https://raw.githubusercontent.com/PrismJS/prism-themes/master/themes/prism-ghcolors.css 2 | import type { PrismTheme } from "../types" 3 | const theme: PrismTheme = { 4 | plain: { 5 | color: "#393A34", 6 | backgroundColor: "#f6f8fa", 7 | }, 8 | styles: [ 9 | { 10 | types: ["comment", "prolog", "doctype", "cdata"], 11 | style: { 12 | color: "#999988", 13 | fontStyle: "italic", 14 | }, 15 | }, 16 | { 17 | types: ["namespace"], 18 | style: { 19 | opacity: 0.7, 20 | }, 21 | }, 22 | { 23 | types: ["string", "attr-value"], 24 | style: { 25 | color: "#e3116c", 26 | }, 27 | }, 28 | { 29 | types: ["punctuation", "operator"], 30 | style: { 31 | color: "#393A34", 32 | }, 33 | }, 34 | { 35 | types: [ 36 | "entity", 37 | "url", 38 | "symbol", 39 | "number", 40 | "boolean", 41 | "variable", 42 | "constant", 43 | "property", 44 | "regex", 45 | "inserted", 46 | ], 47 | style: { 48 | color: "#36acaa", 49 | }, 50 | }, 51 | { 52 | types: ["atrule", "keyword", "attr-name", "selector"], 53 | style: { 54 | color: "#00a4db", 55 | }, 56 | }, 57 | { 58 | types: ["function", "deleted", "tag"], 59 | style: { 60 | color: "#d73a49", 61 | }, 62 | }, 63 | { 64 | types: ["function-variable"], 65 | style: { 66 | color: "#6f42c1", 67 | }, 68 | }, 69 | { 70 | types: ["tag", "selector", "keyword"], 71 | style: { 72 | color: "#00009f", 73 | }, 74 | }, 75 | ], 76 | } 77 | export default theme 78 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/gruvboxMaterialDark.ts: -------------------------------------------------------------------------------- 1 | // Gruvbox Material (dark) 2 | // Author: Sainnhe Park (https://github.com/sainnhe) 3 | // https://github.com/sainnhe/gruvbox-material 4 | import type { PrismTheme } from "../types" 5 | const theme: PrismTheme = { 6 | plain: { 7 | color: "#ebdbb2", 8 | backgroundColor: "#292828", 9 | }, 10 | styles: [ 11 | { 12 | types: [ 13 | "imports", 14 | "class-name", 15 | "maybe-class-name", 16 | "constant", 17 | "doctype", 18 | "builtin", 19 | "function", 20 | ], 21 | style: { 22 | color: "#d8a657", 23 | }, 24 | }, 25 | { 26 | types: ["property-access"], 27 | style: { 28 | color: "#7daea3", 29 | }, 30 | }, 31 | { 32 | types: ["tag"], 33 | style: { 34 | color: "#e78a4e", 35 | }, 36 | }, 37 | { 38 | types: ["attr-name", "char", "url", "regex"], 39 | style: { 40 | color: "#a9b665", 41 | }, 42 | }, 43 | { 44 | types: ["attr-value", "string"], 45 | style: { 46 | color: "#89b482", 47 | }, 48 | }, 49 | { 50 | types: ["comment", "prolog", "cdata", "operator", "inserted"], 51 | style: { 52 | color: "#a89984", 53 | }, 54 | }, 55 | { 56 | types: [ 57 | "delimiter", 58 | "boolean", 59 | "keyword", 60 | "selector", 61 | "important", 62 | "atrule", 63 | "property", 64 | "variable", 65 | "deleted", 66 | ], 67 | style: { 68 | color: "#ea6962", 69 | }, 70 | }, 71 | { 72 | types: ["entity", "number", "symbol"], 73 | style: { 74 | color: "#d3869b", 75 | }, 76 | }, 77 | ], 78 | } 79 | export default theme 80 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/gruvboxMaterialLight.ts: -------------------------------------------------------------------------------- 1 | // Gruvbox Material (light) 2 | // Author: Sainnhe Park (https://github.com/sainnhe) 3 | // https://github.com/sainnhe/gruvbox-material 4 | import type { PrismTheme } from "../types" 5 | const theme: PrismTheme = { 6 | plain: { 7 | color: "#654735", 8 | backgroundColor: "#f9f5d7", 9 | }, 10 | styles: [ 11 | { 12 | types: [ 13 | "delimiter", 14 | "boolean", 15 | "keyword", 16 | "selector", 17 | "important", 18 | "atrule", 19 | "property", 20 | "variable", 21 | "deleted", 22 | ], 23 | style: { 24 | color: "#af2528", 25 | }, 26 | }, 27 | { 28 | types: [ 29 | "imports", 30 | "class-name", 31 | "maybe-class-name", 32 | "constant", 33 | "doctype", 34 | "builtin", 35 | ], 36 | style: { 37 | color: "#b4730e", 38 | }, 39 | }, 40 | { 41 | types: ["string", "attr-value"], 42 | style: { 43 | color: "#477a5b", 44 | }, 45 | }, 46 | { 47 | types: ["property-access"], 48 | style: { 49 | color: "#266b79", 50 | }, 51 | }, 52 | { 53 | types: ["function", "attr-name", "char", "url"], 54 | style: { 55 | color: "#72761e", 56 | }, 57 | }, 58 | { 59 | types: ["tag"], 60 | style: { 61 | color: "#b94c07", 62 | }, 63 | }, 64 | { 65 | types: ["comment", "prolog", "cdata", "operator", "inserted"], 66 | style: { 67 | color: "#a89984", 68 | }, 69 | }, 70 | { 71 | types: ["entity", "number", "symbol"], 72 | style: { 73 | color: "#924f79", 74 | }, 75 | }, 76 | ], 77 | } 78 | export default theme 79 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/index.ts: -------------------------------------------------------------------------------- 1 | export { default as dracula } from "./dracula" 2 | export { default as duotoneDark } from "./duotoneDark" 3 | export { default as duotoneLight } from "./duotoneLight" 4 | export { default as github } from "./github" 5 | export { default as nightOwl } from "./nightOwl" 6 | export { default as nightOwlLight } from "./nightOwlLight" 7 | export { default as oceanicNext } from "./oceanicNext" 8 | export { default as okaidia } from "./okaidia" 9 | export { default as palenight } from "./palenight" 10 | export { default as shadesOfPurple } from "./shadesOfPurple" 11 | export { default as synthwave84 } from "./synthwave84" 12 | export { default as ultramin } from "./ultramin" 13 | export { default as vsDark } from "./vsDark" 14 | export { default as vsLight } from "./vsLight" 15 | export { default as jettwaveDark } from "./jettwaveDark" 16 | export { default as jettwaveLight } from "./jettwaveLight" 17 | export { default as oneDark } from "./oneDark" 18 | export { default as oneLight } from "./oneLight" 19 | export { default as gruvboxMaterialDark } from "./gruvboxMaterialDark" 20 | export { default as gruvboxMaterialLight } from "./gruvboxMaterialLight" 21 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/jettwaveDark.ts: -------------------------------------------------------------------------------- 1 | // 2 | // dark version of code viewer styles built for https://jettwave.com 3 | // only uses colors found in default tailwindCSS => https://tailwindcss.com/docs/customizing-colors 4 | // designed by: https://github.com/ryanmogk 5 | // 6 | import type { PrismTheme } from "../types" 7 | const theme: PrismTheme = { 8 | plain: { 9 | color: "#f8fafc", 10 | backgroundColor: "#011627", 11 | }, 12 | styles: [ 13 | { 14 | types: ["prolog"], 15 | style: { 16 | color: "#000080", 17 | }, 18 | }, 19 | { 20 | types: ["comment"], 21 | style: { 22 | color: "#6A9955", 23 | }, 24 | }, 25 | { 26 | types: ["builtin", "changed", "keyword", "interpolation-punctuation"], 27 | style: { 28 | color: "#569CD6", 29 | }, 30 | }, 31 | { 32 | types: ["number", "inserted"], 33 | style: { 34 | color: "#B5CEA8", 35 | }, 36 | }, 37 | { 38 | types: ["constant"], 39 | style: { 40 | color: "#f8fafc", 41 | }, 42 | }, 43 | { 44 | types: ["attr-name", "variable"], 45 | style: { 46 | color: "#9CDCFE", 47 | }, 48 | }, 49 | { 50 | types: ["deleted", "string", "attr-value", "template-punctuation"], 51 | style: { 52 | color: "#cbd5e1", 53 | }, 54 | }, 55 | { 56 | types: ["selector"], 57 | style: { 58 | color: "#D7BA7D", 59 | }, 60 | }, 61 | { 62 | types: ["tag"], 63 | style: { 64 | color: "#0ea5e9", 65 | }, 66 | }, 67 | { 68 | types: ["tag"], 69 | languages: ["markup"], 70 | style: { 71 | color: "#0ea5e9", 72 | }, 73 | }, 74 | { 75 | types: ["punctuation", "operator"], 76 | style: { 77 | color: "#D4D4D4", 78 | }, 79 | }, 80 | { 81 | types: ["punctuation"], 82 | languages: ["markup"], 83 | style: { 84 | color: "#808080", 85 | }, 86 | }, 87 | { 88 | types: ["function"], 89 | style: { 90 | color: "#7dd3fc", 91 | }, 92 | }, 93 | { 94 | types: ["class-name"], 95 | style: { 96 | color: "#0ea5e9", 97 | }, 98 | }, 99 | { 100 | types: ["char"], 101 | style: { 102 | color: "#D16969", 103 | }, 104 | }, 105 | ], 106 | } 107 | export default theme 108 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/jettwaveLight.ts: -------------------------------------------------------------------------------- 1 | // light version of code viewer styles built for https://jettwave.com 2 | // only uses colors found in default tailwindCSS => https://tailwindcss.com/docs/customizing-colors 3 | // designed by: https://github.com/ryanmogk 4 | import type { PrismTheme } from "../types" 5 | const theme: PrismTheme = { 6 | plain: { 7 | color: "#0f172a", 8 | backgroundColor: "#f1f5f9", 9 | }, 10 | styles: [ 11 | { 12 | types: ["prolog"], 13 | style: { 14 | color: "#000080", 15 | }, 16 | }, 17 | { 18 | types: ["comment"], 19 | style: { 20 | color: "#6A9955", 21 | }, 22 | }, 23 | { 24 | types: ["builtin", "changed", "keyword", "interpolation-punctuation"], 25 | style: { 26 | color: "#0c4a6e", 27 | }, 28 | }, 29 | { 30 | types: ["number", "inserted"], 31 | style: { 32 | color: "#B5CEA8", 33 | }, 34 | }, 35 | { 36 | types: ["constant"], 37 | style: { 38 | color: "#0f172a", 39 | }, 40 | }, 41 | { 42 | types: ["attr-name", "variable"], 43 | style: { 44 | color: "#0c4a6e", 45 | }, 46 | }, 47 | { 48 | types: ["deleted", "string", "attr-value", "template-punctuation"], 49 | style: { 50 | color: "#64748b", 51 | }, 52 | }, 53 | { 54 | types: ["selector"], 55 | style: { 56 | color: "#D7BA7D", 57 | }, 58 | }, 59 | { 60 | types: ["tag"], 61 | style: { 62 | color: "#0ea5e9", 63 | }, 64 | }, 65 | { 66 | types: ["tag"], 67 | languages: ["markup"], 68 | style: { 69 | color: "#0ea5e9", 70 | }, 71 | }, 72 | { 73 | types: ["punctuation", "operator"], 74 | style: { 75 | color: "#475569", 76 | }, 77 | }, 78 | { 79 | types: ["punctuation"], 80 | languages: ["markup"], 81 | style: { 82 | color: "#808080", 83 | }, 84 | }, 85 | { 86 | types: ["function"], 87 | style: { 88 | color: "#0e7490", 89 | }, 90 | }, 91 | { 92 | types: ["class-name"], 93 | style: { 94 | color: "#0ea5e9", 95 | }, 96 | }, 97 | { 98 | types: ["char"], 99 | style: { 100 | color: "#D16969", 101 | }, 102 | }, 103 | ], 104 | } 105 | export default theme 106 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/nightOwl.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/sdras/night-owl-vscode-theme 2 | // Converted automatically using ./tools/themeFromVsCode 3 | import type { PrismTheme } from "../types" 4 | const theme: PrismTheme = { 5 | plain: { 6 | color: "#d6deeb", 7 | backgroundColor: "#011627", 8 | }, 9 | styles: [ 10 | { 11 | types: ["changed"], 12 | style: { 13 | color: "rgb(162, 191, 252)", 14 | fontStyle: "italic", 15 | }, 16 | }, 17 | { 18 | types: ["deleted"], 19 | style: { 20 | color: "rgba(239, 83, 80, 0.56)", 21 | fontStyle: "italic", 22 | }, 23 | }, 24 | { 25 | types: ["inserted", "attr-name"], 26 | style: { 27 | color: "rgb(173, 219, 103)", 28 | fontStyle: "italic", 29 | }, 30 | }, 31 | { 32 | types: ["comment"], 33 | style: { 34 | color: "rgb(99, 119, 119)", 35 | fontStyle: "italic", 36 | }, 37 | }, 38 | { 39 | types: ["string", "url"], 40 | style: { 41 | color: "rgb(173, 219, 103)", 42 | }, 43 | }, 44 | { 45 | types: ["variable"], 46 | style: { 47 | color: "rgb(214, 222, 235)", 48 | }, 49 | }, 50 | { 51 | types: ["number"], 52 | style: { 53 | color: "rgb(247, 140, 108)", 54 | }, 55 | }, 56 | { 57 | types: ["builtin", "char", "constant", "function"], 58 | style: { 59 | color: "rgb(130, 170, 255)", 60 | }, 61 | }, 62 | { 63 | // This was manually added after the auto-generation 64 | // so that punctuations are not italicised 65 | types: ["punctuation"], 66 | style: { 67 | color: "rgb(199, 146, 234)", 68 | }, 69 | }, 70 | { 71 | types: ["selector", "doctype"], 72 | style: { 73 | color: "rgb(199, 146, 234)", 74 | fontStyle: "italic", 75 | }, 76 | }, 77 | { 78 | types: ["class-name"], 79 | style: { 80 | color: "rgb(255, 203, 139)", 81 | }, 82 | }, 83 | { 84 | types: ["tag", "operator", "keyword"], 85 | style: { 86 | color: "rgb(127, 219, 202)", 87 | }, 88 | }, 89 | { 90 | types: ["boolean"], 91 | style: { 92 | color: "rgb(255, 88, 116)", 93 | }, 94 | }, 95 | { 96 | types: ["property"], 97 | style: { 98 | color: "rgb(128, 203, 196)", 99 | }, 100 | }, 101 | { 102 | types: ["namespace"], 103 | style: { 104 | color: "rgb(178, 204, 214)", 105 | }, 106 | }, 107 | ], 108 | } 109 | export default theme 110 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/nightOwlLight.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/sdras/night-owl-vscode-theme 2 | // Converted automatically using ./tools/themeFromVsCode 3 | import type { PrismTheme } from "../types" 4 | const theme: PrismTheme = { 5 | plain: { 6 | color: "#403f53", 7 | backgroundColor: "#FBFBFB", 8 | }, 9 | styles: [ 10 | { 11 | types: ["changed"], 12 | style: { 13 | color: "rgb(162, 191, 252)", 14 | fontStyle: "italic", 15 | }, 16 | }, 17 | { 18 | types: ["deleted"], 19 | style: { 20 | color: "rgba(239, 83, 80, 0.56)", 21 | fontStyle: "italic", 22 | }, 23 | }, 24 | { 25 | types: ["inserted", "attr-name"], 26 | style: { 27 | color: "rgb(72, 118, 214)", 28 | fontStyle: "italic", 29 | }, 30 | }, 31 | { 32 | types: ["comment"], 33 | style: { 34 | color: "rgb(152, 159, 177)", 35 | fontStyle: "italic", 36 | }, 37 | }, 38 | { 39 | types: ["string", "builtin", "char", "constant", "url"], 40 | style: { 41 | color: "rgb(72, 118, 214)", 42 | }, 43 | }, 44 | { 45 | types: ["variable"], 46 | style: { 47 | color: "rgb(201, 103, 101)", 48 | }, 49 | }, 50 | { 51 | types: ["number"], 52 | style: { 53 | color: "rgb(170, 9, 130)", 54 | }, 55 | }, 56 | { 57 | // This was manually added after the auto-generation 58 | // so that punctuations are not italicised 59 | types: ["punctuation"], 60 | style: { 61 | color: "rgb(153, 76, 195)", 62 | }, 63 | }, 64 | { 65 | types: ["function", "selector", "doctype"], 66 | style: { 67 | color: "rgb(153, 76, 195)", 68 | fontStyle: "italic", 69 | }, 70 | }, 71 | { 72 | types: ["class-name"], 73 | style: { 74 | color: "rgb(17, 17, 17)", 75 | }, 76 | }, 77 | { 78 | types: ["tag"], 79 | style: { 80 | color: "rgb(153, 76, 195)", 81 | }, 82 | }, 83 | { 84 | types: ["operator", "property", "keyword", "namespace"], 85 | style: { 86 | color: "rgb(12, 150, 155)", 87 | }, 88 | }, 89 | { 90 | types: ["boolean"], 91 | style: { 92 | color: "rgb(188, 84, 84)", 93 | }, 94 | }, 95 | ], 96 | } 97 | export default theme 98 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/oceanicNext.ts: -------------------------------------------------------------------------------- 1 | // Oceanic Next 2 | // Author: Dmitri Voronianski (https://github.com/voronianski) 3 | // https://github.com/voronianski/oceanic-next-color-scheme 4 | // Adapted from: https://github.com/reactjs/reactjs.org/blob/428d52b/src/prism-styles.js 5 | import type { PrismTheme } from "../types" 6 | const colors = { 7 | char: "#D8DEE9", 8 | comment: "#999999", 9 | keyword: "#c5a5c5", 10 | primitive: "#5a9bcf", 11 | string: "#8dc891", 12 | variable: "#d7deea", 13 | boolean: "#ff8b50", 14 | punctuation: "#5FB3B3", 15 | tag: "#fc929e", 16 | function: "#79b6f2", 17 | className: "#FAC863", 18 | method: "#6699CC", 19 | operator: "#fc929e", 20 | } 21 | const theme: PrismTheme = { 22 | plain: { 23 | backgroundColor: "#282c34", 24 | color: "#ffffff", 25 | }, 26 | styles: [ 27 | { 28 | types: ["attr-name"], 29 | style: { 30 | color: colors.keyword, 31 | }, 32 | }, 33 | { 34 | types: ["attr-value"], 35 | style: { 36 | color: colors.string, 37 | }, 38 | }, 39 | { 40 | types: [ 41 | "comment", 42 | "block-comment", 43 | "prolog", 44 | "doctype", 45 | "cdata", 46 | "shebang", 47 | ], 48 | style: { 49 | color: colors.comment, 50 | }, 51 | }, 52 | { 53 | types: [ 54 | "property", 55 | "number", 56 | "function-name", 57 | "constant", 58 | "symbol", 59 | "deleted", 60 | ], 61 | style: { 62 | color: colors.primitive, 63 | }, 64 | }, 65 | { 66 | types: ["boolean"], 67 | style: { 68 | color: colors.boolean, 69 | }, 70 | }, 71 | { 72 | types: ["tag"], 73 | style: { 74 | color: colors.tag, 75 | }, 76 | }, 77 | { 78 | types: ["string"], 79 | style: { 80 | color: colors.string, 81 | }, 82 | }, 83 | { 84 | types: ["punctuation"], 85 | style: { 86 | color: colors.string, 87 | }, 88 | }, 89 | { 90 | types: ["selector", "char", "builtin", "inserted"], 91 | style: { 92 | color: colors.char, 93 | }, 94 | }, 95 | { 96 | types: ["function"], 97 | style: { 98 | color: colors.function, 99 | }, 100 | }, 101 | { 102 | types: ["operator", "entity", "url", "variable"], 103 | style: { 104 | color: colors.variable, 105 | }, 106 | }, 107 | { 108 | types: ["keyword"], 109 | style: { 110 | color: colors.keyword, 111 | }, 112 | }, 113 | { 114 | types: ["atrule", "class-name"], 115 | style: { 116 | color: colors.className, 117 | }, 118 | }, 119 | { 120 | types: ["important"], 121 | style: { 122 | fontWeight: "400", 123 | }, 124 | }, 125 | { 126 | types: ["bold"], 127 | style: { 128 | fontWeight: "bold", 129 | }, 130 | }, 131 | { 132 | types: ["italic"], 133 | style: { 134 | fontStyle: "italic", 135 | }, 136 | }, 137 | { 138 | types: ["namespace"], 139 | style: { 140 | opacity: 0.7, 141 | }, 142 | }, 143 | ], 144 | } 145 | export default theme 146 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/okaidia.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Adapted from the Prism Okaidia theme 3 | https://github.com/PrismJS/prism/blob/1761513e3db48ca9222037644a9c68746e24f039/themes/prism-okaidia.css 4 | https://github.com/ocodia/okaidia-prismjs-theme 5 | */ 6 | import type { PrismTheme } from "../types" 7 | const theme: PrismTheme = { 8 | plain: { 9 | color: "#f8f8f2", 10 | backgroundColor: "#272822", 11 | }, 12 | styles: [ 13 | { 14 | types: ["changed"], 15 | style: { 16 | color: "rgb(162, 191, 252)", 17 | fontStyle: "italic", 18 | }, 19 | }, 20 | { 21 | types: ["deleted"], 22 | style: { 23 | color: "#f92672", 24 | fontStyle: "italic", 25 | }, 26 | }, 27 | { 28 | types: ["inserted"], 29 | style: { 30 | color: "rgb(173, 219, 103)", 31 | fontStyle: "italic", 32 | }, 33 | }, 34 | { 35 | types: ["comment"], 36 | style: { 37 | color: "#8292a2", 38 | fontStyle: "italic", 39 | }, 40 | }, 41 | { 42 | types: ["string", "url"], 43 | style: { 44 | color: "#a6e22e", 45 | }, 46 | }, 47 | { 48 | types: ["variable"], 49 | style: { 50 | color: "#f8f8f2", 51 | }, 52 | }, 53 | { 54 | types: ["number"], 55 | style: { 56 | color: "#ae81ff", 57 | }, 58 | }, 59 | { 60 | types: ["builtin", "char", "constant", "function", "class-name"], 61 | style: { 62 | color: "#e6db74", 63 | }, 64 | }, 65 | { 66 | types: ["punctuation"], 67 | style: { 68 | color: "#f8f8f2", 69 | }, 70 | }, 71 | { 72 | types: ["selector", "doctype"], 73 | style: { 74 | color: "#a6e22e", 75 | fontStyle: "italic", 76 | }, 77 | }, 78 | { 79 | types: ["tag", "operator", "keyword"], 80 | style: { 81 | color: "#66d9ef", 82 | }, 83 | }, 84 | { 85 | types: ["boolean"], 86 | style: { 87 | color: "#ae81ff", 88 | }, 89 | }, 90 | { 91 | types: ["namespace"], 92 | style: { 93 | color: "rgb(178, 204, 214)", 94 | opacity: 0.7, 95 | }, 96 | }, 97 | { 98 | types: ["tag", "property"], 99 | style: { 100 | color: "#f92672", 101 | }, 102 | }, 103 | { 104 | types: ["attr-name"], 105 | style: { 106 | color: "#a6e22e !important", 107 | }, 108 | }, 109 | { 110 | types: ["doctype"], 111 | style: { 112 | color: "#8292a2", 113 | }, 114 | }, 115 | { 116 | types: ["rule"], 117 | style: { 118 | color: "#e6db74", 119 | }, 120 | }, 121 | ], 122 | } 123 | export default theme 124 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/oneDark.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Adapted from the Prism One Dark Theme 3 | https://github.com/PrismJS/prism-themes/blob/master/themes/prism-one-dark.css 4 | Created by Marc Rousavy (@mrousavy) on 26.9.2023 5 | */ 6 | import type { PrismTheme } from "../types" 7 | 8 | const theme: PrismTheme = { 9 | plain: { 10 | backgroundColor: "hsl(220, 13%, 18%)", 11 | color: "hsl(220, 14%, 71%)", 12 | textShadow: "0 1px rgba(0, 0, 0, 0.3)", 13 | }, 14 | styles: [ 15 | { 16 | types: ["comment", "prolog", "cdata"], 17 | style: { 18 | color: "hsl(220, 10%, 40%)", 19 | }, 20 | }, 21 | { 22 | types: ["doctype", "punctuation", "entity"], 23 | style: { 24 | color: "hsl(220, 14%, 71%)", 25 | }, 26 | }, 27 | { 28 | types: [ 29 | "attr-name", 30 | "class-name", 31 | "maybe-class-name", 32 | "boolean", 33 | "constant", 34 | "number", 35 | "atrule", 36 | ], 37 | style: { color: "hsl(29, 54%, 61%)" }, 38 | }, 39 | { 40 | types: ["keyword"], 41 | style: { color: "hsl(286, 60%, 67%)" }, 42 | }, 43 | { 44 | types: ["property", "tag", "symbol", "deleted", "important"], 45 | style: { 46 | color: "hsl(355, 65%, 65%)", 47 | }, 48 | }, 49 | 50 | { 51 | types: [ 52 | "selector", 53 | "string", 54 | "char", 55 | "builtin", 56 | "inserted", 57 | "regex", 58 | "attr-value", 59 | ], 60 | style: { 61 | color: "hsl(95, 38%, 62%)", 62 | }, 63 | }, 64 | { 65 | types: ["variable", "operator", "function"], 66 | style: { 67 | color: "hsl(207, 82%, 66%)", 68 | }, 69 | }, 70 | { 71 | types: ["url"], 72 | style: { 73 | color: "hsl(187, 47%, 55%)", 74 | }, 75 | }, 76 | { 77 | types: ["deleted"], 78 | style: { 79 | textDecorationLine: "line-through", 80 | }, 81 | }, 82 | { 83 | types: ["inserted"], 84 | style: { 85 | textDecorationLine: "underline", 86 | }, 87 | }, 88 | { 89 | types: ["italic"], 90 | style: { 91 | fontStyle: "italic", 92 | }, 93 | }, 94 | { 95 | types: ["important", "bold"], 96 | style: { 97 | fontWeight: "bold", 98 | }, 99 | }, 100 | { 101 | types: ["important"], 102 | style: { 103 | color: "hsl(220, 14%, 71%)", 104 | }, 105 | }, 106 | ], 107 | } 108 | 109 | export default theme 110 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/oneLight.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Adapted from the Prism One Light Theme 3 | https://github.com/PrismJS/prism-themes/blob/master/themes/prism-one-light.css 4 | Created by Marc Rousavy (@mrousavy) on 26.9.2023 5 | */ 6 | import type { PrismTheme } from "../types" 7 | 8 | const theme: PrismTheme = { 9 | plain: { 10 | backgroundColor: "hsl(230, 1%, 98%)", 11 | color: "hsl(230, 8%, 24%)", 12 | }, 13 | styles: [ 14 | { 15 | types: ["comment", "prolog", "cdata"], 16 | style: { 17 | color: "hsl(230, 4%, 64%)", 18 | }, 19 | }, 20 | { 21 | types: ["doctype", "punctuation", "entity"], 22 | style: { 23 | color: "hsl(230, 8%, 24%)", 24 | }, 25 | }, 26 | { 27 | types: [ 28 | "attr-name", 29 | "class-name", 30 | "boolean", 31 | "constant", 32 | "number", 33 | "atrule", 34 | ], 35 | style: { 36 | color: "hsl(35, 99%, 36%)", 37 | }, 38 | }, 39 | { 40 | types: ["keyword"], 41 | style: { 42 | color: "hsl(301, 63%, 40%)", 43 | }, 44 | }, 45 | 46 | { 47 | types: ["property", "tag", "symbol", "deleted", "important"], 48 | style: { 49 | color: "hsl(5, 74%, 59%)", 50 | }, 51 | }, 52 | { 53 | types: [ 54 | "selector", 55 | "string", 56 | "char", 57 | "builtin", 58 | "inserted", 59 | "regex", 60 | "attr-value", 61 | "punctuation", 62 | ], 63 | style: { 64 | color: "hsl(119, 34%, 47%)", 65 | }, 66 | }, 67 | { 68 | types: ["variable", "operator", "function"], 69 | style: { 70 | color: "hsl(221, 87%, 60%)", 71 | }, 72 | }, 73 | { 74 | types: ["url"], 75 | style: { 76 | color: "hsl(198, 99%, 37%)", 77 | }, 78 | }, 79 | { 80 | types: ["deleted"], 81 | style: { 82 | textDecorationLine: "line-through", 83 | }, 84 | }, 85 | { 86 | types: ["inserted"], 87 | style: { 88 | textDecorationLine: "underline", 89 | }, 90 | }, 91 | { 92 | types: ["italic"], 93 | style: { 94 | fontStyle: "italic", 95 | }, 96 | }, 97 | { 98 | types: ["important", "bold"], 99 | style: { 100 | fontWeight: "bold", 101 | }, 102 | }, 103 | { 104 | types: ["important"], 105 | style: { 106 | color: "hsl(230, 8%, 24%)", 107 | }, 108 | }, 109 | ], 110 | } 111 | 112 | export default theme 113 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/palenight.ts: -------------------------------------------------------------------------------- 1 | // Converted automatically using ./tools/themeFromVsCode 2 | import type { PrismTheme } from "../types" 3 | const theme: PrismTheme = { 4 | plain: { 5 | color: "#bfc7d5", 6 | backgroundColor: "#292d3e", 7 | }, 8 | styles: [ 9 | { 10 | types: ["comment"], 11 | style: { 12 | color: "rgb(105, 112, 152)", 13 | fontStyle: "italic", 14 | }, 15 | }, 16 | { 17 | types: ["string", "inserted"], 18 | style: { 19 | color: "rgb(195, 232, 141)", 20 | }, 21 | }, 22 | { 23 | types: ["number"], 24 | style: { 25 | color: "rgb(247, 140, 108)", 26 | }, 27 | }, 28 | { 29 | types: ["builtin", "char", "constant", "function"], 30 | style: { 31 | color: "rgb(130, 170, 255)", 32 | }, 33 | }, 34 | { 35 | types: ["punctuation", "selector"], 36 | style: { 37 | color: "rgb(199, 146, 234)", 38 | }, 39 | }, 40 | { 41 | types: ["variable"], 42 | style: { 43 | color: "rgb(191, 199, 213)", 44 | }, 45 | }, 46 | { 47 | types: ["class-name", "attr-name"], 48 | style: { 49 | color: "rgb(255, 203, 107)", 50 | }, 51 | }, 52 | { 53 | types: ["tag", "deleted"], 54 | style: { 55 | color: "rgb(255, 85, 114)", 56 | }, 57 | }, 58 | { 59 | types: ["operator"], 60 | style: { 61 | color: "rgb(137, 221, 255)", 62 | }, 63 | }, 64 | { 65 | types: ["boolean"], 66 | style: { 67 | color: "rgb(255, 88, 116)", 68 | }, 69 | }, 70 | { 71 | types: ["keyword"], 72 | style: { 73 | fontStyle: "italic", 74 | }, 75 | }, 76 | { 77 | types: ["doctype"], 78 | style: { 79 | color: "rgb(199, 146, 234)", 80 | fontStyle: "italic", 81 | }, 82 | }, 83 | { 84 | types: ["namespace"], 85 | style: { 86 | color: "rgb(178, 204, 214)", 87 | }, 88 | }, 89 | { 90 | types: ["url"], 91 | style: { 92 | color: "rgb(221, 221, 221)", 93 | }, 94 | }, 95 | ], 96 | } 97 | export default theme 98 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/shadesOfPurple.ts: -------------------------------------------------------------------------------- 1 | // Shades of Purple 2 | // Author: Ahmad Awais https://twitter.com/MrAhmadAwais 3 | // Original: https://github.com/ahmadawais/shades-of-purple-vscode/ 4 | // Converted automatically using ./tools/themeFromVsCode and then customized manually. 5 | import type { PrismTheme } from "../types" 6 | const theme: PrismTheme = { 7 | plain: { 8 | color: "#9EFEFF", 9 | backgroundColor: "#2D2A55", 10 | }, 11 | styles: [ 12 | { 13 | types: ["changed"], 14 | style: { 15 | color: "rgb(255, 238, 128)", 16 | }, 17 | }, 18 | { 19 | types: ["deleted"], 20 | style: { 21 | color: "rgba(239, 83, 80, 0.56)", 22 | }, 23 | }, 24 | { 25 | types: ["inserted"], 26 | style: { 27 | color: "rgb(173, 219, 103)", 28 | }, 29 | }, 30 | { 31 | types: ["comment"], 32 | style: { 33 | color: "rgb(179, 98, 255)", 34 | fontStyle: "italic", 35 | }, 36 | }, 37 | { 38 | types: ["punctuation"], 39 | style: { 40 | color: "rgb(255, 255, 255)", 41 | }, 42 | }, 43 | { 44 | types: ["constant"], 45 | style: { 46 | color: "rgb(255, 98, 140)", 47 | }, 48 | }, 49 | { 50 | types: ["string", "url"], 51 | style: { 52 | color: "rgb(165, 255, 144)", 53 | }, 54 | }, 55 | { 56 | types: ["variable"], 57 | style: { 58 | color: "rgb(255, 238, 128)", 59 | }, 60 | }, 61 | { 62 | types: ["number", "boolean"], 63 | style: { 64 | color: "rgb(255, 98, 140)", 65 | }, 66 | }, 67 | { 68 | types: ["attr-name"], 69 | style: { 70 | color: "rgb(255, 180, 84)", 71 | }, 72 | }, 73 | { 74 | types: [ 75 | "keyword", 76 | "operator", 77 | "property", 78 | "namespace", 79 | "tag", 80 | "selector", 81 | "doctype", 82 | ], 83 | style: { 84 | color: "rgb(255, 157, 0)", 85 | }, 86 | }, 87 | { 88 | types: ["builtin", "char", "constant", "function", "class-name"], 89 | style: { 90 | color: "rgb(250, 208, 0)", 91 | }, 92 | }, 93 | ], 94 | } 95 | export default theme 96 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/synthwave84.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Synthwave '84 Theme originally by Robb Owen [@Robb0wen] for Visual Studio Code 3 | * Demo: https://marc.dev/demo/prism-synthwave84 4 | * 5 | * Ported for PrismJS by Marc Backes [@themarcba]: https://github.com/themarcba/prism-themes/blob/master/themes/prism-synthwave84.css 6 | * Ported for prism-react-renderer by Forrest Akin [@forrest-akin] 7 | */ 8 | import type { PrismTheme } from "../types" 9 | const theme: PrismTheme = { 10 | plain: { 11 | backgroundColor: "linear-gradient(to bottom, #2a2139 75%, #34294f)", 12 | backgroundImage: "#34294f", 13 | color: "#f92aad", 14 | textShadow: "0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3", 15 | }, 16 | styles: [ 17 | { 18 | types: ["comment", "block-comment", "prolog", "doctype", "cdata"], 19 | style: { 20 | color: "#495495", 21 | fontStyle: "italic", 22 | }, 23 | }, 24 | { 25 | types: ["punctuation"], 26 | style: { 27 | color: "#ccc", 28 | }, 29 | }, 30 | { 31 | types: [ 32 | "tag", 33 | "attr-name", 34 | "namespace", 35 | "number", 36 | "unit", 37 | "hexcode", 38 | "deleted", 39 | ], 40 | style: { 41 | color: "#e2777a", 42 | }, 43 | }, 44 | { 45 | types: ["property", "selector"], 46 | style: { 47 | color: "#72f1b8", 48 | textShadow: "0 0 2px #100c0f, 0 0 10px #257c5575, 0 0 35px #21272475", 49 | }, 50 | }, 51 | { 52 | types: ["function-name"], 53 | style: { 54 | color: "#6196cc", 55 | }, 56 | }, 57 | { 58 | types: ["boolean", "selector-id", "function"], 59 | style: { 60 | color: "#fdfdfd", 61 | textShadow: 62 | "0 0 2px #001716, 0 0 3px #03edf975, 0 0 5px #03edf975, 0 0 8px #03edf975", 63 | }, 64 | }, 65 | { 66 | types: ["class-name", "maybe-class-name", "builtin"], 67 | style: { 68 | color: "#fff5f6", 69 | textShadow: 70 | "0 0 2px #000, 0 0 10px #fc1f2c75, 0 0 5px #fc1f2c75, 0 0 25px #fc1f2c75", 71 | }, 72 | }, 73 | { 74 | types: ["constant", "symbol"], 75 | style: { 76 | color: "#f92aad", 77 | textShadow: "0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3", 78 | }, 79 | }, 80 | { 81 | types: ["important", "atrule", "keyword", "selector-class"], 82 | style: { 83 | color: "#f4eee4", 84 | textShadow: "0 0 2px #393a33, 0 0 8px #f39f0575, 0 0 2px #f39f0575", 85 | }, 86 | }, 87 | { 88 | types: ["string", "char", "attr-value", "regex", "variable"], 89 | style: { 90 | color: "#f87c32", 91 | }, 92 | }, 93 | { 94 | types: ["parameter"], 95 | style: { 96 | fontStyle: "italic", 97 | }, 98 | }, 99 | { 100 | types: ["entity", "url"], 101 | style: { 102 | color: "#67cdcc", 103 | }, 104 | }, 105 | { 106 | types: ["operator"], 107 | style: { 108 | color: "ffffffee", 109 | }, 110 | }, 111 | { 112 | types: ["important", "bold"], 113 | style: { 114 | fontWeight: "bold", 115 | }, 116 | }, 117 | { 118 | types: ["italic"], 119 | style: { 120 | fontStyle: "italic", 121 | }, 122 | }, 123 | { 124 | types: ["entity"], 125 | style: { 126 | cursor: "help", 127 | }, 128 | }, 129 | { 130 | types: ["inserted"], 131 | style: { 132 | color: "green", 133 | }, 134 | }, 135 | ], 136 | } 137 | export default theme 138 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/ultramin.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/damienstanton/ultramin 2 | // Converted automatically using ./tools/themeFromVsCode 3 | import type { PrismTheme } from "../types" 4 | const theme: PrismTheme = { 5 | plain: { 6 | color: "#282a2e", 7 | backgroundColor: "#ffffff", 8 | }, 9 | styles: [ 10 | { 11 | types: ["comment"], 12 | style: { 13 | color: "rgb(197, 200, 198)", 14 | }, 15 | }, 16 | { 17 | types: ["string", "number", "builtin", "variable"], 18 | style: { 19 | color: "rgb(150, 152, 150)", 20 | }, 21 | }, 22 | { 23 | types: ["class-name", "function", "tag", "attr-name"], 24 | style: { 25 | color: "rgb(40, 42, 46)", 26 | }, 27 | }, 28 | ], 29 | } 30 | export default theme 31 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/vsDark.ts: -------------------------------------------------------------------------------- 1 | // Converted automatically using ./tools/themeFromVsCode 2 | import type { PrismTheme } from "../types" 3 | const theme: PrismTheme = { 4 | plain: { 5 | color: "#9CDCFE", 6 | backgroundColor: "#1E1E1E", 7 | }, 8 | styles: [ 9 | { 10 | types: ["prolog"], 11 | style: { 12 | color: "rgb(0, 0, 128)", 13 | }, 14 | }, 15 | { 16 | types: ["comment"], 17 | style: { 18 | color: "rgb(106, 153, 85)", 19 | }, 20 | }, 21 | { 22 | types: ["builtin", "changed", "keyword", "interpolation-punctuation"], 23 | style: { 24 | color: "rgb(86, 156, 214)", 25 | }, 26 | }, 27 | { 28 | types: ["number", "inserted"], 29 | style: { 30 | color: "rgb(181, 206, 168)", 31 | }, 32 | }, 33 | { 34 | types: ["constant"], 35 | style: { 36 | color: "rgb(100, 102, 149)", 37 | }, 38 | }, 39 | { 40 | types: ["attr-name", "variable"], 41 | style: { 42 | color: "rgb(156, 220, 254)", 43 | }, 44 | }, 45 | { 46 | types: ["deleted", "string", "attr-value", "template-punctuation"], 47 | style: { 48 | color: "rgb(206, 145, 120)", 49 | }, 50 | }, 51 | { 52 | types: ["selector"], 53 | style: { 54 | color: "rgb(215, 186, 125)", 55 | }, 56 | }, 57 | { 58 | // Fix tag color 59 | types: ["tag"], 60 | style: { 61 | color: "rgb(78, 201, 176)", 62 | }, 63 | }, 64 | { 65 | // Fix tag color for HTML 66 | types: ["tag"], 67 | languages: ["markup"], 68 | style: { 69 | color: "rgb(86, 156, 214)", 70 | }, 71 | }, 72 | { 73 | types: ["punctuation", "operator"], 74 | style: { 75 | color: "rgb(212, 212, 212)", 76 | }, 77 | }, 78 | { 79 | // Fix punctuation color for HTML 80 | types: ["punctuation"], 81 | languages: ["markup"], 82 | style: { 83 | color: "#808080", 84 | }, 85 | }, 86 | { 87 | types: ["function"], 88 | style: { 89 | color: "rgb(220, 220, 170)", 90 | }, 91 | }, 92 | { 93 | types: ["class-name"], 94 | style: { 95 | color: "rgb(78, 201, 176)", 96 | }, 97 | }, 98 | { 99 | types: ["char"], 100 | style: { 101 | color: "rgb(209, 105, 105)", 102 | }, 103 | }, 104 | ], 105 | } 106 | export default theme 107 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/themes/vsLight.ts: -------------------------------------------------------------------------------- 1 | // Converted automatically using ./tools/themeFromVsCode 2 | import type { PrismTheme } from "../types" 3 | const theme: PrismTheme = { 4 | plain: { 5 | color: "#000000", 6 | backgroundColor: "#ffffff", 7 | }, 8 | styles: [ 9 | { 10 | types: ["comment"], 11 | style: { 12 | color: "rgb(0, 128, 0)", 13 | }, 14 | }, 15 | { 16 | types: ["builtin"], 17 | style: { 18 | color: "rgb(0, 112, 193)", 19 | }, 20 | }, 21 | { 22 | types: ["number", "variable", "inserted"], 23 | style: { 24 | color: "rgb(9, 134, 88)", 25 | }, 26 | }, 27 | { 28 | types: ["operator"], 29 | style: { 30 | color: "rgb(0, 0, 0)", 31 | }, 32 | }, 33 | { 34 | types: ["constant", "char"], 35 | style: { 36 | color: "rgb(129, 31, 63)", 37 | }, 38 | }, 39 | { 40 | types: ["tag"], 41 | style: { 42 | color: "rgb(128, 0, 0)", 43 | }, 44 | }, 45 | { 46 | types: ["attr-name"], 47 | style: { 48 | color: "rgb(255, 0, 0)", 49 | }, 50 | }, 51 | { 52 | types: ["deleted", "string"], 53 | style: { 54 | color: "rgb(163, 21, 21)", 55 | }, 56 | }, 57 | { 58 | types: ["changed", "punctuation"], 59 | style: { 60 | color: "rgb(4, 81, 165)", 61 | }, 62 | }, 63 | { 64 | types: ["function", "keyword"], 65 | style: { 66 | color: "rgb(0, 0, 255)", 67 | }, 68 | }, 69 | { 70 | types: ["class-name"], 71 | style: { 72 | color: "rgb(38, 127, 153)", 73 | }, 74 | }, 75 | ], 76 | } 77 | export default theme 78 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { CSSProperties } from "react" 2 | import type { Token as PrismToken, Grammar } from "prismjs" 3 | import Prism from "prismjs" 4 | 5 | export type Language = string 6 | export type PrismGrammar = Grammar 7 | export type PrismLib = typeof Prism 8 | 9 | export type Token = { 10 | types: string[] 11 | content: string 12 | empty?: boolean 13 | } 14 | 15 | export type EnvConfig = { 16 | code: string 17 | grammar: PrismGrammar 18 | language: Language 19 | tokens: (string | PrismToken)[] 20 | } 21 | 22 | export type StyleObj = CSSProperties 23 | 24 | export type LineInputProps = { 25 | style?: StyleObj 26 | className?: string 27 | line: Token[] 28 | [key: string]: unknown 29 | } 30 | export type LineOutputProps = { 31 | style?: StyleObj 32 | className: string 33 | [key: string]: unknown 34 | } 35 | export type TokenInputProps = { 36 | style?: StyleObj 37 | className?: string 38 | token: Token 39 | [key: string]: unknown 40 | } 41 | export type TokenOutputProps = { 42 | style?: StyleObj 43 | className: string 44 | children: string 45 | [key: string]: unknown 46 | } 47 | export type InternalHighlightProps = { 48 | prism: PrismLib 49 | theme: PrismTheme 50 | language: Language 51 | code: string 52 | children: (props: RenderProps) => JSX.Element 53 | } 54 | export type HighlightProps = { 55 | prism?: PrismLib 56 | theme?: PrismTheme 57 | language: Language 58 | code: string 59 | children: (props: RenderProps) => JSX.Element 60 | } 61 | export type RenderProps = { 62 | tokens: Token[][] 63 | className: string 64 | style: CSSProperties 65 | getLineProps: (input: LineInputProps) => LineOutputProps 66 | getTokenProps: (input: TokenInputProps) => TokenOutputProps 67 | } 68 | export type PrismThemeEntry = { 69 | color?: string 70 | cursor?: string 71 | background?: string 72 | backgroundImage?: string 73 | backgroundColor?: string 74 | textShadow?: string 75 | fontStyle?: "normal" | "italic" 76 | fontWeight?: 77 | | "normal" 78 | | "bold" 79 | | "100" 80 | | "200" 81 | | "300" 82 | | "400" 83 | | "500" 84 | | "600" 85 | | "700" 86 | | "800" 87 | | "900" 88 | textDecorationLine?: 89 | | "none" 90 | | "underline" 91 | | "line-through" 92 | | "underline line-through" 93 | opacity?: number 94 | } 95 | export type PrismTheme = { 96 | plain: PrismThemeEntry 97 | styles: Array<{ 98 | types: string[] 99 | style: PrismThemeEntry 100 | languages?: Language[] 101 | }> 102 | } 103 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/utils/__tests__/normalizeTokens.test.ts: -------------------------------------------------------------------------------- 1 | import normalizeTokens from "../normalizeTokens" 2 | import { Token } from "prismjs" 3 | describe("normalizeTokens", () => { 4 | it("handles plain strings", () => { 5 | const input = ["hello", "world"] 6 | const output = normalizeTokens(input) 7 | expect(output).toEqual([ 8 | [ 9 | { 10 | types: ["plain"], 11 | content: "hello", 12 | }, 13 | { 14 | types: ["plain"], 15 | content: "world", 16 | }, 17 | ], 18 | ]) 19 | }) 20 | it("handles flat tokens", () => { 21 | const input = [ 22 | { 23 | type: "test1", 24 | content: "hello", 25 | }, 26 | { 27 | type: "test2", 28 | content: "world", 29 | }, 30 | ] as Token[] 31 | const output = normalizeTokens(input) 32 | expect(output).toEqual([ 33 | [ 34 | { 35 | types: ["test1"], 36 | content: "hello", 37 | }, 38 | { 39 | types: ["test2"], 40 | content: "world", 41 | }, 42 | ], 43 | ]) 44 | }) 45 | it("handles nested tokens", () => { 46 | const input = [ 47 | { 48 | type: "test1", 49 | content: [ 50 | { 51 | type: "nest1", 52 | content: "he", 53 | }, 54 | { 55 | type: "nest2", 56 | content: "llo", 57 | }, 58 | ], 59 | }, 60 | { 61 | type: "test2", 62 | content: "world", 63 | }, 64 | ] as Token[] 65 | const output = normalizeTokens(input) 66 | expect(output).toEqual([ 67 | [ 68 | { 69 | types: ["test1", "nest1"], 70 | content: "he", 71 | }, 72 | { 73 | types: ["test1", "nest2"], 74 | content: "llo", 75 | }, 76 | { 77 | types: ["test2"], 78 | content: "world", 79 | }, 80 | ], 81 | ]) 82 | }) 83 | it("handles nested & mixed tokens", () => { 84 | const input = [ 85 | { 86 | type: "test1", 87 | content: [ 88 | { 89 | type: "nest", 90 | content: "he", 91 | }, 92 | "llo", 93 | ], 94 | }, 95 | { 96 | type: "test2", 97 | content: "world", 98 | }, 99 | "!", 100 | ] as Token[] 101 | const output = normalizeTokens(input) 102 | expect(output).toEqual([ 103 | [ 104 | { 105 | types: ["test1", "nest"], 106 | content: "he", 107 | }, 108 | { 109 | types: ["test1"], 110 | content: "llo", 111 | }, 112 | { 113 | types: ["test2"], 114 | content: "world", 115 | }, 116 | { 117 | types: ["plain"], 118 | content: "!", 119 | }, 120 | ], 121 | ]) 122 | }) 123 | it("handles deeply nested tokens", () => { 124 | const input = [ 125 | { 126 | type: "1", 127 | content: [ 128 | { 129 | type: "2", 130 | content: [ 131 | { 132 | type: "3", 133 | content: "hello", 134 | }, 135 | ], 136 | }, 137 | ], 138 | }, 139 | ] as Token[] 140 | const output = normalizeTokens(input) 141 | expect(output).toEqual([ 142 | [ 143 | { 144 | types: ["1", "2", "3"], 145 | content: "hello", 146 | }, 147 | ], 148 | ]) 149 | }) 150 | it("handles plain strings with newlines", () => { 151 | const input = ["hello", " \nworld"] 152 | const output = normalizeTokens(input) 153 | expect(output).toEqual([ 154 | [ 155 | { 156 | types: ["plain"], 157 | content: "hello", 158 | }, 159 | { 160 | types: ["plain"], 161 | content: " ", 162 | }, 163 | ], 164 | [ 165 | { 166 | types: ["plain"], 167 | content: "world", 168 | }, 169 | ], 170 | ]) 171 | }) 172 | it("handles flat tokens with newlines", () => { 173 | const input = [ 174 | { 175 | type: "test1", 176 | content: "hello", 177 | }, 178 | { 179 | type: "test2", 180 | content: "wor\nld", 181 | }, 182 | ] as Token[] 183 | const output = normalizeTokens(input) 184 | expect(output).toEqual([ 185 | [ 186 | { 187 | types: ["test1"], 188 | content: "hello", 189 | }, 190 | { 191 | types: ["test2"], 192 | content: "wor", 193 | }, 194 | ], 195 | [ 196 | { 197 | types: ["test2"], 198 | content: "ld", 199 | }, 200 | ], 201 | ]) 202 | }) 203 | it("handles nested tokens with newlines", () => { 204 | const input = [ 205 | { 206 | type: "test1", 207 | content: [ 208 | { 209 | type: "nest1", 210 | content: "he", 211 | }, 212 | { 213 | type: "nest2", 214 | content: "l\nlo", 215 | }, 216 | ], 217 | }, 218 | { 219 | type: "test2", 220 | content: "wor\nld", 221 | }, 222 | ] as Token[] 223 | const output = normalizeTokens(input) 224 | expect(output).toEqual([ 225 | [ 226 | { 227 | types: ["test1", "nest1"], 228 | content: "he", 229 | }, 230 | { 231 | types: ["test1", "nest2"], 232 | content: "l", 233 | }, 234 | ], 235 | [ 236 | { 237 | types: ["test1", "nest2"], 238 | content: "lo", 239 | }, 240 | { 241 | types: ["test2"], 242 | content: "wor", 243 | }, 244 | ], 245 | [ 246 | { 247 | types: ["test2"], 248 | content: "ld", 249 | }, 250 | ], 251 | ]) 252 | }) 253 | it("handles nested & mixed tokens with newlines", () => { 254 | const input = [ 255 | { 256 | type: "test1", 257 | content: [ 258 | { 259 | type: "nest", 260 | content: "h\ne", 261 | }, 262 | "l\nlo", 263 | ], 264 | }, 265 | "world\n!", 266 | ] as Token[] 267 | const output = normalizeTokens(input) 268 | expect(output).toEqual([ 269 | [ 270 | { 271 | types: ["test1", "nest"], 272 | content: "h", 273 | }, 274 | ], 275 | [ 276 | { 277 | types: ["test1", "nest"], 278 | content: "e", 279 | }, 280 | { 281 | types: ["test1"], 282 | content: "l", 283 | }, 284 | ], 285 | [ 286 | { 287 | types: ["test1"], 288 | content: "lo", 289 | }, 290 | { 291 | types: ["plain"], 292 | content: "world", 293 | }, 294 | ], 295 | [ 296 | { 297 | types: ["plain"], 298 | content: "!", 299 | }, 300 | ], 301 | ]) 302 | }) 303 | it("handles deeply nested tokens with newlines", () => { 304 | const input = [ 305 | { 306 | type: "1", 307 | content: [ 308 | { 309 | type: "2", 310 | content: [ 311 | { 312 | type: "3", 313 | content: "hel\nlo", 314 | }, 315 | ], 316 | }, 317 | ], 318 | }, 319 | ] as Token[] 320 | const output = normalizeTokens(input) 321 | expect(output).toEqual([ 322 | [ 323 | { 324 | types: ["1", "2", "3"], 325 | content: "hel", 326 | }, 327 | ], 328 | [ 329 | { 330 | types: ["1", "2", "3"], 331 | content: "lo", 332 | }, 333 | ], 334 | ]) 335 | }) 336 | it("handles empty lines gracefully", () => { 337 | const input = ["\n\n"] 338 | const output = normalizeTokens(input) 339 | expect(output).toEqual([ 340 | [ 341 | { 342 | types: ["plain"], 343 | content: "\n", 344 | empty: true, 345 | }, 346 | ], 347 | [ 348 | { 349 | types: ["plain"], 350 | content: "\n", 351 | empty: true, 352 | }, 353 | ], 354 | [ 355 | { 356 | types: ["plain"], 357 | content: "\n", 358 | empty: true, 359 | }, 360 | ], 361 | ]) 362 | }) 363 | }) 364 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/utils/__tests__/themeToDict.test.ts: -------------------------------------------------------------------------------- 1 | import themeToDict from "../themeToDict" 2 | describe("themeToDict", () => { 3 | it("converts entry.types to dictionary", () => { 4 | const input = { 5 | plain: { 6 | color: "red", 7 | }, 8 | styles: [ 9 | { 10 | types: ["1", "2"], 11 | style: { 12 | color: "green", 13 | }, 14 | }, 15 | { 16 | types: ["3"], 17 | style: { 18 | color: "blue", 19 | }, 20 | }, 21 | { 22 | types: ["2"], 23 | style: { 24 | color: "orange", 25 | }, 26 | }, 27 | ], 28 | } 29 | const expected = { 30 | root: { 31 | color: "red", 32 | }, 33 | plain: { 34 | color: "red", 35 | backgroundColor: undefined, 36 | }, 37 | 1: { 38 | color: "green", 39 | }, 40 | 2: { 41 | color: "orange", 42 | }, 43 | 3: { 44 | color: "blue", 45 | }, 46 | } 47 | expect(themeToDict(input, "js")).toEqual(expected) 48 | // Check order in which keys were added to implicitly test merge strategy 49 | expect(Object.keys(themeToDict(input, "js"))).toEqual(Object.keys(expected)) 50 | }) 51 | it("limits entries by entry.languages", () => { 52 | const input = { 53 | plain: {}, 54 | styles: [ 55 | { 56 | types: ["test"], 57 | languages: ["js"], 58 | style: { 59 | color: "green", 60 | }, 61 | }, 62 | ], 63 | } 64 | expect(themeToDict(input, "js").test).toEqual({ 65 | color: "green", 66 | }) 67 | expect(themeToDict(input, "ocaml").test).toEqual(undefined) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/utils/normalizeTokens.ts: -------------------------------------------------------------------------------- 1 | import type { Token } from "../types" 2 | import type { Token as PrismToken, TokenStream } from "prismjs" 3 | 4 | const newlineRe = /\r\n|\r|\n/ 5 | 6 | // Empty lines need to contain a single empty token, denoted with { empty: true } 7 | const normalizeEmptyLines = (line: Token[]) => { 8 | if (line.length === 0) { 9 | line.push({ 10 | types: ["plain"], 11 | content: "\n", 12 | empty: true, 13 | }) 14 | } else if (line.length === 1 && line[0].content === "") { 15 | line[0].content = "\n" 16 | line[0].empty = true 17 | } 18 | } 19 | 20 | const appendTypes = (types: string[], add: string[] | string): string[] => { 21 | const typesSize = types.length 22 | 23 | if (typesSize > 0 && types[typesSize - 1] === add) { 24 | return types 25 | } 26 | 27 | return types.concat(add) 28 | } 29 | 30 | // Takes an array of Prism's tokens and groups them by line, turning plain 31 | // strings into tokens as well. Tokens can become recursive in some cases, 32 | // which means that their types are concatenated. Plain-string tokens however 33 | // are always of type "plain". 34 | // This is not recursive to avoid exceeding the call-stack limit, since it's unclear 35 | // how nested Prism's tokens can become 36 | const normalizeTokens = (tokens: (PrismToken | string)[]): Token[][] => { 37 | const typeArrStack: string[][] = [[]] 38 | const tokenArrStack = [tokens] 39 | const tokenArrIndexStack = [0] 40 | const tokenArrSizeStack = [tokens.length] 41 | let i = 0 42 | let stackIndex = 0 43 | let currentLine: Token[] = [] 44 | const acc = [currentLine] 45 | 46 | while (stackIndex > -1) { 47 | while ( 48 | (i = tokenArrIndexStack[stackIndex]++) < tokenArrSizeStack[stackIndex] 49 | ) { 50 | let content: TokenStream 51 | let types = typeArrStack[stackIndex] 52 | const tokenArr = tokenArrStack[stackIndex] 53 | const token = tokenArr[i] 54 | 55 | // Determine content and append type to types if necessary 56 | if (typeof token === "string") { 57 | types = stackIndex > 0 ? types : ["plain"] 58 | content = token 59 | } else { 60 | types = appendTypes(types, token.type) 61 | 62 | if (token.alias) { 63 | types = appendTypes(types, token.alias) 64 | } 65 | 66 | content = token.content 67 | } 68 | 69 | // If token.content is an array, increase the stack depth and repeat this while-loop 70 | if (typeof content !== "string") { 71 | stackIndex++ 72 | typeArrStack.push(types) 73 | tokenArrStack.push(content as PrismToken[]) 74 | tokenArrIndexStack.push(0) 75 | tokenArrSizeStack.push(content.length) 76 | continue 77 | } 78 | 79 | // Split by newlines 80 | const splitByNewlines = content.split(newlineRe) 81 | const newlineCount = splitByNewlines.length 82 | currentLine.push({ 83 | types, 84 | content: splitByNewlines[0], 85 | }) 86 | 87 | // Create a new line for each string on a new line 88 | for (let i = 1; i < newlineCount; i++) { 89 | normalizeEmptyLines(currentLine) 90 | acc.push((currentLine = [])) 91 | currentLine.push({ 92 | types, 93 | content: splitByNewlines[i], 94 | }) 95 | } 96 | } 97 | 98 | // Decreate the stack depth 99 | stackIndex-- 100 | typeArrStack.pop() 101 | tokenArrStack.pop() 102 | tokenArrIndexStack.pop() 103 | tokenArrSizeStack.pop() 104 | } 105 | 106 | normalizeEmptyLines(currentLine) 107 | return acc 108 | } 109 | 110 | export default normalizeTokens 111 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/src/utils/themeToDict.ts: -------------------------------------------------------------------------------- 1 | import type { Language, StyleObj, PrismTheme } from "../types" 2 | export type ThemeDict = { 3 | root: StyleObj 4 | plain: StyleObj 5 | [type: string]: StyleObj 6 | } 7 | 8 | const themeToDict = (theme: PrismTheme, language: Language): ThemeDict => { 9 | const { plain } = theme 10 | const themeDict = theme.styles.reduce304 |367 |308 | 311 | (function someDemo() { 312 | 313 |314 |318 | 321 | var test = "Hello World!"; 322 | 323 |324 |328 | 331 | console.log(test); 332 | 333 |334 |338 | 341 | })(); 342 | 343 |344 |348 | 352 | 353 | 354 | 355 |356 |360 | 363 | return () => <App />; 364 | 365 |366 |((acc, themeEntry) => { 11 | const { languages, style } = themeEntry 12 | 13 | if (languages && !languages.includes(language)) { 14 | return acc 15 | } 16 | 17 | themeEntry.types.forEach(type => { 18 | const accStyle: StyleObj = { ...acc[type], ...style } 19 | acc[type] = accStyle 20 | }) 21 | return acc 22 | }, {} as ThemeDict) 23 | 24 | themeDict.root = plain as StyleObj 25 | themeDict.plain = { ...plain, backgroundColor: undefined } as StyleObj 26 | return themeDict 27 | } 28 | 29 | export default themeToDict 30 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup" 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | sourcemap: true, 6 | clean: true, 7 | dts: true, 8 | format: ["cjs", "esm"], 9 | target: "es6", 10 | }) 11 | -------------------------------------------------------------------------------- /packages/prism-react-renderer/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config" 2 | import react from "@vitejs/plugin-react" 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | test: { 7 | globals: true, 8 | environment: "happy-dom", 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /patches/prismjs@1.29.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/prism.js b/prism.js 2 | index 4b9c9a0f8a0356a2dfbf4446bbd4bfe01498ce91..9fd04334b4dc420aeaf82905859616774346a3f7 100644 3 | --- a/prism.js 4 | +++ b/prism.js 5 | @@ -5,14 +5,6 @@ 6 | 7 | /// 8 | 9 | -var _self = (typeof window !== 'undefined') 10 | - ? window // if in browser 11 | - : ( 12 | - (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) 13 | - ? self // if in worker 14 | - : {} // if in node js 15 | - ); 16 | - 17 | /** 18 | * Prism: Lightweight, robust, elegant syntax highlighting 19 | * 20 | @@ -21,7 +13,7 @@ var _self = (typeof window !== 'undefined') 21 | * @namespace 22 | * @public 23 | */ 24 | -var Prism = (function (_self) { 25 | +var Prism = (function () { 26 | 27 | // Private helper vars 28 | var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i; 29 | @@ -32,51 +24,6 @@ var Prism = (function (_self) { 30 | 31 | 32 | var _ = { 33 | - /** 34 | - * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the 35 | - * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load 36 | - * additional languages or plugins yourself. 37 | - * 38 | - * By setting this value to `true`, Prism will not automatically highlight all code elements on the page. 39 | - * 40 | - * You obviously have to change this value before the automatic highlighting started. To do this, you can add an 41 | - * empty Prism object into the global scope before loading the Prism script like this: 42 | - * 43 | - * ```js 44 | - * window.Prism = window.Prism || {}; 45 | - * Prism.manual = true; 46 | - * // add a new