├── .eslintignore ├── .eslintrc.json ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.json ├── .vscode └── launch.json ├── CHAGNELOG.md ├── LICENSE ├── README.md ├── demo ├── commonjs │ ├── package.json │ └── run.js └── es6 │ └── run.js ├── image ├── code_blocks.png └── named_code_blocks.png ├── jest.config.ts ├── package-lock.json ├── package.json ├── src └── index.ts ├── test ├── fixture │ ├── hljs10 │ │ ├── codeblock.txt │ │ ├── codeblock_highlightjs.txt │ │ ├── codeblock_highlightjs_full_override.txt │ │ └── codeblock_inline_css.txt │ └── hljs11 │ │ ├── codeblock.txt │ │ ├── codeblock_highlightjs.txt │ │ ├── codeblock_highlightjs_full_override.txt │ │ └── codeblock_inline_css.txt └── index.test.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | demo 3 | jest.config.ts 4 | test -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "jest": true, 7 | "browser": true 8 | }, 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "parser": "@typescript-eslint/parser", 15 | "parserOptions": { 16 | "ecmaVersion": 2020, 17 | "sourceType": "module", 18 | "project": "./tsconfig.json" 19 | }, 20 | "plugins": ["@typescript-eslint"] 21 | } 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | groups: 8 | dependencies: 9 | patterns: 10 | - "*" 11 | - package-ecosystem: "npm" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | groups: 16 | dependencies: 17 | patterns: 18 | - "*" 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: 7 | - "v[0-9]+.[0-9]+.[0-9]+*" 8 | pull_request: 9 | branches: [master] 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | node-version: [18.x, 20.x, 22.x] 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm ci 24 | - run: npm run build --if-present 25 | - run: npm run test:all 26 | - name: Upload coverage reports to Codecov 27 | if: matrix.node-version == '20.x' && github.repository == 'tsutsu3/markdown-it-named-code-blocks' 28 | uses: codecov/codecov-action@v5 29 | env: 30 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 31 | fail_ci_if_error: true 32 | 33 | publish: 34 | needs: [test] 35 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | # Setup .npmrc file to publish to npm 40 | - uses: actions/setup-node@v4 41 | with: 42 | node-version: "18.x" 43 | registry-url: "https://registry.npmjs.org" 44 | - run: npm ci 45 | - run: npm run build 46 | - run: npm publish 47 | env: 48 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .vscode 4 | demo 5 | image 6 | node_modules 7 | test 8 | tsconfig.json 9 | jest.config.ts 10 | package-lock.json 11 | CHAGNELOG.md 12 | .eslintignore 13 | .eslintrc.json 14 | .prettierignore 15 | .prettierrc.json -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | node_modules 3 | package.json 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"] 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /CHAGNELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.0.6 (2024/11/10) 4 | 5 | - Update dev packages version 6 | 7 | ## 1.0.5 (2023/06/18) 8 | 9 | - Update 3rd libs version 10 | 11 | ## 1.0.4 (2023/12/24) 12 | 13 | - Fix missing dist in npm package 14 | 15 | ## 1.0.3 (2023/12/24) 16 | 17 | - Add npmignore 18 | 19 | ## 1.0.2 (2023/12/23) 20 | 21 | - Migrated implementation to typescript (#31) 22 | 23 | ## 0.2.0 (2023/06/17) 24 | 25 | - Update dev dependencies version (#9, #10, #12, #13) 26 | - Update dependencies version (#11) 27 | - Add dependabot (#7, #8) 28 | - Add type support (#1) [@KyleSmith0905](https://github.com/KyleSmith0905) 29 | - Add CI (#5, #6) 30 | - Fix npm audit (#4) 31 | - Change to use const instead of var in samples (#2) [@KyleSmith0905](https://github.com/KyleSmith0905) 32 | - Fix typos in README.md (#3) [@KyleSmith0905](https://github.com/KyleSmith0905) 33 | 34 | ## 0.1.0 (2020/10/16) 35 | 36 | - First release 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 tsutsu3 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 | # markdown-it-named-code-blocks 2 | 3 | A [markdown-it](https://github.com/markdown-it/markdown-it#readme) plugin to create named code blocks. 4 | 5 | [![CI](https://github.com/tsutsu3/markdown-it-named-code-blocks/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/tsutsu3/markdown-it-named-code-blocks/actions/workflows/ci.yml) 6 | [![NPM version](https://img.shields.io/npm/v/markdown-it-named-code-blocks.svg?style=flat)](https://www.npmjs.org/package/markdown-it-named-code-blocks) 7 | [![Code Climate](https://codeclimate.com/github/tsutsu3/markdown-it-named-code-blocks/badges/gpa.svg)](https://codeclimate.com/github/tsutsu3/markdown-it-named-code-blocks) 8 | [![codecov](https://codecov.io/gh/tsutsu3/markdown-it-named-code-blocks/graph/badge.svg?token=RCS2WCEGWP)](https://codecov.io/gh/tsutsu3/markdown-it-named-code-blocks) 9 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/tsutsu3/markdown-it-named-code-blocks/LICENSE) 10 | 11 | ## 🧐 About 12 | 13 | With this plugin you can create named code blocks like: 14 | 15 | ````txt 16 | ```js:hello.js 17 | console.log("Hello World!") 18 | ``` 19 | ```` 20 | 21 | Rendered as: 22 | 23 | ```html 24 |
console.log("Hello World!");
 25 | 
hello.js
26 | ``` 27 | 28 | After applying the css, it looks like this: 29 | 30 | ![Rendered markdown](image/named_code_blocks.png) 31 | 32 | ## 🏁 Getting Started 33 | 34 | ### Prerequisites 35 | 36 | - [Node.js](https://nodejs.org/) 37 | 38 | ### Installing 39 | 40 | ```bash 41 | npm install markdown-it-named-code-blocks 42 | ``` 43 | 44 | ## 🎈 Usage 45 | 46 | Use this same as a normal markdown-it plugin: 47 | 48 | ```js 49 | const md = require("markdown-it"); 50 | const hljs = require("highlight.js"); 51 | const namedCodeBlocks = require("."); 52 | 53 | const parser = md({ 54 | highlight: function (str, lang) { 55 | if (lang && hljs.getLanguage(lang)) { 56 | return ( 57 | '
' +
 58 |         hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
 59 |         "
" 60 | ); 61 | } 62 | 63 | return ( 64 | '
' + md.utils.escapeHtml(str) + "
" 65 | ); 66 | } 67 | }).use(namedCodeBlocks); 68 | 69 | const str = '```js:hello.js\nconsole.log("Hello World!");\n```'; 70 | 71 | const result = parser.render(str); 72 | 73 | console.log(result); 74 | ``` 75 | 76 | Output: 77 | 78 | ```html 79 |
console.log("Hello World!");
 80 | 
hello.js
81 | ``` 82 | 83 | Apply CSS like this: 84 | 85 | ```css 86 | .named-fence-block { 87 | position: relative; 88 | padding-top: 2em; 89 | } 90 | 91 | .named-fence-filename { 92 | position: absolute; 93 | top: 0; 94 | left: 0; 95 | padding: 0 4px; 96 | font-weight: bold; 97 | color: #000000; 98 | background: #c0c0c0; 99 | opacity: 0.6; 100 | } 101 | ``` 102 | 103 | Rendered: 104 | 105 | ![Rendered markdown](image/named_code_blocks.png) 106 | 107 | If you want to enable inline CSS: 108 | 109 | ```js 110 | const parser = md().use(namedCodeBlocks, {isEnableInlineCss: true}); 111 | ``` 112 | 113 | Output: 114 | 115 | ```html 116 |
console.log("Hello World!");
117 | 
hello.js
118 | ``` 119 | 120 | ## 🎉 License 121 | 122 | Distributed under the `MIT` License. See [LICENSE](https://github.com/tsutsu3/markdown-it-named-code-blocks/blob/master/LICENSE) for more information. 123 | -------------------------------------------------------------------------------- /demo/commonjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } -------------------------------------------------------------------------------- /demo/commonjs/run.js: -------------------------------------------------------------------------------- 1 | import MarkdownIt from "markdown-it"; 2 | import namedCodeBlocks from "../../dist/index.js"; 3 | 4 | const parser = new MarkdownIt().use(namedCodeBlocks); 5 | 6 | const str = '```js:hello.js\nconsole.log("Hello World!);```'; 7 | 8 | const result = parser.render(str); 9 | console.log(result); 10 | -------------------------------------------------------------------------------- /demo/es6/run.js: -------------------------------------------------------------------------------- 1 | const md = require("markdown-it"); 2 | const namedCodeBlocks = require("../../dist"); 3 | 4 | const parser = md().use(namedCodeBlocks); 5 | 6 | const str = '```js:hello.js\nconsole.log("Hello World!);```'; 7 | 8 | const result = parser.render(str); 9 | console.log(result); 10 | -------------------------------------------------------------------------------- /image/code_blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsutsu3/markdown-it-named-code-blocks/b55572ca1c2e63329411f9582138e1d069f00730/image/code_blocks.png -------------------------------------------------------------------------------- /image/named_code_blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsutsu3/markdown-it-named-code-blocks/b55572ca1c2e63329411f9582138e1d069f00730/image/named_code_blocks.png -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "@jest/types"; 2 | 3 | const config: Config.InitialOptions = { 4 | preset: "ts-jest", 5 | testEnvironment: "node", 6 | moduleFileExtensions: ["ts", "js", "json", "node"], 7 | testMatch: ["**/test/**/*.ts"], 8 | transform: { 9 | "^.+\\.ts$": "ts-jest" 10 | }, 11 | moduleNameMapper: { 12 | "^@/(.*)$": "./src/$1" 13 | }, 14 | testPathIgnorePatterns: ["/node_modules/", "/dist/"] 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-it-named-code-blocks", 3 | "version": "1.0.6", 4 | "description": "A markdown-it plugin to create named code blocks.", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "lint": "eslint . --ext .ts", 9 | "format": "prettier --write \"**/*.ts\"", 10 | "install-hljs10": "npm install highlight.js@^10.7.3 --no-save", 11 | "install-hljs11": "npm install highlight.js@^11.0.0 --no-save", 12 | "test-hljs10": "npm run install-hljs10 && jest --coverage", 13 | "test-hljs11": "npm run install-hljs11 && jest --coverage", 14 | "test:all": "npm run test-hljs10 && npm run test-hljs11", 15 | "test": "jest --coverage", 16 | "build": "tsc && npm run minify", 17 | "minify": "terser dist/index.js -o dist/index.min.js --compress --mangle", 18 | "clean": "npm run clean:os", 19 | "clean:os": "npm run clean:windows || npm run clean:linux", 20 | "clean:windows": "rimraf dist", 21 | "clean:linux": "rm -rf dist || true" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/tsutsu3/markdown-it-named-code-blocks.git" 26 | }, 27 | "keywords": [ 28 | "markdown", 29 | "markdown-it", 30 | "markdown-it-plugin", 31 | "code-blocks", 32 | "fence" 33 | ], 34 | "author": "tsutsu3", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/tsutsu3/markdown-it-named-code-blocks/issues" 38 | }, 39 | "homepage": "https://github.com/tsutsu3/markdown-it-named-code-blocks#readme", 40 | "devDependencies": { 41 | "@types/jest": "^29.5.11", 42 | "@types/markdown-it": "^14.1.2", 43 | "@typescript-eslint/eslint-plugin": "^8.13.0", 44 | "@typescript-eslint/parser": "^8.13.0", 45 | "eslint": "^9.14.0", 46 | "eslint-config-prettier": "^10.0.1", 47 | "highlight.js": "^11.10.0", 48 | "jest": "^29.7.0", 49 | "markdown-it": "^14.0.0", 50 | "markdown-it-testgen": "^0.1.6", 51 | "path": "^0.12.7", 52 | "prettier": "^3.1.1", 53 | "terser": "^5.26.0", 54 | "ts-jest": "^29.1.1", 55 | "ts-node": "^10.9.2", 56 | "typescript": "^5.3.3" 57 | }, 58 | "dependencies": { 59 | "node-html-parser": "^7.0.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { parse, HTMLElement } from "node-html-parser"; 4 | import { Token, Options, PluginWithOptions } from "markdown-it"; 5 | import Renderer from "markdown-it/lib/renderer.mjs"; 6 | 7 | type ParsedFenceInfo = { 8 | langName: string; 9 | fileName: string; 10 | langAttrs: string; 11 | }; 12 | 13 | type CssOptions = { 14 | isEnableInlineCss: boolean; 15 | }; 16 | 17 | type Env = { 18 | [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any 19 | }; 20 | 21 | const fencBlockName = "named-fence-block"; 22 | const fenceFileName = "named-fence-filename"; 23 | const defaultStyleOptions = { 24 | mincbBlock: "position: relative; padding-top: 2em;", 25 | mincbName: 26 | "position: absolute; top: 0; left: 0; padding: 0 4px; " + 27 | "font-weight: bold; color: #000000; background: #c0c0c0; opacity: .6;" 28 | }; 29 | 30 | const namedCodeBlocks: PluginWithOptions = (md, options) => { 31 | const isEnableInlineCss = options?.isEnableInlineCss ?? false; 32 | 33 | const defaultRender = md.renderer.rules?.fence; 34 | if (!defaultRender) { 35 | throw new Error("defaultRender is undefined"); 36 | } 37 | 38 | md.renderer.rules.fence = ( 39 | tokens: Token[], 40 | idx: number, 41 | options: Options, 42 | env: Env, 43 | self: Renderer 44 | ) => { 45 | const token = tokens[idx]; 46 | const orgInfo = token.info; 47 | const info = token.info ? String(token.info).trim() : ""; 48 | let parsedFenceInfo: ParsedFenceInfo; 49 | 50 | if (info) { 51 | parsedFenceInfo = parseInfo(info); 52 | updateTokenInfo(token, parsedFenceInfo); 53 | } else { 54 | parsedFenceInfo = { langName: "", fileName: "", langAttrs: "" }; 55 | } 56 | 57 | const rootElement = parse(defaultRender(tokens, idx, options, env, self)); 58 | token.info = orgInfo; 59 | 60 | updateElement(rootElement, parsedFenceInfo, isEnableInlineCss); 61 | 62 | return rootElement.toString(); 63 | }; 64 | }; 65 | 66 | function updateElement( 67 | element: HTMLElement, 68 | parsedFenceInfo: ParsedFenceInfo, 69 | isEnableInlineCss: boolean 70 | ): void { 71 | if (parsedFenceInfo.fileName && parsedFenceInfo.langName) { 72 | addNamedFenceBlockAttr(element, isEnableInlineCss); 73 | 74 | addNamedFenceFilenameAtrr( 75 | element, 76 | parsedFenceInfo.fileName, 77 | isEnableInlineCss 78 | ); 79 | } 80 | } 81 | 82 | function addNamedFenceBlockAttr( 83 | element: HTMLElement, 84 | isEnableInlineCss: boolean 85 | ): void { 86 | if (element.firstChild instanceof HTMLElement) { 87 | const existClass = element.firstChild.getAttribute("class"); 88 | 89 | if (element.firstChild.getAttribute("class")) { 90 | element.firstChild.setAttribute( 91 | "class", 92 | `${existClass} ${fencBlockName}` 93 | ); 94 | } else { 95 | element.firstChild.setAttribute("class", fencBlockName); 96 | } 97 | 98 | if (isEnableInlineCss) { 99 | element.firstChild.setAttribute("style", defaultStyleOptions.mincbBlock); 100 | } 101 | } 102 | } 103 | 104 | function addNamedFenceFilenameAtrr( 105 | element: HTMLElement, 106 | fileName: string, 107 | isEnableInlineCss: boolean 108 | ): void { 109 | const node = parse(`
${fileName}
`); 110 | 111 | const firstChild = node.firstChild; 112 | 113 | if (firstChild instanceof HTMLElement && isEnableInlineCss) { 114 | firstChild.setAttribute("style", defaultStyleOptions.mincbName); 115 | } 116 | 117 | if (element.firstChild instanceof HTMLElement) { 118 | element.firstChild.appendChild(node); 119 | } 120 | } 121 | 122 | function parseInfo(info: string): ParsedFenceInfo { 123 | // https://regex101.com/r/PacPRb/4 124 | const data = { langName: "", fileName: "", langAttrs: "" }; 125 | const arr = info.split(/(\s+)/g); 126 | const match = arr[0].match(/^([^:\n]+)?(:([^:\n]*))?([^:\n]*)?$/); 127 | const langAttrs = arr.slice(2).join(""); 128 | if (match) { 129 | data.langName = match[1] || ""; 130 | data.fileName = match[3] || ""; 131 | data.langAttrs = langAttrs; 132 | return data; 133 | } 134 | 135 | return data; 136 | } 137 | 138 | function updateTokenInfo(token: Token, parsedFenceInfo: ParsedFenceInfo): void { 139 | if (parsedFenceInfo.langName) { 140 | token.info = parsedFenceInfo.langName + " " + parsedFenceInfo.langAttrs; 141 | } else { 142 | token.info = ""; 143 | } 144 | } 145 | 146 | // export default namedCodeBlocks; 147 | export = namedCodeBlocks; 148 | -------------------------------------------------------------------------------- /test/fixture/hljs10/codeblock.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs10/codeblock_highlightjs.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs10/codeblock_highlightjs_full_override.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs10/codeblock_inline_css.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs11/codeblock.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs11/codeblock_highlightjs.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs11/codeblock_highlightjs_full_override.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/fixture/hljs11/codeblock_inline_css.txt: -------------------------------------------------------------------------------- 1 | none 2 | . 3 | ``` 4 | console.log("Hello World!"); 5 | ``` 6 | . 7 |
console.log("Hello World!");
 8 | 
9 | . 10 | 11 | js 12 | . 13 | ```js 14 | console.log("Hello World!"); 15 | ``` 16 | . 17 |
console.log("Hello World!");
18 | 
19 | . 20 | 21 | js: 22 | . 23 | ```js: 24 | console.log("Hello World!"); 25 | ``` 26 | . 27 |
console.log("Hello World!");
28 | 
29 | . 30 | 31 | js:hello.js (named code blocks) 32 | . 33 | ```js:hello.js 34 | console.log("Hello World!"); 35 | ``` 36 | . 37 |
console.log("Hello World!");
38 | 
hello.js
39 | . 40 | 41 | :hello.js 42 | . 43 | ```:hello.js 44 | console.log("Hello World!"); 45 | ``` 46 | . 47 |
console.log("Hello World!");
48 | 
49 | . 50 | 51 | hello.js 52 | . 53 | ```hello.js 54 | console.log("Hello World!"); 55 | ``` 56 | . 57 |
console.log("Hello World!");
58 | 
59 | . 60 | 61 | js:hello.js:second.js 62 | . 63 | ```js:hello.js:second.js 64 | console.log("Hello World!"); 65 | ``` 66 | . 67 |
console.log("Hello World!");
68 | 
69 | . 70 | 71 | hello.js:second.js (named code blocks, but language is incorrect) 72 | . 73 | ```hello.js:second.js 74 | console.log("Hello World!"); 75 | ``` 76 | . 77 |
console.log("Hello World!");
78 | 
second.js
79 | . 80 | 81 | js langAttr 82 | . 83 | ```js langAttr 84 | console.log("Hello World!"); 85 | ``` 86 | . 87 |
console.log("Hello World!");
88 | 
89 | . 90 | 91 | js:hello.js langAttr 92 | . 93 | ```js:hello.js langAttr 94 | console.log("Hello World!"); 95 | ``` 96 | . 97 |
console.log("Hello World!");
98 | 
hello.js
99 | . -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import path from "path"; 4 | 5 | const generate = require("markdown-it-testgen"); // eslint-disable-line @typescript-eslint/no-var-requires 6 | import hljs from "highlight.js"; 7 | import MarkdownIt from "markdown-it"; 8 | import namedCodeBlocks from "../src"; 9 | 10 | const hljsVersion = require("highlight.js/package.json").version; 11 | const fixturePath = hljsVersion.startsWith("10") 12 | ? "fixture/hljs10" 13 | : "fixture/hljs11"; 14 | 15 | console.log(`Running tests with highlight.js version: ${hljsVersion}`); 16 | 17 | describe("markdown-it-named-code-blocks", () => { 18 | const md = new MarkdownIt().use(namedCodeBlocks); 19 | 20 | generate( 21 | path.join(__dirname, `./${fixturePath}/codeblock.txt`), 22 | { header: true }, 23 | md 24 | ); 25 | }); 26 | 27 | describe("markdown-it-named-code-blocks-default-highlight", () => { 28 | const md = new MarkdownIt({ 29 | highlight: function (str: string, lang: string) { 30 | if (lang && hljs.getLanguage(lang)) { 31 | return hljs.highlight(str, { language: lang }).value; 32 | } 33 | return ""; 34 | } 35 | }).use(namedCodeBlocks); 36 | 37 | generate( 38 | path.join(__dirname, `./${fixturePath}/codeblock_highlightjs.txt`), 39 | { header: true }, 40 | md 41 | ); 42 | }); 43 | 44 | describe("markdown-it-named-code-blocks-override-highlight", () => { 45 | const md: MarkdownIt = new MarkdownIt({ 46 | highlight: function (str: string, lang: string) { 47 | if (lang && hljs.getLanguage(lang)) { 48 | return ( 49 | '
' +
50 |           hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
51 |           "
" 52 | ); 53 | } 54 | 55 | return ( 56 | '
' + md.utils.escapeHtml(str) + "
" 57 | ); 58 | } 59 | }).use(namedCodeBlocks); 60 | 61 | generate( 62 | path.join( 63 | __dirname, 64 | `./${fixturePath}/codeblock_highlightjs_full_override.txt` 65 | ), 66 | { header: true }, 67 | md 68 | ); 69 | }); 70 | 71 | describe("markdown-it-named-code-blocks-inline-css", () => { 72 | const md = new MarkdownIt().use(namedCodeBlocks, { 73 | isEnableInlineCss: true 74 | }); 75 | 76 | generate( 77 | path.join(__dirname, `./${fixturePath}/codeblock_inline_css.txt`), 78 | { header: true }, 79 | md 80 | ); 81 | }); 82 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "declaration": true 10 | }, 11 | "include": ["src/**/*.ts"], 12 | "exclude": ["node_modules", "dist", "demo"] 13 | } 14 | --------------------------------------------------------------------------------