From 9a44dae4f94ad90f4482cbb74258eef44adeeeac Mon Sep 17 00:00:00 2001 From: Nikita Khromov Date: Mon, 5 Aug 2024 17:28:06 +0700 Subject: [PATCH] Jsdocs api plugins generation script --- scripts/sdkjs_common/jsdoc/README.md | 21 ++- .../jsdoc/config/{ => builder}/cell.json | 0 .../config/{ => builder}/correct_doclets.js | 0 .../jsdoc/config/{ => builder}/forms.json | 0 .../jsdoc/config/{ => builder}/slide.json | 0 .../jsdoc/config/{ => builder}/word.json | 0 .../jsdoc/config/plugins/cell.json | 16 +++ .../jsdoc/config/plugins/common.json | 16 +++ .../jsdoc/config/plugins/correct_doclets.js | 85 +++++++++++ .../jsdoc/config/plugins/forms.json | 16 +++ .../jsdoc/config/plugins/slide.json | 16 +++ .../jsdoc/config/plugins/word.json | 16 +++ .../sdkjs_common/jsdoc/generate_docs_json.py | 18 +-- .../jsdoc/generate_docs_plugins_json.py | 132 ++++++++++++++++++ 14 files changed, 326 insertions(+), 10 deletions(-) rename scripts/sdkjs_common/jsdoc/config/{ => builder}/cell.json (100%) rename scripts/sdkjs_common/jsdoc/config/{ => builder}/correct_doclets.js (100%) rename scripts/sdkjs_common/jsdoc/config/{ => builder}/forms.json (100%) rename scripts/sdkjs_common/jsdoc/config/{ => builder}/slide.json (100%) rename scripts/sdkjs_common/jsdoc/config/{ => builder}/word.json (100%) create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/cell.json create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/common.json create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/correct_doclets.js create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/forms.json create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/slide.json create mode 100644 scripts/sdkjs_common/jsdoc/config/plugins/word.json create mode 100644 scripts/sdkjs_common/jsdoc/generate_docs_plugins_json.py diff --git a/scripts/sdkjs_common/jsdoc/README.md b/scripts/sdkjs_common/jsdoc/README.md index 61ad80af..01c14063 100644 --- a/scripts/sdkjs_common/jsdoc/README.md +++ b/scripts/sdkjs_common/jsdoc/README.md @@ -1,7 +1,7 @@ # Documentation Generation Guide -This guide explains how to generate documentation for Onlyoffice API using the provided Python scripts, `generate_docs_json.py` and `generate_docs_md.py`. These scripts are used to create JSON and Markdown documentation for the `apiBuilder.js` files from the word, cell, and slide editors. +This guide explains how to generate documentation for Onlyoffice Builder/Plugins API using the provided Python scripts: `generate_docs_json.py`, `generate_docs_plugins_json.py`, `generate_docs_md.py`. These scripts are used to create JSON and Markdown documentation for the `apiBuilder.js` files from the word, cell, and slide editors. ## Prerequisites @@ -24,7 +24,19 @@ This script generates JSON documentation based on the `apiBuilder.js` files. ``` - **Parameters**: - - `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `Onlyoffice/sdkjs/deploy/api_builder/json`. + - `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `Onlyoffice/document-builder-declarations/document-builder`. + +### `generate_docs_plugins_json.py` + +This script generates JSON documentation based on the `api_plugins.js` files. + +- **Usage**: + ```bash + python generate_docs_plugins_json.py output_path + ``` + +- **Parameters**: + - `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `Onlyoffice/document-builder-declarations/document-builder-plugin`. ### `generate_docs_md.py` @@ -45,6 +57,11 @@ To generate JSON documentation with the default output path: python generate_docs_json.py /path/to/save/json ``` +To generate JSON documentation with the default output path: +```bash +python generate_docs_plugins_json.py /path/to/save/json +``` + To generate Markdown documentation and specify a custom output path: ```bash python generate_docs_md.py /path/to/save/markdown diff --git a/scripts/sdkjs_common/jsdoc/config/cell.json b/scripts/sdkjs_common/jsdoc/config/builder/cell.json similarity index 100% rename from scripts/sdkjs_common/jsdoc/config/cell.json rename to scripts/sdkjs_common/jsdoc/config/builder/cell.json diff --git a/scripts/sdkjs_common/jsdoc/config/correct_doclets.js b/scripts/sdkjs_common/jsdoc/config/builder/correct_doclets.js similarity index 100% rename from scripts/sdkjs_common/jsdoc/config/correct_doclets.js rename to scripts/sdkjs_common/jsdoc/config/builder/correct_doclets.js diff --git a/scripts/sdkjs_common/jsdoc/config/forms.json b/scripts/sdkjs_common/jsdoc/config/builder/forms.json similarity index 100% rename from scripts/sdkjs_common/jsdoc/config/forms.json rename to scripts/sdkjs_common/jsdoc/config/builder/forms.json diff --git a/scripts/sdkjs_common/jsdoc/config/slide.json b/scripts/sdkjs_common/jsdoc/config/builder/slide.json similarity index 100% rename from scripts/sdkjs_common/jsdoc/config/slide.json rename to scripts/sdkjs_common/jsdoc/config/builder/slide.json diff --git a/scripts/sdkjs_common/jsdoc/config/word.json b/scripts/sdkjs_common/jsdoc/config/builder/word.json similarity index 100% rename from scripts/sdkjs_common/jsdoc/config/word.json rename to scripts/sdkjs_common/jsdoc/config/builder/word.json diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/cell.json b/scripts/sdkjs_common/jsdoc/config/plugins/cell.json new file mode 100644 index 00000000..b49b71ea --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/cell.json @@ -0,0 +1,16 @@ +{ + "source": { + "include": ["../../../../sdkjs/cell/api_plugins.js"] + }, + "plugins": ["./correct_doclets.js"], + "opts": { + "destination": "./out", + "recurse": true, + "encoding": "utf8" + }, + "templates": { + "json": { + "pretty": true + } + } +} diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/common.json b/scripts/sdkjs_common/jsdoc/config/plugins/common.json new file mode 100644 index 00000000..4bf510c2 --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/common.json @@ -0,0 +1,16 @@ +{ + "source": { + "include": ["../../../../sdkjs/common/plugins/plugin_base_api.js" ,"../../../../sdkjs/common/apiBase_plugins.js"] + }, + "plugins": ["./correct_doclets.js"], + "opts": { + "destination": "./out", + "recurse": true, + "encoding": "utf8" + }, + "templates": { + "json": { + "pretty": true + } + } +} \ No newline at end of file diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/correct_doclets.js b/scripts/sdkjs_common/jsdoc/config/plugins/correct_doclets.js new file mode 100644 index 00000000..bea3873c --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/correct_doclets.js @@ -0,0 +1,85 @@ +exports.handlers = { + processingComplete: function(e) { + const filteredDoclets = []; + + function checkNullProps(oDoclet) { + for (let key of Object.keys(oDoclet)) { + if (oDoclet[key] == null) { + delete oDoclet[key]; + } + if (typeof(oDoclet[key]) == "object") { + checkNullProps(oDoclet[key]); + } + } + } + + for (let i = 0; i < e.doclets.length; i++) { + const doclet = e.doclets[i]; + if (true == doclet.undocumented || doclet.kind == 'package') { + continue; + } + + const filteredDoclet = { + comment: doclet.comment, + + meta: doclet.meta ? { + lineno: doclet.meta.lineno, + columnno: doclet.meta.columnno + } : doclet.meta, + + kind: doclet.kind, + since: doclet.since, + name: doclet.name, + type: doclet.type ? { + names: doclet.type.names, + parsedType: doclet.type.parsedType + } : doclet.type, + + description: doclet.description, + memberof: doclet.memberof, + + properties: doclet.properties ? doclet.properties.map(property => ({ + type: property.type ? { + names: property.type.names, + parsedType: property.type.parsedType + } : property.type, + + name: property.name, + description: property.description, + optional: property.optional, + defaultvalue: property.defaultvalue + })) : doclet.properties, + + longname: doclet.longname, + scope: doclet.scope, + alias: doclet.alias, + + params: doclet.params ? doclet.params.map(param => ({ + type: param.type ? { + names: param.type.names, + parsedType: param.type.parsedType + } : param.type, + + name: param.name, + description: param.description, + optional: param.optional, + defaultvalue: param.defaultvalue + })) : doclet.params, + + returns: doclet.returns ? doclet.returns.map(returnObj => ({ + type: { + names: returnObj.type.names, + parsedType: returnObj.type.parsedType + } + })) : doclet.returns, + see: doclet.see + }; + + checkNullProps(filteredDoclet) + + filteredDoclets.push(filteredDoclet); + } + + e.doclets.splice(0, e.doclets.length, ...filteredDoclets); + } +}; \ No newline at end of file diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/forms.json b/scripts/sdkjs_common/jsdoc/config/plugins/forms.json new file mode 100644 index 00000000..65cb6a2e --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/forms.json @@ -0,0 +1,16 @@ +{ + "source": { + "include": ["../../../../sdkjs-forms/apiPlugins.js"] + }, + "plugins": ["./correct_doclets.js"], + "opts": { + "destination": "./out", + "recurse": true, + "encoding": "utf8" + }, + "templates": { + "json": { + "pretty": true + } + } +} \ No newline at end of file diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/slide.json b/scripts/sdkjs_common/jsdoc/config/plugins/slide.json new file mode 100644 index 00000000..d0151716 --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/slide.json @@ -0,0 +1,16 @@ +{ + "source": { + "include": ["../../../../sdkjs/slide/api_plugins.js"] + }, + "plugins": ["./correct_doclets.js"], + "opts": { + "destination": "./out", + "recurse": true, + "encoding": "utf8" + }, + "templates": { + "json": { + "pretty": true + } + } +} diff --git a/scripts/sdkjs_common/jsdoc/config/plugins/word.json b/scripts/sdkjs_common/jsdoc/config/plugins/word.json new file mode 100644 index 00000000..b06743ea --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/config/plugins/word.json @@ -0,0 +1,16 @@ +{ + "source": { + "include": ["../../../../sdkjs/word/api_plugins.js", "../../../../sdkjs-forms/apiPlugins.js"] + }, + "plugins": ["./correct_doclets.js"], + "opts": { + "destination": "./out", + "recurse": true, + "encoding": "utf8" + }, + "templates": { + "json": { + "pretty": true + } + } +} \ No newline at end of file diff --git a/scripts/sdkjs_common/jsdoc/generate_docs_json.py b/scripts/sdkjs_common/jsdoc/generate_docs_json.py index b4710bba..e5c73d95 100644 --- a/scripts/sdkjs_common/jsdoc/generate_docs_json.py +++ b/scripts/sdkjs_common/jsdoc/generate_docs_json.py @@ -4,12 +4,14 @@ import argparse import re +root = '../../../..' + # Configuration files configs = [ - "./config/word.json", - "./config/cell.json", - "./config/slide.json", - "./config/forms.json" + "./config/builder/word.json", + "./config/builder/cell.json", + "./config/builder/slide.json", + "./config/builder/forms.json" ] editors_maps = { @@ -55,7 +57,7 @@ def generate(output_dir, md=False): else: doclet['see'][0] = doclet['see'][0].replace('{Editor}', editor_name.title()) - file_path = '../../../../' + doclet['see'][0] + file_path = f'{root}/' + doclet['see'][0] if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as see_file: @@ -85,7 +87,7 @@ def generate(output_dir, md=False): with open(output_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=4) - print("Documentation generation completed.") + print("Documentation generation for builder completed.") def remove_builder_lines(text): lines = text.splitlines() # Split text into lines @@ -125,11 +127,11 @@ def get_current_branch(path): type=str, help="Destination directory for the generated documentation", nargs='?', # Indicates the argument is optional - default="../../../../document-builder-declarations/document-builder" # Default value + default=f"{root}/document-builder-declarations/document-builder" # Default value ) args = parser.parse_args() - branch_name = get_current_branch("../../../../sdkjs") + branch_name = get_current_branch(f"{root}/sdkjs") if branch_name: args.destination = f"{args.destination}/{branch_name}" diff --git a/scripts/sdkjs_common/jsdoc/generate_docs_plugins_json.py b/scripts/sdkjs_common/jsdoc/generate_docs_plugins_json.py new file mode 100644 index 00000000..12df9817 --- /dev/null +++ b/scripts/sdkjs_common/jsdoc/generate_docs_plugins_json.py @@ -0,0 +1,132 @@ +import os +import subprocess +import json +import argparse +import re + +# Configuration files +configs = [ + "./config/plugins/common.json", + "./config/plugins/word.json", + "./config/plugins/cell.json", + "./config/plugins/slide.json", + "./config/plugins/forms.json" +] + +root = '../../../..' + +def generate(output_dir, md=False): + missing_examples_file = f'{output_dir}/missing_examples.txt' + + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Recreate missing_examples.txt file + with open(missing_examples_file, 'w', encoding='utf-8') as f: + f.write('') + + # Generate JSON documentation + for config in configs: + editor_name = config.split('/')[-1].replace('.json', '') + output_file = os.path.join(output_dir, editor_name + ".json") + command = f"npx jsdoc -c {config} -X > {output_file}" + print(f"Generating {editor_name}.json: {command}") + subprocess.run(command, shell=True) + + # Append examples to JSON documentation + for config in configs: + editor_name = config.split('/')[-1].replace('.json', '') + output_file = os.path.join(output_dir, editor_name + ".json") + + # Read the JSON file + with open(output_file, 'r', encoding='utf-8') as f: + data = json.load(f) + + # Modify JSON data + for doclet in data: + if 'see' in doclet: + if doclet['see'] is not None: + if editor_name == 'forms': + doclet['see'][0] = doclet['see'][0].replace('{Editor}', 'Word') + else: + doclet['see'][0] = doclet['see'][0].replace('{Editor}', editor_name.title()) + + file_path = f'{root}/' + doclet['see'][0] + + if os.path.exists(file_path): + with open(file_path, 'r', encoding='utf-8') as see_file: + example_content = see_file.read() + + # Extract the first line as a comment if it exists + lines = example_content.split('\n') + if lines[0].startswith('//'): + comment = lines[0] + '\n' + code_content = '\n'.join(lines[1:]) + else: + comment = '' + code_content = example_content + + # Format content for doclet['example'] + doclet['example'] = remove_js_comments(comment) + "```js\n" + code_content + "\n```" + + if md == False: + doclet['description'] = doclet['description'] + f'\n\n## Try it\n\n ```js document-builder={{"documentType": "{editor_name.title()}"}}\n{code_content}\n```' + + else: + # Record missing examples in missing_examples.txt + with open(missing_examples_file, 'a', encoding='utf-8') as missing_file: + missing_file.write(f"{file_path}\n") + + # Write the modified JSON file back + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=4) + + print("Documentation generation for builder completed.") + +def remove_builder_lines(text): + lines = text.splitlines() # Split text into lines + filtered_lines = [line for line in lines if not line.strip().startswith("builder.")] + return "\n".join(filtered_lines) + +def remove_js_comments(text): + # Remove single-line comments, leaving text after // + text = re.sub(r'^\s*//\s?', '', text, flags=re.MULTILINE) + # Remove multi-line comments, leaving text after /* + text = re.sub(r'/\*\s*|\s*\*/', '', text, flags=re.DOTALL) + return text.strip() + +def get_current_branch(path): + try: + # Navigate to the specified directory and get the current branch name + result = subprocess.run( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + cwd=path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + if result.returncode == 0: + return result.stdout.strip() + else: + print(f"Error: {result.stderr}") + return None + except Exception as e: + print(f"Exception: {e}") + return None + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate documentation") + parser.add_argument( + "destination", + type=str, + help="Destination directory for the generated documentation", + nargs='?', # Indicates the argument is optional + default=f"{root}/document-builder-declarations/document-builder-plugin" # Default value + ) + args = parser.parse_args() + + branch_name = get_current_branch(f"{root}/sdkjs") + if branch_name: + args.destination = f"{args.destination}/{branch_name}" + + generate(args.destination)