;
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 RemarkFrontmatter from "remark-frontmatter";
137 | import RemarkBookmarks from "remark-bookmarks";
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-contrib/html-loader) to your `webpack.config.js`
317 |
318 | ```js
319 | import md from "markdown-file.md";
320 | console.log(md);
321 | ```
322 |
323 | **webpack.config.js**
324 |
325 | ```js
326 | import RemarkHTML from "remark-html";
327 |
328 | module.exports = {
329 | // ...
330 | module: {
331 | rules: [
332 | {
333 | test: /\.md$/,
334 | use: [
335 | {
336 | loader: "html-loader",
337 | },
338 | {
339 | loader: "remark-loader",
340 | options: {
341 | remarkOptions: {
342 | plugins: [RemarkHTML],
343 | },
344 | },
345 | },
346 | ],
347 | },
348 | ],
349 | },
350 | };
351 | ```
352 |
353 | ### Markdown to Markdown
354 |
355 | **index.js**
356 |
357 | ```js
358 | import md from "markdown-file.md";
359 | console.log(md);
360 | ```
361 |
362 | **webpack.config.js**
363 |
364 | ```js
365 | module.exports = {
366 | // ...
367 | module: {
368 | rules: [
369 | {
370 | test: /\.md$/,
371 | use: [
372 | {
373 | loader: "remark-loader",
374 | },
375 | ],
376 | },
377 | ],
378 | },
379 | };
380 | ```
381 |
382 | ## Contributing
383 |
384 | We welcome all contributions!
385 | If you're new here, please take a moment to review our contributing guidelines before submitting issues or pull requests.
386 |
387 | [CONTRIBUTING](./.github/CONTRIBUTING.md)
388 |
389 | ## License
390 |
391 | [MIT](./LICENSE)
392 |
393 | [npm]: https://img.shields.io/npm/v/remark-loader.svg
394 | [npm-url]: https://npmjs.com/package/remark-loader
395 | [node]: https://img.shields.io/node/v/remark-loader.svg
396 | [node-url]: https://nodejs.org
397 | [tests]: https://github.com/webpack-contrib/remark-loader/workflows/remark-loader/badge.svg
398 | [tests-url]: https://github.com/webpack-contrib/remark-loader/actions
399 | [cover]: https://codecov.io/gh/webpack-contrib/remark-loader/branch/master/graph/badge.svg
400 | [cover-url]: https://codecov.io/gh/webpack-contrib/remark-loader
401 | [discussion]: https://img.shields.io/github/discussions/webpack/webpack
402 | [discussion-url]: https://github.com/webpack/webpack/discussions
403 | [size]: https://packagephobia.now.sh/badge?p=remark-loader
404 | [size-url]: https://packagephobia.now.sh/result?p=remark-loader
405 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remark-loader",
3 | "version": "6.0.0",
4 | "description": "Load markdown through remark with some react-specific features.",
5 | "license": "MIT",
6 | "repository": "webpack-contrib/remark-loader",
7 | "author": "Greg Venech",
8 | "homepage": "https://github.com/webpack-contrib/remark-loader",
9 | "bugs": "https://github.com/webpack-contrib/remark-loader/issues",
10 | "funding": {
11 | "type": "opencollective",
12 | "url": "https://opencollective.com/webpack"
13 | },
14 | "main": "dist/cjs.js",
15 | "engines": {
16 | "node": ">= 18.12.0"
17 | },
18 | "scripts": {
19 | "start": "npm run build -- -w",
20 | "clean": "del-cli dist",
21 | "prebuild": "npm run clean",
22 | "build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
23 | "commitlint": "commitlint --from=master",
24 | "security": "npm audit --production",
25 | "lint:prettier": "prettier --cache --list-different .",
26 | "lint:js": "eslint --cache .",
27 | "lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"",
28 | "lint": "npm-run-all -l -p \"lint:**\"",
29 | "fix:js": "npm run lint:js -- --fix",
30 | "fix:prettier": "npm run lint:prettier -- --write",
31 | "fix": "npm-run-all -l fix:js fix:prettier",
32 | "test:only": "cross-env NODE_OPTIONS='--loader ./node_modules/ts-node/esm.mjs' jest",
33 | "test:watch": "npm run test:only -- --watch",
34 | "test:coverage": "npm run test:only -- --collectCoverageFrom=\"dist/**/*.js\" --coverage",
35 | "pretest": "npm run lint",
36 | "test": "npm run test:coverage",
37 | "prepare": "husky && npm run build",
38 | "release": "standard-version"
39 | },
40 | "files": [
41 | "dist"
42 | ],
43 | "peerDependencies": {
44 | "remark": "^14.0.0",
45 | "webpack": "^5.0.0"
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 | "@webpack-contrib/eslint-config-webpack": "^3.0.0",
58 | "babel-jest": "^29.7.0",
59 | "cross-env": "^7.0.3",
60 | "cspell": "^8.10.0",
61 | "del": "^7.1.0",
62 | "del-cli": "^5.1.0",
63 | "eslint": "^8.57.0",
64 | "eslint-config-prettier": "^9.1.0",
65 | "eslint-plugin-import": "^2.29.1",
66 | "html-loader": "^5.0.0",
67 | "husky": "^9.1.3",
68 | "jest": "^29.7.0",
69 | "jest-light-runner": "^0.6.0",
70 | "lint-staged": "^15.2.7",
71 | "memfs": "^4.9.3",
72 | "npm-run-all": "^4.1.5",
73 | "prettier": "^3.3.2",
74 | "remark": "^15.0.1",
75 | "remark-bookmarks": "^3.0.0",
76 | "remark-frontmatter": "^5.0.0",
77 | "remark-gfm": "^4.0.0",
78 | "remark-html": "^16.0.1",
79 | "standard-version": "^9.3.1",
80 | "ts-node": "^10.9.2",
81 | "webpack": "^5.92.1"
82 | },
83 | "keywords": [
84 | "react",
85 | "markdown",
86 | "webpack",
87 | "webpack-loader",
88 | "loader"
89 | ]
90 | }
91 |
--------------------------------------------------------------------------------
/src/cjs.js:
--------------------------------------------------------------------------------
1 | module.exports = require("./index").default;
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/remark-loader#removefrontmatter"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/test/__snapshots__/loader.test.mjs.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
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-)
\\nh2 Heading
\\nh3 Heading
\\nh4 Heading
\\nh5 Heading
\\nh6 Heading
\\nHorizontal Rules
\\n
\\n
\\n
\\nTypographic replacements
\\nEnable typographer option to see result.
\\n(c) (C) (r) (R) (tm) (TM) (p) (P) +-
\\ntest.. test... test..... test?..... test!....
\\n!!!!!! ???? ,, -- ---
\\n\\"Smartypants, double quotes\\" and 'single quotes'
\\nEmphasis
\\nThis is bold text
\\nThis is bold text
\\nThis is italic text
\\nThis is italic text
\\n~~Strikethrough~~
\\nBlockquotes
\\n\\nBlockquotes can also be nested...
\\n\\n...by using additional greater-than signs right next to each other...
\\n\\n...or with spaces between arrows.
\\n
\\n
\\n
\\nLists
\\nUnordered
\\n\\n- Create a list by starting a line with
+
, -
, or *
\\n- Sub-lists are made by indenting 2 spaces:\\n
\\n- Marker character change forces new list start:\\n
\\n- Ac tristique libero volutpat at
\\n
\\n\\n- Facilisis in pretium nisl aliquet
\\n
\\n\\n- Nulla volutpat aliquam velit
\\n
\\n \\n
\\n \\n- Very easy!
\\n
\\nOrdered
\\n\\n- \\n
Lorem ipsum dolor sit amet
\\n \\n- \\n
Consectetur adipiscing elit
\\n \\n- \\n
Integer molestie lorem at massa
\\n \\n- \\n
You can use sequential numbers...
\\n \\n- \\n
...or keep all the numbers as 1.
\\n \\n
\\nStart numbering with offset:
\\n\\n- foo
\\n- bar
\\n
\\nCode
\\nInline code
\\nIndented code
\\n// Some comments\\nline 1 of code\\nline 2 of code\\nline 3 of code\\n
\\nBlock code \\"fences\\"
\\nSample text here...\\n
\\nSyntax highlighting
\\nvar foo = function (bar) {\\n return bar++;\\n};\\n\\nconsole.log(foo(5));\\n
\\nTables
\\n| Option | Description |\\n| ------ | ----------- |\\n| data | path to data files to supply the data that will be passed into templates. |\\n| engine | engine to be used for processing templates. Handlebars is the default. |\\n| ext | extension to be used for dest files. |
\\nRight aligned columns
\\n| Option | Description |\\n| ------:| -----------:|\\n| data | path to data files to supply the data that will be passed into templates. |\\n| engine | engine to be used for processing templates. Handlebars is the default. |\\n| ext | extension to be used for dest files. |
\\nLinks
\\nlink text
\\nlink with title
\\nAutoconverted link https://github.com/nodeca/pica (enable linkify to see)
\\nImages
\\n
\\n
\\nLike links, Images also have a footnote style syntax
\\n
\\nWith a reference later in the document defining the URL location:
\\nPlugins
\\nThe killer feature of markdown-it
is very effective support of\\nsyntax plugins.
\\n\\n\\nClassic markup: :wink: :crush: :cry: :tear: :laughing: :yum:
\\nShortcuts (emoticons): :-) :-( 8-) ;)
\\n
\\nsee how to change output with twemoji.
\\n\\n\\n\\n++Inserted text++
\\n\\n==Marked text==
\\n\\nFootnote 1 link[^first].
\\nFootnote 2 link[^second].
\\nInline footnote^[Text of inline footnote] definition.
\\nDuplicated footnote reference[^second].
\\n[^first]: Footnote can have markup
\\nand multiple paragraphs.\\n
\\n[^second]: Footnote text.
\\n\\nTerm 1
\\n: Definition 1\\nwith lazy continuation.
\\nTerm 2 with inline markup
\\n: Definition 2
\\n { some code, part of Definition 2 }\\n\\nThird paragraph of definition 2.\\n
\\nCompact style:
\\nTerm 1\\n~ Definition 1
\\nTerm 2\\n~ Definition 2a\\n~ Definition 2b
\\n\\nThis is HTML abbreviation example.
\\nIt converts \\"HTML\\", but keep intact partial entries like \\"xxxHTMLyyy\\" and so on.
\\n*[HTML]: Hyper Text Markup Language
\\n\\n::: warning\\nhere be dragons\\n:::
\\n";
70 | // Exports
71 | export default code;"
72 | `;
73 |
74 | exports[`loader should work markdown to html: warnings 1`] = `[]`;
75 |
76 | exports[`loader should work markdown to markdown: errors 1`] = `[]`;
77 |
78 | exports[`loader should work markdown to markdown: md 1`] = `
79 | "# h1 Heading 8-)
80 |
81 | ## h2 Heading
82 |
83 | ### h3 Heading
84 |
85 | #### h4 Heading
86 |
87 | ##### h5 Heading
88 |
89 | ###### h6 Heading
90 |
91 | ## Horizontal Rules
92 |
93 | ***
94 |
95 | ***
96 |
97 | ***
98 |
99 | ## Typographic replacements
100 |
101 | Enable typographer option to see result.
102 |
103 | (c) (C) (r) (R) (tm) (TM) (p) (P) +-
104 |
105 | test.. test... test..... test?..... test!....
106 |
107 | !!!!!! ???? ,, -- ---
108 |
109 | "Smartypants, double quotes" and 'single quotes'
110 |
111 | ## Emphasis
112 |
113 | **This is bold text**
114 |
115 | **This is bold text**
116 |
117 | *This is italic text*
118 |
119 | *This is italic text*
120 |
121 | \\~~Strikethrough~~
122 |
123 | ## Blockquotes
124 |
125 | > Blockquotes can also be nested...
126 | >
127 | > > ...by using additional greater-than signs right next to each other...
128 | > >
129 | > > > ...or with spaces between arrows.
130 |
131 | ## Lists
132 |
133 | Unordered
134 |
135 | * Create a list by starting a line with \`+\`, \`-\`, or \`*\`
136 | * Sub-lists are made by indenting 2 spaces:
137 | * Marker character change forces new list start:
138 | * Ac tristique libero volutpat at
139 | - Facilisis in pretium nisl aliquet
140 | * Nulla volutpat aliquam velit
141 | * Very easy!
142 |
143 | Ordered
144 |
145 | 1. Lorem ipsum dolor sit amet
146 |
147 | 2. Consectetur adipiscing elit
148 |
149 | 3. Integer molestie lorem at massa
150 |
151 | 4. You can use sequential numbers...
152 |
153 | 5. ...or keep all the numbers as \`1.\`
154 |
155 | Start numbering with offset:
156 |
157 | 57. foo
158 | 58. bar
159 |
160 | ## Code
161 |
162 | Inline \`code\`
163 |
164 | Indented code
165 |
166 | \`\`\`
167 | // Some comments
168 | line 1 of code
169 | line 2 of code
170 | line 3 of code
171 | \`\`\`
172 |
173 | Block code "fences"
174 |
175 | \`\`\`
176 | Sample text here...
177 | \`\`\`
178 |
179 | Syntax highlighting
180 |
181 | \`\`\`js
182 | var foo = function (bar) {
183 | return bar++;
184 | };
185 |
186 | console.log(foo(5));
187 | \`\`\`
188 |
189 | ## Tables
190 |
191 | | Option | Description |
192 | | ------ | ----------- |
193 | | data | path to data files to supply the data that will be passed into templates. |
194 | | engine | engine to be used for processing templates. Handlebars is the default. |
195 | | ext | extension to be used for dest files. |
196 |
197 | Right aligned columns
198 |
199 | | Option | Description |
200 | | ------:| -----------:|
201 | | data | path to data files to supply the data that will be passed into templates. |
202 | | engine | engine to be used for processing templates. Handlebars is the default. |
203 | | ext | extension to be used for dest files. |
204 |
205 | ## Links
206 |
207 | [link text](http://dev.nodeca.com)
208 |
209 | [link with title](http://nodeca.github.io/pica/demo/ "title text!")
210 |
211 | Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
212 |
213 | ## Images
214 |
215 | 
216 | 
217 |
218 | Like links, Images also have a footnote style syntax
219 |
220 | ![Alt text][id]
221 |
222 | With a reference later in the document defining the URL location:
223 |
224 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
225 |
226 | ## Plugins
227 |
228 | The killer feature of \`markdown-it\` is very effective support of
229 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).
230 |
231 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji)
232 |
233 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:
234 | >
235 | > Shortcuts (emoticons): :-) :-( 8-) ;)
236 |
237 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.
238 |
239 | ### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup)
240 |
241 | * 19^th^
242 | * H~2~O
243 |
244 | ### [\\](https://github.com/markdown-it/markdown-it-ins)
245 |
246 | ++Inserted text++
247 |
248 | ### [\\](https://github.com/markdown-it/markdown-it-mark)
249 |
250 | \\==Marked text==
251 |
252 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)
253 |
254 | Footnote 1 link\\[^first].
255 |
256 | Footnote 2 link\\[^second].
257 |
258 | Inline footnote^\\[Text of inline footnote] definition.
259 |
260 | Duplicated footnote reference\\[^second].
261 |
262 | \\[^first]: Footnote **can have markup**
263 |
264 | \`\`\`
265 | and multiple paragraphs.
266 | \`\`\`
267 |
268 | \\[^second]: Footnote text.
269 |
270 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)
271 |
272 | Term 1
273 |
274 | : Definition 1
275 | with lazy continuation.
276 |
277 | Term 2 with *inline markup*
278 |
279 | : Definition 2
280 |
281 | \`\`\`
282 | { some code, part of Definition 2 }
283 |
284 | Third paragraph of definition 2.
285 | \`\`\`
286 |
287 | *Compact style:*
288 |
289 | Term 1
290 | \\~ Definition 1
291 |
292 | Term 2
293 | \\~ Definition 2a
294 | \\~ Definition 2b
295 |
296 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)
297 |
298 | This is HTML abbreviation example.
299 |
300 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.
301 |
302 | \\*\\[HTML]: Hyper Text Markup Language
303 |
304 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container)
305 |
306 | ::: warning
307 | *here be dragons*
308 | :::
309 | "
310 | `;
311 |
312 | exports[`loader should work markdown to markdown: warnings 1`] = `[]`;
313 |
314 | exports[`loader should work with the 'data' option: errors 1`] = `[]`;
315 |
316 | exports[`loader should work with the 'data' option: md 1`] = `
317 | "# My header
318 |
319 | Test content.
320 |
321 | \`remark-bookmarks\` is on \\[npm]\\[]!
322 |
323 | Example list:
324 |
325 | * item 1
326 | * item 2
327 | * item 3
328 | "
329 | `;
330 |
331 | exports[`loader should work with the 'data' option: warnings 1`] = `[]`;
332 |
333 | exports[`loader should work with the 'remark-gfm' plugin: errors 1`] = `[]`;
334 |
335 | exports[`loader should work with the 'remark-gfm' plugin: md 1`] = `
336 | "# h1 Heading 8-)
337 |
338 | ## h2 Heading
339 |
340 | ### h3 Heading
341 |
342 | #### h4 Heading
343 |
344 | ##### h5 Heading
345 |
346 | ###### h6 Heading
347 |
348 | ## Horizontal Rules
349 |
350 | ***
351 |
352 | ***
353 |
354 | ***
355 |
356 | ## Typographic replacements
357 |
358 | Enable typographer option to see result.
359 |
360 | (c) (C) (r) (R) (tm) (TM) (p) (P) +-
361 |
362 | test.. test... test..... test?..... test!....
363 |
364 | !!!!!! ???? ,, -- ---
365 |
366 | "Smartypants, double quotes" and 'single quotes'
367 |
368 | ## Emphasis
369 |
370 | **This is bold text**
371 |
372 | **This is bold text**
373 |
374 | *This is italic text*
375 |
376 | *This is italic text*
377 |
378 | ~~Strikethrough~~
379 |
380 | ## Blockquotes
381 |
382 | > Blockquotes can also be nested...
383 | >
384 | > > ...by using additional greater-than signs right next to each other...
385 | > >
386 | > > > ...or with spaces between arrows.
387 |
388 | ## Lists
389 |
390 | Unordered
391 |
392 | * Create a list by starting a line with \`+\`, \`-\`, or \`*\`
393 | * Sub-lists are made by indenting 2 spaces:
394 | * Marker character change forces new list start:
395 | * Ac tristique libero volutpat at
396 | - Facilisis in pretium nisl aliquet
397 | * Nulla volutpat aliquam velit
398 | * Very easy!
399 |
400 | Ordered
401 |
402 | 1. Lorem ipsum dolor sit amet
403 |
404 | 2. Consectetur adipiscing elit
405 |
406 | 3. Integer molestie lorem at massa
407 |
408 | 4. You can use sequential numbers...
409 |
410 | 5. ...or keep all the numbers as \`1.\`
411 |
412 | Start numbering with offset:
413 |
414 | 57. foo
415 | 58. bar
416 |
417 | ## Code
418 |
419 | Inline \`code\`
420 |
421 | Indented code
422 |
423 | \`\`\`
424 | // Some comments
425 | line 1 of code
426 | line 2 of code
427 | line 3 of code
428 | \`\`\`
429 |
430 | Block code "fences"
431 |
432 | \`\`\`
433 | Sample text here...
434 | \`\`\`
435 |
436 | Syntax highlighting
437 |
438 | \`\`\`js
439 | var foo = function (bar) {
440 | return bar++;
441 | };
442 |
443 | console.log(foo(5));
444 | \`\`\`
445 |
446 | ## Tables
447 |
448 | | Option | Description |
449 | | ------ | ------------------------------------------------------------------------- |
450 | | data | path to data files to supply the data that will be passed into templates. |
451 | | engine | engine to be used for processing templates. Handlebars is the default. |
452 | | ext | extension to be used for dest files. |
453 |
454 | Right aligned columns
455 |
456 | | Option | Description |
457 | | -----: | ------------------------------------------------------------------------: |
458 | | data | path to data files to supply the data that will be passed into templates. |
459 | | engine | engine to be used for processing templates. Handlebars is the default. |
460 | | ext | extension to be used for dest files. |
461 |
462 | ## Links
463 |
464 | [link text](http://dev.nodeca.com)
465 |
466 | [link with title](http://nodeca.github.io/pica/demo/ "title text!")
467 |
468 | Autoconverted link (enable linkify to see)
469 |
470 | ## Images
471 |
472 | 
473 | 
474 |
475 | Like links, Images also have a footnote style syntax
476 |
477 | ![Alt text][id]
478 |
479 | With a reference later in the document defining the URL location:
480 |
481 | [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
482 |
483 | ## Plugins
484 |
485 | The killer feature of \`markdown-it\` is very effective support of
486 | [syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).
487 |
488 | ### [Emojies](https://github.com/markdown-it/markdown-it-emoji)
489 |
490 | > Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:
491 | >
492 | > Shortcuts (emoticons): :-) :-( 8-) ;)
493 |
494 | see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.
495 |
496 | ### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup)
497 |
498 | * 19^th^
499 | * H~~2~~O
500 |
501 | ### [\\](https://github.com/markdown-it/markdown-it-ins)
502 |
503 | ++Inserted text++
504 |
505 | ### [\\](https://github.com/markdown-it/markdown-it-mark)
506 |
507 | \\==Marked text==
508 |
509 | ### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)
510 |
511 | Footnote 1 link[^first].
512 |
513 | Footnote 2 link[^second].
514 |
515 | Inline footnote^\\[Text of inline footnote] definition.
516 |
517 | Duplicated footnote reference[^second].
518 |
519 | [^first]: Footnote **can have markup**
520 |
521 | and multiple paragraphs.
522 |
523 | [^second]: Footnote text.
524 |
525 | ### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)
526 |
527 | Term 1
528 |
529 | : Definition 1
530 | with lazy continuation.
531 |
532 | Term 2 with *inline markup*
533 |
534 | : Definition 2
535 |
536 | \`\`\`
537 | { some code, part of Definition 2 }
538 |
539 | Third paragraph of definition 2.
540 | \`\`\`
541 |
542 | *Compact style:*
543 |
544 | Term 1
545 | \\~ Definition 1
546 |
547 | Term 2
548 | \\~ Definition 2a
549 | \\~ Definition 2b
550 |
551 | ### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)
552 |
553 | This is HTML abbreviation example.
554 |
555 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.
556 |
557 | \\*\\[HTML]: Hyper Text Markup Language
558 |
559 | ### [Custom containers](https://github.com/markdown-it/markdown-it-container)
560 |
561 | ::: warning
562 | *here be dragons*
563 | :::
564 | "
565 | `;
566 |
567 | exports[`loader should work with the 'remark-gfm' plugin: warnings 1`] = `[]`;
568 |
569 | exports[`loader should work with the 'settings' option in plugins: errors 1`] = `[]`;
570 |
571 | exports[`loader should work with the 'settings' option in plugins: md 1`] = `
572 | "# My header
573 |
574 | Test content.
575 |
576 | \`remark-bookmarks\` is on \\[npm]\\[]!
577 |
578 | Example list:
579 |
580 | + item 1
581 | + item 2
582 | + item 3
583 | "
584 | `;
585 |
586 | exports[`loader should work with the 'settings' option in plugins: warnings 1`] = `[]`;
587 |
588 | exports[`loader should work with the 'settings' option: errors 1`] = `[]`;
589 |
590 | exports[`loader should work with the 'settings' option: md 1`] = `
591 | "# My header
592 |
593 | Test content.
594 |
595 | \`remark-bookmarks\` is on \\[npm]\\[]!
596 |
597 | Example list:
598 |
599 | + item 1
600 | + item 2
601 | + item 3
602 | "
603 | `;
604 |
605 | exports[`loader should work with the 'settings' option: warnings 1`] = `[]`;
606 |
--------------------------------------------------------------------------------
/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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-contrib/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/test/fixtures/multipleArgs.js:
--------------------------------------------------------------------------------
1 | import md from './multipleArgs.md';
2 |
3 | __export__ = md;
4 |
5 | export default md;
6 |
--------------------------------------------------------------------------------
/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/fixtures/simple.js:
--------------------------------------------------------------------------------
1 | import md from './simple.md';
2 |
3 | __export__ = md;
4 |
5 | export default md;
6 |
--------------------------------------------------------------------------------
/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 | 
149 | 
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/test/helpers/execute.mjs:
--------------------------------------------------------------------------------
1 | import Module from "module";
2 | import path from "path";
3 | import { fileURLToPath } from "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 | // eslint-disable-next-line no-underscore-dangle
11 | module.paths = Module._nodeModulePaths(
12 | path.resolve(__dirname, "../fixtures"),
13 | );
14 | module.filename = resource;
15 |
16 | // eslint-disable-next-line no-underscore-dangle
17 | module._compile(
18 | `let __export__;${code};module.exports = __export__;`,
19 | resource,
20 | );
21 |
22 | return module.exports;
23 | };
24 |
--------------------------------------------------------------------------------
/test/helpers/getCompiler.mjs:
--------------------------------------------------------------------------------
1 | import path from "path";
2 |
3 | import webpack from "webpack";
4 | import { createFsFromVolume, Volume } from "memfs";
5 | import { fileURLToPath } from "url";
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 |
--------------------------------------------------------------------------------
/test/helpers/getErrors.mjs:
--------------------------------------------------------------------------------
1 | import normalizeErrors from "./normalizeErrors.mjs";
2 |
3 | export default (stats) => normalizeErrors(stats.compilation.errors.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 |
--------------------------------------------------------------------------------
/test/helpers/getWarnings.mjs:
--------------------------------------------------------------------------------
1 | import normalizeErrors from "./normalizeErrors.mjs";
2 |
3 | export default (stats) => normalizeErrors(stats.compilation.warnings.sort());
4 |
--------------------------------------------------------------------------------
/test/helpers/index.mjs:
--------------------------------------------------------------------------------
1 | import compile from "./compile.mjs";
2 | import execute from "./execute.mjs";
3 | import getExecutedCode from "./getExecutedCode.mjs";
4 | import getCompiler from "./getCompiler.mjs";
5 | import getErrors from "./getErrors.mjs";
6 | import getWarnings from "./getWarnings.mjs";
7 | import normalizeErrors from "./normalizeErrors.mjs";
8 | import readAsset from "./readAsset.mjs";
9 | import readsAssets from "./readAssets.mjs";
10 |
11 | export {
12 | compile,
13 | execute,
14 | getExecutedCode,
15 | getCompiler,
16 | getErrors,
17 | getWarnings,
18 | normalizeErrors,
19 | readAsset,
20 | readsAssets,
21 | };
22 |
--------------------------------------------------------------------------------
/test/helpers/normalizeErrors.mjs:
--------------------------------------------------------------------------------
1 | function removeCWD(str) {
2 | const isWin = process.platform === "win32";
3 | let cwd = process.cwd();
4 |
5 | if (isWin) {
6 | // eslint-disable-next-line no-param-reassign
7 | str = str.replace(/\\/g, "/");
8 | // eslint-disable-next-line no-param-reassign
9 | cwd = cwd.replace(/\\/g, "/");
10 | }
11 |
12 | return str
13 | .replace(/\(from .*?\)/, "(from `replaced original path`)")
14 | .replace(new RegExp(cwd, "g"), "");
15 | }
16 |
17 | export default (errors) =>
18 | errors.map((error) =>
19 | removeCWD(error.toString().split("\n").slice(0, 2).join("\n")),
20 | );
21 |
--------------------------------------------------------------------------------
/test/helpers/readAsset.mjs:
--------------------------------------------------------------------------------
1 | import path from "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 |
--------------------------------------------------------------------------------
/test/helpers/readAssets.mjs:
--------------------------------------------------------------------------------
1 | import readAsset from "./readAsset.mjs";
2 |
3 | export default function readAssets(compiler, stats) {
4 | const assets = {};
5 |
6 | Object.keys(stats.compilation.assets).forEach((asset) => {
7 | assets[asset] = readAsset(asset, compiler, stats);
8 | });
9 |
10 | return assets;
11 | }
12 |
--------------------------------------------------------------------------------
/test/helpers/testLoader.cjs:
--------------------------------------------------------------------------------
1 | function testLoader(content) {
2 | const result = { md: content };
3 |
4 | return `export default ${JSON.stringify(result)}`;
5 | }
6 |
7 | module.exports = testLoader;
8 |
--------------------------------------------------------------------------------
/test/loader.test.mjs:
--------------------------------------------------------------------------------
1 | import path from "path";
2 |
3 | import {
4 | compile,
5 | getExecutedCode,
6 | getCompiler,
7 | getErrors,
8 | getWarnings,
9 | } from "./helpers/index.mjs";
10 | import { fileURLToPath } from "url";
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).toEqual("bravo");
182 | expect(charlie).toEqual("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/validate-options.test.mjs:
--------------------------------------------------------------------------------
1 | import { getCompiler, compile } from "./helpers/index.mjs";
2 |
3 | let RemarkGFM;
4 |
5 | (async () => {
6 | RemarkGFM = await import("remark-gfm");
7 | })();
8 |
9 | describe("validate options", () => {
10 | const tests = {
11 | remarkOptions: {
12 | success: [
13 | {
14 | plugins: [RemarkGFM],
15 | },
16 | {
17 | settings: {
18 | bullet: "+",
19 | listItemIndent: "one",
20 | },
21 | },
22 | {
23 | data: {
24 | alpha: "bravo",
25 | charlie: "delta",
26 | },
27 | },
28 | ],
29 | failure: [
30 | true,
31 | "test",
32 | () => {},
33 | [],
34 | { unknown: false },
35 | { plugins: true },
36 | { settings: 100 },
37 | { data: "true" },
38 | ],
39 | },
40 | removeFrontMatter: {
41 | success: [true, false],
42 | failure: ["true", "test", () => {}, /test/],
43 | },
44 | unknown: {
45 | success: [],
46 | failure: [1, true, false, "test", /test/, [], {}, { foo: "bar" }],
47 | },
48 | };
49 |
50 | function stringifyValue(value) {
51 | if (
52 | Array.isArray(value) ||
53 | (value && typeof value === "object" && value.constructor === Object)
54 | ) {
55 | return JSON.stringify(value);
56 | }
57 |
58 | return value;
59 | }
60 |
61 | async function createTestCase(key, value, type) {
62 | it(`should ${
63 | type === "success" ? "successfully validate" : "throw an error on"
64 | } the "${key}" option with "${stringifyValue(value)}" value`, async () => {
65 | const compiler = getCompiler("simple.js", { [key]: value });
66 |
67 | let stats;
68 |
69 | try {
70 | stats = await compile(compiler);
71 | } finally {
72 | if (type === "success") {
73 | expect(stats.hasErrors()).toBe(false);
74 | } else if (type === "failure") {
75 | const {
76 | compilation: { errors },
77 | } = stats;
78 |
79 | expect(errors).toHaveLength(1);
80 | expect(() => {
81 | throw new Error(errors[0].error.message);
82 | }).toThrowErrorMatchingSnapshot();
83 | }
84 | }
85 | });
86 | }
87 |
88 | for (const [key, values] of Object.entries(tests)) {
89 | for (const type of Object.keys(values)) {
90 | for (const value of values[type]) {
91 | createTestCase(key, value, type);
92 | }
93 | }
94 | }
95 | });
96 |
--------------------------------------------------------------------------------