├── .husky ├── pre-commit └── commit-msg ├── src ├── cjs.js ├── options.json └── index.js ├── .prettierignore ├── .gitattributes ├── test ├── fixtures │ ├── simple.js │ ├── multipleArgs.js │ ├── multipleArgs.md │ └── simple.md ├── helpers │ ├── getErrors.mjs │ ├── getWarnings.mjs │ ├── getExecutedCode.mjs │ ├── testLoader.cjs │ ├── compile.mjs │ ├── readAssets.mjs │ ├── normalizeErrors.mjs │ ├── index.mjs │ ├── execute.mjs │ ├── readAsset.mjs │ └── getCompiler.mjs ├── cjs.test.mjs ├── validate-options.test.mjs ├── loader.test.mjs └── __snapshots__ │ ├── validate-options.test.mjs.snap │ └── loader.test.mjs.snap ├── jest.config.js ├── lint-staged.config.js ├── commitlint.config.js ├── eslint.config.mjs ├── .editorconfig ├── .gitignore ├── .github └── workflows │ ├── dependency-review.yml │ └── nodejs.yml ├── babel.config.js ├── .cspell.json ├── LICENSE ├── package.json ├── CHANGELOG.md └── README.md /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | commitlint --edit $1 2 | -------------------------------------------------------------------------------- /src/cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./index").default; 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /dist 3 | /node_modules 4 | /test/fixtures 5 | CHANGELOG.md -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | bin/* eol=lf 3 | yarn.lock -diff 4 | package-lock.json -diff -------------------------------------------------------------------------------- /test/fixtures/simple.js: -------------------------------------------------------------------------------- 1 | import md from './simple.md'; 2 | 3 | __export__ = md; 4 | 5 | export default md; 6 | -------------------------------------------------------------------------------- /test/fixtures/multipleArgs.js: -------------------------------------------------------------------------------- 1 | import md from './multipleArgs.md'; 2 | 3 | __export__ = md; 4 | 5 | export default md; 6 | -------------------------------------------------------------------------------- /test/helpers/getErrors.mjs: -------------------------------------------------------------------------------- 1 | import normalizeErrors from "./normalizeErrors.mjs"; 2 | 3 | export default (stats) => normalizeErrors(stats.compilation.errors.sort()); 4 | -------------------------------------------------------------------------------- /test/helpers/getWarnings.mjs: -------------------------------------------------------------------------------- 1 | import normalizeErrors from "./normalizeErrors.mjs"; 2 | 3 | export default (stats) => normalizeErrors(stats.compilation.warnings.sort()); 4 | -------------------------------------------------------------------------------- /test/helpers/getExecutedCode.mjs: -------------------------------------------------------------------------------- 1 | import { execute, readAsset } from "./index.mjs"; 2 | 3 | export default (asset, compiler, stats) => 4 | execute(readAsset(asset, compiler, stats)); 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testMatch: [ 3 | "**/__tests__/**/*.(m|c)?[jt]s?(x)", 4 | "**/?(*.)+(spec|test).(m|c)?[jt]s?(x)", 5 | ], 6 | runner: "jest-light-runner", 7 | }; 8 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "*": [ 3 | "prettier --cache --write --ignore-unknown", 4 | "cspell --cache --no-must-find-files", 5 | ], 6 | "*.js": ["eslint --cache --fix"], 7 | }; 8 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | rules: { 4 | "header-max-length": [0], 5 | "body-max-line-length": [0], 6 | "footer-max-line-length": [0], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "eslint/config"; 2 | import configs from "eslint-config-webpack/configs.js"; 3 | 4 | export default defineConfig([ 5 | { 6 | extends: [configs["recommended-dirty"]], 7 | }, 8 | ]); 9 | -------------------------------------------------------------------------------- /test/helpers/testLoader.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function testLoader(content) { 4 | const result = { md: content }; 5 | 6 | return `export default ${JSON.stringify(result)}`; 7 | } 8 | 9 | module.exports = testLoader; 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /test/helpers/compile.mjs: -------------------------------------------------------------------------------- 1 | export default (compiler) => 2 | new Promise((resolve, reject) => { 3 | compiler.run((error, stats) => { 4 | if (error) { 5 | return reject(error); 6 | } 7 | 8 | return resolve(stats); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | .vscode 3 | logs 4 | *.log 5 | npm-debug.log* 6 | .cspellcache 7 | .eslintcache 8 | /coverage 9 | /dist 10 | /local 11 | /reports 12 | /node_modules 13 | .DS_Store 14 | Thumbs.db 15 | .idea 16 | *.iml 17 | *.sublime-project 18 | *.sublime-workspace 19 | -------------------------------------------------------------------------------- /test/fixtures/multipleArgs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Just hack'n 3 | description: Nothing to see here 4 | meta: some meta data 5 | --- 6 | 7 | # My header 8 | 9 | Test content. 10 | 11 | `remark-bookmarks` is on [npm][]! 12 | 13 | Example list: 14 | - item 1 15 | - item 2 16 | - item 3 17 | -------------------------------------------------------------------------------- /test/helpers/readAssets.mjs: -------------------------------------------------------------------------------- 1 | import readAsset from "./readAsset.mjs"; 2 | 3 | export default function readAssets(compiler, stats) { 4 | const assets = {}; 5 | 6 | for (const asset of Object.keys(stats.compilation.assets)) { 7 | assets[asset] = readAsset(asset, compiler, stats); 8 | } 9 | 10 | return assets; 11 | } 12 | -------------------------------------------------------------------------------- /test/cjs.test.mjs: -------------------------------------------------------------------------------- 1 | import { createRequire } from "node:module"; 2 | 3 | const require = createRequire(import.meta.url); 4 | 5 | describe("CJS", () => { 6 | it("should export loader", () => { 7 | const src = require("../dist/index.js").default; 8 | const cjs = require("../dist/cjs.js"); 9 | 10 | expect(cjs).toEqual(src); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: "Dependency Review" 2 | on: [pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependency-review: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: "Checkout Repository" 12 | uses: actions/checkout@v5 13 | - name: "Dependency Review" 14 | uses: actions/dependency-review-action@v4 15 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const MIN_BABEL_VERSION = 7; 2 | 3 | module.exports = (api) => { 4 | api.assertVersion(MIN_BABEL_VERSION); 5 | api.cache(true); 6 | 7 | return { 8 | presets: [ 9 | [ 10 | "@babel/preset-env", 11 | { 12 | exclude: ["proposal-dynamic-import"], 13 | targets: { 14 | node: "18.12.0", 15 | }, 16 | }, 17 | ], 18 | ], 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /test/helpers/normalizeErrors.mjs: -------------------------------------------------------------------------------- 1 | function removeCWD(str) { 2 | const isWin = process.platform === "win32"; 3 | let cwd = process.cwd(); 4 | 5 | if (isWin) { 6 | str = str.replaceAll("\\", "/"); 7 | 8 | cwd = cwd.replaceAll("\\", "/"); 9 | } 10 | 11 | return str 12 | .replace(/\(from .*?\)/, "(from `replaced original path`)") 13 | .replaceAll(new RegExp(cwd, "g"), ""); 14 | } 15 | 16 | export default (errors) => 17 | errors.map((error) => 18 | removeCWD(error.toString().split("\n").slice(0, 2).join("\n")), 19 | ); 20 | -------------------------------------------------------------------------------- /test/helpers/index.mjs: -------------------------------------------------------------------------------- 1 | export { default as compile } from "./compile.mjs"; 2 | export { default as normalizeErrors } from "./normalizeErrors.mjs"; 3 | export { default as execute } from "./execute.mjs"; 4 | export { default as getErrors } from "./getErrors.mjs"; 5 | export { default as getCompiler } from "./getCompiler.mjs"; 6 | export { default as getWarnings } from "./getWarnings.mjs"; 7 | export { default as getExecutedCode } from "./getExecutedCode.mjs"; 8 | export { default as readsAssets } from "./readAssets.mjs"; 9 | export { default as readAsset } from "./readAsset.mjs"; 10 | -------------------------------------------------------------------------------- /test/helpers/execute.mjs: -------------------------------------------------------------------------------- 1 | import Module from "node:module"; 2 | import path from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default (code) => { 8 | const resource = "test.js"; 9 | const module = new Module(resource); 10 | 11 | module.paths = Module._nodeModulePaths( 12 | path.resolve(__dirname, "../fixtures"), 13 | ); 14 | module.filename = resource; 15 | 16 | module._compile( 17 | `let __export__;${code};module.exports = __export__;`, 18 | resource, 19 | ); 20 | 21 | return module.exports; 22 | }; 23 | -------------------------------------------------------------------------------- /test/helpers/readAsset.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | export default (asset, compiler, stats) => { 4 | const usedFs = compiler.outputFileSystem; 5 | const outputPath = stats.compilation.outputOptions.path; 6 | 7 | let data = ""; 8 | let targetFile = asset; 9 | 10 | const queryStringIdx = targetFile.indexOf("?"); 11 | 12 | if (queryStringIdx >= 0) { 13 | targetFile = targetFile.slice(0, queryStringIdx); 14 | } 15 | 16 | try { 17 | data = usedFs.readFileSync(path.join(outputPath, targetFile)).toString(); 18 | } catch (error) { 19 | data = error.toString(); 20 | } 21 | 22 | return data; 23 | }; 24 | -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en,en-gb", 4 | "words": [ 5 | "commitlint", 6 | "Frontmatter", 7 | "frontmatter", 8 | "marksy", 9 | "vfile", 10 | "babelfish", 11 | "Strikethrough", 12 | "tristique", 13 | "volutpat", 14 | "Facilisis", 15 | "pretium", 16 | "nisl", 17 | "aliquet", 18 | "Nulla", 19 | "aliquam", 20 | "velit", 21 | "amet", 22 | "Consectetur", 23 | "adipiscing", 24 | "elit", 25 | "elit", 26 | "Autoconverted", 27 | "Stormtroopocat", 28 | "Dojocat", 29 | "Emojies", 30 | "molestie", 31 | "Lyyy", 32 | "memfs", 33 | "eslintcache", 34 | "cspellcache" 35 | ], 36 | "ignorePaths": [ 37 | "CHANGELOG.md", 38 | "package.json", 39 | "dist/**", 40 | "**/__snapshots__/**", 41 | "package-lock.json", 42 | "node_modules", 43 | "coverage", 44 | "*.log" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/helpers/getCompiler.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | import { fileURLToPath } from "node:url"; 4 | import { Volume, createFsFromVolume } from "memfs"; 5 | import webpack from "webpack"; 6 | 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | 9 | export default (fixture, loaderOptions = {}, config = {}) => { 10 | const fullConfig = { 11 | mode: "development", 12 | devtool: config.devtool || false, 13 | context: path.resolve(__dirname, "../fixtures"), 14 | entry: path.resolve(__dirname, "../fixtures", fixture), 15 | output: { 16 | path: path.resolve(__dirname, "../outputs"), 17 | filename: "[name].bundle.js", 18 | chunkFilename: "[name].chunk.js", 19 | library: "remarkLoaderExport", 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.md$/i, 25 | rules: [ 26 | { 27 | loader: path.resolve(__dirname, "./testLoader.cjs"), 28 | }, 29 | { 30 | loader: path.resolve(__dirname, "../../dist/cjs.js"), 31 | options: loaderOptions || {}, 32 | }, 33 | ], 34 | }, 35 | ], 36 | }, 37 | plugins: [], 38 | ...config, 39 | }; 40 | 41 | const compiler = webpack(fullConfig); 42 | 43 | if (!config.outputFileSystem) { 44 | compiler.outputFileSystem = createFsFromVolume(new Volume()); 45 | } 46 | 47 | return compiler; 48 | }; 49 | -------------------------------------------------------------------------------- /src/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Remark Loader options", 3 | "type": "object", 4 | "additionalProperties": false, 5 | "properties": { 6 | "remarkOptions": { 7 | "type": "object", 8 | "description": "Options for remark.", 9 | "link": "https://github.com/webpack/remark-loader#remarkOptions", 10 | "additionalProperties": false, 11 | "properties": { 12 | "plugins": { 13 | "type": "array", 14 | "description": "Allows to connect remark plugins.", 15 | "link": "https://github.com/webpack/remark-loader#plugins" 16 | }, 17 | "settings": { 18 | "type": "object", 19 | "description": "Pass remark-stringify options and remark-parse options options to the remark.", 20 | "link": "https://github.com/webpack/remark-loader#settings", 21 | "additionalProperties": true 22 | }, 23 | "data": { 24 | "type": "object", 25 | "description": "Configure the remark with information available to all plugins. Information is stored in an in-memory key-value store.", 26 | "link": "https://github.com/webpack/remark-loader#data", 27 | "additionalProperties": true 28 | } 29 | } 30 | }, 31 | "removeFrontMatter": { 32 | "type": "boolean", 33 | "description": "By default, the frontMatter is removed. To override this behavior, set removeFrontMatter to false and add remark-frontmatter to plugins.", 34 | "link": "https://github.com/webpack/remark-loader#removefrontmatter" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import frontMatter from "front-matter"; 2 | 3 | import schema from "./options.json"; 4 | 5 | export default async function loader(content) { 6 | const options = this.getOptions(schema); 7 | const remarkOptions = 8 | typeof options.remarkOptions !== "undefined" ? options.remarkOptions : {}; 9 | const callback = this.async(); 10 | 11 | let remark; 12 | 13 | try { 14 | ({ remark } = await import("remark")); 15 | } catch (error) { 16 | callback(error); 17 | 18 | return; 19 | } 20 | 21 | const processor = remark(); 22 | 23 | if (typeof remarkOptions.plugins !== "undefined") { 24 | for (const item of remarkOptions.plugins) { 25 | if (Array.isArray(item)) { 26 | const [plugin, pluginOptions] = item; 27 | 28 | processor.use(plugin, pluginOptions); 29 | } else { 30 | processor.use(item); 31 | } 32 | } 33 | } 34 | 35 | if (typeof remarkOptions.settings !== "undefined") { 36 | processor.use({ settings: remarkOptions.settings }); 37 | } 38 | 39 | if (typeof remarkOptions.data !== "undefined") { 40 | processor.data(remarkOptions.data); 41 | } 42 | 43 | const removeFrontMatter = 44 | typeof options.removeFrontMatter !== "undefined" 45 | ? options.removeFrontMatter 46 | : true; 47 | 48 | let file; 49 | 50 | try { 51 | file = await processor.process( 52 | removeFrontMatter ? frontMatter(content).body : content, 53 | ); 54 | } catch (error) { 55 | callback(error); 56 | 57 | return; 58 | } 59 | 60 | callback(null, String(file)); 61 | } 62 | -------------------------------------------------------------------------------- /test/validate-options.test.mjs: -------------------------------------------------------------------------------- 1 | import { compile, getCompiler } from "./helpers/index.mjs"; 2 | 3 | const { default: RemarkGFM } = await import("remark-gfm"); 4 | 5 | describe("validate options", () => { 6 | const tests = { 7 | remarkOptions: { 8 | success: [ 9 | { 10 | plugins: [RemarkGFM], 11 | }, 12 | { 13 | settings: { 14 | bullet: "+", 15 | listItemIndent: "one", 16 | }, 17 | }, 18 | { 19 | data: { 20 | alpha: "bravo", 21 | charlie: "delta", 22 | }, 23 | }, 24 | ], 25 | failure: [ 26 | true, 27 | "test", 28 | () => {}, 29 | [], 30 | { unknown: false }, 31 | { plugins: true }, 32 | { settings: 100 }, 33 | { data: "true" }, 34 | ], 35 | }, 36 | removeFrontMatter: { 37 | success: [true, false], 38 | failure: ["true", "test", () => {}, /test/], 39 | }, 40 | unknown: { 41 | success: [], 42 | failure: [1, true, false, "test", /test/, [], {}, { foo: "bar" }], 43 | }, 44 | }; 45 | 46 | function stringifyValue(value) { 47 | if ( 48 | Array.isArray(value) || 49 | (value && typeof value === "object" && value.constructor === Object) 50 | ) { 51 | return JSON.stringify(value); 52 | } 53 | 54 | return value; 55 | } 56 | 57 | async function createTestCase(key, value, type) { 58 | it(`should ${ 59 | type === "success" ? "successfully validate" : "throw an error on" 60 | } the "${key}" option with "${stringifyValue(value)}" value`, async () => { 61 | const compiler = getCompiler("simple.js", { [key]: value }); 62 | 63 | let stats; 64 | 65 | try { 66 | stats = await compile(compiler); 67 | } finally { 68 | if (type === "success") { 69 | expect(stats.hasErrors()).toBe(false); 70 | } else if (type === "failure") { 71 | const { 72 | compilation: { errors }, 73 | } = stats; 74 | 75 | expect(errors).toHaveLength(1); 76 | expect(() => { 77 | throw new Error(errors[0].error.message); 78 | }).toThrowErrorMatchingSnapshot(); 79 | } 80 | } 81 | }); 82 | } 83 | 84 | for (const [key, values] of Object.entries(tests)) { 85 | for (const type of Object.keys(values)) { 86 | for (const value of values[type]) { 87 | createTestCase(key, value, type); 88 | } 89 | } 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: remark-loader 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - next 8 | pull_request: 9 | branches: 10 | - main 11 | - next 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | lint: 18 | name: Lint - ${{ matrix.os }} - Node v${{ matrix.node-version }} 19 | 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | strategy: 24 | matrix: 25 | os: [ubuntu-latest] 26 | node-version: [lts/*] 27 | 28 | runs-on: ${{ matrix.os }} 29 | 30 | concurrency: 31 | group: lint-${{ matrix.os }}-v${{ matrix.node-version }}-${{ github.ref }} 32 | cancel-in-progress: true 33 | 34 | steps: 35 | - uses: actions/checkout@v5 36 | with: 37 | fetch-depth: 0 38 | 39 | - name: Use Node.js ${{ matrix.node-version }} 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: ${{ matrix.node-version }} 43 | cache: "npm" 44 | 45 | - name: Install dependencies 46 | run: npm ci 47 | 48 | - name: Lint 49 | run: npm run lint 50 | 51 | - name: Security audit 52 | run: npm run security 53 | 54 | - name: Validate PR commits with commitlint 55 | if: github.event_name == 'pull_request' 56 | run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose 57 | 58 | test: 59 | name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version }}, Webpack ${{ matrix.webpack-version }} 60 | 61 | strategy: 62 | matrix: 63 | os: [ubuntu-latest, windows-latest, macos-latest] 64 | node-version: [18.x, 20.x, 22.x, 24.x] 65 | webpack-version: [latest] 66 | 67 | runs-on: ${{ matrix.os }} 68 | 69 | concurrency: 70 | group: test-${{ matrix.os }}-v${{ matrix.node-version }}-${{ matrix.webpack-version }}-${{ github.ref }} 71 | cancel-in-progress: true 72 | 73 | steps: 74 | - uses: actions/checkout@v5 75 | 76 | - name: Use Node.js ${{ matrix.node-version }} 77 | uses: actions/setup-node@v4 78 | with: 79 | node-version: ${{ matrix.node-version }} 80 | cache: "npm" 81 | 82 | - name: Install dependencies 83 | run: npm ci 84 | 85 | - name: Install webpack ${{ matrix.webpack-version }} 86 | if: matrix.webpack-version != 'latest' 87 | run: npm i webpack@${{ matrix.webpack-version }} 88 | 89 | - name: Run tests for webpack version ${{ matrix.webpack-version }} 90 | run: npm run test:coverage -- --ci 91 | 92 | - name: Submit coverage data to codecov 93 | uses: codecov/codecov-action@v5 94 | with: 95 | token: ${{ secrets.CODECOV_TOKEN }} 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remark-loader", 3 | "version": "6.0.0", 4 | "description": "Load markdown through remark with some react-specific features.", 5 | "keywords": [ 6 | "react", 7 | "markdown", 8 | "webpack", 9 | "webpack-loader", 10 | "loader" 11 | ], 12 | "homepage": "https://github.com/webpack/remark-loader", 13 | "bugs": "https://github.com/webpack/remark-loader/issues", 14 | "repository": "webpack/remark-loader", 15 | "funding": { 16 | "type": "opencollective", 17 | "url": "https://opencollective.com/webpack" 18 | }, 19 | "license": "MIT", 20 | "author": "Greg Venech", 21 | "main": "dist/cjs.js", 22 | "files": [ 23 | "dist" 24 | ], 25 | "scripts": { 26 | "start": "npm run build -- -w", 27 | "clean": "del-cli dist", 28 | "prebuild": "npm run clean", 29 | "build": "cross-env NODE_ENV=production babel src -d dist --copy-files", 30 | "commitlint": "commitlint --from=main", 31 | "security": "npm audit --production", 32 | "lint:prettier": "prettier --cache --list-different .", 33 | "lint:js": "eslint --cache .", 34 | "lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"", 35 | "lint": "npm-run-all -l -p \"lint:**\"", 36 | "fix:js": "npm run lint:js -- --fix", 37 | "fix:prettier": "npm run lint:prettier -- --write", 38 | "fix": "npm-run-all -l fix:js fix:prettier", 39 | "test:only": "cross-env NODE_OPTIONS='--loader ./node_modules/ts-node/esm.mjs' jest", 40 | "test:watch": "npm run test:only -- --watch", 41 | "test:coverage": "npm run test:only -- --collectCoverageFrom=\"dist/**/*.js\" --coverage", 42 | "pretest": "npm run lint", 43 | "test": "npm run test:coverage", 44 | "prepare": "husky && npm run build", 45 | "release": "standard-version" 46 | }, 47 | "dependencies": { 48 | "front-matter": "^4.0.2" 49 | }, 50 | "devDependencies": { 51 | "@babel/cli": "^7.24.7", 52 | "@babel/core": "^7.24.7", 53 | "@babel/eslint-parser": "^7.24.7", 54 | "@babel/preset-env": "^7.24.7", 55 | "@commitlint/cli": "^18.6.1", 56 | "@commitlint/config-conventional": "^18.6.2", 57 | "@eslint/js": "^9.32.0", 58 | "@eslint/markdown": "^7.1.0", 59 | "@stylistic/eslint-plugin": "^5.2.2", 60 | "babel-jest": "^30.0.0", 61 | "cross-env": "^7.0.3", 62 | "cspell": "^8.10.0", 63 | "del": "^7.1.0", 64 | "del-cli": "^5.1.0", 65 | "eslint": "^9.32.0", 66 | "eslint-config-prettier": "^10.1.8", 67 | "eslint-config-webpack": "^4.5.1", 68 | "eslint-plugin-import": "^2.32.0", 69 | "eslint-plugin-jest": "^29.0.1", 70 | "eslint-plugin-n": "^17.21.2", 71 | "eslint-plugin-prettier": "^5.5.3", 72 | "eslint-plugin-unicorn": "^60.0.0", 73 | "globals": "^16.3.0", 74 | "html-loader": "^5.0.0", 75 | "husky": "^9.1.3", 76 | "jest": "^30.0.0", 77 | "jest-light-runner": "^0.7.9", 78 | "lint-staged": "^15.2.7", 79 | "memfs": "^4.9.3", 80 | "npm-run-all": "^4.1.5", 81 | "prettier": "^3.3.2", 82 | "remark": "^15.0.1", 83 | "remark-bookmarks": "^3.0.0", 84 | "remark-frontmatter": "^5.0.0", 85 | "remark-gfm": "^4.0.0", 86 | "remark-html": "^16.0.1", 87 | "standard-version": "^9.3.1", 88 | "ts-node": "^10.9.2", 89 | "typescript-eslint": "^8.38.0", 90 | "webpack": "^5.92.1" 91 | }, 92 | "peerDependencies": { 93 | "remark": "^14.0.0", 94 | "webpack": "^5.0.0" 95 | }, 96 | "engines": { 97 | "node": ">= 18.12.0" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/fixtures/simple.md: -------------------------------------------------------------------------------- 1 | --- 2 | __Advertisement :)__ 3 | 4 | - __[pica](https://nodeca.github.io/pica/demo/)__ - high quality and fast image 5 | resize in browser. 6 | - __[babelfish](https://github.com/nodeca/babelfish/)__ - developer friendly 7 | i18n with plurals support and easy syntax. 8 | 9 | You will like those projects! 10 | 11 | --- 12 | 13 | # h1 Heading 8-) 14 | ## h2 Heading 15 | ### h3 Heading 16 | #### h4 Heading 17 | ##### h5 Heading 18 | ###### h6 Heading 19 | 20 | 21 | ## Horizontal Rules 22 | 23 | ___ 24 | 25 | --- 26 | 27 | *** 28 | 29 | 30 | ## Typographic replacements 31 | 32 | Enable typographer option to see result. 33 | 34 | (c) (C) (r) (R) (tm) (TM) (p) (P) +- 35 | 36 | test.. test... test..... test?..... test!.... 37 | 38 | !!!!!! ???? ,, -- --- 39 | 40 | "Smartypants, double quotes" and 'single quotes' 41 | 42 | 43 | ## Emphasis 44 | 45 | **This is bold text** 46 | 47 | __This is bold text__ 48 | 49 | *This is italic text* 50 | 51 | _This is italic text_ 52 | 53 | ~~Strikethrough~~ 54 | 55 | 56 | ## Blockquotes 57 | 58 | 59 | > Blockquotes can also be nested... 60 | >> ...by using additional greater-than signs right next to each other... 61 | > > > ...or with spaces between arrows. 62 | 63 | 64 | ## Lists 65 | 66 | Unordered 67 | 68 | + Create a list by starting a line with `+`, `-`, or `*` 69 | + Sub-lists are made by indenting 2 spaces: 70 | - Marker character change forces new list start: 71 | * Ac tristique libero volutpat at 72 | + Facilisis in pretium nisl aliquet 73 | - Nulla volutpat aliquam velit 74 | + Very easy! 75 | 76 | Ordered 77 | 78 | 1. Lorem ipsum dolor sit amet 79 | 2. Consectetur adipiscing elit 80 | 3. Integer molestie lorem at massa 81 | 82 | 83 | 1. You can use sequential numbers... 84 | 1. ...or keep all the numbers as `1.` 85 | 86 | Start numbering with offset: 87 | 88 | 57. foo 89 | 1. bar 90 | 91 | 92 | ## Code 93 | 94 | Inline `code` 95 | 96 | Indented code 97 | 98 | // Some comments 99 | line 1 of code 100 | line 2 of code 101 | line 3 of code 102 | 103 | 104 | Block code "fences" 105 | 106 | ``` 107 | Sample text here... 108 | ``` 109 | 110 | Syntax highlighting 111 | 112 | ``` js 113 | var foo = function (bar) { 114 | return bar++; 115 | }; 116 | 117 | console.log(foo(5)); 118 | ``` 119 | 120 | ## Tables 121 | 122 | | Option | Description | 123 | | ------ | ----------- | 124 | | data | path to data files to supply the data that will be passed into templates. | 125 | | engine | engine to be used for processing templates. Handlebars is the default. | 126 | | ext | extension to be used for dest files. | 127 | 128 | Right aligned columns 129 | 130 | | Option | Description | 131 | | ------:| -----------:| 132 | | data | path to data files to supply the data that will be passed into templates. | 133 | | engine | engine to be used for processing templates. Handlebars is the default. | 134 | | ext | extension to be used for dest files. | 135 | 136 | 137 | ## Links 138 | 139 | [link text](http://dev.nodeca.com) 140 | 141 | [link with title](http://nodeca.github.io/pica/demo/ "title text!") 142 | 143 | Autoconverted link https://github.com/nodeca/pica (enable linkify to see) 144 | 145 | 146 | ## Images 147 | 148 | ![Minion](https://octodex.github.com/images/minion.png) 149 | ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") 150 | 151 | Like links, Images also have a footnote style syntax 152 | 153 | ![Alt text][id] 154 | 155 | With a reference later in the document defining the URL location: 156 | 157 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" 158 | 159 | 160 | ## Plugins 161 | 162 | The killer feature of `markdown-it` is very effective support of 163 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). 164 | 165 | 166 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji) 167 | 168 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 169 | > 170 | > Shortcuts (emoticons): :-) :-( 8-) ;) 171 | 172 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. 173 | 174 | 175 | ### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) 176 | 177 | - 19^th^ 178 | - H~2~O 179 | 180 | 181 | ### [\](https://github.com/markdown-it/markdown-it-ins) 182 | 183 | ++Inserted text++ 184 | 185 | 186 | ### [\](https://github.com/markdown-it/markdown-it-mark) 187 | 188 | ==Marked text== 189 | 190 | 191 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) 192 | 193 | Footnote 1 link[^first]. 194 | 195 | Footnote 2 link[^second]. 196 | 197 | Inline footnote^[Text of inline footnote] definition. 198 | 199 | Duplicated footnote reference[^second]. 200 | 201 | [^first]: Footnote **can have markup** 202 | 203 | and multiple paragraphs. 204 | 205 | [^second]: Footnote text. 206 | 207 | 208 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) 209 | 210 | Term 1 211 | 212 | : Definition 1 213 | with lazy continuation. 214 | 215 | Term 2 with *inline markup* 216 | 217 | : Definition 2 218 | 219 | { some code, part of Definition 2 } 220 | 221 | Third paragraph of definition 2. 222 | 223 | _Compact style:_ 224 | 225 | Term 1 226 | ~ Definition 1 227 | 228 | Term 2 229 | ~ Definition 2a 230 | ~ Definition 2b 231 | 232 | 233 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) 234 | 235 | This is HTML abbreviation example. 236 | 237 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. 238 | 239 | *[HTML]: Hyper Text Markup Language 240 | 241 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container) 242 | 243 | ::: warning 244 | *here be dragons* 245 | ::: 246 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [6.0.0](https://github.com/webpack-contrib/remark-loader/compare/v5.0.0...v6.0.0) (2024-01-18) 6 | 7 | 8 | ### ⚠ BREAKING CHANGES 9 | 10 | * minimum supported Node.js version is `18.12.0` ([#114](https://github.com/webpack-contrib/remark-loader/issues/114)) ([1d613db](https://github.com/webpack-contrib/remark-loader/commit/1d613db4b351cdcd5428f15473a550b9480e2661)) 11 | 12 | ## [5.0.0](https://github.com/webpack-contrib/remark-loader/compare/v4.0.0...v5.0.0) (2022-06-02) 13 | 14 | 15 | ### ⚠ BREAKING CHANGES 16 | 17 | * minimum supported `Node.js` version is `14.15.0` (#65) 18 | * minimum supported `remark` version is `14.0.0` 19 | 20 | ### Features 21 | 22 | * output links and descriptions on errors ([#44](https://github.com/webpack-contrib/remark-loader/issues/44)) ([482647b](https://github.com/webpack-contrib/remark-loader/commit/482647b3111421e4ca39b7937be3f2612277aa1f)) 23 | 24 | ## [4.0.0](https://github.com/webpack-contrib/remark-loader/compare/v3.0.0...v4.0.0) (2021-05-18) 25 | 26 | 27 | ### ⚠ BREAKING CHANGES 28 | 29 | * minimum supported `Node.js` version is `12.13.0` 30 | 31 | ## [3.0.0](https://github.com/webpack-contrib/remark-loader/compare/v2.0.0...v3.0.0) (2021-02-01) 32 | 33 | 34 | ### ⚠ BREAKING CHANGES 35 | 36 | * minimum supported `webpack` version is `5` 37 | 38 | ## [2.0.0](https://github.com/webpack-contrib/remark-loader/compare/v1.0.1...v2.0.0) (2020-11-24) 39 | 40 | 41 | ### ⚠ BREAKING CHANGES 42 | 43 | * `remark` is now in peer dependencies , so you need to install `remark` itself, please read `remark` [changelog](https://github.com/remarkjs/remark/releases) before updating `remark`, there are a lot of breaking changes 44 | 45 | ### Features 46 | 47 | * allow to use `remark@12` and `remark@13` (other versions is not tested, but it may work too) 48 | 49 | ### [1.0.1](https://github.com/webpack-contrib/remark-loader/compare/v1.0.0...v1.0.1) (2020-10-09) 50 | 51 | ### Chore 52 | 53 | * update `schema-utils` 54 | 55 | ## [1.0.0](https://github.com/webpack-contrib/remark-loader/compare/v1.0.0-rc.0...v1.0.0) (2020-08-06) 56 | 57 | 58 | ### ⚠ BREAKING CHANGES 59 | 60 | * minimum required `Node.js` version is `10.13.0` 61 | * minimum required webpack version is `4` 62 | * loader doesn't return HTML, you need to use `html-loader`, please look at [this example](https://github.com/webpack-contrib/remark-loader#markdown-to-html) 63 | * update `remark` to `12.0.0` version, some plugins can be incompatible, please update them 64 | 65 | 66 | ### Features 67 | 68 | * added `remarkOptions` option, you can specify `plugins`, `settings` and `data` 69 | * added `removeFrontMatter` option ([#19](https://github.com/webpack-contrib/remark-loader/issues/19)) ([3550105](https://github.com/webpack-contrib/remark-loader/commit/3550105c774caa9aa4f4fc58e6fa23b5eb30c332)) 70 | * validate options by default ([c6b6ed2](https://github.com/webpack-contrib/remark-loader/commit/c6b6ed2f18f89c085a80ebe0db6d7adbfcc26e26)) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * improved performance 76 | 77 | 78 | 79 | # [1.0.0-rc.0](https://github.com/skipjack/remark-loader/compare/v0.3.2...v1.0.0-rc.0) (2019-06-10) 80 | 81 | 82 | ### Chores 83 | 84 | * remove react specific code ([f4e64c4](https://github.com/skipjack/remark-loader/commit/f4e64c4)), closes [#1](https://github.com/skipjack/remark-loader/issues/1) 85 | 86 | 87 | ### BREAKING CHANGES 88 | 89 | * The `react` option has been removed. Our implementation 90 | was extremely hacky and led to a bunch of pitfalls. Please use MDX as an 91 | alternative. 92 | 93 | 94 | 95 | 96 | ## [0.3.2](https://github.com/skipjack/remark-loader/compare/v0.3.1...v0.3.2) (2018-02-06) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * **react:** make `
` tags self-closing ([8c1f754](https://github.com/skipjack/remark-loader/commit/8c1f754)) 102 | 103 | 104 | 105 | 106 | ## [0.3.1](https://github.com/skipjack/remark-loader/compare/v0.3.0...v0.3.1) (2018-01-24) 107 | 108 | 109 | ### Bug Fixes 110 | 111 | * allow specified `template` to receive any higher-level `props` ([b365efa](https://github.com/skipjack/remark-loader/commit/b365efa)) 112 | * use ES6 module syntax for the output modules ([d550fa4](https://github.com/skipjack/remark-loader/commit/d550fa4)) 113 | 114 | 115 | 116 | 117 | # [0.3.0](https://github.com/skipjack/remark-loader/compare/v0.2.3...v0.3.0) (2017-08-10) 118 | 119 | 120 | ### Features 121 | 122 | * allow users to pass plugins ([0f125c0](https://github.com/skipjack/remark-loader/commit/0f125c0)) 123 | * **react:** provide YAML attributes to templates ([8072585](https://github.com/skipjack/remark-loader/commit/8072585)) 124 | 125 | 126 | 127 | 128 | ## [0.2.3](https://github.com/skipjack/remark-loader/compare/v0.2.2...v0.2.3) (2017-08-08) 129 | 130 | 131 | ### Bug Fixes 132 | 133 | * **react:** fix syntax error in build script ([cb951f6](https://github.com/skipjack/remark-loader/commit/cb951f6)) 134 | 135 | 136 | 137 | 138 | ## [0.2.2](https://github.com/skipjack/remark-loader/compare/v0.2.1...v0.2.2) (2017-08-08) 139 | 140 | 141 | ### Bug Fixes 142 | 143 | * **react:** fix missing newline and bracket usage in code blocks ([4f05570](https://github.com/skipjack/remark-loader/commit/4f05570)) 144 | 145 | 146 | 147 | 148 | ## [0.2.1](https://github.com/skipjack/remark-loader/compare/v0.2.0...v0.2.1) (2017-08-04) 149 | 150 | 151 | ### Bug Fixes 152 | 153 | * **deps:** update dependencies in package.json ([c42c645](https://github.com/skipjack/remark-loader/commit/c42c645)) 154 | 155 | 156 | 157 | 158 | # [0.2.0](https://github.com/skipjack/remark-loader/compare/v0.1.0...v0.2.0) (2017-08-04) 159 | 160 | 161 | ### Features 162 | 163 | * resolve local resources using the html-loader ([77ac7b2](https://github.com/skipjack/remark-loader/commit/77ac7b2)) 164 | * switch to remark parser allowing plugins and output options to be passed ([d8fbcef](https://github.com/skipjack/remark-loader/commit/d8fbcef)) 165 | * **react:** re-integrate the react build script ([35cd6f7](https://github.com/skipjack/remark-loader/commit/35cd6f7)) 166 | 167 | 168 | 169 | 170 | # [0.1.0](https://github.com/skipjack/remark-loader/releases/tag/v0.1.0) (2017-08-01) 171 | 172 | Port and refactor the repository from [`react-markdown-loader`](https://github.com/javiercf/react-markdown-loader). Still working on the switching over to [`remark`](https://github.com/wooorm/remark) over [`remarkable`](https://github.com/jonschlinkert/remarkable). 173 | -------------------------------------------------------------------------------- /test/loader.test.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | import { fileURLToPath } from "node:url"; 4 | import { 5 | compile, 6 | getCompiler, 7 | getErrors, 8 | getExecutedCode, 9 | getWarnings, 10 | } from "./helpers/index.mjs"; 11 | 12 | describe("loader", () => { 13 | it("should work markdown to markdown", async () => { 14 | const compiler = getCompiler("simple.js"); 15 | const stats = await compile(compiler); 16 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 17 | 18 | expect(codeFromBundle.md).toMatchSnapshot("md"); 19 | expect(getErrors(stats)).toMatchSnapshot("errors"); 20 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 21 | }); 22 | 23 | it("should work markdown to html", async () => { 24 | const RemarkHTML = (await import("remark-html")).default; 25 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 26 | 27 | const compiler = getCompiler( 28 | "simple.js", 29 | {}, 30 | { 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.md$/i, 35 | rules: [ 36 | { 37 | loader: path.resolve(__dirname, "./helpers/testLoader.cjs"), 38 | }, 39 | { 40 | loader: "html-loader", 41 | }, 42 | { 43 | loader: path.resolve(__dirname, "../dist/cjs.js"), 44 | options: { 45 | remarkOptions: { 46 | plugins: [RemarkHTML], 47 | }, 48 | }, 49 | }, 50 | ], 51 | }, 52 | ], 53 | }, 54 | }, 55 | ); 56 | const stats = await compile(compiler); 57 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 58 | 59 | expect(codeFromBundle.md).toMatchSnapshot("md"); 60 | expect(getErrors(stats)).toMatchSnapshot("errors"); 61 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 62 | }); 63 | 64 | it("should work with the 'remark-gfm' plugin", async () => { 65 | const RemarkGFM = (await import("remark-gfm")).default; 66 | 67 | const compiler = getCompiler("simple.js", { 68 | remarkOptions: { 69 | plugins: [RemarkGFM], 70 | }, 71 | }); 72 | const stats = await compile(compiler); 73 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 74 | 75 | expect(codeFromBundle.md).toMatchSnapshot("md"); 76 | expect(getErrors(stats)).toMatchSnapshot("errors"); 77 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 78 | }); 79 | 80 | it("should work if plugins are array", async () => { 81 | const RemarkBookmarks = (await import("remark-bookmarks")).default; 82 | 83 | const compiler = getCompiler("multipleArgs.js", { 84 | remarkOptions: { 85 | plugins: [ 86 | [ 87 | RemarkBookmarks, 88 | { 89 | bookmarks: { 90 | npm: "https://npmjs.com/package/remark-bookmarks", 91 | }, 92 | }, 93 | ], 94 | ], 95 | }, 96 | }); 97 | const stats = await compile(compiler); 98 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 99 | 100 | expect(codeFromBundle.md).toMatchSnapshot("md"); 101 | expect(getErrors(stats)).toMatchSnapshot("errors"); 102 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 103 | }); 104 | 105 | it("should not remove frontmatter", async () => { 106 | const RemarkFrontmatter = (await import("remark-frontmatter")).default; 107 | 108 | const compiler = getCompiler("multipleArgs.js", { 109 | removeFrontMatter: false, 110 | remarkOptions: { 111 | plugins: [RemarkFrontmatter], 112 | }, 113 | }); 114 | const stats = await compile(compiler); 115 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 116 | 117 | expect(codeFromBundle.md).toMatchSnapshot("md"); 118 | expect(getErrors(stats)).toMatchSnapshot("errors"); 119 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 120 | }); 121 | 122 | it("should work with the 'settings' option", async () => { 123 | const compiler = getCompiler("multipleArgs.js", { 124 | remarkOptions: { 125 | settings: { 126 | bullet: "+", 127 | listItemIndent: "one", 128 | }, 129 | }, 130 | }); 131 | const stats = await compile(compiler); 132 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 133 | 134 | expect(codeFromBundle.md).toMatchSnapshot("md"); 135 | expect(getErrors(stats)).toMatchSnapshot("errors"); 136 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 137 | }); 138 | 139 | it("should work with the 'settings' option in plugins", async () => { 140 | const compiler = getCompiler("multipleArgs.js", { 141 | remarkOptions: { 142 | plugins: [ 143 | { 144 | settings: { 145 | bullet: "+", 146 | listItemIndent: "one", 147 | }, 148 | }, 149 | ], 150 | }, 151 | }); 152 | const stats = await compile(compiler); 153 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 154 | 155 | expect(codeFromBundle.md).toMatchSnapshot("md"); 156 | expect(getErrors(stats)).toMatchSnapshot("errors"); 157 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 158 | }); 159 | 160 | it("should work with the 'data' option", async () => { 161 | let alpha; 162 | let charlie; 163 | 164 | function extractDataPlugin() { 165 | alpha = this.data("alpha"); 166 | charlie = this.data("charlie"); 167 | } 168 | 169 | const compiler = getCompiler("multipleArgs.js", { 170 | remarkOptions: { 171 | plugins: [extractDataPlugin], 172 | data: { 173 | alpha: "bravo", 174 | charlie: "delta", 175 | }, 176 | }, 177 | }); 178 | const stats = await compile(compiler); 179 | const codeFromBundle = getExecutedCode("main.bundle.js", compiler, stats); 180 | 181 | expect(alpha).toBe("bravo"); 182 | expect(charlie).toBe("delta"); 183 | expect(codeFromBundle.md).toMatchSnapshot("md"); 184 | expect(getErrors(stats)).toMatchSnapshot("errors"); 185 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 186 | }); 187 | 188 | it("should throw error #1", async () => { 189 | const RemarkFrontmatter = (await import("remark-frontmatter")).default; 190 | 191 | const compiler = getCompiler("multipleArgs.js", { 192 | removeFrontMatter: false, 193 | remarkOptions: { 194 | plugins: [[RemarkFrontmatter, { marker: "*" }]], 195 | }, 196 | }); 197 | 198 | const stats = await compile(compiler); 199 | 200 | expect(getErrors(stats)).toMatchSnapshot("errors"); 201 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 202 | }); 203 | 204 | it("should throw error #2", async () => { 205 | const errorGenerationPlugin = () => () => { 206 | throw new Error("Error"); 207 | }; 208 | 209 | const compiler = getCompiler("multipleArgs.js", { 210 | removeFrontMatter: false, 211 | remarkOptions: { 212 | plugins: [errorGenerationPlugin], 213 | }, 214 | }); 215 | 216 | const stats = await compile(compiler); 217 | 218 | expect(getErrors(stats)).toMatchSnapshot("errors"); 219 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 220 | }); 221 | }); 222 | -------------------------------------------------------------------------------- /test/__snapshots__/validate-options.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`validate options should throw an error on the "remarkOptions" option with "() => {}" value 1`] = ` 4 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 5 | - options.remarkOptions should be an object: 6 | object { plugins?, settings?, data? } 7 | -> Options for remark. 8 | -> Read more at https://github.com/webpack/remark-loader#remarkOptions" 9 | `; 10 | 11 | exports[`validate options should throw an error on the "remarkOptions" option with "[]" value 1`] = ` 12 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 13 | - options.remarkOptions should be an object: 14 | object { plugins?, settings?, data? } 15 | -> Options for remark. 16 | -> Read more at https://github.com/webpack/remark-loader#remarkOptions" 17 | `; 18 | 19 | exports[`validate options should throw an error on the "remarkOptions" option with "{"data":"true"}" value 1`] = ` 20 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 21 | - options.remarkOptions.data should be an object: 22 | object { … } 23 | -> Configure the remark with information available to all plugins. Information is stored in an in-memory key-value store. 24 | -> Read more at https://github.com/webpack/remark-loader#data" 25 | `; 26 | 27 | exports[`validate options should throw an error on the "remarkOptions" option with "{"plugins":true}" value 1`] = ` 28 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 29 | - options.remarkOptions.plugins should be an array: 30 | [any, ...] 31 | -> Allows to connect remark plugins. 32 | -> Read more at https://github.com/webpack/remark-loader#plugins" 33 | `; 34 | 35 | exports[`validate options should throw an error on the "remarkOptions" option with "{"settings":100}" value 1`] = ` 36 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 37 | - options.remarkOptions.settings should be an object: 38 | object { … } 39 | -> Pass remark-stringify options and remark-parse options options to the remark. 40 | -> Read more at https://github.com/webpack/remark-loader#settings" 41 | `; 42 | 43 | exports[`validate options should throw an error on the "remarkOptions" option with "{"unknown":false}" value 1`] = ` 44 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 45 | - options.remarkOptions has an unknown property 'unknown'. These properties are valid: 46 | object { plugins?, settings?, data? } 47 | -> Options for remark. 48 | -> Read more at https://github.com/webpack/remark-loader#remarkOptions" 49 | `; 50 | 51 | exports[`validate options should throw an error on the "remarkOptions" option with "test" value 1`] = ` 52 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 53 | - options.remarkOptions should be an object: 54 | object { plugins?, settings?, data? } 55 | -> Options for remark. 56 | -> Read more at https://github.com/webpack/remark-loader#remarkOptions" 57 | `; 58 | 59 | exports[`validate options should throw an error on the "remarkOptions" option with "true" value 1`] = ` 60 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 61 | - options.remarkOptions should be an object: 62 | object { plugins?, settings?, data? } 63 | -> Options for remark. 64 | -> Read more at https://github.com/webpack/remark-loader#remarkOptions" 65 | `; 66 | 67 | exports[`validate options should throw an error on the "removeFrontMatter" option with "() => {}" value 1`] = ` 68 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 69 | - options.removeFrontMatter should be a boolean. 70 | -> By default, the frontMatter is removed. To override this behavior, set removeFrontMatter to false and add remark-frontmatter to plugins. 71 | -> Read more at https://github.com/webpack/remark-loader#removefrontmatter" 72 | `; 73 | 74 | exports[`validate options should throw an error on the "removeFrontMatter" option with "/test/" value 1`] = ` 75 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 76 | - options.removeFrontMatter should be a boolean. 77 | -> By default, the frontMatter is removed. To override this behavior, set removeFrontMatter to false and add remark-frontmatter to plugins. 78 | -> Read more at https://github.com/webpack/remark-loader#removefrontmatter" 79 | `; 80 | 81 | exports[`validate options should throw an error on the "removeFrontMatter" option with "test" value 1`] = ` 82 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 83 | - options.removeFrontMatter should be a boolean. 84 | -> By default, the frontMatter is removed. To override this behavior, set removeFrontMatter to false and add remark-frontmatter to plugins. 85 | -> Read more at https://github.com/webpack/remark-loader#removefrontmatter" 86 | `; 87 | 88 | exports[`validate options should throw an error on the "removeFrontMatter" option with "true" value 1`] = ` 89 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 90 | - options.removeFrontMatter should be a boolean. 91 | -> By default, the frontMatter is removed. To override this behavior, set removeFrontMatter to false and add remark-frontmatter to plugins. 92 | -> Read more at https://github.com/webpack/remark-loader#removefrontmatter" 93 | `; 94 | 95 | exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = ` 96 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 97 | - options has an unknown property 'unknown'. These properties are valid: 98 | object { remarkOptions?, removeFrontMatter? }" 99 | `; 100 | 101 | exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = ` 102 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 103 | - options has an unknown property 'unknown'. These properties are valid: 104 | object { remarkOptions?, removeFrontMatter? }" 105 | `; 106 | 107 | exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = ` 108 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 109 | - options has an unknown property 'unknown'. These properties are valid: 110 | object { remarkOptions?, removeFrontMatter? }" 111 | `; 112 | 113 | exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = ` 114 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 115 | - options has an unknown property 'unknown'. These properties are valid: 116 | object { remarkOptions?, removeFrontMatter? }" 117 | `; 118 | 119 | exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = ` 120 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 121 | - options has an unknown property 'unknown'. These properties are valid: 122 | object { remarkOptions?, removeFrontMatter? }" 123 | `; 124 | 125 | exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = ` 126 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 127 | - options has an unknown property 'unknown'. These properties are valid: 128 | object { remarkOptions?, removeFrontMatter? }" 129 | `; 130 | 131 | exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = ` 132 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 133 | - options has an unknown property 'unknown'. These properties are valid: 134 | object { remarkOptions?, removeFrontMatter? }" 135 | `; 136 | 137 | exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = ` 138 | "Invalid options object. Remark Loader has been initialized using an options object that does not match the API schema. 139 | - options has an unknown property 'unknown'. These properties are valid: 140 | object { remarkOptions?, removeFrontMatter? }" 141 | `; 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | 7 | [![npm][npm]][npm-url] 8 | [![node][node]][node-url] 9 | [![tests][tests]][tests-url] 10 | [![cover][cover]][cover-url] 11 | [![discussion][discussion]][discussion-url] 12 | [![size][size]][size-url] 13 | 14 | # Remark Loader 15 | 16 | Load markdown through `remark`. 17 | 18 | ## Usage 19 | 20 | Simply add the loader to your configuration, and pass options. 21 | 22 | ```js 23 | import md from "markdown-file.md"; 24 | 25 | console.log(md); 26 | ``` 27 | 28 | **webpack.config.js** 29 | 30 | ```js 31 | import RemarkHTML from "remark-html"; 32 | 33 | module.exports = { 34 | // ... 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.md$/, 39 | use: [ 40 | { 41 | loader: "html-loader", 42 | }, 43 | { 44 | loader: "remark-loader", 45 | options: { 46 | remarkOptions: { 47 | plugins: [RemarkHTML], 48 | }, 49 | }, 50 | }, 51 | ], 52 | }, 53 | ], 54 | }, 55 | }; 56 | ``` 57 | 58 | Here's the full list of [`remark` plugins](https://github.com/wooorm/remark/blob/master/doc/plugins.md). 59 | 60 | We no longer support any `react` specific features. 61 | Please see the wonderful [MDX](https://mdxjs.com/) project if you're interested in mixing JSX with Markdown. 62 | 63 | ## Options 64 | 65 | - **[`remarkOptions`](#remarkOptions)** 66 | - **[`removeFrontMatter`](#removeFrontMatter)** 67 | 68 | ### remarkOptions 69 | 70 | Remark options 71 | 72 | Type: 73 | 74 | ```ts 75 | interface remarkOptions { 76 | plugins: (string | any[])[]; 77 | settings: object; 78 | data: object; 79 | } 80 | ``` 81 | 82 | - **[`plugins`](#plugins)** 83 | - **[`settings`](#settings)** 84 | - **[`data`](#data)** 85 | 86 | #### plugins 87 | 88 | Allows to connect [`remark` plugins](https://github.com/wooorm/remark/blob/master/doc/plugins.md) 89 | 90 | Type: 91 | 92 | ```ts 93 | type plugins = (string | any[])[]; 94 | ``` 95 | 96 | Default: `[]` 97 | 98 | Allows to connect [`remark` plugins](https://github.com/wooorm/remark/blob/master/doc/plugins.md) 99 | 100 | ##### string 101 | 102 | **webpack.config.js** 103 | 104 | ```js 105 | import RemarkFrontmatter from "remark-frontmatter"; 106 | 107 | module.exports = { 108 | // ... 109 | module: { 110 | rules: [ 111 | { 112 | test: /\.md$/, 113 | use: [ 114 | { 115 | loader: "remark-loader", 116 | options: { 117 | remarkOptions: { 118 | plugins: [RemarkFrontmatter], 119 | }, 120 | }, 121 | }, 122 | ], 123 | }, 124 | ], 125 | }, 126 | }; 127 | ``` 128 | 129 | ##### array 130 | 131 | If need to specify options for the plugin, you can pass the plugin using an array, where the second argument will be the `options`. 132 | 133 | **webpack.config.js** 134 | 135 | ```js 136 | import RemarkBookmarks from "remark-bookmarks"; 137 | import RemarkFrontmatter from "remark-frontmatter"; 138 | 139 | module.exports = { 140 | // ... 141 | module: { 142 | rules: [ 143 | { 144 | test: /\.md$/, 145 | use: [ 146 | { 147 | loader: "remark-loader", 148 | options: { 149 | remarkOptions: { 150 | plugins: [ 151 | RemarkFrontmatter, 152 | [ 153 | RemarkBookmarks, 154 | { 155 | bookmarks: { 156 | npm: "https://npmjs.com/package/remark-bookmarks", 157 | }, 158 | }, 159 | ], 160 | ], 161 | }, 162 | }, 163 | }, 164 | ], 165 | }, 166 | ], 167 | }, 168 | }; 169 | ``` 170 | 171 | #### settings 172 | 173 | Remark settings 174 | 175 | Type: 176 | 177 | ```ts 178 | type settings = object; 179 | ``` 180 | 181 | Default: `undefined` 182 | 183 | Pass [`remark-stringify` options](https://github.com/remarkjs/remark/tree/main/packages/remark-stringify#options) and [`remark-parse` options](https://github.com/remarkjs/remark/tree/main/packages/remark-parse#options) to `remark`. 184 | 185 | **webpack.config.js** 186 | 187 | ```js 188 | module.exports = { 189 | // ... 190 | module: { 191 | rules: [ 192 | { 193 | test: /\.md$/, 194 | use: [ 195 | { 196 | loader: "remark-loader", 197 | options: { 198 | remarkOptions: { 199 | settings: { 200 | bullet: "+", 201 | listItemIndent: "1", 202 | }, 203 | }, 204 | }, 205 | }, 206 | ], 207 | }, 208 | ], 209 | }, 210 | }; 211 | ``` 212 | 213 | #### data 214 | 215 | Information available to all plugins 216 | 217 | Type: 218 | 219 | ```ts 220 | type data = object; 221 | ``` 222 | 223 | Default: `undefined` 224 | 225 | Configure [`remark`](https://github.com/unifiedjs/unified#processordatakey-value) with information available to all plugins. 226 | This information is stored in an in-memory key-value store. 227 | 228 | **webpack.config.js** 229 | 230 | ```js 231 | function examplePluginUsingData() { 232 | console.log(this.data); 233 | // { alpha: 'bravo', charlie: 'delta' } 234 | } 235 | 236 | module.exports = { 237 | // ... 238 | module: { 239 | rules: [ 240 | { 241 | test: /\.md$/, 242 | use: [ 243 | { 244 | loader: "remark-loader", 245 | options: { 246 | remarkOptions: { 247 | plugins: [examplePluginUsingData], 248 | data: { 249 | alpha: "bravo", 250 | charlie: "delta", 251 | }, 252 | }, 253 | }, 254 | }, 255 | ], 256 | }, 257 | ], 258 | }, 259 | }; 260 | ``` 261 | 262 | ### removeFrontMatter 263 | 264 | Remove removeFrontMatter 265 | 266 | Type: 267 | 268 | ```ts 269 | type removeFrontMatter = boolean; 270 | ``` 271 | 272 | Default: `true` 273 | 274 | By default, the frontMatter is removed. 275 | To override this behavior, set `removeFrontMatter` to `false` and add `remark-frontmatter` to plugins. 276 | 277 | **webpack.config.js** 278 | 279 | ```js 280 | import RemarkFrontmatter from "remark-frontmatter"; 281 | 282 | module.exports = { 283 | // ... 284 | module: { 285 | rules: [ 286 | { 287 | test: /\.md$/, 288 | use: [ 289 | { 290 | loader: "remark-loader", 291 | options: { 292 | removeFrontMatter: false, 293 | remarkOptions: { 294 | plugins: [RemarkFrontmatter], 295 | }, 296 | }, 297 | }, 298 | ], 299 | }, 300 | ], 301 | }, 302 | }; 303 | ``` 304 | 305 | ## Inspiration 306 | 307 | This project was inspired the following open source work: 308 | 309 | - [`react-markdown-loader`](https://github.com/javiercf/react-markdown-loader) 310 | - [`marksy`](https://github.com/cerebral/marksy) 311 | 312 | ## Examples 313 | 314 | ### Markdown to HTML 315 | 316 | To get HTML, you need to add [`remark-html`](https://github.com/wooorm/remark-html) to the remark plugins and add [`html-loader`](https://github.com/webpack/html-loader) to your `webpack.config.js` 317 | 318 | ```js 319 | import md from "markdown-file.md"; 320 | 321 | console.log(md); 322 | ``` 323 | 324 | **webpack.config.js** 325 | 326 | ```js 327 | import RemarkHTML from "remark-html"; 328 | 329 | module.exports = { 330 | // ... 331 | module: { 332 | rules: [ 333 | { 334 | test: /\.md$/, 335 | use: [ 336 | { 337 | loader: "html-loader", 338 | }, 339 | { 340 | loader: "remark-loader", 341 | options: { 342 | remarkOptions: { 343 | plugins: [RemarkHTML], 344 | }, 345 | }, 346 | }, 347 | ], 348 | }, 349 | ], 350 | }, 351 | }; 352 | ``` 353 | 354 | ### Markdown to Markdown 355 | 356 | **index.js** 357 | 358 | ```js 359 | import md from "markdown-file.md"; 360 | 361 | console.log(md); 362 | ``` 363 | 364 | **webpack.config.js** 365 | 366 | ```js 367 | module.exports = { 368 | // ... 369 | module: { 370 | rules: [ 371 | { 372 | test: /\.md$/, 373 | use: [ 374 | { 375 | loader: "remark-loader", 376 | }, 377 | ], 378 | }, 379 | ], 380 | }, 381 | }; 382 | ``` 383 | 384 | ## Contributing 385 | 386 | We welcome all contributions! 387 | If you're new here, please take a moment to review our contributing guidelines before submitting issues or pull requests. 388 | 389 | [CONTRIBUTING](https://github.com/webpack/remark-loader?tab=contributing-ov-file#contributing) 390 | 391 | ## License 392 | 393 | [MIT](./LICENSE) 394 | 395 | [npm]: https://img.shields.io/npm/v/remark-loader.svg 396 | [npm-url]: https://npmjs.com/package/remark-loader 397 | [node]: https://img.shields.io/node/v/remark-loader.svg 398 | [node-url]: https://nodejs.org 399 | [tests]: https://github.com/webpack/remark-loader/workflows/remark-loader/badge.svg 400 | [tests-url]: https://github.com/webpack/remark-loader/actions 401 | [cover]: https://codecov.io/gh/webpack/remark-loader/branch/main/graph/badge.svg 402 | [cover-url]: https://codecov.io/gh/webpack/remark-loader 403 | [discussion]: https://img.shields.io/github/discussions/webpack/webpack 404 | [discussion-url]: https://github.com/webpack/webpack/discussions 405 | [size]: https://packagephobia.now.sh/badge?p=remark-loader 406 | [size-url]: https://packagephobia.now.sh/result?p=remark-loader 407 | -------------------------------------------------------------------------------- /test/__snapshots__/loader.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing 2 | 3 | exports[`loader should not remove frontmatter: errors 1`] = `[]`; 4 | 5 | exports[`loader should not remove frontmatter: md 1`] = ` 6 | "--- 7 | title: Just hack'n 8 | description: Nothing to see here 9 | meta: some meta data 10 | --- 11 | 12 | # My header 13 | 14 | Test content. 15 | 16 | \`remark-bookmarks\` is on \\[npm]\\[]! 17 | 18 | Example list: 19 | 20 | * item 1 21 | * item 2 22 | * item 3 23 | " 24 | `; 25 | 26 | exports[`loader should not remove frontmatter: warnings 1`] = `[]`; 27 | 28 | exports[`loader should throw error #1: errors 1`] = ` 29 | [ 30 | "ModuleBuildError: Module build failed (from \`replaced original path\`): 31 | Error: Missing \`type\` in matter \`{"marker":"*"}\`", 32 | ] 33 | `; 34 | 35 | exports[`loader should throw error #1: warnings 1`] = `[]`; 36 | 37 | exports[`loader should throw error #2: errors 1`] = ` 38 | [ 39 | "ModuleBuildError: Module build failed (from \`replaced original path\`): 40 | Error: Error", 41 | ] 42 | `; 43 | 44 | exports[`loader should throw error #2: warnings 1`] = `[]`; 45 | 46 | exports[`loader should work if plugins are array: errors 1`] = `[]`; 47 | 48 | exports[`loader should work if plugins are array: md 1`] = ` 49 | "# My header 50 | 51 | Test content. 52 | 53 | \`remark-bookmarks\` is on \\[npm]\\[]! 54 | 55 | Example list: 56 | 57 | * item 1 58 | * item 2 59 | * item 3 60 | " 61 | `; 62 | 63 | exports[`loader should work if plugins are array: warnings 1`] = `[]`; 64 | 65 | exports[`loader should work markdown to html: errors 1`] = `[]`; 66 | 67 | exports[`loader should work markdown to html: md 1`] = ` 68 | "// Module 69 | var code = \`

h1 Heading 8-)

70 |

h2 Heading

71 |

h3 Heading

72 |

h4 Heading

73 |
h5 Heading
74 |
h6 Heading
75 |

Horizontal Rules

76 |
77 |
78 |
79 |

Typographic replacements

80 |

Enable typographer option to see result.

81 |

(c) (C) (r) (R) (tm) (TM) (p) (P) +-

82 |

test.. test... test..... test?..... test!....

83 |

!!!!!! ???? ,, -- ---

84 |

"Smartypants, double quotes" and 'single quotes'

85 |

Emphasis

86 |

This is bold text

87 |

This is bold text

88 |

This is italic text

89 |

This is italic text

90 |

~~Strikethrough~~

91 |

Blockquotes

92 |
93 |

Blockquotes can also be nested...

94 |
95 |

...by using additional greater-than signs right next to each other...

96 |
97 |

...or with spaces between arrows.

98 |
99 |
100 |
101 |

Lists

102 |

Unordered

103 | 122 |

Ordered

123 |
    124 |
  1. 125 |

    Lorem ipsum dolor sit amet

    126 |
  2. 127 |
  3. 128 |

    Consectetur adipiscing elit

    129 |
  4. 130 |
  5. 131 |

    Integer molestie lorem at massa

    132 |
  6. 133 |
  7. 134 |

    You can use sequential numbers...

    135 |
  8. 136 |
  9. 137 |

    ...or keep all the numbers as 1.

    138 |
  10. 139 |
140 |

Start numbering with offset:

141 |
    142 |
  1. foo
  2. 143 |
  3. bar
  4. 144 |
145 |

Code

146 |

Inline code

147 |

Indented code

148 |
// Some comments
149 | line 1 of code
150 | line 2 of code
151 | line 3 of code
152 | 
153 |

Block code "fences"

154 |
Sample text here...
155 | 
156 |

Syntax highlighting

157 |
var foo = function (bar) {
158 |   return bar++;
159 | };
160 | 
161 | console.log(foo(5));
162 | 
163 |

Tables

164 |

| Option | Description | 165 | | ------ | ----------- | 166 | | data | path to data files to supply the data that will be passed into templates. | 167 | | engine | engine to be used for processing templates. Handlebars is the default. | 168 | | ext | extension to be used for dest files. |

169 |

Right aligned columns

170 |

| Option | Description | 171 | | ------:| -----------:| 172 | | data | path to data files to supply the data that will be passed into templates. | 173 | | engine | engine to be used for processing templates. Handlebars is the default. | 174 | | ext | extension to be used for dest files. |

175 |

Links

176 |

link text

177 |

link with title

178 |

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)

179 |

Images

180 |

Minion 181 | Stormtroopocat

182 |

Like links, Images also have a footnote style syntax

183 |

Alt text

184 |

With a reference later in the document defining the URL location:

185 |

Plugins

186 |

The killer feature of markdown-it is very effective support of 187 | syntax plugins.

188 |

Emojies

189 |
190 |

Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:

191 |

Shortcuts (emoticons): :-) :-( 8-) ;)

192 |
193 |

see how to change output with twemoji.

194 |

Subscript / Superscript

195 | 199 |

<ins>

200 |

++Inserted text++

201 |

<mark>

202 |

==Marked text==

203 |

Footnotes

204 |

Footnote 1 link[^first].

205 |

Footnote 2 link[^second].

206 |

Inline footnote^[Text of inline footnote] definition.

207 |

Duplicated footnote reference[^second].

208 |

[^first]: Footnote can have markup

209 |
and multiple paragraphs.
210 | 
211 |

[^second]: Footnote text.

212 |

Definition lists

213 |

Term 1

214 |

: Definition 1 215 | with lazy continuation.

216 |

Term 2 with inline markup

217 |

: Definition 2

218 |
    { some code, part of Definition 2 }
219 | 
220 | Third paragraph of definition 2.
221 | 
222 |

Compact style:

223 |

Term 1 224 | ~ Definition 1

225 |

Term 2 226 | ~ Definition 2a 227 | ~ Definition 2b

228 |

Abbreviations

229 |

This is HTML abbreviation example.

230 |

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

231 |

*[HTML]: Hyper Text Markup Language

232 |

Custom containers

233 |

::: warning 234 | here be dragons 235 | :::

236 | \`; 237 | // Exports 238 | export default code;" 239 | `; 240 | 241 | exports[`loader should work markdown to html: warnings 1`] = `[]`; 242 | 243 | exports[`loader should work markdown to markdown: errors 1`] = `[]`; 244 | 245 | exports[`loader should work markdown to markdown: md 1`] = ` 246 | "# h1 Heading 8-) 247 | 248 | ## h2 Heading 249 | 250 | ### h3 Heading 251 | 252 | #### h4 Heading 253 | 254 | ##### h5 Heading 255 | 256 | ###### h6 Heading 257 | 258 | ## Horizontal Rules 259 | 260 | *** 261 | 262 | *** 263 | 264 | *** 265 | 266 | ## Typographic replacements 267 | 268 | Enable typographer option to see result. 269 | 270 | (c) (C) (r) (R) (tm) (TM) (p) (P) +- 271 | 272 | test.. test... test..... test?..... test!.... 273 | 274 | !!!!!! ???? ,, -- --- 275 | 276 | "Smartypants, double quotes" and 'single quotes' 277 | 278 | ## Emphasis 279 | 280 | **This is bold text** 281 | 282 | **This is bold text** 283 | 284 | *This is italic text* 285 | 286 | *This is italic text* 287 | 288 | \\~~Strikethrough~~ 289 | 290 | ## Blockquotes 291 | 292 | > Blockquotes can also be nested... 293 | > 294 | > > ...by using additional greater-than signs right next to each other... 295 | > > 296 | > > > ...or with spaces between arrows. 297 | 298 | ## Lists 299 | 300 | Unordered 301 | 302 | * Create a list by starting a line with \`+\`, \`-\`, or \`*\` 303 | * Sub-lists are made by indenting 2 spaces: 304 | * Marker character change forces new list start: 305 | * Ac tristique libero volutpat at 306 | - Facilisis in pretium nisl aliquet 307 | * Nulla volutpat aliquam velit 308 | * Very easy! 309 | 310 | Ordered 311 | 312 | 1. Lorem ipsum dolor sit amet 313 | 314 | 2. Consectetur adipiscing elit 315 | 316 | 3. Integer molestie lorem at massa 317 | 318 | 4. You can use sequential numbers... 319 | 320 | 5. ...or keep all the numbers as \`1.\` 321 | 322 | Start numbering with offset: 323 | 324 | 57. foo 325 | 58. bar 326 | 327 | ## Code 328 | 329 | Inline \`code\` 330 | 331 | Indented code 332 | 333 | \`\`\` 334 | // Some comments 335 | line 1 of code 336 | line 2 of code 337 | line 3 of code 338 | \`\`\` 339 | 340 | Block code "fences" 341 | 342 | \`\`\` 343 | Sample text here... 344 | \`\`\` 345 | 346 | Syntax highlighting 347 | 348 | \`\`\`js 349 | var foo = function (bar) { 350 | return bar++; 351 | }; 352 | 353 | console.log(foo(5)); 354 | \`\`\` 355 | 356 | ## Tables 357 | 358 | | Option | Description | 359 | | ------ | ----------- | 360 | | data | path to data files to supply the data that will be passed into templates. | 361 | | engine | engine to be used for processing templates. Handlebars is the default. | 362 | | ext | extension to be used for dest files. | 363 | 364 | Right aligned columns 365 | 366 | | Option | Description | 367 | | ------:| -----------:| 368 | | data | path to data files to supply the data that will be passed into templates. | 369 | | engine | engine to be used for processing templates. Handlebars is the default. | 370 | | ext | extension to be used for dest files. | 371 | 372 | ## Links 373 | 374 | [link text](http://dev.nodeca.com) 375 | 376 | [link with title](http://nodeca.github.io/pica/demo/ "title text!") 377 | 378 | Autoconverted link https://github.com/nodeca/pica (enable linkify to see) 379 | 380 | ## Images 381 | 382 | ![Minion](https://octodex.github.com/images/minion.png) 383 | ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") 384 | 385 | Like links, Images also have a footnote style syntax 386 | 387 | ![Alt text][id] 388 | 389 | With a reference later in the document defining the URL location: 390 | 391 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" 392 | 393 | ## Plugins 394 | 395 | The killer feature of \`markdown-it\` is very effective support of 396 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). 397 | 398 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji) 399 | 400 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 401 | > 402 | > Shortcuts (emoticons): :-) :-( 8-) ;) 403 | 404 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. 405 | 406 | ### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) 407 | 408 | * 19^th^ 409 | * H~2~O 410 | 411 | ### [\\](https://github.com/markdown-it/markdown-it-ins) 412 | 413 | ++Inserted text++ 414 | 415 | ### [\\](https://github.com/markdown-it/markdown-it-mark) 416 | 417 | \\==Marked text== 418 | 419 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) 420 | 421 | Footnote 1 link\\[^first]. 422 | 423 | Footnote 2 link\\[^second]. 424 | 425 | Inline footnote^\\[Text of inline footnote] definition. 426 | 427 | Duplicated footnote reference\\[^second]. 428 | 429 | \\[^first]: Footnote **can have markup** 430 | 431 | \`\`\` 432 | and multiple paragraphs. 433 | \`\`\` 434 | 435 | \\[^second]: Footnote text. 436 | 437 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) 438 | 439 | Term 1 440 | 441 | : Definition 1 442 | with lazy continuation. 443 | 444 | Term 2 with *inline markup* 445 | 446 | : Definition 2 447 | 448 | \`\`\` 449 | { some code, part of Definition 2 } 450 | 451 | Third paragraph of definition 2. 452 | \`\`\` 453 | 454 | *Compact style:* 455 | 456 | Term 1 457 | \\~ Definition 1 458 | 459 | Term 2 460 | \\~ Definition 2a 461 | \\~ Definition 2b 462 | 463 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) 464 | 465 | This is HTML abbreviation example. 466 | 467 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. 468 | 469 | \\*\\[HTML]: Hyper Text Markup Language 470 | 471 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container) 472 | 473 | ::: warning 474 | *here be dragons* 475 | ::: 476 | " 477 | `; 478 | 479 | exports[`loader should work markdown to markdown: warnings 1`] = `[]`; 480 | 481 | exports[`loader should work with the 'data' option: errors 1`] = `[]`; 482 | 483 | exports[`loader should work with the 'data' option: md 1`] = ` 484 | "# My header 485 | 486 | Test content. 487 | 488 | \`remark-bookmarks\` is on \\[npm]\\[]! 489 | 490 | Example list: 491 | 492 | * item 1 493 | * item 2 494 | * item 3 495 | " 496 | `; 497 | 498 | exports[`loader should work with the 'data' option: warnings 1`] = `[]`; 499 | 500 | exports[`loader should work with the 'remark-gfm' plugin: errors 1`] = `[]`; 501 | 502 | exports[`loader should work with the 'remark-gfm' plugin: md 1`] = ` 503 | "# h1 Heading 8-) 504 | 505 | ## h2 Heading 506 | 507 | ### h3 Heading 508 | 509 | #### h4 Heading 510 | 511 | ##### h5 Heading 512 | 513 | ###### h6 Heading 514 | 515 | ## Horizontal Rules 516 | 517 | *** 518 | 519 | *** 520 | 521 | *** 522 | 523 | ## Typographic replacements 524 | 525 | Enable typographer option to see result. 526 | 527 | (c) (C) (r) (R) (tm) (TM) (p) (P) +- 528 | 529 | test.. test... test..... test?..... test!.... 530 | 531 | !!!!!! ???? ,, -- --- 532 | 533 | "Smartypants, double quotes" and 'single quotes' 534 | 535 | ## Emphasis 536 | 537 | **This is bold text** 538 | 539 | **This is bold text** 540 | 541 | *This is italic text* 542 | 543 | *This is italic text* 544 | 545 | ~~Strikethrough~~ 546 | 547 | ## Blockquotes 548 | 549 | > Blockquotes can also be nested... 550 | > 551 | > > ...by using additional greater-than signs right next to each other... 552 | > > 553 | > > > ...or with spaces between arrows. 554 | 555 | ## Lists 556 | 557 | Unordered 558 | 559 | * Create a list by starting a line with \`+\`, \`-\`, or \`*\` 560 | * Sub-lists are made by indenting 2 spaces: 561 | * Marker character change forces new list start: 562 | * Ac tristique libero volutpat at 563 | - Facilisis in pretium nisl aliquet 564 | * Nulla volutpat aliquam velit 565 | * Very easy! 566 | 567 | Ordered 568 | 569 | 1. Lorem ipsum dolor sit amet 570 | 571 | 2. Consectetur adipiscing elit 572 | 573 | 3. Integer molestie lorem at massa 574 | 575 | 4. You can use sequential numbers... 576 | 577 | 5. ...or keep all the numbers as \`1.\` 578 | 579 | Start numbering with offset: 580 | 581 | 57. foo 582 | 58. bar 583 | 584 | ## Code 585 | 586 | Inline \`code\` 587 | 588 | Indented code 589 | 590 | \`\`\` 591 | // Some comments 592 | line 1 of code 593 | line 2 of code 594 | line 3 of code 595 | \`\`\` 596 | 597 | Block code "fences" 598 | 599 | \`\`\` 600 | Sample text here... 601 | \`\`\` 602 | 603 | Syntax highlighting 604 | 605 | \`\`\`js 606 | var foo = function (bar) { 607 | return bar++; 608 | }; 609 | 610 | console.log(foo(5)); 611 | \`\`\` 612 | 613 | ## Tables 614 | 615 | | Option | Description | 616 | | ------ | ------------------------------------------------------------------------- | 617 | | data | path to data files to supply the data that will be passed into templates. | 618 | | engine | engine to be used for processing templates. Handlebars is the default. | 619 | | ext | extension to be used for dest files. | 620 | 621 | Right aligned columns 622 | 623 | | Option | Description | 624 | | -----: | ------------------------------------------------------------------------: | 625 | | data | path to data files to supply the data that will be passed into templates. | 626 | | engine | engine to be used for processing templates. Handlebars is the default. | 627 | | ext | extension to be used for dest files. | 628 | 629 | ## Links 630 | 631 | [link text](http://dev.nodeca.com) 632 | 633 | [link with title](http://nodeca.github.io/pica/demo/ "title text!") 634 | 635 | Autoconverted link (enable linkify to see) 636 | 637 | ## Images 638 | 639 | ![Minion](https://octodex.github.com/images/minion.png) 640 | ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") 641 | 642 | Like links, Images also have a footnote style syntax 643 | 644 | ![Alt text][id] 645 | 646 | With a reference later in the document defining the URL location: 647 | 648 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" 649 | 650 | ## Plugins 651 | 652 | The killer feature of \`markdown-it\` is very effective support of 653 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). 654 | 655 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji) 656 | 657 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum: 658 | > 659 | > Shortcuts (emoticons): :-) :-( 8-) ;) 660 | 661 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. 662 | 663 | ### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) 664 | 665 | * 19^th^ 666 | * H~~2~~O 667 | 668 | ### [\\](https://github.com/markdown-it/markdown-it-ins) 669 | 670 | ++Inserted text++ 671 | 672 | ### [\\](https://github.com/markdown-it/markdown-it-mark) 673 | 674 | \\==Marked text== 675 | 676 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) 677 | 678 | Footnote 1 link[^first]. 679 | 680 | Footnote 2 link[^second]. 681 | 682 | Inline footnote^\\[Text of inline footnote] definition. 683 | 684 | Duplicated footnote reference[^second]. 685 | 686 | [^first]: Footnote **can have markup** 687 | 688 | and multiple paragraphs. 689 | 690 | [^second]: Footnote text. 691 | 692 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) 693 | 694 | Term 1 695 | 696 | : Definition 1 697 | with lazy continuation. 698 | 699 | Term 2 with *inline markup* 700 | 701 | : Definition 2 702 | 703 | \`\`\` 704 | { some code, part of Definition 2 } 705 | 706 | Third paragraph of definition 2. 707 | \`\`\` 708 | 709 | *Compact style:* 710 | 711 | Term 1 712 | \\~ Definition 1 713 | 714 | Term 2 715 | \\~ Definition 2a 716 | \\~ Definition 2b 717 | 718 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) 719 | 720 | This is HTML abbreviation example. 721 | 722 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. 723 | 724 | \\*\\[HTML]: Hyper Text Markup Language 725 | 726 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container) 727 | 728 | ::: warning 729 | *here be dragons* 730 | ::: 731 | " 732 | `; 733 | 734 | exports[`loader should work with the 'remark-gfm' plugin: warnings 1`] = `[]`; 735 | 736 | exports[`loader should work with the 'settings' option in plugins: errors 1`] = `[]`; 737 | 738 | exports[`loader should work with the 'settings' option in plugins: md 1`] = ` 739 | "# My header 740 | 741 | Test content. 742 | 743 | \`remark-bookmarks\` is on \\[npm]\\[]! 744 | 745 | Example list: 746 | 747 | + item 1 748 | + item 2 749 | + item 3 750 | " 751 | `; 752 | 753 | exports[`loader should work with the 'settings' option in plugins: warnings 1`] = `[]`; 754 | 755 | exports[`loader should work with the 'settings' option: errors 1`] = `[]`; 756 | 757 | exports[`loader should work with the 'settings' option: md 1`] = ` 758 | "# My header 759 | 760 | Test content. 761 | 762 | \`remark-bookmarks\` is on \\[npm]\\[]! 763 | 764 | Example list: 765 | 766 | + item 1 767 | + item 2 768 | + item 3 769 | " 770 | `; 771 | 772 | exports[`loader should work with the 'settings' option: warnings 1`] = `[]`; 773 | --------------------------------------------------------------------------------