Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions docs/userGuide/puml.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,27 @@ A PlantUML diagram file (.puml) can be inserted into a Markbind page using a `<p
the same attributes as the `<pic>` tag, listed below:

****Options****
Name | Type | Default | Description
--- | --- | --- | ---
alt | `string` | | **This must be specified.**<br>The alternative text of the diagram.
height | `string` | | The height of the diagram in pixels.
src | `string` | | **This must be specified.**<br>The URL of the diagram.<br>The URL can be specified as absolute or relative references. More info in: _[Intra-Site Links]({{baseUrl}}/userGuide/formattingContents.html#intraSiteLinks)_
width | `string` | | The width of the diagram in pixels.<br>If both width and height are specified, width takes priority over height. It is to maintain the diagram's aspect ratio.
Name | Type | Description
--- | --- | ---
alt | `string` | **This must be specified.**<br>The alternative text of the diagram.
height | `string` | The height of the diagram in pixels.
name | `string` | The name of the output file.
src | `string` | The URL of the diagram if your PUML input is in another file.<br>The URL can be specified as absolute or relative references. More info in: _[Intra-Site Links]({{baseUrl}}/userGuide/formattingContents.html#intraSiteLinks)_
width | `string` | The width of the diagram in pixels.<br>If both width and height are specified, width takes priority over height. It is to maintain the diagram's aspect ratio.


### Example

You could have your PUML be written in a separate file or inline.

<include src="outputBox.md" boilerplate>
<span id="code">

_Markbind page separate file_:
```
<puml src="diagrams/sequence.puml" width=300 />
```

_diagrams/sequence.puml_:
```
@startuml
Expand All @@ -57,9 +65,20 @@ return success
@enduml
```

_Markbind page_:
_Markbind page inline_:
```
<puml src="diagrams/sequence.puml" width=300/>
<puml width=300>
@startuml
alice -> bob ++ : hello
bob -> bob ++ : self call
bob -> bib ++ #005500 : hello
bob -> george ** : create
return done
return rc
bob -> george !! : delete
return success
@enduml
</puml>
```

</span>
Expand Down
46 changes: 16 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"cheerio": "^0.22.0",
"chokidar": "^3.3.0",
"commander": "^3.0.2",
"crypto-js": "^4.0.0",
"fastmatter": "^2.1.1",
"figlet": "^1.2.4",
"find-up": "^4.1.0",
Expand Down
108 changes: 47 additions & 61 deletions src/plugins/default/markbind-plugin-plantuml.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,92 +6,73 @@
const cheerio = module.parent.require('cheerio');
const fs = require('fs');
const path = require('path');

const { exec } = require('child_process');
const logger = require('../../util/logger');
const pluginUtil = require('../../util/pluginUtil');

const JAR_PATH = path.resolve(__dirname, 'plantuml.jar');

const {
ERR_PROCESSING,
ERR_READING,
} = require('../../constants');

// Tracks diagrams that have already been processed
const processedDiagrams = new Set();

/**
* Generates diagram and replaces src attribute of puml tag
* @param src src attribute of the puml tag
* @param cwf original file that contains the puml tag, we resolve relative src to this file
* Generates diagram and returns the file name of the diagram
* @param fileName name of the file to be generated
* @param content puml dsl used to generate the puml diagram
* @param config sourcePath and resultPath from parser context
* @returns {string} resolved src attribute
* @returns {string} file name of diagram
*/
function generateDiagram(src, cwf, config) {
const { sourcePath, resultPath } = config;
const _cwf = cwf || sourcePath;
function generateDiagram(fileName, content, config) {
const { resultPath } = config;

// For replacing img.src
const diagramSrc = src.replace('.puml', '.png');
const outputDir = path.join(path.dirname(resultPath), path.dirname(fileName));
// Path of the .puml file
const rawDiagramPath = path.resolve(path.dirname(_cwf), src);
// Path of the .png to be generated
const outputFilePath = path.resolve(resultPath, path.relative(sourcePath, rawDiagramPath)
.replace('.puml', '.png'));
// Output dir for the png file
const outputDir = path.dirname(outputFilePath);

const outputFilePath = path.join(outputDir, path.basename(fileName));
// Tracks built files to avoid accessing twice
if (processedDiagrams.has(outputFilePath)) { return diagramSrc; }
if (processedDiagrams.has(outputFilePath)) { return fileName; }
processedDiagrams.add(outputFilePath);

// Creates output dir if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

// Read diagram file, launch PlantUML jar
fs.readFile(rawDiagramPath, (err, data) => {
if (err) {
logger.debug(err);
logger.error(`${ERR_READING} ${rawDiagramPath}`);
return;
}

const umlCode = data.toString();

// Java command to launch PlantUML jar
const cmd = `java -jar "${JAR_PATH}" -pipe > "${outputFilePath}"`;
const childProcess = exec(cmd);

let errorLog = '';

childProcess.stdin.write(
umlCode,
(e) => {
if (e) {
logger.debug(e);
logger.error(`${ERR_PROCESSING} ${rawDiagramPath}`);
}
childProcess.stdin.end();
},
);

childProcess.on('error', (error) => {
logger.debug(error);
logger.error(`${ERR_PROCESSING} ${rawDiagramPath}`);
});
// Java command to launch PlantUML jar
const cmd = `java -jar "${JAR_PATH}" -pipe > "${outputFilePath}"`;
const childProcess = exec(cmd);

let errorLog = '';
childProcess.stdin.write(
content,
(e) => {
if (e) {
logger.debug(e);
logger.error(`${ERR_PROCESSING} ${fileName}`);
}
childProcess.stdin.end();
},
);

childProcess.on('error', (error) => {
logger.debug(error);
logger.error(`${ERR_PROCESSING} ${fileName}`);
});

childProcess.stderr.on('data', (errorMsg) => {
errorLog += errorMsg;
});
childProcess.stderr.on('data', (errorMsg) => {
errorLog += errorMsg;
});

childProcess.on('exit', () => {
// This goes to the log file, but not shown on the console
logger.debug(errorLog);
});
childProcess.on('exit', () => {
// This goes to the log file, but not shown on the console
logger.debug(errorLog);
});

return diagramSrc;
return fileName;
}

module.exports = {
Expand All @@ -101,16 +82,21 @@ module.exports = {
// Processes all <puml> tags
const $ = cheerio.load(content, { xmlMode: true });
$('puml').each((i, tag) => {
// eslint-disable-next-line no-param-reassign
tag.name = 'pic';
const { src, cwf } = tag.attribs;
// eslint-disable-next-line no-param-reassign
tag.attribs.src = generateDiagram(src, cwf, config);
const { cwf } = tag.attribs;
const pumlContent = pluginUtil.getPluginContent($, tag, cwf);

const filePath = pluginUtil.getFilePathForPlugin(tag.attribs, pumlContent);

tag.attribs.src = generateDiagram(filePath, pumlContent, config);
tag.children = [];
});

return $.html();
},
getSources: () => ({
tagMap: [['puml', 'src']],
}),

getSpecialTags: () => ['puml'],
};
Loading