Skip to content

Commit 8d23966

Browse files
plugins: initial javascript based plugin (#1518)
1 parent 6e3c73a commit 8d23966

18 files changed

Lines changed: 1118 additions & 65 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2026 znai maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.znai.extensions.javascript;
18+
19+
import org.testingisdocumenting.znai.core.ComponentsRegistry;
20+
import org.testingisdocumenting.znai.extensions.PluginParams;
21+
import org.testingisdocumenting.znai.extensions.PluginResult;
22+
import org.testingisdocumenting.znai.extensions.include.IncludePlugin;
23+
import org.testingisdocumenting.znai.parser.ParserHandler;
24+
import org.testingisdocumenting.znai.search.SearchScore;
25+
import org.testingisdocumenting.znai.search.SearchText;
26+
import org.testingisdocumenting.znai.utils.CollectionUtils;
27+
28+
import java.nio.file.Path;
29+
import java.util.Collection;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.StringJoiner;
33+
34+
public class JavascriptFunctionIncludePlugin implements IncludePlugin {
35+
private String functionName;
36+
private Map<String, Object> args;
37+
38+
@Override
39+
public String id() {
40+
return "javascript-function";
41+
}
42+
43+
@Override
44+
public IncludePlugin create() {
45+
return new JavascriptFunctionIncludePlugin();
46+
}
47+
48+
@Override
49+
public PluginResult process(ComponentsRegistry componentsRegistry,
50+
ParserHandler parserHandler,
51+
Path markupPath,
52+
PluginParams pluginParams) {
53+
functionName = pluginParams.getFreeParam();
54+
if (functionName == null || functionName.isEmpty()) {
55+
throw new IllegalArgumentException("javascript function name must be provided as a free param," +
56+
" e.g. :include-javascript-function: myFunction");
57+
}
58+
59+
args = pluginParams.getOpts().toMap();
60+
61+
return PluginResult.docElement("JavascriptFunction", CollectionUtils.createMap(
62+
"functionName", functionName,
63+
"args", args));
64+
}
65+
66+
@Override
67+
public List<SearchText> textForSearch() {
68+
StringJoiner joiner = new StringJoiner(" ");
69+
joiner.add(functionName);
70+
collectSearchableValues(args, joiner);
71+
72+
return List.of(SearchScore.STANDARD.text(joiner.toString()));
73+
}
74+
75+
private static void collectSearchableValues(Object value, StringJoiner joiner) {
76+
if (value == null) {
77+
return;
78+
}
79+
80+
if (value instanceof Map) {
81+
((Map<?, ?>) value).values().forEach(v -> collectSearchableValues(v, joiner));
82+
} else if (value instanceof Collection) {
83+
((Collection<?>) value).forEach(v -> collectSearchableValues(v, joiner));
84+
} else {
85+
joiner.add(value.toString());
86+
}
87+
}
88+
}

znai-core/src/main/resources/META-INF/services/org.testingisdocumenting.znai.extensions.include.IncludePlugin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ org.testingisdocumenting.znai.extensions.api.ApiParametersIncludePlugin
2222
org.testingisdocumenting.znai.extensions.toc.PageTocIncludePlugin
2323
org.testingisdocumenting.znai.extensions.json.JsonIncludePlugin
2424
org.testingisdocumenting.znai.extensions.ocaml.OcamlCommentIncludePlugin
25+
org.testingisdocumenting.znai.extensions.javascript.JavascriptFunctionIncludePlugin
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2026 znai maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.znai.extensions.javascript
18+
19+
import org.testingisdocumenting.znai.extensions.include.PluginsTestUtils
20+
import org.testingisdocumenting.znai.search.SearchScore
21+
import org.junit.Test
22+
23+
import static org.testingisdocumenting.webtau.Matchers.code
24+
import static org.testingisdocumenting.webtau.Matchers.throwException
25+
26+
class JavascriptFunctionIncludePluginTest {
27+
@Test
28+
void "should pass function name and args to JavascriptFunction doc element"() {
29+
def elements = process('themeBox {title: "hello", size: 50}')
30+
elements.should == [[type: 'JavascriptFunction',
31+
functionName: 'themeBox',
32+
args: [title: 'hello', size: 50]]]
33+
}
34+
35+
@Test
36+
void "should fail when function name is missing"() {
37+
code {
38+
process('')
39+
} should throwException(~/javascript function name must be provided as a free param/)
40+
}
41+
42+
@Test
43+
void "should contribute function name and args values to search text"() {
44+
def plugin = PluginsTestUtils.processAndGetIncludePlugin(
45+
':include-javascript-function: themeBox {title: "hello world", count: 42, tags: ["alpha", "beta"]}')
46+
47+
def searchText = plugin.textForSearch()
48+
searchText.size().should == 1
49+
searchText[0].score.should == SearchScore.STANDARD
50+
searchText[0].text.should == 'themeBox hello world 42 alpha beta'
51+
}
52+
53+
private static List<Map<String, ?>> process(params) {
54+
return PluginsTestUtils.processInclude(":include-javascript-function: $params")*.toMap()
55+
}
56+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
Add `extensions.json` next to your `toc` file to inject custom CSS, JavaScript,
2+
or HTML into the generated site. This is how you bring in your own stylesheets,
3+
load scripts used by the [javascript plugin](plugins/javascript-plugin),
4+
embed tracking snippets, or ship extra static files along with the docs.
5+
6+
```json {title: "extensions.json"}
7+
{
8+
"cssResources": ["custom.css"],
9+
"jsResources": ["custom.js"],
10+
"htmlResources": ["custom.html"],
11+
"htmlHeadResources": ["tracking.html"]
12+
}
13+
```
14+
15+
All paths are resolved relative to your documentation root (the directory that
16+
holds the `toc` file).
17+
18+
# cssResources
19+
20+
List of CSS files to load on every page.
21+
22+
```json
23+
{
24+
"cssResources": ["custom.css", "plugins/javascript/theme-box.css"]
25+
}
26+
```
27+
28+
Note: a file named `style.css` placed next to `toc` is picked up automatically,
29+
no need to list it here. See [Styling](configuration/styling).
30+
31+
# jsResources
32+
33+
List of JavaScript files to load on every page. Use this to register
34+
custom functions for the [javascript plugin](plugins/javascript-plugin).
35+
36+
```json
37+
{
38+
"jsResources": ["custom.js", "plugins/javascript/theme-box.js"]
39+
}
40+
```
41+
42+
# htmlResources
43+
44+
List of HTML snippets injected into the `<body>` of every page — useful for
45+
widgets, chat bubbles, or any markup that needs to live alongside the rendered
46+
content.
47+
48+
```json
49+
{
50+
"htmlResources": ["custom.html"]
51+
}
52+
```
53+
54+
# htmlHeadResources
55+
56+
List of HTML snippets injected into the `<head>` of every page. Common use
57+
cases are analytics tags, preconnect hints, or extra `<meta>` entries.
58+
59+
```json
60+
{
61+
"htmlHeadResources": ["tracking.html"]
62+
}
63+
```
64+
65+
Note: a file named `tracking.html` placed next to `toc` is picked up
66+
automatically.
67+
68+
# additionalFilesToDeploy
69+
70+
List of extra files to copy into the deploy output. Use this for assets
71+
referenced by your custom CSS/JS that znai would not otherwise discover —
72+
fonts, icons, supporting JSON, and so on.
73+
74+
```json
75+
{
76+
"additionalFilesToDeploy": ["fonts/Inter.woff2", "data/config.json"]
77+
}
78+
```
79+
80+
Note: for broader file/directory copying, see
81+
[additional files](deployment/additional-files).

znai-docs/znai/extensions.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"cssResources": ["custom.css"],
3-
"jsResources": ["custom.js"],
2+
"cssResources": ["custom.css", "plugins/javascript/theme-box.css"],
3+
"jsResources": ["custom.js", "plugins/javascript/theme-box.js"],
44
"htmlResources": ["custom.html"],
55
"htmlHeadResources": ["tracking.html"]
66
}

0 commit comments

Comments
 (0)