├── test ├── fixtures │ ├── test.exs │ ├── test.rs │ ├── test.js │ ├── test.ts │ ├── custom.hbs │ ├── line.js │ ├── marker.cpp │ └── template.hbs ├── patterns │ ├── import-only │ │ ├── actual.md │ │ ├── test.js │ │ └── expected.md │ ├── import-cpp-bug-workaround │ │ ├── test.cpp │ │ ├── actual.md │ │ ├── expected.md │ │ └── book.js │ ├── import-hardcode │ │ ├── test.ts │ │ ├── actual.md │ │ └── expected.md │ ├── import-slice │ │ ├── actual.md │ │ ├── test.js │ │ └── expected.md │ ├── import-title │ │ ├── test.js │ │ ├── actual.md │ │ └── expected.md │ ├── import-unindent │ │ ├── actual.md │ │ ├── test.js │ │ ├── expected.md │ │ └── book.js │ ├── import-exlixir-extension │ │ ├── test.exs │ │ ├── actual.md │ │ └── expected.md │ ├── import-rust-extension │ │ ├── actual.md │ │ ├── test.rs │ │ └── expected.md │ ├── import-ace-template │ │ ├── test.js │ │ ├── actual.md │ │ ├── book.js │ │ └── expected.md │ ├── import-book-options │ │ ├── actual.md │ │ ├── test.js │ │ ├── expected.md │ │ └── book.js │ ├── import-codeblock-issues-55 │ │ ├── actual.md │ │ ├── test.js │ │ ├── expected.md │ │ └── book.js │ ├── import-full-template │ │ ├── actual.md │ │ ├── test.js │ │ ├── book.js │ │ └── expected.md │ ├── import-snippet │ │ ├── actual.md │ │ ├── expected.md │ │ └── test.ts │ ├── import-acefull-template │ │ ├── actual.md │ │ ├── test.js │ │ ├── book.js │ │ └── expected.md │ ├── import-custom-template │ │ ├── test.js │ │ ├── actual.md │ │ ├── expected.md │ │ └── book.js │ ├── import-default-template │ │ ├── actual.md │ │ ├── test.js │ │ ├── expected.md │ │ └── book.js │ ├── import-output-escape │ │ ├── test&1.js │ │ ├── actual.md │ │ ├── book.js │ │ ├── dump.hbs │ │ └── expected.md │ ├── import-snippet-inner │ │ ├── actual.md │ │ ├── expected.md │ │ └── test.ts │ ├── import-unindent-slice │ │ ├── actual.md │ │ ├── test.js │ │ ├── expected.md │ │ └── book.js │ ├── import-book-command-options │ │ ├── test.js │ │ ├── actual.md │ │ ├── expected.md │ │ └── book.js │ ├── import-slice-only-first │ │ ├── actual.md │ │ ├── expected.md │ │ └── line.js │ ├── import-hardcode-not-found-warn │ │ ├── test.unknownLang │ │ ├── actual.md │ │ └── expected.md │ ├── import-slice-only-last │ │ ├── actual.md │ │ ├── expected.md │ │ └── line.js │ ├── import-snippet-multi │ │ ├── actual.md │ │ ├── expected.md │ │ └── test.ts │ ├── import-labels-ace │ │ ├── test.js │ │ ├── actual.md │ │ ├── book.js │ │ └── expected.md │ └── import-infinity-loop-issue-57 │ │ ├── book.js │ │ ├── actual.md │ │ └── expected.md ├── title-test.js ├── generate-test.js ├── language-detection-test.js ├── slicer-test.js ├── marker-test.js └── parser-test.js ├── examples ├── ace │ ├── src │ │ ├── test.js │ │ ├── test.ts │ │ ├── marker.ts │ │ └── line.js │ ├── user-template.hbs │ ├── SUMMARY.md │ ├── book.js │ ├── README.md │ ├── package.json │ ├── test_asciidoc.adoc │ └── test_markdown.md ├── custom │ ├── src │ │ ├── test.js │ │ ├── test.ts │ │ ├── marker.ts │ │ └── line.js │ ├── user-template.hbs │ ├── SUMMARY.md │ ├── README.md │ ├── book.js │ ├── package.json │ ├── test_asciidoc.adoc │ └── test_markdown.md └── default │ ├── src │ ├── test.js │ ├── test.ts │ ├── marker.ts │ └── line.js │ ├── SUMMARY.md │ ├── book.js │ ├── README.md │ ├── package.json │ ├── test_asciidoc.adoc │ └── test_markdown.md ├── .babelrc ├── .githooks └── pre-commit ├── .mocharc.json ├── templates ├── default-template.hbs ├── ace-template.hbs ├── full-template.hbs ├── acefull-template.hbs └── README.md ├── .editorconfig ├── .github └── workflows │ └── test.yml ├── src ├── title.js ├── backtick-maker.js ├── include-codeblock.js ├── ace-check.js ├── unescape-string.js ├── template.js ├── slicer.js ├── language-detection.js ├── marker.js ├── options.js └── parser.js ├── .eslintrc.js ├── bin └── include-codeblock.js ├── .gitignore ├── LICENSE ├── package.json └── README.md /test/fixtures/test.exs: -------------------------------------------------------------------------------- 1 | IO.puts "test" -------------------------------------------------------------------------------- /test/fixtures/test.rs: -------------------------------------------------------------------------------- 1 | extern crate num; -------------------------------------------------------------------------------- /test/fixtures/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/fixtures/test.ts: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/ace/src/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/ace/src/test.ts: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/custom/src/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/custom/src/test.ts: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/default/src/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /examples/default/src/test.ts: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/patterns/import-only/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) -------------------------------------------------------------------------------- /test/patterns/import-only/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/patterns/import-cpp-bug-workaround/test.cpp: -------------------------------------------------------------------------------- 1 | int i: -------------------------------------------------------------------------------- /test/patterns/import-hardcode/test.ts: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/patterns/import-slice/actual.md: -------------------------------------------------------------------------------- 1 | [import:1-2](test.js) -------------------------------------------------------------------------------- /test/patterns/import-title/test.js: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/patterns/import-unindent/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) -------------------------------------------------------------------------------- /test/patterns/import-exlixir-extension/test.exs: -------------------------------------------------------------------------------- 1 | IO.puts "test" -------------------------------------------------------------------------------- /test/patterns/import-rust-extension/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.rs) -------------------------------------------------------------------------------- /test/patterns/import-rust-extension/test.rs: -------------------------------------------------------------------------------- 1 | extern crate num; -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npx --no-install lint-staged 3 | -------------------------------------------------------------------------------- /test/patterns/import-ace-template/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-book-options/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-book-options/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-codeblock-issues-55/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) -------------------------------------------------------------------------------- /test/patterns/import-cpp-bug-workaround/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.cpp) -------------------------------------------------------------------------------- /test/patterns/import-exlixir-extension/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.exs) -------------------------------------------------------------------------------- /test/patterns/import-full-template/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-snippet/actual.md: -------------------------------------------------------------------------------- 1 | [import:"marker0"](test.ts) -------------------------------------------------------------------------------- /test/patterns/import-acefull-template/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-acefull-template/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-custom-template/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-default-template/actual.md: -------------------------------------------------------------------------------- 1 | [import](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-default-template/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-full-template/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-output-escape/test&1.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-snippet-inner/actual.md: -------------------------------------------------------------------------------- 1 | [import:"marker1"](test.ts) -------------------------------------------------------------------------------- /test/patterns/import-title/actual.md: -------------------------------------------------------------------------------- 1 | [include,title:"custom.js"](test.js) -------------------------------------------------------------------------------- /test/patterns/import-unindent-slice/actual.md: -------------------------------------------------------------------------------- 1 | [import:1-2](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-unindent/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "@babel/register" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/patterns/import-book-command-options/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; 4 | -------------------------------------------------------------------------------- /test/patterns/import-slice-only-first/actual.md: -------------------------------------------------------------------------------- 1 | [include:9-, line.js](line.js) -------------------------------------------------------------------------------- /test/patterns/import-unindent-slice/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | bar; 3 | baz; -------------------------------------------------------------------------------- /test/patterns/import-cpp-bug-workaround/expected.md: -------------------------------------------------------------------------------- 1 | ``` cpp 2 | int i: 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-hardcode-not-found-warn/test.unknownLang: -------------------------------------------------------------------------------- 1 | console.log("test"); -------------------------------------------------------------------------------- /test/patterns/import-hardcode/actual.md: -------------------------------------------------------------------------------- 1 | [import, lang:"typescript"](test.ts) 2 | -------------------------------------------------------------------------------- /test/patterns/import-snippet-inner/expected.md: -------------------------------------------------------------------------------- 1 | ``` xml 2 | var b; 3 | ``` 4 | -------------------------------------------------------------------------------- /templates/default-template.hbs: -------------------------------------------------------------------------------- 1 | {{{backtick}}} {{lang}} 2 | {{{content}}} 3 | {{{backtick}}} -------------------------------------------------------------------------------- /test/patterns/import-ace-template/actual.md: -------------------------------------------------------------------------------- 1 | [import,check:"false",edit:"true"](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-custom-template/actual.md: -------------------------------------------------------------------------------- 1 | [import, title:"title", id:"foo"](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-exlixir-extension/expected.md: -------------------------------------------------------------------------------- 1 | ``` elixir 2 | IO.puts "test" 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-hardcode/expected.md: -------------------------------------------------------------------------------- 1 | ``` typescript 2 | console.log("test"); 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-only/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | console.log("test"); 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-rust-extension/expected.md: -------------------------------------------------------------------------------- 1 | ``` rust 2 | extern crate num; 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-slice-only-last/actual.md: -------------------------------------------------------------------------------- 1 | [include:-2, title:"line.js", line.js](line.js) -------------------------------------------------------------------------------- /test/patterns/import-slice/test.js: -------------------------------------------------------------------------------- 1 | console.log(1); 2 | console.log(2); 3 | console.log(3); -------------------------------------------------------------------------------- /test/patterns/import-snippet-multi/actual.md: -------------------------------------------------------------------------------- 1 | [import:"marker0,marker1,marker2"](test.ts) 2 | -------------------------------------------------------------------------------- /test/patterns/import-title/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | console.log("test"); 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-unindent-slice/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | foo; 3 | bar; 4 | ``` 5 | -------------------------------------------------------------------------------- /test/patterns/import-labels-ace/test.js: -------------------------------------------------------------------------------- 1 | foo; 2 | //! [tag] 3 | bar; 4 | //! [tag] 5 | baz; 6 | -------------------------------------------------------------------------------- /test/patterns/import-unindent/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | foo; 3 | bar; 4 | baz; 5 | ``` 6 | -------------------------------------------------------------------------------- /test/patterns/import-book-command-options/actual.md: -------------------------------------------------------------------------------- 1 | [import, template:"ace", check:"false"](test.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-default-template/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | foo; 3 | bar; 4 | baz; 5 | 6 | ``` 7 | -------------------------------------------------------------------------------- /test/patterns/import-hardcode-not-found-warn/actual.md: -------------------------------------------------------------------------------- 1 | [import, lang:"unknownLang"](test.unknownLang) 2 | -------------------------------------------------------------------------------- /test/patterns/import-slice/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | console.log(1); 3 | console.log(2); 4 | ``` 5 | -------------------------------------------------------------------------------- /test/patterns/import-snippet/expected.md: -------------------------------------------------------------------------------- 1 | ``` xml 2 | var a; 3 | var b; 4 | var c; 5 | ``` 6 | -------------------------------------------------------------------------------- /test/fixtures/custom.hbs: -------------------------------------------------------------------------------- 1 | custom title "{{title}}" with id "{{id}}" 2 | ``` {{lang}} 3 | {{{content}}} 4 | ``` 5 | -------------------------------------------------------------------------------- /test/patterns/import-hardcode-not-found-warn/expected.md: -------------------------------------------------------------------------------- 1 | ``` unknownLang 2 | console.log("test"); 3 | ``` 4 | -------------------------------------------------------------------------------- /test/patterns/import-snippet-multi/expected.md: -------------------------------------------------------------------------------- 1 | ``` xml 2 | var a; 3 | var c; 4 | var e; 5 | ``` 6 | -------------------------------------------------------------------------------- /test/patterns/import-labels-ace/actual.md: -------------------------------------------------------------------------------- 1 | [include:"tag",check:"false",edit:"true",theme:"monokai",template:"ace"](test.js) 2 | -------------------------------------------------------------------------------- /examples/ace/user-template.hbs: -------------------------------------------------------------------------------- 1 | > {{fileName}} 2 | 3 | ``` {{lang}} 4 | {{{content}}} 5 | ``` -------------------------------------------------------------------------------- /examples/custom/user-template.hbs: -------------------------------------------------------------------------------- 1 | > {{fileName}} 2 | 3 | ``` {{lang}} 4 | {{{content}}} 5 | ``` -------------------------------------------------------------------------------- /test/patterns/import-codeblock-issues-55/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @example 3 | * ```js 4 | * a() 5 | * ``` 6 | */ 7 | function a(){ 8 | 9 | } -------------------------------------------------------------------------------- /test/patterns/import-custom-template/expected.md: -------------------------------------------------------------------------------- 1 | custom title "title" with id "foo" 2 | ``` javascript 3 | foo; 4 | bar; 5 | baz; 6 | 7 | ``` 8 | -------------------------------------------------------------------------------- /test/patterns/import-slice-only-first/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | console.log("this is line 9"); 3 | console.log("this is line 10"); 4 | ``` 5 | -------------------------------------------------------------------------------- /test/patterns/import-slice-only-last/expected.md: -------------------------------------------------------------------------------- 1 | ``` javascript 2 | console.log("this is line 1"); 3 | console.log("this is line 2"); 4 | ``` 5 | -------------------------------------------------------------------------------- /examples/ace/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # SUMMARY 2 | 3 | - [Readme](./README.md) 4 | - [Test markdown](./test_markdown.md) 5 | - [Test Asciidoc](./test_asciidoc.adoc) 6 | -------------------------------------------------------------------------------- /test/patterns/import-infinity-loop-issue-57/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": {} 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /examples/custom/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # SUMMARY 2 | 3 | - [Readme](./README.md) 4 | - [Test markdown](./test_markdown.md) 5 | - [Test Asciidoc](./test_asciidoc.adoc) 6 | -------------------------------------------------------------------------------- /examples/default/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # SUMMARY 2 | 3 | - [Readme](./README.md) 4 | - [Test markdown](./test_markdown.md) 5 | - [Test Asciidoc](./test_asciidoc.adoc) 6 | -------------------------------------------------------------------------------- /examples/ace/src/marker.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker1] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker0] 9 | } -------------------------------------------------------------------------------- /test/patterns/import-codeblock-issues-55/expected.md: -------------------------------------------------------------------------------- 1 | ```` javascript 2 | /** 3 | * @example 4 | * ```js 5 | * a() 6 | * ``` 7 | */ 8 | function a(){ 9 | 10 | } 11 | ```` -------------------------------------------------------------------------------- /examples/custom/src/marker.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker1] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker0] 9 | } -------------------------------------------------------------------------------- /examples/default/src/marker.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker1] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker0] 9 | } -------------------------------------------------------------------------------- /test/patterns/import-unindent-slice/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pluginsConfig: { 3 | "include-codeblock": { 4 | unindent: true 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-ace-template/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "ace", 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-codeblock-issues-55/book.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | module.exports = { 3 | "pluginsConfig": { 4 | "include-codeblock": { 5 | } 6 | } 7 | }; -------------------------------------------------------------------------------- /test/patterns/import-labels-ace/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "ace", 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-snippet/test.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker1] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker0] 9 | } -------------------------------------------------------------------------------- /test/patterns/import-cpp-bug-workaround/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "fixlang":"true" 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-full-template/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "full", 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-output-escape/actual.md: -------------------------------------------------------------------------------- 1 | [import, title:"title<1>", id:"edit:true<2>", lang:"javascript+theme:abc<3>", class="class<4>", edit=true, check=true, theme="monokai"](./test&1.js) 2 | -------------------------------------------------------------------------------- /test/patterns/import-snippet-inner/test.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker1] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker0] 9 | } -------------------------------------------------------------------------------- /test/patterns/import-acefull-template/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "acefull", 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-default-template/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "default", 5 | } 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/patterns/import-unindent/book.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | module.exports = { 3 | "pluginsConfig": { 4 | "include-codeblock": { 5 | "unindent": true 6 | } 7 | } 8 | }; -------------------------------------------------------------------------------- /templates/ace-template.hbs: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %}++++{% endif %} 2 | {%ace edit={{edit}}, check={{check}}, theme="{{theme}}", lang="{{lang}}" %} 3 | {{{content}}} 4 | {%endace%} 5 | {% if file.type=="asciidoc" %}++++{% endif %} 6 | -------------------------------------------------------------------------------- /test/patterns/import-labels-ace/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %}++++{% endif %} 2 | {%ace edit=true, check=false, theme="monokai", lang="javascript" %} 3 | bar; 4 | {%endace%} 5 | {% if file.type=="asciidoc" %}++++{% endif %} 6 | -------------------------------------------------------------------------------- /examples/default/book.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | module.exports = { 3 | "gitbook": "3.x.x", 4 | "title": "gitbook-plugin-include-codeblock example-default", 5 | "plugins": [ 6 | "include-codeblock" 7 | ] 8 | }; 9 | -------------------------------------------------------------------------------- /test/patterns/import-output-escape/book.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | module.exports = { 3 | "pluginsConfig": { 4 | "include-codeblock": { 5 | "template": path.join(__dirname, "dump.hbs") 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test/patterns/import-ace-template/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %}++++{% endif %} 2 | {%ace edit=true, check=false, theme="chrome", lang="javascript" %} 3 | foo; 4 | bar; 5 | baz; 6 | 7 | {%endace%} 8 | {% if file.type=="asciidoc" %}++++{% endif %} 9 | -------------------------------------------------------------------------------- /test/patterns/import-book-options/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %}++++{% endif %} 2 | {%ace edit=true, check=true, theme="monokai", lang="javascript" %} 3 | foo; 4 | bar; 5 | baz; 6 | 7 | {%endace%} 8 | {% if file.type=="asciidoc" %}++++{% endif %} 9 | -------------------------------------------------------------------------------- /test/patterns/import-book-command-options/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %}++++{% endif %} 2 | {%ace edit=true, check=false, theme="monokai", lang="javascript" %} 3 | foo; 4 | bar; 5 | baz; 6 | 7 | {%endace%} 8 | {% if file.type=="asciidoc" %}++++{% endif %} 9 | -------------------------------------------------------------------------------- /test/patterns/import-custom-template/book.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | module.exports = { 3 | "pluginsConfig": { 4 | "include-codeblock": { 5 | "template": path.join(__dirname,"../../fixtures/custom.hbs") 6 | } 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test/patterns/import-full-template/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %} 2 | > [[test.js]]link:test.js[test.js] 3 | {% else %} 4 | > test.js 5 | {% endif %} 6 | 7 | ``` javascript 8 | foo; 9 | bar; 10 | baz; 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /test/patterns/import-snippet-multi/test.ts: -------------------------------------------------------------------------------- 1 | function test(){ 2 | //! [marker0] 3 | var a; 4 | //! [marker0] 5 | var b; 6 | //! [marker1] 7 | var c; 8 | //! [marker1] 9 | var d; 10 | //! [marker2] 11 | var e; 12 | //! [marker2] 13 | } 14 | -------------------------------------------------------------------------------- /test/patterns/import-book-options/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "ace", 5 | "edit": "true", 6 | "check": "true", 7 | "theme": "monokai" 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/patterns/import-book-command-options/book.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "pluginsConfig": { 3 | "include-codeblock": { 4 | "template": "default", 5 | "edit": "true", 6 | "check": "true", 7 | "theme": "monokai" 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/patterns/import-output-escape/dump.hbs: -------------------------------------------------------------------------------- 1 | {{lang}} 2 | {{{lang}}} 3 | 4 | {{{content}}} 5 | 6 | {{originalPath}} 7 | {{{originalPath}}} 8 | 9 | {{fileName}} 10 | {{{fileName}}} 11 | 12 | {{title}} 13 | {{{title}}} 14 | 15 | {{id}} 16 | {{{id}}} 17 | 18 | {{class}} 19 | {{{class}}} 20 | 21 | {{edit}} 22 | 23 | {{check}} 24 | 25 | {{theme}} 26 | -------------------------------------------------------------------------------- /test/fixtures/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /examples/ace/src/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /examples/custom/src/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /examples/default/src/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /examples/ace/book.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | module.exports = { 3 | "gitbook": "3.x.x", 4 | "title": "gitbook-plugin-include-codeblock example-ace", 5 | "plugins": [ 6 | "include-codeblock", 7 | "ace" 8 | ], 9 | "pluginsConfig": { 10 | "include-codeblock": { 11 | "template": "ace" 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/patterns/import-slice-only-first/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /test/patterns/import-slice-only-last/line.js: -------------------------------------------------------------------------------- 1 | console.log("this is line 1"); 2 | console.log("this is line 2"); 3 | console.log("this is line 3"); 4 | console.log("this is line 4"); 5 | console.log("this is line 5"); 6 | console.log("this is line 6"); 7 | console.log("this is line 7"); 8 | console.log("this is line 8"); 9 | console.log("this is line 9"); 10 | console.log("this is line 10"); -------------------------------------------------------------------------------- /examples/ace/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## Test example 10 | 11 | Test full codes. 12 | 13 | test.js 14 | 15 | [import](./src/test.js) 16 | 17 | line.js 18 | 19 | [import](./src/line.js) 20 | 21 | test.ts 22 | 23 | [import](./src/test.ts) 24 | 25 | marker.ts 26 | 27 | [import](./src/marker.ts) 28 | -------------------------------------------------------------------------------- /test/patterns/import-output-escape/expected.md: -------------------------------------------------------------------------------- 1 | javascript+theme:abc<3> 2 | javascript+theme:abc<3> 3 | 4 | foo; 5 | bar; 6 | baz; 7 | 8 | 9 | ./test&1.js 10 | ./test&1.js 11 | 12 | test&1.js 13 | test&1.js 14 | 15 | title<1> 16 | title<1> 17 | 18 | edit:true<2> 19 | edit:true<2> 20 | 21 | class<4> 22 | class<4> 23 | 24 | true 25 | 26 | true 27 | 28 | monokai 29 | -------------------------------------------------------------------------------- /examples/custom/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## Test example 10 | 11 | Test full codes. 12 | 13 | test.js 14 | 15 | [import](./src/test.js) 16 | 17 | line.js 18 | 19 | [import](./src/line.js) 20 | 21 | test.ts 22 | 23 | [import](./src/test.ts) 24 | 25 | marker.ts 26 | 27 | [import](./src/marker.ts) 28 | -------------------------------------------------------------------------------- /examples/default/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## Test example 10 | 11 | Test full codes. 12 | 13 | test.js 14 | 15 | [import](./src/test.js) 16 | 17 | line.js 18 | 19 | [import](./src/line.js) 20 | 21 | test.ts 22 | 23 | [import](./src/test.ts) 24 | 25 | marker.ts 26 | 27 | [import](./src/marker.ts) 28 | -------------------------------------------------------------------------------- /test/patterns/import-acefull-template/expected.md: -------------------------------------------------------------------------------- 1 | {% if file.type=="asciidoc" %} 2 | > [[test.js]]link:test.js[test.js] 3 | {% else %} 4 | > test.js 5 | {% endif %} 6 | 7 | {% if file.type=="asciidoc" %}++++{% endif %} 8 | {%ace edit=false, check=false, theme="chrome", lang="javascript" %} 9 | foo; 10 | bar; 11 | baz; 12 | 13 | {%endace%} 14 | {% if file.type=="asciidoc" %}++++{% endif %} 15 | -------------------------------------------------------------------------------- /examples/custom/book.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const fs = require("fs"); 3 | module.exports = { 4 | "gitbook": "3.x.x", 5 | "title": "gitbook-plugin-include-codeblock example-custom", 6 | "plugins": [ 7 | "include-codeblock" 8 | ], 9 | "pluginsConfig": { 10 | "include-codeblock": { 11 | "template": path.join(__dirname,"user-template.hbs") 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | # Matches multiple files with brace expansion notation 9 | # Set default charset 10 | [*.{js}] 11 | charset = utf-8 12 | indent_style = space 13 | indent_size = 4 14 | 15 | # Matches the exact files either package.json or .travis.yml 16 | [{package.json,.travis.yml}] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /examples/ace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-ace", 3 | "version": "1.0.0", 4 | "description": "gitbook-plugin-include-codeblock", 5 | "private": true, 6 | "scripts": { 7 | "start": "honkit serve", 8 | "build": "honkit build", 9 | "test": "yarn install && npm run build" 10 | }, 11 | "keywords": [ 12 | "gitbook" 13 | ], 14 | "author": "azu", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "honkit": "^3.6.23", 18 | "gitbook-plugin-include-codeblock": "file:../../" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-custom", 3 | "version": "1.0.0", 4 | "description": "gitbook-plugin-include-codeblock", 5 | "private": true, 6 | "scripts": { 7 | "start": "honkit serve", 8 | "build": "honkit build", 9 | "test": "yarn install && npm run build" 10 | }, 11 | "keywords": [ 12 | "gitbook" 13 | ], 14 | "author": "azu", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "honkit": "^3.6.23", 18 | "gitbook-plugin-include-codeblock": "file:../../" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-default", 3 | "version": "1.0.0", 4 | "description": "gitbook-plugin-include-codeblock", 5 | "private": true, 6 | "scripts": { 7 | "start": "honkit serve", 8 | "build": "honkit build", 9 | "test": "yarn install && npm run build" 10 | }, 11 | "keywords": [ 12 | "gitbook" 13 | ], 14 | "author": "azu", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "honkit": "^3.6.23", 18 | "gitbook-plugin-include-codeblock": "file:../../" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/title-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const assert = require("assert"); 4 | import { getTitle } from "../src/title"; 5 | 6 | describe("title", function () { 7 | context("#getTitle", function () { 8 | it("should return the title", function () { 9 | const obj = { 10 | title: "an example of title", 11 | id: "test", 12 | marker: undefined 13 | }; 14 | const title = getTitle(obj); 15 | assert.equal(title, "an example of title"); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | name: "Test on Node.js ${{ matrix.node-version }}" 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | node-version: [12, 14, 16] 10 | steps: 11 | - name: checkout 12 | uses: actions/checkout@v2 13 | - name: setup Node.js ${{ matrix.node-version }} 14 | uses: actions/setup-node@v2 15 | with: 16 | node-version: ${{ matrix.node-version }} 17 | - name: Install 18 | run: yarn install 19 | - name: Test 20 | run: yarn test 21 | -------------------------------------------------------------------------------- /src/title.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | /* 3 | * Gibook usage: 4 | * 5 | * [import,title:](path/to/file) 6 | */ 7 | 8 | /* Get the specified 9 | * @example: 10 | * getTitle(keyValObject) 11 | * @param {Object} keyValObject 12 | * @return {string} 13 | */ 14 | export function getTitle(keyValObject) { 15 | return keyValObject.title; 16 | } 17 | 18 | /* Check if a title is specified in the option 19 | * @param {Object} keyValObject 20 | * @return {boolean} 21 | */ 22 | export function hasTitle(keyValObject) { 23 | const title = getTitle(keyValObject); 24 | return title !== undefined; 25 | } 26 | -------------------------------------------------------------------------------- /src/backtick-maker.js: -------------------------------------------------------------------------------- 1 | // MIT © 2017 azu 2 | "use strict"; 3 | 4 | const codeBlockBackTick = /```/; 5 | 6 | /** 7 | * backtcik count is 3 by default. 8 | * But, We should increase backtick if content include ```. 9 | * https://github.com/azu/gitbook-plugin-include-codeblock/issues/55 10 | * https://stackoverflow.com/questions/33224686/how-to-render-triple-backticks-as-inline-code-block-in-markdown 11 | * @param {string} content 12 | * @return {string} codebloack begin/end backtick 13 | */ 14 | export function codeBlockBacktick(content) { 15 | if (codeBlockBackTick.test(content)) { 16 | return "````"; 17 | } 18 | return "```"; 19 | } 20 | -------------------------------------------------------------------------------- /examples/ace/test_asciidoc.adoc: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/ace/test_markdown.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/custom/test_asciidoc.adoc: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/custom/test_markdown.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/default/test_asciidoc.adoc: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/default/test_markdown.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ## Usage 4 | 5 | npm install 6 | npm start 7 | open http://localhost:4000/ 8 | 9 | ## import/include 10 | 11 | [import, test.js](./src/test.js) 12 | 13 | ## Hardcoded class 14 | 15 | 16 | [import, test.ts, lang-typescript](src/test.ts) 17 | 18 | ### Sliced Code 19 | 20 | [import:5-8](src/line.js) 21 | 22 | ### Snippet code 23 | 24 | [import:"marker0", lang-typescript](src/marker.ts) 25 | 26 | 27 | {% if file.type=="asciidoc" %} 28 | > link:test.js[test.js] 29 | {% else %} 30 | > test.js 31 | {% endif %} 32 | 33 | ``` javascript 34 | console.log("test"); 35 | ``` 36 | -------------------------------------------------------------------------------- /test/fixtures/marker.cpp: -------------------------------------------------------------------------------- 1 | // test.cpp source code 2 | int main() 3 | { 4 | // Test inner markers. 5 | 6 | //! [marker0] 7 | int a; 8 | //! [marker1] 9 | int b; 10 | //! [marker1] 11 | int c; 12 | //! [marker0] 13 | 14 | // Test different comment style. 15 | 16 | /** [marker2] */ 17 | int d; 18 | /** [marker2] */ 19 | /// [marker3] 20 | int e; 21 | /// [marker3] 22 | 23 | // Test different naming and spacing. 24 | 25 | /// [marker 4] 26 | int f; 27 | /// [marker 4] 28 | 29 | /// [marker5 space] 30 | int g; 31 | /// [marker5 space] 32 | /// [ marker 6 ] 33 | int h; 34 | /// [ marker 6 ] 35 | } -------------------------------------------------------------------------------- /test/fixtures/template.hbs: -------------------------------------------------------------------------------- 1 | {{#if title}} 2 | {{#if id}} 3 | {% if file.type=="asciidoc" %} 4 | > [[{{id}}]]link:{{originalPath}}[{{title}}] 5 | {% else %} 6 | > {{title}} 7 | {% endif %} 8 | {{else}} 9 | {% if file.type=="asciidoc" %} 10 | > [[{{title}}]]link:{{originalPath}}[{{title}}] 11 | {% else %} 12 | > {{title}} 13 | {% endif %} 14 | {{/if}} 15 | {{else}} 16 | {% if file.type=="asciidoc" %} 17 | > [[{{fileName}}]]link:{{originalPath}}[{{fileName}}] 18 | {% else %} 19 | > {{fileName}} 20 | {% endif %} 21 | {{/if}} 22 | 23 | ``` {{lang}} 24 | {{{content}}} 25 | ``` -------------------------------------------------------------------------------- /templates/full-template.hbs: -------------------------------------------------------------------------------- 1 | {{#if title}} 2 | {{#if id}} 3 | {% if file.type=="asciidoc" %} 4 | > [[{{id}}]]link:{{originalPath}}[{{title}}] 5 | {% else %} 6 | > {{title}} 7 | {% endif %} 8 | {{else}} 9 | {% if file.type=="asciidoc" %} 10 | > [[{{title}}]]link:{{originalPath}}[{{title}}] 11 | {% else %} 12 | > {{title}} 13 | {% endif %} 14 | {{/if}} 15 | {{else}} 16 | {% if file.type=="asciidoc" %} 17 | > [[{{fileName}}]]link:{{originalPath}}[{{fileName}}] 18 | {% else %} 19 | > {{fileName}} 20 | {% endif %} 21 | {{/if}} 22 | 23 | {{{backtick}}} {{lang}} 24 | {{{content}}} 25 | {{{backtick}}} -------------------------------------------------------------------------------- /src/include-codeblock.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | var path = require("path"); 4 | import { parse } from "./parser"; 5 | import { aceCheck } from "./ace-check"; 6 | 7 | aceCheck(); 8 | 9 | module.exports = { 10 | hooks: { 11 | "page:before": function (page) { 12 | var options = this.options.pluginsConfig["include-codeblock"]; 13 | var pageDir = path.dirname(page.rawPath); 14 | var results = parse(page.content, pageDir, options); 15 | results.forEach((result) => { 16 | var { target, replaced } = result; 17 | page.content = page.content.replace(target, replaced); 18 | }); 19 | return page; 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | node: true, 6 | es6: true, 7 | mocha: true 8 | }, 9 | plugins: ["prettier"], 10 | extends: ["eslint:recommended"], 11 | parserOptions: { 12 | sourceType: "module" 13 | }, 14 | rules: { 15 | "prettier/prettier": [ 16 | "error", 17 | { 18 | singleQuote: false, 19 | printWidth: 120, 20 | tabWidth: 4, 21 | trailingComma: "none" 22 | } 23 | ], 24 | "prefer-const": "error", 25 | "no-console": 0, 26 | "no-extra-semi": 0, 27 | "no-mixed-spaces-and-tabs": 0 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/ace-check.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // Check that ace plugin is loaded after include-codeblock 3 | export function aceCheck() { 4 | // Check ace is used. 5 | try { 6 | require.resolve("gitbook-plugin-ace"); 7 | 8 | // Check is not currently loaded. 9 | const aceLoaded = Boolean(require("module")._cache[require.resolve("gitbook-plugin-ace")]); 10 | if (aceLoaded) { 11 | console.log(""); // flush 12 | console.error("`gitbook-plugin-include-codeblock` plugin must be loaded before `gitbook-plugin-ace`!"); 13 | } 14 | } catch (e) { 15 | console.log(""); // flush 16 | console.warn("ace features disabled (`gitbook-plugin-ace` required)"); 17 | } 18 | } 19 | 20 | /* eslint-enable */ 21 | -------------------------------------------------------------------------------- /templates/acefull-template.hbs: -------------------------------------------------------------------------------- 1 | {{#if title}} 2 | {{#if id}} 3 | {% if file.type=="asciidoc" %} 4 | > [[{{id}}]]link:{{originalPath}}[{{title}}] 5 | {% else %} 6 | > {{title}} 7 | {% endif %} 8 | {{else}} 9 | {% if file.type=="asciidoc" %} 10 | > [[{{title}}]]link:{{originalPath}}[{{title}}] 11 | {% else %} 12 | > {{title}} 13 | {% endif %} 14 | {{/if}} 15 | {{else}} 16 | {% if file.type=="asciidoc" %} 17 | > [[{{fileName}}]]link:{{originalPath}}[{{fileName}}] 18 | {% else %} 19 | > {{fileName}} 20 | {% endif %} 21 | {{/if}} 22 | 23 | {% if file.type=="asciidoc" %}++++{% endif %} 24 | {%ace edit={{edit}}, check={{check}}, theme="{{theme}}", lang="{{lang}}" %} 25 | {{{content}}} 26 | {%endace%} 27 | {% if file.type=="asciidoc" %}++++{% endif %} 28 | -------------------------------------------------------------------------------- /test/patterns/import-infinity-loop-issue-57/actual.md: -------------------------------------------------------------------------------- 1 | Issue by [Infinite loop with a specific markdown file · Issue #57 · azu/gitbook-plugin-include-codeblock](https://github.com/azu/gitbook-plugin-include-codeblock/issues/57 "Infinite loop with a specific markdown file · Issue #57 · azu/gitbook-plugin-include-codeblock") 2 | 3 | [\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890](\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890 4 | -------------------------------------------------------------------------------- /test/patterns/import-infinity-loop-issue-57/expected.md: -------------------------------------------------------------------------------- 1 | Issue by [Infinite loop with a specific markdown file · Issue #57 · azu/gitbook-plugin-include-codeblock](https://github.com/azu/gitbook-plugin-include-codeblock/issues/57 "Infinite loop with a specific markdown file · Issue #57 · azu/gitbook-plugin-include-codeblock") 2 | 3 | [\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890[\1\2\34567890](\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890(\1\2\34567890 4 | -------------------------------------------------------------------------------- /src/unescape-string.js: -------------------------------------------------------------------------------- 1 | // The code in this file is extracted from commonmark.js 2 | // (https://github.com/jgm/commonmark.js), which is owned by John MacFarlane. 3 | // LICENSE : BSD-2-Clause 4 | "use strict"; 5 | 6 | var decodeHTML = require("entities").decodeHTML; 7 | 8 | var C_BACKSLASH = 92; 9 | var ENTITY = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"; 10 | var reBackslashOrAmp = /[\\&]/; 11 | var ESCAPABLE = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"; 12 | var reEntityOrEscapedChar = new RegExp("\\\\" + ESCAPABLE + "|" + ENTITY, "gi"); 13 | 14 | function unescapeChar(s) { 15 | if (s.charCodeAt(0) === C_BACKSLASH) { 16 | return s.charAt(1); 17 | } else { 18 | return decodeHTML(s); 19 | } 20 | } 21 | 22 | // Replace entities and backslash escapes with literal characters. 23 | export function unescapeString(s) { 24 | if (reBackslashOrAmp.test(s)) { 25 | return s.replace(reEntityOrEscapedChar, unescapeChar); 26 | } else { 27 | return s; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bin/include-codeblock.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | const meow = require('meow'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const parse = require("../lib/parser").parse; 7 | const cli = meow(` 8 | Usage 9 | $ include-codeblock --output 10 | 11 | Options 12 | --output Output to write index json file 13 | 14 | Other Options: 15 | same with gitbook config 16 | For example, --unindent=true 17 | 18 | Example: 19 | $ include-codeblock ./README.md --output RENDER_README.md 20 | `); 21 | // main 22 | const input = cli.input[0]; 23 | if (!input) { 24 | cli.showHelp(); 25 | } 26 | const inputContent = fs.readFileSync(input, "utf-8"); 27 | const baseDir = path.dirname(path.resolve(process.cwd(), input)); 28 | const replacements = parse(inputContent, baseDir, cli.flags); 29 | const replaceContent = (inputContent, replacements) => { 30 | replacements.forEach(result => { 31 | const { target, replaced } = result; 32 | inputContent = inputContent.replace(target, replaced); 33 | }); 34 | return inputContent; 35 | }; 36 | const outputContent = replaceContent(inputContent, replacements); 37 | if (cli.flags.output) { 38 | fs.writeFileSync(cli.flags.output, outputContent, "utf-8"); 39 | } else { 40 | console.log(outputContent); 41 | } 42 | -------------------------------------------------------------------------------- /src/template.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const fs = require("fs"); 4 | import { defaultBookOptionsMap, defaultTemplateMap } from "./options.js"; 5 | 6 | /** 7 | * Sunc file read with path check 8 | * @param {string} path 9 | * @return {string} 10 | */ 11 | export function readFileFromPath(path) { 12 | let content; 13 | try { 14 | content = fs.readFileSync(path, "utf8"); 15 | } catch (err) { 16 | if (err.code === "ENOENT") { 17 | console.warn("Error: file not found: " + path); 18 | return "Error: file not found: " + path; 19 | } else { 20 | throw err; 21 | } 22 | } 23 | return content; 24 | } 25 | 26 | /** 27 | * Load template from template label 28 | * @param {object} kvMap 29 | * @return {string} 30 | */ 31 | export function getTemplateContent(kvMap) { 32 | const t = kvMap.template; 33 | const dt = defaultBookOptionsMap.template; 34 | const tPath = defaultTemplateMap[t]; 35 | const dtPath = defaultTemplateMap[dt]; 36 | 37 | const isTemplateDefault = t === dt; 38 | const isTemplatePath = tPath === undefined; 39 | 40 | let p; 41 | // No template option. 42 | if (isTemplateDefault) { 43 | p = dtPath; 44 | } else if (isTemplatePath) { 45 | // Template option is a path. 46 | p = t; 47 | } else { 48 | // Template option one of template/ directory. 49 | p = tPath || dtPath; 50 | } 51 | return readFileFromPath(p); 52 | } 53 | -------------------------------------------------------------------------------- /test/generate-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const assert = require("assert"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | import { parse } from "../src/parser"; 7 | function trim(str) { 8 | return str.replace(/^\s+|\s+$/, ""); 9 | } 10 | 11 | describe("generate test", () => { 12 | const fixturesDir = path.join(__dirname, "patterns"); 13 | fs.readdirSync(fixturesDir).map((caseName) => { 14 | it(`should ${caseName.split("-").join(" ")}`, () => { 15 | const fixtureDir = path.join(fixturesDir, caseName); 16 | let actualPath = path.join(fixtureDir, "actual.md"); 17 | let content = fs.readFileSync(actualPath, "utf-8"); 18 | const bookjs = path.join(fixtureDir, "book.js"); 19 | const options = {}; 20 | try { 21 | const book = require(bookjs); 22 | Object.assign(options, book.pluginsConfig["include-codeblock"]); 23 | } catch (e) { 24 | // no book 25 | } 26 | const results = parse(content, fixtureDir, options); 27 | results.forEach((result) => { 28 | const { target, replaced } = result; 29 | content = content.replace(target, replaced); 30 | }); 31 | if (path.sep === "\\") { 32 | // Specific case of windows, transformFileSync return code with '/' 33 | actualPath = actualPath.replace(/\\/g, "/"); 34 | } 35 | 36 | const expected = fs 37 | .readFileSync(path.join(fixtureDir, "expected.md")) 38 | .toString() 39 | .replace(/%FIXTURE_PATH%/g, actualPath); 40 | assert.equal(trim(content), trim(expected)); 41 | return true; 42 | }); 43 | return true; 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | _book/ 3 | ### https://raw.github.com/github/gitignore/408c616ae0ad8f4b8101d8e876b9b67ac6b14059/Node.gitignore 4 | 5 | # Logs 6 | logs 7 | *.log 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | # Commenting this out is preferred by some people, see 31 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 32 | node_modules 33 | 34 | 35 | ### https://raw.github.com/github/gitignore/408c616ae0ad8f4b8101d8e876b9b67ac6b14059/Global/JetBrains.gitignore 36 | 37 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 38 | 39 | *.iml 40 | 41 | ## Directory-based project format: 42 | .idea/ 43 | # if you remove the above rule, at least ignore the following: 44 | 45 | # User-specific stuff: 46 | # .idea/workspace.xml 47 | # .idea/tasks.xml 48 | # .idea/dictionaries 49 | 50 | # Sensitive or high-churn files: 51 | # .idea/dataSources.ids 52 | # .idea/dataSources.xml 53 | # .idea/sqlDataSources.xml 54 | # .idea/dynamic.xml 55 | # .idea/uiDesigner.xml 56 | 57 | # Gradle: 58 | # .idea/gradle.xml 59 | # .idea/libraries 60 | 61 | # Mongo Explorer plugin: 62 | # .idea/mongoSettings.xml 63 | 64 | ## File-based project format: 65 | *.ipr 66 | *.iws 67 | 68 | ## Plugin-specific files: 69 | 70 | # IntelliJ 71 | out/ 72 | 73 | # mpeltonen/sbt-idea plugin 74 | .idea_modules/ 75 | 76 | # JIRA plugin 77 | atlassian-ide-plugin.xml 78 | 79 | # Crashlytics plugin (for Android Studio and IntelliJ) 80 | com_crashlytics_export_strings.xml 81 | crashlytics.properties 82 | crashlytics-build.properties 83 | 84 | 85 | /.eslintcache 86 | -------------------------------------------------------------------------------- /src/slicer.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | /* 4 | format: [import:-](path/to/file) 5 | lineNumber start with 1. 6 | 7 | Patterns: 8 | 9 | All: [import, hello-world.js](../src/hello-world.js) 10 | 1-2: [import:1-2, hello-world.js](../src/hello-world.js) 11 | 2-3: [import:2-3, hello-world.js](../src/hello-world.js) 12 | 2>=: [import:2-, hello-world.js](../src/hello-world.js) 13 | <=3: [import:-3, hello-world.js](../src/hello-world.js) 14 | */ 15 | /** 16 | * get range from label 17 | * @param {string} label 18 | * @returns {number[]} 19 | */ 20 | export function getSliceRange(label) { 21 | const regExp = /^(?:include|import):(\d*)-(\d*)[,\s]?.*$/; 22 | const matches = regExp.exec(label); 23 | if (matches === null) { 24 | return []; 25 | } 26 | // return [undefined, undefined] if not matched, else contains [all,start,end]. 27 | const [start, end] = matches.slice(1, 3); 28 | const startOrUndefined = start !== "" ? parseInt(start, 10) : undefined; 29 | const endOrUndefined = end !== "" ? parseInt(end, 10) : undefined; 30 | return [startOrUndefined, endOrUndefined]; 31 | } 32 | 33 | /** 34 | * has range command in the label 35 | * @param {string} label 36 | * @returns {boolean} 37 | */ 38 | export function hasSliceRange(label) { 39 | const range = getSliceRange(label); 40 | const [start, end] = range; 41 | return start !== undefined || end !== undefined; 42 | } 43 | 44 | /** 45 | * slice {@link code} with {@link start} and {@link end} 46 | * @param {string} code 47 | * @param {number|undefined} [start] 48 | * @param {number|undefined} [end] 49 | * @param {boolean|undefined} [untrimmed] 50 | * @returns {string} 51 | */ 52 | export function sliceCode(code, start, end, untrimmed) { 53 | if (start === undefined && end === undefined) { 54 | return code; 55 | } 56 | const slitted = code.split("\n"); 57 | if (start === undefined) { 58 | start = 1; 59 | } 60 | if (end === undefined) { 61 | end = slitted.length; 62 | } 63 | const sliced = slitted.slice(start - 1, end).join("\n"); 64 | return untrimmed ? sliced : sliced.trim(); 65 | } 66 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Template 2 | 3 | - Should remove space each line 4 | - `{{` and `}}` is GitBook template 5 | - [Templating · GitBook Toolchain Documentation](http://toolchain.gitbook.com/templating/) 6 | - `{%` and `%}` is handlebar template 7 | - [Handlebars.js: Minimal Templating on Steroids](http://handlebarsjs.com/) 8 | 9 | ## Values 10 | 11 | - `{{lang}}`: language for highlight 12 | - `{{{content}}}`: Code 13 | - `{{originalPath}}`: file path 14 | - `{{fileName}}`: file name 15 | - `{{count}}` : number of CodeBlock 16 | - `{{title}}`: if label contain `title:` like this `[import, title:]` 17 | - `{{id}}`: if label contain `id:` like this `[import, id:]` 18 | - `{{class}}`: if label contain `class:` like this `[import, class:]` 19 | 20 | ### ace plugin specific 21 | 22 | - {{edit}} : allow code edition 23 | - {{check}} : allow syntax validation 24 | - {{theme}} : label for the theme 25 | 26 | ## Limitation 27 | 28 | You should write with no space(indent). 29 | Spacing conflict GitBook behavior. 30 | 31 | ## Template Example 32 | 33 | Version 1.x template. 34 | 35 | {{#if title}} 36 | {{#if id}} 37 | {% if file.type=="asciidoc" %} 38 | > [[{{id}}]]link:{{originalPath}}[{{title}}] 39 | {% else %} 40 | > {{title}} 41 | {% endif %} 42 | {{else}} 43 | {% if file.type=="asciidoc" %} 44 | > [[{{title}}]]link:{{originalPath}}[{{title}}] 45 | {% else %} 46 | > {{title}} 47 | {% endif %} 48 | {{/if}} 49 | {{else}} 50 | {% if file.type=="asciidoc" %} 51 | > [[{{fileName}}]]link:{{originalPath}}[{{fileName}}] 52 | {% else %} 53 | > {{fileName}} 54 | {% endif %} 55 | {{/if}} 56 | 57 | ``` {{lang}} 58 | {{{content}}} 59 | ``` 60 | 61 | ### Compile Example 62 | 63 | If use version 1.x template 64 | 65 | [import](test.rs) 66 | 67 | to be 68 | 69 | {% if file.type=="asciidoc" %} 70 | > [[test.rs]]link:test.rs[test.rs] 71 | {% else %} 72 | > test.rs 73 | {% endif %} 74 | 75 | ``` rust 76 | extern crate num; 77 | ``` 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 azu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | --- 22 | 23 | src/unescape-string.js is extracted from https://github.com/jgm/commonmark.js: 24 | 25 | Copyright (c) 2014, John MacFarlane 26 | 27 | All rights reserved. 28 | 29 | Redistribution and use in source and binary forms, with or without 30 | modification, are permitted provided that the following conditions are met: 31 | 32 | * Redistributions of source code must retain the above copyright 33 | notice, this list of conditions and the following disclaimer. 34 | 35 | * Redistributions in binary form must reproduce the above 36 | copyright notice, this list of conditions and the following 37 | disclaimer in the documentation and/or other materials provided 38 | with the distribution. 39 | 40 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 41 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 42 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 43 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 44 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 46 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 50 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 | -------------------------------------------------------------------------------- /test/language-detection-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const assert = require("assert"); 4 | import { defaultKeyValueMap } from "../src/options.js"; 5 | import { getLang, lookupLanguageByAceMode, lookupLanguageByExtension } from "../src/language-detection"; 6 | 7 | const kvmap = defaultKeyValueMap; 8 | 9 | describe("language-detection", function () { 10 | describe("#lookupLanguageByAceMode", function () { 11 | it("should resolve language type by acemode", function () { 12 | const kvm = Object.assign({}, kvmap); 13 | kvm.lang = "typescript"; 14 | const aceMode = lookupLanguageByAceMode(kvm); 15 | assert.equal(aceMode, "typescript"); 16 | }); 17 | }); 18 | describe("#lookupLanguageByExtension", function () { 19 | context("lang ext or file ext", function () { 20 | it("should resolve acemode by lang ext", function () { 21 | const kvm = Object.assign({}, kvmap); 22 | kvm.lang = ".js"; 23 | const aceMode = lookupLanguageByExtension(kvm, "/path/to/file.rs"); 24 | assert.equal(aceMode, "javascript"); 25 | }); 26 | it("should resolve acemode by file ext", function () { 27 | const aceMode = lookupLanguageByExtension(kvmap, "/path/to/file.rs"); 28 | assert.equal(aceMode, "rust"); 29 | }); 30 | }); 31 | }); 32 | describe("#getLang", function () { 33 | context("specified aceMode", function () { 34 | it("should prefer use aceMode", function () { 35 | const kvm = Object.assign({}, kvmap); 36 | kvm.lang = "typescript"; 37 | Object.freeze(kvm); 38 | const kvml = getLang(kvm, "/path/to/file.js"); 39 | const lang = kvml.lang; 40 | assert.equal(lang, "typescript"); 41 | }); 42 | it("should detect using aceMode", function () { 43 | const kvm = Object.assign({}, kvmap); 44 | kvm.lang = "typescript"; 45 | Object.freeze(kvm); 46 | const kvml = getLang(kvm, "/path/to/file.ts"); 47 | const lang = kvml.lang; 48 | assert.equal(lang, "typescript"); 49 | }); 50 | it("should detect use default, aceMode not found (+warn)", function () { 51 | const kvml = getLang(kvmap, "/path/to/file.fakext"); 52 | const lang = kvml.lang; 53 | assert.equal(lang, ""); 54 | }); 55 | }); 56 | context("other using ext", function () { 57 | it("should detect using ext", function () { 58 | const kvml = getLang(kvmap, "/path/to/file.rs"); 59 | const lang = kvml.lang; 60 | assert.equal(lang, "rust"); 61 | }); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/language-detection.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const path = require("path"); 4 | const languageMap = require("language-map"); 5 | import { defaultKeyValueMap } from "./options.js"; 6 | 7 | // Workaround for not working languages. 8 | // Redefine aceMode locally. 9 | // @param {string} 10 | // @return {string} 11 | export function languageAceModeFix(resultAceMode) { 12 | if (resultAceMode == "c_cpp") { 13 | resultAceMode = "cpp"; 14 | } 15 | return resultAceMode; 16 | } 17 | 18 | /** 19 | * Return aceMode from lang in kvMap. 20 | * @param {object} kvMap 21 | * @return {object} 22 | */ 23 | export function lookupLanguageByAceMode(kvMap) { 24 | let resultAceMode; 25 | const matchLang = kvMap.lang; 26 | Object.keys(languageMap).some((langKey) => { 27 | const aceMode = languageMap[langKey].aceMode; 28 | if (matchLang === aceMode) { 29 | resultAceMode = aceMode; 30 | return resultAceMode; 31 | } 32 | return undefined; 33 | }); 34 | return resultAceMode; 35 | } 36 | 37 | /** 38 | * Return aceMode from file extension or lang in kvMap, if is 39 | * an extension. 40 | * @param {object} kvMap 41 | * @param {string} filePath 42 | * @return {object} 43 | */ 44 | export function lookupLanguageByExtension(kvMap, filePath) { 45 | const lang = kvMap.lang; 46 | let ext; 47 | // Check first if map `lang` is an extension string. 48 | const matchext = /(.+)/g.exec(lang); 49 | if (matchext != null) { 50 | ext = matchext[1]; 51 | } else { 52 | // Load from file extension. 53 | ext = path.extname(filePath); 54 | } 55 | let aceMode; 56 | Object.keys(languageMap).some((langKey) => { 57 | const extensions = languageMap[langKey].extensions; 58 | if (!extensions) { 59 | return false; 60 | } 61 | return extensions.some((extension) => { 62 | if (ext === extension) { 63 | aceMode = languageMap[langKey].aceMode; 64 | } 65 | return false; 66 | }); 67 | }); 68 | return aceMode; 69 | } 70 | 71 | /** 72 | * Update key-value map lang with aceMode lang. 73 | * @param {object} kvMap 74 | * @param {string} filePath 75 | * @return {object} 76 | */ 77 | export function getLang(kvMap, filePath) { 78 | let aceMode; 79 | // Retrieve ace mode from lang. 80 | if (kvMap.lang !== defaultKeyValueMap.lang) { 81 | aceMode = lookupLanguageByAceMode(kvMap); 82 | } 83 | // Retrieve ace mode from file ext or lang ext. 84 | if (aceMode === undefined) { 85 | aceMode = lookupLanguageByExtension(kvMap, filePath); 86 | } 87 | // Ace mode not found, keep default. 88 | if (aceMode === undefined) { 89 | console.warn("include-codeblock: unknown language `" + kvMap.lang + "`, use default"); 90 | return kvMap; 91 | } 92 | if (kvMap.fixlang) { 93 | aceMode = languageAceModeFix(aceMode); 94 | } 95 | const kvm = Object.assign({}, kvMap); 96 | kvm.lang = aceMode; 97 | return Object.freeze(kvm); 98 | } 99 | -------------------------------------------------------------------------------- /src/marker.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | /* 3 | * Feature: doxygen like snippet code. 4 | * For code source documenting, see 5 | * https://www.stack.nl/~dimitri/doxygen/manual/commands.html#cmdsnippet 6 | * 7 | * Gibook usage: 8 | * 9 | * [import:](path/to/file) 10 | * 11 | * NB: markername must begin with a letter to avoid conflict with slice 12 | * line range. 13 | */ 14 | "use strict"; 15 | const commentOpen = "(/+/+|#|%|/\\*|)?"; 17 | const doxChar = "[*!/#]"; // doxygen documentation character 18 | const spaces = "[ \t]*"; // h spaces 19 | const spacesAny = "\\s*"; // h+v spaces 20 | const markerNameFormat = "(\\s*[a-zA-Z][\\w\\s]*)"; // Must contain a char. 21 | 22 | /* 23 | * format: [import:](path/to/file) 24 | * @param {Object} keyValObject 25 | * @return {string} 26 | */ 27 | export function getMarker(keyValObject) { 28 | return keyValObject.marker; 29 | } 30 | 31 | /** 32 | * format: [import:](path/to/file) 33 | * check if the import filled has a markername. 34 | * @example: 35 | * hasMarker(label) 36 | * @param {Object} keyValObject 37 | * @returns {boolean} 38 | */ 39 | export function hasMarker(keyValObject) { 40 | const marker = getMarker(keyValObject); 41 | return marker !== undefined && marker !== ""; 42 | } 43 | 44 | /* Parse the code from given markers 45 | * 46 | * see test/marker-test.js 47 | */ 48 | /** 49 | * get sliced code by {@link markername} 50 | * @param {string} code 51 | * @param {string} markers 52 | * @returns {string} 53 | */ 54 | export function markerSliceCode(code, markers) { 55 | if (markers === undefined || markers === "") { 56 | return code; 57 | } 58 | var parsedcode = ""; 59 | const markerlist = markers.split(","); 60 | 61 | let i = 0; 62 | // regex 63 | markerlist.forEach((marker) => { 64 | const balise = "\\[" + marker + "\\]"; 65 | const pattern = "\\n" + spacesAny + commentOpen + doxChar + spaces + balise + spaces + commentClose + spaces; 66 | 67 | const regstr = pattern + "\\n*([\\s\\S]*)" + pattern; 68 | const reg = new RegExp(regstr); 69 | const res = code.match(reg); 70 | 71 | if (res) { 72 | parsedcode += res[3]; // count parenthesis in pattern. 73 | } else { 74 | console.warn("markersSliceCode(): marker `" + marker + "` not found"); 75 | parsedcode += "Error: marker `" + marker + "` not found"; 76 | } 77 | if (markerlist.length > 0 && i < markerlist.length - 1) { 78 | parsedcode += "\n"; 79 | } 80 | i++; 81 | }); 82 | return parsedcode; 83 | } 84 | 85 | /** Replace all regex occurence by sub in the string str, 86 | * @param {string} str 87 | * @param {string} reg 88 | * @param {string} sub 89 | * @return {string} 90 | */ 91 | export function replaceAll(str, reg, sub) { 92 | return str.replace(new RegExp(reg, "g"), sub); 93 | } 94 | 95 | /** Function that remove all markers in the given code 96 | * @param {string} code 97 | * @return {string} 98 | */ 99 | export function removeMarkers(code) { 100 | // various language comment 101 | const tag = "\\[" + markerNameFormat + "\\]"; 102 | const pattern = spacesAny + commentOpen + doxChar + spaces + tag + spaces + commentClose + spaces; 103 | 104 | return replaceAll(code, pattern, ""); 105 | } 106 | -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | // Notes: 3 | // 1) If you add new options type, you have to update type checks in parser.js 4 | // (see parseVariableFromMap). 5 | // 2) The default map (objects) are immutable (frozen). They are updated (new map 6 | // with different names) while parsing book.json options first, then eventually 7 | // overwriten by commands options. 8 | "use strict"; 9 | const path = require("path"); 10 | const cfg = require("../package.json").gitbook.properties; 11 | 12 | export const defaultTemplateMap = Object.freeze({ 13 | default: path.join(__dirname, "..", "templates", "default-template.hbs"), 14 | full: path.join(__dirname, "..", "templates", "full-template.hbs"), 15 | ace: path.join(__dirname, "..", "templates", "ace-template.hbs"), 16 | acefull: path.join(__dirname, "..", "templates", "acefull-template.hbs") 17 | }); 18 | 19 | // Map for Book.json options. (avoid `undefined` for ace options), 20 | // NB: Default book option, type, desc are set in the package.json file. 21 | export const defaultBookOptionsMap = Object.freeze({ 22 | check: cfg.check.default, 23 | edit: cfg.edit.default, 24 | lang: cfg.lang.default, 25 | fixlang: cfg.fixlang.default, 26 | template: cfg.template.default, 27 | theme: cfg.theme.default, 28 | unindent: cfg.unindent.default 29 | }); 30 | 31 | // Possible command key-values (kv). 32 | // (avoid undefined default value because we check value types). 33 | export const defaultKeyValueMap = Object.freeze({ 34 | // Local 35 | class: "", 36 | id: "", 37 | marker: "", 38 | name: "", 39 | title: "", 40 | // Global/Local 41 | check: defaultBookOptionsMap.check, 42 | edit: defaultBookOptionsMap.edit, 43 | lang: defaultBookOptionsMap.lang, 44 | fixlang: defaultBookOptionsMap.fixlang, 45 | template: defaultBookOptionsMap.template, 46 | theme: defaultBookOptionsMap.theme, 47 | unindent: defaultBookOptionsMap.unindent 48 | }); 49 | 50 | /** 51 | * Convert string value to value type. 52 | * @param {string} valtype 53 | */ 54 | export function convertValue(valstr, valtype) { 55 | // remove quotes 56 | if (valtype === "boolean" || valtype === "number") { 57 | return JSON.parse(valstr); 58 | } 59 | return valstr; 60 | } 61 | 62 | /** 63 | * Check that maps types equal to default key value map. 64 | * @param {object} kvMap 65 | * @param {string} funcLabel 66 | */ 67 | export function checkMapTypes(kvMap, funcLabel) { 68 | Object.keys(kvMap).forEach((key) => { 69 | if (defaultKeyValueMap[key] !== undefined) { 70 | const leftType = typeof kvMap[key]; 71 | const rightType = typeof defaultKeyValueMap[key]; 72 | if (!(leftType === rightType)) { 73 | console.error( 74 | `include-codeblock: checkMapTypes (${funcLabel}) : wrong value type for key \`${key}\`: key type: \`${leftType}\` (!= \`${rightType}\`)` 75 | ); 76 | } 77 | } 78 | }); 79 | } 80 | 81 | /** 82 | * Check that maps types equal to default key value map. 83 | * @param {{template?: string}} options 84 | * @return {object} kvMap 85 | */ 86 | export function initOptions(options) { 87 | const dbom = defaultBookOptionsMap; 88 | const kv = Object.assign({}, defaultKeyValueMap); 89 | // Overwrite default value with user book options. 90 | Object.keys(dbom).forEach((key) => { 91 | if (options[key] != undefined) { 92 | kv[key] = convertValue(options[key], typeof dbom[key]); 93 | } 94 | }); 95 | const kvmap = Object.freeze(kv); 96 | checkMapTypes(kvmap, "initOptions"); 97 | return kvmap; 98 | } 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitbook-plugin-include-codeblock", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/azu/gitbook-plugin-include-codeblock.git" 6 | }, 7 | "author": "azu", 8 | "email": "azuciao@gmail.com", 9 | "homepage": "https://github.com/azu/gitbook-plugin-include-codeblock", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/azu/gitbook-plugin-include-codeblock/issues" 13 | }, 14 | "version": "3.2.3", 15 | "description": "GitBook plugin for including file", 16 | "main": "lib/include-codeblock.js", 17 | "bin": { 18 | "include-codeblock": "./bin/include-codeblock.js" 19 | }, 20 | "files": [ 21 | "bin", 22 | "lib", 23 | "templates", 24 | "src" 25 | ], 26 | "directories": { 27 | "test": "test" 28 | }, 29 | "scripts": { 30 | "build": "NODE_ENV=production babel src --out-dir lib --source-maps", 31 | "lint": "eslint --cache src/*.js test/*.js", 32 | "lint:fix": "eslint --fix src/*.js test/*.js", 33 | "watch": "babel src --out-dir lib --watch --source-maps", 34 | "prepublish": "npm run --if-present build", 35 | "test": "npm run lint && mocha", 36 | "test:example-default": "cd examples/default && yarn && npm run build", 37 | "test:example-ace": "cd examples/ace && yarn && npm run build", 38 | "test:example-custom": "cd examples/custom && yarn && npm run build", 39 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 40 | "format": "prettier --write \"**/*.{js,jsx,ts,tsx,css}\"", 41 | "prepare": "git config --local core.hooksPath .githooks" 42 | }, 43 | "keywords": [ 44 | "gitbook", 45 | "plugin", 46 | "gitbook-plugin" 47 | ], 48 | "engines": { 49 | "gitbook": "*" 50 | }, 51 | "gitbook": { 52 | "properties": { 53 | "check": { 54 | "type": "boolean", 55 | "description": "ace syntax validation (ace* template required)", 56 | "default": false, 57 | "required": false 58 | }, 59 | "edit": { 60 | "type": "boolean", 61 | "description": "ace code edition (ace* template required)", 62 | "default": false, 63 | "required": false 64 | }, 65 | "lang": { 66 | "type": "string", 67 | "description": "language for all codes", 68 | "default": "", 69 | "required": false 70 | }, 71 | "fixlang": { 72 | "type": "boolean", 73 | "description": "Fix lang label (c++,...)", 74 | "default": false, 75 | "required": false 76 | }, 77 | "template": { 78 | "type": "string", 79 | "description": "Template string", 80 | "default": "default", 81 | "required": false 82 | }, 83 | "theme": { 84 | "type": "string", 85 | "description": "ace code editor theme (ace* template required)", 86 | "default": "chrome", 87 | "required": false 88 | }, 89 | "unindent": { 90 | "type": "boolean", 91 | "description": "undindent inner snippets", 92 | "default": false, 93 | "required": false 94 | } 95 | } 96 | }, 97 | "dependencies": { 98 | "entities": "^1.1.1", 99 | "handlebars": "^4.0.5", 100 | "language-map": "^1.1.1", 101 | "meow": "^4.0.0" 102 | }, 103 | "devDependencies": { 104 | "@babel/cli": "^7.0.0", 105 | "@babel/core": "^7.0.0", 106 | "@babel/preset-env": "^7.0.0", 107 | "@babel/register": "^7.0.0", 108 | "eslint": "^8.6.0", 109 | "eslint-config-prettier": "^8.3.0", 110 | "eslint-plugin-prettier": "^4.0.0", 111 | "lint-staged": "^12.1.7", 112 | "mocha": "^9.1.3", 113 | "prettier": "^2.5.1" 114 | }, 115 | "prettier": { 116 | "singleQuote": false, 117 | "printWidth": 120, 118 | "tabWidth": 4, 119 | "trailingComma": "none" 120 | }, 121 | "lint-staged": { 122 | "*.{js,jsx,ts,tsx,css}": [ 123 | "prettier --write" 124 | ] 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /test/slicer-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const assert = require("assert"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | import { getSliceRange, hasSliceRange, sliceCode } from "../src/slicer"; 7 | const lineFixture = fs.readFileSync(path.join(__dirname, "fixtures/line.js"), "utf-8"); 8 | describe("slicer-test", function () { 9 | describe("#hasSliceRange", function () { 10 | context("when range is defined", function () { 11 | it("should return [undefined, undefined]", function () { 12 | assert(hasSliceRange("import:0-0 ,")); 13 | assert(hasSliceRange("import:1-2 ,")); 14 | assert(hasSliceRange("import:2-3 ,")); 15 | assert(hasSliceRange("import:2- ,")); 16 | assert(hasSliceRange("import:-3 ,")); 17 | }); 18 | }); 19 | context("when range is not defined", function () { 20 | it("should return [undefined, undefined]", function () { 21 | assert(!hasSliceRange("import:0")); 22 | assert(!hasSliceRange("import:test")); 23 | assert(!hasSliceRange("import")); 24 | assert(!hasSliceRange("1-5 only")); 25 | assert(!hasSliceRange("1-5")); 26 | }); 27 | }); 28 | }); 29 | describe("#getSliceRange", function () { 30 | it("should return [start,end]", function () { 31 | const [start, end] = getSliceRange("import:1-5 , label"); 32 | assert.equal(start, 1); 33 | assert.equal(end, 5); 34 | }); 35 | context("when not defined range", function () { 36 | it("should return [undefined, undefined]", function () { 37 | const [start, end] = getSliceRange("import:test"); 38 | assert(start === undefined); 39 | assert(end === undefined); 40 | }); 41 | }); 42 | context("when `import:1-2, hello-world.js`", function () { 43 | it("should return [1, 2]", function () { 44 | const [start, end] = getSliceRange("import:1-2, hello-world.js"); 45 | assert(start === 1); 46 | assert(end === 2); 47 | }); 48 | }); 49 | context("when `import:2-3, hello-world.js`", function () { 50 | it("should return [2, 3]", function () { 51 | const [start, end] = getSliceRange("import:2-3, hello-world.js"); 52 | assert(start === 2); 53 | assert(end === 3); 54 | }); 55 | }); 56 | context("when `import:2-, hello-world.js`", function () { 57 | it("should return [2, undefined]", function () { 58 | const [start, end] = getSliceRange("import:2-, hello-world.js"); 59 | assert(start === 2); 60 | assert(end === undefined); 61 | }); 62 | }); 63 | context("when `import:-3, hello-world.js`", function () { 64 | it("should return [2, undefined]", function () { 65 | const [start, end] = getSliceRange("import:-3, hello-world.js"); 66 | assert(start === undefined); 67 | assert(end === 3); 68 | }); 69 | }); 70 | }); 71 | describe("#sliceCode", function () { 72 | it("should return sliced object for replace", function () { 73 | const [start, end] = getSliceRange("include:4-6, line.js"); 74 | const result = sliceCode(lineFixture, start, end); 75 | assert(result.length > 0); 76 | const expected = 77 | 'console.log("this is line 4");\n' + 78 | 'console.log("this is line 5");\n' + 79 | 'console.log("this is line 6");'; 80 | assert.equal(result, expected); 81 | }); 82 | it("should return sliced `start`- text", function () { 83 | const [start, end] = getSliceRange("include:9-, line.js"); 84 | const result = sliceCode(lineFixture, start, end); 85 | assert(result.length > 0); 86 | const expected = 'console.log("this is line 9");\n' + 'console.log("this is line 10");'; 87 | assert.equal(result, expected); 88 | }); 89 | it("should return sliced -`end` text", function () { 90 | const [start, end] = getSliceRange("include:-2, line.js"); 91 | const result = sliceCode(lineFixture, start, end); 92 | assert(result.length > 0); 93 | const expected = 'console.log("this is line 1");\n' + 'console.log("this is line 2");'; 94 | assert.equal(result, expected); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/marker-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const assert = require("assert"); 4 | import { getMarker, hasMarker, markerSliceCode, removeMarkers } from "../src/marker"; 5 | 6 | // ------------------------------------------------------------------------------- 7 | // Test C++ Code 8 | const cppcode = ` 9 | // test.cpp source code 10 | int main() 11 | { 12 | // Test inner markers. 13 | 14 | //! [marker0] 15 | int a; 16 | //! [marker1] 17 | int b; 18 | //! [marker1] 19 | int c; 20 | //! [marker0] 21 | 22 | // Test different comment style. 23 | 24 | /** [marker2] */ 25 | int d; 26 | /** [marker2] */ 27 | /// [marker3] 28 | int e; 29 | /// [marker3] 30 | ## [marker4] 31 | int f; 32 | ## [marker4] 33 | 34 | // Test different naming and spacing. 35 | 36 | /// [marker 5] 37 | int g; 38 | /// [marker 5] 39 | 40 | /// [marker6 space] 41 | int h; 42 | /// [marker6 space] 43 | /// [ marker 7 ] 44 | int i; 45 | /// [ marker 7 ] 46 | } 47 | `; 48 | // ------------------------------------------------------------------------------- 49 | 50 | const htmlcode = ` 51 | 52 |
This is title
53 | 54 | 55 | Hello world 56 | 57 | Hola Mundo 58 | 59 | 60 | Hallo Welt 61 | 62 | 63 | 64 | 65 | `; 66 | 67 | // Expected results. 68 | const expectedMarker0 = ` int a; 69 | //! [marker1] 70 | int b; 71 | //! [marker1] 72 | int c;`; 73 | const expectedMarker01 = ` int a; 74 | int b; 75 | int c;`; 76 | const expectedMarker1 = " int b;"; 77 | const expectedMarker2 = " int d;"; 78 | const expectedMarker3 = " int e;"; 79 | const expectedMarker4 = " int f;"; 80 | const expectedMarker5 = " int g;"; 81 | const expectedMarker6 = " int h;"; 82 | const expectedMarker7 = " int i;"; 83 | 84 | // Expected HTML results. 85 | const expectedHTMLMarker0 = ` Hello world 86 | 87 | Hola Mundo 88 | 89 | 90 | Hallo Welt 91 | `; 92 | 93 | const expectedHTMLMarker1 = " Hola Mundo"; 94 | 95 | const expectedHTMLMarker2 = " Hallo Welt"; 96 | 97 | describe("marker", function () { 98 | describe("#hasMarker", function () { 99 | context("when have not marker", function () { 100 | it("should return false", function () { 101 | assert(!hasMarker({ title: undefined, id: undefined, marker: undefined })); 102 | }); 103 | }); 104 | context("when have marker", function () { 105 | it("should return true", function () { 106 | assert(hasMarker({ title: undefined, id: undefined, marker: "test" })); 107 | }); 108 | }); 109 | }); 110 | describe("marker-label", function () { 111 | describe("#getMarkerName", function () { 112 | it("should return", function () { 113 | const result = getMarker({ title: undefined, id: undefined, marker: "my marker" }); 114 | assert.equal(result, "my marker"); 115 | }); 116 | }); 117 | }); 118 | describe("marker-slice", function () { 119 | context("#nested", function () { 120 | it("should slice code between [marker0] keeping inner markers", function () { 121 | const markerName = "marker0"; 122 | const result = markerSliceCode(cppcode, markerName); 123 | assert.equal(result, expectedMarker0); 124 | }); 125 | }); 126 | context("#comment-style", function () { 127 | it("should slice code between [marker1] with comment //:", function () { 128 | const markerName = "marker1"; 129 | const result = markerSliceCode(cppcode, markerName); 130 | assert.equal(result, expectedMarker1); 131 | }); 132 | it("should slice code between [marker2] using comment /**", function () { 133 | const markerName = "marker2"; 134 | const result = markerSliceCode(cppcode, markerName); 135 | assert.equal(result, expectedMarker2); 136 | }); 137 | it("should slice code between [marker3] using comment ///", function () { 138 | const markerName = "marker3"; 139 | const result = markerSliceCode(cppcode, markerName); 140 | assert.equal(result, expectedMarker3); 141 | }); 142 | it("should slice code between [marker4] using comment ##", function () { 143 | const markerName = "marker4"; 144 | const result = markerSliceCode(cppcode, markerName); 145 | assert.equal(result, expectedMarker4); 146 | }); 147 | it("should slice code between [marker0] with HTML comment ", function () { 148 | const markerName = "marker0"; 149 | const result = markerSliceCode(htmlcode, markerName); 150 | assert.equal(result, expectedHTMLMarker0); 151 | }); 152 | it("should slice code between [marker1] with HTML comment ", function () { 153 | const markerName = "marker1"; 154 | const result = markerSliceCode(htmlcode, markerName); 155 | assert.equal(result, expectedHTMLMarker1); 156 | }); 157 | it("should slice code between [marker2] with HTML comment ", function () { 158 | const markerName = "marker2"; 159 | const result = markerSliceCode(htmlcode, markerName); 160 | assert.equal(result, expectedHTMLMarker2); 161 | }); 162 | }); 163 | context("#comment-style", function () { 164 | it("should slice code between [marker 5]", function () { 165 | const markerName = "marker 5"; 166 | const result = markerSliceCode(cppcode, markerName); 167 | assert.equal(result, expectedMarker5); 168 | }); 169 | it("should slice code between [marker6 space]", function () { 170 | const markerName = "marker6 space"; 171 | const result = markerSliceCode(cppcode, markerName); 172 | assert.equal(result, expectedMarker6); 173 | }); 174 | it("should slice code between [ marker 7 ]", function () { 175 | const markerName = " marker 7 "; 176 | const result = markerSliceCode(cppcode, markerName); 177 | assert.equal(result, expectedMarker7); 178 | }); 179 | }); 180 | }); 181 | describe("remove-marker", function () { 182 | context("#getMarkerName", function () { 183 | it("should slice code between [marker0] and remove markers [marker1]", function () { 184 | const markerName = "marker0"; 185 | const result = removeMarkers(markerSliceCode(cppcode, markerName)); 186 | assert.equal(result, expectedMarker01); 187 | }); 188 | }); 189 | }); 190 | }); 191 | -------------------------------------------------------------------------------- /src/parser.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | const path = require("path"); 4 | const Handlebars = require("handlebars"); 5 | import { defaultKeyValueMap, initOptions, checkMapTypes } from "./options.js"; 6 | import { unescapeString } from "./unescape-string.js"; 7 | import { getLang } from "./language-detection"; 8 | import { getMarker, hasMarker, markerSliceCode, removeMarkers } from "./marker"; 9 | import { sliceCode, hasSliceRange, getSliceRange } from "./slicer"; 10 | import { hasTitle } from "./title"; 11 | import { getTemplateContent, readFileFromPath } from "./template"; 12 | import { codeBlockBacktick } from "./backtick-maker"; 13 | 14 | const markdownLinkFormatRegExp = /\[(?=((?:[^\]]|\\.)*))\1\]\((?=((?:[^)]|\\.)*))\2\)/gm; 15 | 16 | const keyEx = "\\w+"; 17 | const kvsepEx = "[:=]"; 18 | const spacesEx = "\\s*"; 19 | const quoteEx = "[\"']"; 20 | const valEx = "(?:[^'\"\\\\]|\\\\.)*"; 21 | const argEx = `${quoteEx}${valEx}${quoteEx}|true|false`; 22 | const expressionEx = `(${keyEx})${kvsepEx}${spacesEx}(${argEx})`; 23 | const expressionRegExp = new RegExp(expressionEx, "g"); 24 | 25 | const markerRegExp = /^\s*(([-\w\s]*,?)*)$/; 26 | 27 | /** 28 | * A counter to count how many code are imported. 29 | */ 30 | var codeCounter = (function () { 31 | var count = 0; 32 | return function () { 33 | return count++; 34 | }; // Return and increment 35 | })(); 36 | 37 | /** 38 | * split label to commands 39 | * @param {string} label 40 | * @returns {Array} 41 | */ 42 | export function splitLabelToCommands(label = "") { 43 | const result = label.split(/(:|[,\s])/); 44 | if (!result) { 45 | return []; 46 | } 47 | // remove null command 48 | return result 49 | .map((command) => { 50 | return command.trim(); 51 | }) 52 | .filter((command) => { 53 | return command.length > 0; 54 | }); 55 | } 56 | 57 | /** 58 | * Unindent code 59 | * @param {string} s 60 | * @return {string} 61 | */ 62 | export function strip(s) { 63 | // inspired from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb 64 | if (s === undefined || s === "") { 65 | return s; 66 | } 67 | const indents = s 68 | .split(/\n/) 69 | .map((s) => s.match(/^[ \t]*(?=\S)/)) 70 | .filter((m) => m) 71 | .map((m) => m[0]); 72 | const smallestIndent = indents.sort((a, b) => a.length - b.length)[0]; 73 | return s.replace(new RegExp(`^${smallestIndent}`, "gm"), ""); 74 | } 75 | 76 | /** 77 | * if contain "include" or "import" command, then return true 78 | * @param {Array} commands 79 | * @returns {boolean} 80 | */ 81 | export function containIncludeCommand(commands = []) { 82 | const reg = /^(include|import)$/; 83 | return commands.length > 0 ? reg.test(commands[0].trim()) : false; 84 | } 85 | 86 | /** 87 | * Parse the given value to the given type. Returns the value if valid, otherwise returns undefined. 88 | * @param {string} value 89 | * @param {string} type "string", "boolean" 90 | * @param {string} key 91 | * @return {boolean|string|undefined} 92 | */ 93 | export function parseValue(value, type, key) { 94 | if (type === "string") { 95 | const unescapedvalue = unescapeString(value.substring(1, value.length - 1)); 96 | if (key === "marker" && !markerRegExp.test(unescapedvalue)) { 97 | console.error( 98 | "include-codeblock: parseVariablesFromLabel: invalid value " + `\`${unescapedvalue}\` in key \`marker\`` 99 | ); 100 | return undefined; 101 | } 102 | return unescapedvalue; 103 | } 104 | 105 | if (type === "boolean") { 106 | if (["true", '"true"', "'true'"].indexOf(value) >= 0) { 107 | return true; 108 | } 109 | 110 | if (["false", '"false"', "'false'"].indexOf(value) >= 0) { 111 | return false; 112 | } 113 | 114 | console.error( 115 | "include-codeblock: parseVariablesFromLabel: invalid value " + 116 | `\`${value}\` in key \`${key}\`. Expect true or false.` 117 | ); 118 | return undefined; 119 | } 120 | 121 | console.error(`include-codeblock: parseVariablesFromLabel: unknown key type \`${type}\` (see options.js)`); 122 | return undefined; 123 | } 124 | 125 | /** Parse the command label and return a new key-value object 126 | * @example 127 | * [import,title:"",label:""](path/to/file.ext) 128 | * @param {object} kvMap 129 | * @param {string} label 130 | * @return {object} 131 | */ 132 | export function parseVariablesFromLabel(kvMap, label) { 133 | const kv = Object.assign({}, kvMap); 134 | 135 | let match = ""; 136 | while ((match = expressionRegExp.exec(label))) { 137 | let key = match[1]; 138 | if (key === "include" || key === "import") { 139 | key = "marker"; 140 | } 141 | const value = match[2]; 142 | 143 | if (!Object.prototype.hasOwnProperty.call(kv, key)) { 144 | console.error("include-codeblock: parseVariablesFromLabel: unknown key " + `\`${key}\` (see options.js)`); 145 | return; 146 | } 147 | 148 | const parsedValue = parseValue(value, typeof defaultKeyValueMap[key], key); 149 | if (parsedValue !== undefined) { 150 | kv[key] = parsedValue; 151 | } 152 | } 153 | 154 | return Object.freeze(kv); 155 | } 156 | 157 | /** 158 | * generate code from options 159 | * @param {object} kvMap 160 | * @param {string} fileName 161 | * @param {string} originalPath 162 | * @param {string} content 163 | * @param {string} backtick 164 | * @return {string} 165 | */ 166 | export function generateEmbedCode(kvMap, { fileName, originalPath, content, backtick }) { 167 | const tContent = getTemplateContent(kvMap); 168 | const kv = Object.assign({}, kvMap); 169 | const count = hasTitle(kv) ? codeCounter() : -1; 170 | checkMapTypes(kvMap, "generatedEmbedCode"); 171 | const contextMap = Object.assign({}, kvMap, { 172 | content: content, 173 | count: count, 174 | fileName: fileName, 175 | originalPath: originalPath, 176 | backtick 177 | }); 178 | // compile template 179 | const handlebars = Handlebars.compile(tContent); 180 | // compile with data. 181 | return handlebars(contextMap); 182 | } 183 | 184 | /** 185 | * return content from file or url. 186 | * @param {string} filePath it should be absolute path 187 | * @return {string} 188 | */ 189 | export function getContent(filePath) { 190 | return readFileFromPath(filePath); 191 | } 192 | 193 | /** 194 | * generate code with options 195 | * @param {object} kvMap 196 | * @param {string} filePath 197 | * @param {string} originalPath 198 | * @param {string} label 199 | * @return {string} 200 | */ 201 | export function embedCode(kvMap, { filePath, originalPath, label }) { 202 | const code = getContent(filePath); 203 | const fileName = path.basename(filePath); 204 | const kvmparsed = parseVariablesFromLabel(kvMap, label); 205 | const kvm = getLang(kvmparsed, originalPath); 206 | const unindent = kvm.unindent; 207 | 208 | let content = code; 209 | // Slice content via line numbers. 210 | if (hasSliceRange(label)) { 211 | const [start, end] = getSliceRange(label); 212 | content = sliceCode(code, start, end, unindent); 213 | } else if (hasMarker(kvm)) { 214 | // Slice content via markers. 215 | const marker = getMarker(kvm); 216 | content = removeMarkers(markerSliceCode(code, marker)); 217 | } 218 | if (unindent === true) { 219 | content = strip(content); 220 | } 221 | 222 | const backtick = codeBlockBacktick(content); 223 | return generateEmbedCode(kvm, { fileName, originalPath, content, backtick }); 224 | } 225 | 226 | /** 227 | * Parse command using options from pluginConfig. 228 | * @param {string} content 229 | * @param {string} baseDir 230 | * @param {{template?: string}} options 231 | * @return {Array} 232 | */ 233 | export function parse(content, baseDir, options = {}) { 234 | const results = []; 235 | const kvMap = initOptions(options); 236 | let res = true; 237 | while ((res = markdownLinkFormatRegExp.exec(content))) { 238 | const [all, label, originalPath] = res; 239 | const commands = splitLabelToCommands(label); 240 | if (containIncludeCommand(commands)) { 241 | const absolutePath = path.resolve(baseDir, originalPath); 242 | const replacedContent = embedCode(kvMap, { 243 | filePath: absolutePath, 244 | originalPath: originalPath, 245 | label 246 | }); 247 | results.push({ 248 | target: all, 249 | replaced: replacedContent 250 | }); 251 | } 252 | } 253 | return results; 254 | } 255 | -------------------------------------------------------------------------------- /test/parser-test.js: -------------------------------------------------------------------------------- 1 | // LICENSE : MIT 2 | "use strict"; 3 | import assert from "assert"; 4 | import { defaultKeyValueMap } from "../src/options.js"; 5 | import { containIncludeCommand, splitLabelToCommands, strip, parseValue, parseVariablesFromLabel } from "../src/parser"; 6 | 7 | const kvmap = defaultKeyValueMap; 8 | 9 | describe("parse", function () { 10 | describe("#splitLabelToCommands", function () { 11 | it("should split label to commands", function () { 12 | const commands = splitLabelToCommands("import"); 13 | assert.equal(commands.length, 1); 14 | assert.equal(commands[0], "import"); 15 | }); 16 | it("should not contain space in command", function () { 17 | const commands = splitLabelToCommands("command1 command2, command3 "); 18 | // Should 3 but 4.. 19 | assert(commands.length > 3); 20 | assert(commands.indexOf("command1") !== -1); 21 | assert(commands.indexOf("command2") !== -1); 22 | assert(commands.indexOf("command3") !== -1); 23 | }); 24 | }); 25 | describe("containIncludeLabel", function () { 26 | it("support import", function () { 27 | const commands = splitLabelToCommands("import"); 28 | assert(containIncludeCommand(commands)); 29 | }); 30 | it("support include", function () { 31 | const commands = splitLabelToCommands("include"); 32 | assert(containIncludeCommand(commands)); 33 | }); 34 | it("support command split by space", function () { 35 | const commands = splitLabelToCommands("import title"); 36 | assert(containIncludeCommand(commands)); 37 | }); 38 | it("support command split by ,", function () { 39 | const commands = splitLabelToCommands("import, title"); 40 | assert(containIncludeCommand(commands)); 41 | }); 42 | it("only match first command", function () { 43 | const commands = splitLabelToCommands("Something import title"); 44 | assert(!containIncludeCommand(commands)); 45 | }); 46 | }); 47 | describe("parseValue", function () { 48 | it("should unescape string parameter", function () { 49 | const result = parseValue( 50 | '"\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\-\\.\\/' + 51 | "\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~" + 52 | '&<>AA"', 53 | "string", 54 | "" 55 | ); 56 | assert.strictEqual(result, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~&<>AA"); 57 | }); 58 | it("should backslash unescape commonmark defined characters only", function () { 59 | const result = parseValue('"\\r\\n\\[\\]"', "string", ""); 60 | assert.strictEqual(result, "\\r\\n[]"); // \r and \n should not be unescaped. 61 | }); 62 | it("should validate markers", function () { 63 | let result = parseValue('" marker0 , marker1 "', "string", "marker"); 64 | assert.strictEqual(result, " marker0 , marker1 "); 65 | 66 | result = parseValue('"~invalid~"', "string", "marker"); 67 | assert.strictEqual(result, undefined); 68 | }); 69 | it("should parse boolean values", function () { 70 | let result = parseValue("true", "boolean", ""); 71 | assert.strictEqual(result, true); 72 | result = parseValue('"true"', "boolean", ""); 73 | assert.strictEqual(result, true); 74 | result = parseValue("'true'", "boolean", ""); 75 | assert.strictEqual(result, true); 76 | 77 | result = parseValue("false", "boolean", ""); 78 | assert.strictEqual(result, false); 79 | result = parseValue('"false"', "boolean", ""); 80 | assert.strictEqual(result, false); 81 | result = parseValue("'false'", "boolean", ""); 82 | assert.strictEqual(result, false); 83 | }); 84 | }); 85 | describe("parseVariablesFromLabel ", function () { 86 | it("should retrieve edit boolean", function () { 87 | const resmap = parseVariablesFromLabel(kvmap, "include,edit:true"); 88 | const results = resmap; 89 | assert.equal(results.edit, true); 90 | assert.equal(results.marker, ""); 91 | }); 92 | it("should retrieve edit boolean with quotes", function () { 93 | const resmap = parseVariablesFromLabel(kvmap, 'include,edit:"true"'); 94 | const results = resmap; 95 | assert.equal(results.edit, true); 96 | assert.equal(results.marker, ""); 97 | }); 98 | it("should retrieve string title", function () { 99 | const resmap = parseVariablesFromLabel(kvmap, 'include,title:"a test"'); 100 | const results = resmap; 101 | assert.equal(results.title, "a test"); 102 | assert.equal(results.marker, ""); 103 | }); 104 | it("should retrieve include marker", function () { 105 | const resmap = parseVariablesFromLabel(kvmap, '[include:"marker0"](/path/to/file.ext)'); 106 | const results = resmap; 107 | assert.equal(results.marker, "marker0"); 108 | }); 109 | it("should retrieve import marker", function () { 110 | const resmap = parseVariablesFromLabel(kvmap, '[import:"marker0"](/path/to/file.ext)'); 111 | const results = resmap; 112 | assert.equal(results.marker, "marker0"); 113 | }); 114 | it("should retrieve include marker with title", function () { 115 | const resmap = parseVariablesFromLabel(kvmap, '[include:"marker0",title:"test"](/path/to/file.ext)'); 116 | const results = resmap; 117 | assert.equal(results.marker, "marker0"); 118 | assert.equal(results.title, "test"); 119 | }); 120 | it("should retrieve import multi markers", function () { 121 | const resmap = parseVariablesFromLabel(kvmap, '[import:"marker0,marker1,marker2"](/path/to/file.ext)'); 122 | const results = resmap; 123 | assert.equal(results.marker, "marker0,marker1,marker2"); 124 | }); 125 | it("should retrieve each attribute", function () { 126 | const resmap = parseVariablesFromLabel( 127 | kvmap, 128 | 'include:"marker",title:"a test",id:"code1",class:"myclass",edit=false,check="true",theme:"monokai",template:"ace",unindent:true,fixlang:"false"' 129 | ); 130 | const results = resmap; 131 | assert.equal(results.marker, "marker"); 132 | assert.equal(results.title, "a test"); 133 | assert.equal(results.id, "code1"); 134 | assert.equal(results.class, "myclass"); 135 | assert.equal(results.edit, false); 136 | assert.equal(results.check, true); 137 | assert.equal(results.theme, "monokai"); 138 | assert.equal(results.template, "ace"); 139 | assert.equal(results.unindent, true); 140 | assert.equal(results.fixlang, false); 141 | }); 142 | it("should retrieve nothing", function () { 143 | const resmap = parseVariablesFromLabel(kvmap, "[import](/path/to/file.ext)"); 144 | const results = resmap; 145 | assert.equal(results.marker, ""); 146 | }); 147 | it("should handle characters for string parameter", function () { 148 | const resmap = parseVariablesFromLabel(kvmap, 'import,title="test+with-special*string"'); 149 | const results = resmap; 150 | assert.strictEqual(results.title, "test+with-special*string"); 151 | }); 152 | it("should unescape string parameter", function () { 153 | const resmap = parseVariablesFromLabel( 154 | kvmap, 155 | 'import,title="\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\-\\.\\/' + 156 | "\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~" + 157 | '&<>AA"' 158 | ); 159 | const results = resmap; 160 | assert.strictEqual(results.title, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~&<>AA"); 161 | }); 162 | it("should backslash unescape commonmark defined characters only", function () { 163 | const resmap = parseVariablesFromLabel(kvmap, 'import,title="\\r\\n\\[\\]"'); 164 | const results = resmap; 165 | assert.strictEqual(results.title, "\\r\\n[]"); // \r and \n should not be unescaped. 166 | }); 167 | it("should not parse string argument into another key-value pair", function () { 168 | assert.strictEqual(kvmap.edit, false); 169 | const resmap = parseVariablesFromLabel(kvmap, 'import,title="edit:true"'); 170 | const results = resmap; 171 | assert.strictEqual(results.edit, false); 172 | assert.strictEqual(results.title, "edit:true"); 173 | }); 174 | }); 175 | // inspired from https://github.com/rails/rails/blob/master/activesupport/test/core_ext/string_ext_test.rb 176 | describe("strip", function () { 177 | it("strips leading space from empty string", function () { 178 | const stripped = strip(""); 179 | assert.equal(stripped, ""); 180 | }); 181 | it("strips leading space from one-liner", function () { 182 | const stripped = strip(" x"); 183 | assert.equal(stripped, "x"); 184 | }); 185 | it("strips leading space from multi-liner with no margin", function () { 186 | const stripped = strip("foo\n bar\nbaz\n"); 187 | assert.equal(stripped, "foo\n bar\nbaz\n"); 188 | }); 189 | it("strips leading space from multi-liner", function () { 190 | const stripped = strip(" foo\n bar\n baz\n"); 191 | assert.equal(stripped, "foo\n bar\nbaz\n"); 192 | }); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitbook-plugin-include-codeblock [![Actions Status: test](https://github.com/azu/gitbook-plugin-include-codeblock/workflows/test/badge.svg)](https://github.com/azu/gitbook-plugin-include-codeblock/actions?query=workflow%3A"test") 2 | 3 | GitBook Plugin for including file. 4 | 5 | 6 | 1. [Installation](#installation) 7 | 2. [Plugin options](#plugin-options) 8 | 3. [Usage](#usage) 9 | 4. [Example](#example) 10 | 5. [CLI](#cli) 11 | 12 | 13 | ## Installation 14 | 15 | book.json 16 | 17 | ```json 18 | { 19 | "plugins": [ 20 | "include-codeblock" 21 | ] 22 | } 23 | ``` 24 | 25 | and 26 | 27 | ```sh 28 | gitbook install 29 | ``` 30 | 31 | ## Plugin options 32 | 33 | Several options can be set in `book.json` to customize the plugin. 34 | 35 | | option | value | Description | 36 | | --- | --- | --- | 37 | | `template` | `{"default","full","ace",...}` or custom path | reindent code if marker or slice is used | 38 | | `unindent` | `{true,false}` default:`false` | reindent code if marker or slice is used | 39 | | `fixlang` | `{true,false}` default:`false` | fix some errors with code lang (e.g C++, ...) | 40 | | `lang` | `{"c_cpp","javascript", ...}` | lang color syntax (not set => auto deduce, see [lang section](#hardcoded-class)). | 41 | | `edit` | `{true,false}` | [allow edit code](https://github.com/ymcatar/gitbook-plugin-ace/blob/master/README.md) (**ace template required**) | 42 | | `check` | `{true,false}` | [syntax validation](https://github.com/ymcatar/gitbook-plugin-ace/blob/master/README.md) (**ace template required**) | 43 | | `theme` | `{"monokai","coffee",...}` | [check syntax](https://github.com/ymcatar/gitbook-plugin-ace/blob/master/README.md) (**ace template required**) | 44 | 45 | Just add the desired optin under `pluginConfig` in the `book.json` file 46 | 47 | ```js 48 | { 49 | "gitbook": "3.x.x", 50 | "pluginsConfig": { 51 | "include-codeblock": { 52 | "template": "ace", 53 | "unindent": true, 54 | "theme": "monokai" 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | ### Templates 61 | 62 | Templates let customize the rendered code. Several default templates are available 63 | 64 | | template | description | 65 | | --- | --- | 66 | | `"default"` | default template, standard markdown code style | 67 | | `"full"` | enable title, labeling, id, ... | 68 | | `"ace"` | enable ace code rendering (**ace plugin required**) | 69 | | `"acefull"` | enable ace code rendering with title, label, id, ... (**ace plugin required**) | 70 | 71 | - :information_source: For ace template, see [Ace section](#ace-plugin) 72 | - :information_source: For more template, consult the list in [template/](templates/). 73 | 74 | Custom templates can be created to render the code by specifying a custom path 75 | to the template file. 76 | ```js 77 | { 78 | "gitbook": "3.x.x", 79 | "pluginsConfig": { 80 | "include-codeblock": { 81 | "template": __dirname + "/" + "path/to/custom.hbs", 82 | } 83 | } 84 | } 85 | ``` 86 | See [templates/](templates/) and [examples/](examples/) for details. 87 | 88 | Any contribution is welcome. Propose your new templates via pull-requests. 89 | 90 | ### Ace plugin 91 | 92 | It is possible to use the gitbook ace plugin to have code numbering or custom themes 93 | (See [gitbook-ace-plugin](https://github.com/ymcatar/gitbook-plugin-ace) for more details). 94 | To use ace within include-codeblock, you have to **load the ace plugin after include-codeblock!** 95 | and choose an ace temple (see [templates/](templates/)) 96 | 97 | ```js 98 | { 99 | "gitbook": "3.x.x", 100 | "plugins" : [ 101 | "include-codeblock", 102 | "ace" 103 | ] 104 | "pluginsConfig": { 105 | "include-codeblock": { 106 | "template": "ace", // or acefull 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | ## Usage 113 | 114 | 115 | #### General usage: 116 | 117 | ``` 118 | [import:"tag",option0:"value0", ...](path/to/file) 119 | ``` 120 | 121 | where `<...>` are required tags, `<<...>>` are optional tags. 122 | 123 | | tag | description | 124 | | --- | --- | 125 | | `import` | use `import` or `include` tag. | 126 | | `tag` | optional tag to include code snippet (see [snippet](#snippet-code). | 127 | | `optionX` | optional `key:value` or `key=value` option (See [Command options](#command-options)). | 128 | 129 | See [examples](#examples) for more details. 130 | 131 | #### Examples 132 | 133 | **fixtures/test.js** 134 | ```js 135 | console.log("test"); 136 | ``` 137 | 138 | Write following the link with `include` or `import` label. 139 | 140 | ```markdown 141 | [include](fixtures/test.js) 142 | ``` 143 | 144 | or 145 | 146 | ```markdown 147 | [import](fixtures/test.js) 148 | ``` 149 | Result 150 | 151 | ``` js 152 | console.log("test"); 153 | ``` 154 | 155 | :information_source: Do not inline! 156 | ``` js 157 | // won't work 158 | Example of code [import](fixtures/test.js) 159 | ``` 160 | 161 | You could import the same code directly from the repository with nice color template 162 | ```markdown 163 | [import, template:"acefull", title:"example of code", theme:"monokai"](https://raw.githubusercontent.com/azu/gitbook-plugin-include-codeblock/master/test/fixtures/test.js) 164 | ``` 165 | 166 | 167 | ### Command options 168 | 169 | Option can be passed locally and may depend on the template your are using. 170 | 171 | | option | value | Description | 172 | | --- | --- | --- | 173 | | `unindent` | `{"true","false"}` | reindent code if marker or slice is used | 174 | | `title`| `""` | Title for the code **full template required**| 175 | | `name` | `""` | name of the included file **full template required** | 176 | | `class` | `""` | html class for the title **full template required** | 177 | | `id` | `""` | html class for custom style **full template required** | 178 | | `label` | `""` | reference label (latex like) **full template required** | 179 | | `edit` | `{"true","false"}` | allow edit code (**ace template required**) | 180 | | `check` | `{"true","false"}` | check syntax (**ace template required**) | 181 | | `template` | `{default,full,ace,...}` or custom path | reindent code if marker or slice is used | 182 | | `lang` | `{"c_cpp","javascript", ...}` | lang color syntax (not set => auto deduce, see [lang section](#hardcoded-class)). | 183 | | `fixlang` | `{true,false}` default:`false` | fix some errors with code lang (e.g C++, ...) | 184 | | `theme` | `{"monokai","coffee",...}` | check syntax (**ace template required**) | 185 | 186 | For more details see sections below. 187 | 188 | 189 | ### Hardcoded class 190 | 191 | When you import a TypeScript file `.ts`: 192 | The parser correctly finds `.ts` in the 193 | [language-map](https://github.com/blakeembrey/language-map "language-map") 194 | extensions for both TypeScript and XML, then automatically chooses `XML`. 195 | 196 | If you want to specify language type, put `lang:""` to label. 197 | 198 | ```markdown 199 | [import, lang:"typescript"](hello-world.ts) 200 | ``` 201 | 202 | - :information_source: choose `` of `lang-` from language-map's `aceMode` value. 203 | - [blakeembrey/language-map: JSON version of the programming language map used in Linguist](https://github.com/blakeembrey/language-map "blakeembrey/language-map: JSON version of the programming language map used in Linguist") 204 | 205 | e.g.) typescript's aceMode value is `typescript`. 206 | 207 | - https://github.com/blakeembrey/language-map/blob/b72edb8c2cb1b05d098782aa85dd2f573ed96ba3/languages.json#L4140 208 | 209 | 210 | ### Sliced Code 211 | 212 | If you want to slice imported code and show. 213 | 214 | ```markdown 215 | [import:-](path/to/file) 216 | ``` 217 | 218 | - :information_source: lineNumber start with 1. 219 | 220 | All Patterns: 221 | 222 | ``` 223 | All: [import, hello-world.js](../src/hello-world.js) 224 | 1-2: [import:1-2, hello-world.js](../src/hello-world.js) 225 | 2-3: [import:2-3, hello-world.js](../src/hello-world.js) 226 | 2>=: [import:2-, hello-world.js](../src/hello-world.js) 227 | <=3: [import:-3, hello-world.js](../src/hello-world.js) 228 | ``` 229 | 230 | ### Snippet code 231 | 232 | You can also import snippet code delimited by a tag. It follows the 233 | [doxygen snippet standard](https://www.stack.nl/~dimitri/doxygen/manual/commands.html#cmdsnippet) 234 | Snippet is doxygen compatible. 235 | (See also [how to document the code](https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html)) 236 | 237 | ```markdown 238 | [import:''](path/to/file) 239 | ``` 240 | 241 | #### Remarks 242 | - :information_source: **marker name** begins with an alphabet character 243 | - :information_source: tags follows the doxygen standard: **language comment for documenting code** + **tag between bracket** 244 | - :information_source: Several markers separated by a comma will concatene snippets into a unique snippet. Spaces are taken into account. 245 | 246 | For example, considering the following C++ source code 247 | 248 | ```cpp 249 | // test.cpp source code 250 | int main() 251 | { 252 | /// [marker0] 253 | int a; 254 | //! [marker1] 255 | int b; 256 | //! [marker1] 257 | int c; 258 | /// [marker0] 259 | 260 | // [notmarked] 261 | int d; 262 | // [notmarked] 263 | 264 | //! [marker2] 265 | int e; 266 | //! [marker2] 267 | } 268 | 269 | ``` 270 | In GitBook, the following commands 271 | 272 | ```markdown 273 | [import:'marker1'](path/to/test.cpp) 274 | ``` 275 | 276 | will result to 277 | 278 | ```cpp 279 | int b; 280 | ``` 281 | 282 | The command `[import:'marker0'](path/to/test.cpp)` will result to 283 | 284 | ```cpp 285 | int a; 286 | int b; 287 | int c; 288 | ``` 289 | 290 | The command `[import:'marker1,marker2'](path/to/test.cpp)` will result to 291 | ```cpp 292 | int b; 293 | int e; 294 | ``` 295 | 296 | But the command `[import:'notmarked'](path/to/test.cpp)` will fail as it 297 | does not respect the doxygen documenting standard. 298 | (See [documenting the code](https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html)) 299 | 300 | ### Unindented code 301 | 302 | Consider the following source code: 303 | 304 | ```java 305 | class Hello { 306 | /// [some-marker] 307 | void world() { 308 | // nice 309 | } 310 | /// [some-marker] 311 | } 312 | ``` 313 | 314 | And the following command: 315 | 316 | ``` 317 | [import:"some-marker",unindent:"true"](path/to/test.java) 318 | ``` 319 | 320 | This will result in unindented code: 321 | 322 | ```java 323 | void world() { 324 | // nice 325 | } 326 | ``` 327 | 328 | Unindent behaviour can also be specified globally in the plugin configuration. 329 | 330 | See also 331 | 332 | - [Allow to put marker in xml · Issue #63 · azu/gitbook-plugin-include-codeblock](https://github.com/azu/gitbook-plugin-include-codeblock/issues/63) 333 | 334 | ## Example 335 | 336 | Please See [examples/](examples/). 337 | 338 | [![screenshot](https://monosnap.com/file/ydUDWzqXtC2bvPPBmqtplldO8l2QJK.png)](example/) 339 | 340 | ## CLI 341 | 342 | This gitbook plugin include Command line tools. 343 | It just convert markdown to markdown. 344 | 345 | $ npm install -g gitbook-plugin-include-codeblock 346 | # Convert Markdown to Markdown 347 | $ include-codeblock ./README.md --output RENDER_README.md 348 | 349 | ## FAQ 350 | 351 | ### How to migrate Version 1.x to 2.x 352 | 353 | Version 2.0 contain a breaking change. 354 | 355 | - [Breaking Change: change default template by azu · Pull Request #31 · azu/gitbook-plugin-include-codeblock](https://github.com/azu/gitbook-plugin-include-codeblock/pull/31 "Breaking Change: change default template by azu · Pull Request #31 · azu/gitbook-plugin-include-codeblock") 356 | 357 | It change default template for displaying embed code. 358 | 359 | Version 1.x template. 360 | 361 | {{#if title}} 362 | {{#if id}} 363 | {% if file.type=="asciidoc" %} 364 | > [[{{id}}]]link:{{originalPath}}[{{title}}] 365 | {% else %} 366 | > {{title}} 367 | {% endif %} 368 | {{else}} 369 | {% if file.type=="asciidoc" %} 370 | > [[{{title}}]]link:{{originalPath}}[{{title}}] 371 | {% else %} 372 | > {{title}} 373 | {% endif %} 374 | {{/if}} 375 | {{else}} 376 | {% if file.type=="asciidoc" %} 377 | > [[{{fileName}}]]link:{{originalPath}}[{{fileName}}] 378 | {% else %} 379 | > {{fileName}} 380 | {% endif %} 381 | {{/if}} 382 | 383 | ``` {{lang}} 384 | {{{content}}} 385 | ``` 386 | 387 | Version 2.x template. 388 | 389 | ``` {{lang}} 390 | {{{content}}} 391 | ``` 392 | 393 | If you want to use Version 1.x template, please set `template` option to `book.json` or `book.js` 394 | 395 | ```js 396 | const fs = require("fs"); 397 | module.exports = { 398 | "gitbook": "3.x.x", 399 | "title": "gitbook-plugin-include-codeblock example", 400 | "plugins": [ 401 | "include-codeblock" 402 | ], 403 | "pluginsConfig": { 404 | "include-codeblock": { 405 | // Before, create user-template.hbs 406 | "template": fs.readFileSync(__dirname + "/user-template.hbs", "utf-8") 407 | } 408 | } 409 | }; 410 | ``` 411 | 412 | If you want to know more details, please see [templates/](templates/). 413 | 414 | ## Tests 415 | 416 | npm test 417 | 418 | ## Contributing 419 | 420 | 1. Fork it! 421 | 2. Create your feature branch: `git checkout -b my-new-feature` 422 | 3. Commit your changes: `git commit -am 'Add some feature'` 423 | 4. Push to the branch: `git push origin my-new-feature` 424 | 5. Submit a pull request :D 425 | 426 | ## License 427 | 428 | MIT 429 | --------------------------------------------------------------------------------