Skip to content

Commit

Permalink
added an option handleMissingLanguageAs for handling the missing la…
Browse files Browse the repository at this point in the history
…nguage. Added a test file for it and updated the `README.md` as well.
  • Loading branch information
“talatkuyuk” committed Jun 16, 2023
1 parent 167940e commit abea1b6
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ use(remarkCodeTitles, {
containerTagName: string, // optional, default is "div"
containerClassName: string, // optional, default is "remark-code-container"
containerProperties: (language?: string, title?: string) => Record<string, unknown>, // optional, default is undefined
handleMissingLanguageAs: string, // optional, default is undefined
})
```

Expand Down Expand Up @@ -146,6 +147,10 @@ It is a **string** option for providing custom className for the `container` nod

It is an option to set additional properties for the `container` node. It is a callback function that takes the `language` and the `title` as optional arguments and returns the object which is going to be used for adding additional properties into the `container` node.

#### `handleMissingLanguageAs`

It is a **string** option for providing custom language if the language is missing.

## Examples:

#### Example for only container
Expand Down Expand Up @@ -211,6 +216,30 @@ is going to produce the title `span` element just before the code block, like be
</pre>
```

#### Example for handling missing language

````markdown
```:filename
It is a line that does not related with any language.
```
````

```javascript
use(remarkCodeTitles, {
container: false,
handleMissingLanguageAs: "unknown",
});
```

is going to produce the title `span` element just before the code block, like below:

```html
<div class="remark-code-title">filename</div>
<pre>
<code class="language-unknown">It is a line that does not related with any language.</code>
</pre>
```

#### Example for line highlighting and line numbering options (for example: using with _rehype-prism-plus_)

````markdown
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lint": "eslint .",
"prettier": "prettier --write .",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.config.cjs",
"test:file": "NODE_OPTIONS=--experimental-vm-modules jest --config ./jest.config.cjs with_handle_missing_language.spec.ts",
"prepack": "npm run build",
"prepublishOnly": "npm test && npm run prettier && npm run lint"
},
Expand Down
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type CodeTitleOptions = {
containerTagName?: string;
containerClassName?: string;
containerProperties?: TPropertyFunction;
handleMissingLanguageAs?: string;
};

const DEFAULT_SETTINGS: CodeTitleOptions = {
Expand All @@ -27,6 +28,7 @@ const DEFAULT_SETTINGS: CodeTitleOptions = {
containerTagName: "div",
containerClassName: "remark-code-container",
containerProperties: undefined,
handleMissingLanguageAs: undefined,
};

/**
Expand Down Expand Up @@ -227,6 +229,14 @@ export const plugin: Plugin<[CodeTitleOptions?], Root> = (options) => {
}
}

if (
!language &&
options?.handleMissingLanguageAs &&
typeof options.handleMissingLanguageAs === "string"
) {
language = options.handleMissingLanguageAs;
}

return { title, language, meta };
};

Expand Down
276 changes: 276 additions & 0 deletions tests/with_handle_missing_language.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import { unified, type Processor } from "unified";
import remarkParse from "remark-parse";
import gfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import dedent from "dedent";
import type { VFileCompatible } from "vfile";

import plugin from "../src/index";

const compiler: Processor = unified()
.use(remarkParse)
.use(gfm)
.use(plugin, { container: false, handleMissingLanguageAs: "unknown" })
.use(remarkRehype)
.use(rehypeStringify);

const process = async (contents: VFileCompatible): Promise<VFileCompatible> => {
return compiler.process(contents).then((file) => file.value);
};

describe("remark-flexible-code-title, without container with only title", () => {
// ******************************************
it("Considers there is no language or title", async () => {
const input = dedent`
\`\`\`
const a = 1;
\`\`\`
`;

// { type: 'code', lang: null, meta: null, parent: 'root' }

const expected = dedent`
<pre><code>const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Considers there is no language or title, but only colon", async () => {
const input = dedent`
\`\`\`:
const a = 1;
\`\`\`
`;

// { type: 'code', lang: ':', meta: null, parent: 'root' } ==>> make the lang null

const expected = dedent`
<pre><code class="language-unknown">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Considers there is only the regular language", async () => {
const input = dedent`
\`\`\`js
const a = 1;
\`\`\`
`;

// { type: 'code', lang: 'js', meta: null, parent: 'root' }

const expected = dedent`
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles there is no title but colon", async () => {
const input = dedent`
\`\`\`js:
const a = 1;
\`\`\`
`;

// { type: 'code', lang: 'js:', meta: null, parent: 'root' } ==>> delete the colon in the lang

const expected = dedent`
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles there is no language but colon", async () => {
const input = dedent`
\`\`\`:title.js
const a = 1;
\`\`\`
`;

// ==>> make the lang null
// { type: 'code', lang: ':title.js', meta: null, parent: 'root' } first visit
// { type: 'code', lang: '', meta: null, parent: 'root' } second visit ==>> see the lang: null

const expected = dedent`
<div class="remark-code-title">title.js</div>
<pre><code class="language-unknown">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles there is syntax mismatch, having one space around the colon", async () => {
const input = dedent`
\`\`\`js : title.js meta
const a = 1;
\`\`\`
`;
// little complex logic; meta starts with the colon, so remove the first word from meta, if this is not reserved
// { type: 'code', lang: 'js', meta: ': title.js meta', parent: 'root' } ==>> little complex logic; remove title from meta

const expected = dedent`
<div class="remark-code-title">title.js</div>
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles there is syntax mismatch, having more space at the right of the colon", async () => {
const input = dedent`
\`\`\`js: title.js meta
const a = 1;
\`\`\`
`;

// ==>> nothing to do, there is no title, but remove the colon from the language at the end
// { type: 'code', lang: 'js:', meta: 'title.js meta', parent: 'root' }

const expected = dedent`
<div class="remark-code-title">title.js</div>
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles there is syntax mismatch, having more space at the both side of the colon", async () => {
const input = dedent`
\`\`\`js : title.js meta
const a = 1;
\`\`\`
`;

// little complex logic; meta starts with the colon, so remove the first word from meta, if this is not reserved
// { type: 'code', lang: 'js', meta: ': title.js meta', parent: 'root' }

const expected = dedent`
<div class="remark-code-title">title.js</div>
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles the regular language and the regular title", async () => {
const input = dedent`
\`\`\`js:title.js
const a = 1;
\`\`\`
`;

// { type: 'code', lang: 'js:title.js', meta: null, parent: 'root' } first visit
// { type: 'code', lang: 'js', meta: null, parent: 'root' } second visit

const expected = dedent`
<div class="remark-code-title">title.js</div>
<pre><code class="language-js">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles unknown language and the line range string within curly braces", async () => {
const input = dedent`
\`\`\`{2, 3 - 4, 5} showLineNumbers
const a = 1;
\`\`\`
`;

/*
{
type: 'code',
lang: '{2',
meta: ' 3 - 4, 5} showLineNumbers ',
parent: 'root'
}
*/

// { title: undefined, language: null, meta: '{2,3-4,5} showLineNumbers' }

const expected = dedent`
<pre><code class="language-unknown">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles the line range string within curly braces with language", async () => {
const input = dedent`
\`\`\`ts{2 , 3 - 4, 5} showLineNumbers
const a = 1;
\`\`\`
`;

/*
{
type: 'code',
lang: 'ts{2',
meta: ', 3 - 4, 5} showLineNumbers ',
parent: 'root'
}
*/

// { title: undefined, language: 'ts', meta: '{2,3-4,5} showLineNumbers' }

const expected = dedent`
<pre><code class="language-ts">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});

// ******************************************
it("Handles the line range string within curly braces with language and title", async () => {
const input = dedent`
\`\`\`typescript:title{2 , 3 - 4, 5} showLineNumbers
const a = 1;
\`\`\`
`;

/*
{
type: 'code',
lang: 'typescript{2',
meta: ', 3 - 4, 5} showLineNumbers ',
parent: 'root'
}
*/

// { title: undefined, language: 'typescript', meta: '{2,3-4,5} showLineNumbers' }

const expected = dedent`
<div class="remark-code-title">title</div>
<pre><code class="language-typescript">const a = 1;
</code></pre>
`;

expect(await process(input)).toBe(expected);
});
});
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"lib": ["ES2020"],
"target": "ES2020",
"module": "Node16",
"moduleResolution": "node16",
"strict": true,
"declaration": true,
"sourceMap": true,
Expand Down

0 comments on commit abea1b6

Please sign in to comment.