mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
add article vue2md
This commit is contained in:
parent
c5ffbde7c7
commit
506023afc0
7 changed files with 378 additions and 0 deletions
37
vue2md/components/Form/Input.vue
Normal file
37
vue2md/components/Form/Input.vue
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<script setup>
|
||||
/**
|
||||
@name Form/Input.vue
|
||||
@description This component is custom input to be used with forms.
|
||||
@example
|
||||
{
|
||||
name : "my subtitle"
|
||||
value : "P@ssw0rd"
|
||||
type : "password"
|
||||
}
|
||||
@param {string} name
|
||||
@param {string} [value=""]
|
||||
@param {string} [type="text"]
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// id && value && method
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "type",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input :type="props.type" :name="props.name" :value="props.value" />
|
||||
</template>
|
||||
33
vue2md/components/Main.vue
Normal file
33
vue2md/components/Main.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<script setup>
|
||||
/**
|
||||
@name Main.vue
|
||||
@description This component is my main layout for my app.
|
||||
@example
|
||||
{
|
||||
title : "My app",
|
||||
subtitle : "my subtitle"
|
||||
}
|
||||
@param {string} title
|
||||
@param {string} [subtitle=""] - More information if needed
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
// id && value && method
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ props.title }}</h1>
|
||||
<p v-if="props.subtitle">{{ props.subtitle }}</p>
|
||||
</div>
|
||||
</template>
|
||||
40
vue2md/output/COMPONENTS.md
Normal file
40
vue2md/output/COMPONENTS.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
## Form
|
||||
|
||||
### Input.vue
|
||||
|
||||
This component is custom input to be used with forms.
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `name` **[string][4]** 
|
||||
* `value` **[string][4]** (optional, default `""`)
|
||||
* `type` **[string][4]** (optional, default `"text"`)
|
||||
|
||||
#### Examples
|
||||
|
||||
```javascript
|
||||
{
|
||||
name : "my subtitle"
|
||||
value : "P@ssw0rd"
|
||||
type : "password"
|
||||
}
|
||||
```
|
||||
|
||||
## Main.vue
|
||||
|
||||
This component is my main layout for my app.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `title` **[string][4]** 
|
||||
* `subtitle` **[string][4]** More information if needed (optional, default `""`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
{
|
||||
title : "My app",
|
||||
subtitle : "my subtitle"
|
||||
}
|
||||
```
|
||||
|
||||
BIN
vue2md/output/COMPONENTS.pdf
Normal file
BIN
vue2md/output/COMPONENTS.pdf
Normal file
Binary file not shown.
20
vue2md/output/Input.md
Normal file
20
vue2md/output/Input.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
## Form/Input.vue
|
||||
|
||||
This component is custom input to be used with forms.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `name` **[string][4]** 
|
||||
* `value` **[string][4]** (optional, default `""`)
|
||||
* `type` **[string][4]** (optional, default `"text"`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
{
|
||||
name : "my subtitle"
|
||||
value : "P@ssw0rd"
|
||||
type : "password"
|
||||
}
|
||||
```
|
||||
|
||||
18
vue2md/output/Main.md
Normal file
18
vue2md/output/Main.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
## Main.vue
|
||||
|
||||
This component is my main layout for my app.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `title` **[string][4]** 
|
||||
* `subtitle` **[string][4]** More information if needed (optional, default `""`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
{
|
||||
title : "My app",
|
||||
subtitle : "my subtitle"
|
||||
}
|
||||
```
|
||||
|
||||
230
vue2md/vue2md.js
Normal file
230
vue2md/vue2md.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// Merge all components md on this file name
|
||||
const finalFile = "COMPONENTS.md";
|
||||
// Where we have all SFC components
|
||||
const inputFolder = path.join(__dirname, "components");
|
||||
// Where we want to output md components file
|
||||
const ouputFolder = path.join(__dirname, "output");
|
||||
|
||||
// Check that input folder exists
|
||||
if (!fs.existsSync(inputFolder)) {
|
||||
console.error("Input folder does not exist");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Create the output folder if it doesn't exist
|
||||
if (!fs.existsSync(ouputFolder)) {
|
||||
fs.mkdirSync(ouputFolder);
|
||||
}
|
||||
|
||||
// Remove previous content of the output folder
|
||||
fs.readdirSync(ouputFolder).forEach((file) => {
|
||||
fs.unlinkSync(path.join(ouputFolder, file));
|
||||
});
|
||||
|
||||
// Allow to get all subfolders from inputFolder
|
||||
function flatten(lists) {
|
||||
return lists.reduce((a, b) => a.concat(b), []);
|
||||
}
|
||||
|
||||
function getDirectories(srcpath) {
|
||||
return fs
|
||||
.readdirSync(srcpath)
|
||||
.map((file) => path.join(srcpath, file))
|
||||
.filter((path) => fs.statSync(path).isDirectory());
|
||||
}
|
||||
|
||||
function getDirectoriesRecursive(srcpath) {
|
||||
return [
|
||||
srcpath,
|
||||
...flatten(getDirectories(srcpath).map(getDirectoriesRecursive)),
|
||||
];
|
||||
}
|
||||
|
||||
// Get the script part of a Vue file and create a JS file
|
||||
function vue2js() {
|
||||
const folders = getDirectoriesRecursive(inputFolder);
|
||||
// Read every subfolders from the input folder and get all files
|
||||
folders.forEach((folder) => {
|
||||
const files = fs.readdirSync(path.join(folder), {
|
||||
withFileTypes: true,
|
||||
});
|
||||
files.forEach((file) => {
|
||||
// Get only .vue file
|
||||
if (file.isFile() && file.name.endsWith(".vue")) {
|
||||
const src = path.join(folder, file.name);
|
||||
const data = fs.readFileSync(src, "utf8");
|
||||
// Get only the content between <script setup> and </script> tag
|
||||
const script = data.match(/<script setup>([\s\S]*?)<\/script>/g);
|
||||
// I want to remove the <script setup> and </script> tags
|
||||
script[0] = script[0]
|
||||
.replace("<script setup>", "")
|
||||
.replace("</script>", "");
|
||||
// Create a file on the output folder with the same name but with .js extension
|
||||
const fileName = file.name.replace(".vue", ".js");
|
||||
const dest = path.join(ouputFolder, fileName);
|
||||
fs.writeFileSync(dest, script[0], "utf8");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Run a command to render markdown from JS files
|
||||
function js2md() {
|
||||
// Get all files from the output folder
|
||||
const files = fs.readdirSync(ouputFolder, { withFileTypes: true });
|
||||
// Create a markdown file for each JS file
|
||||
files.forEach((file) => {
|
||||
// Run a process `documentation build <filename> -f md > <filename>.md
|
||||
const command = `documentation build ${path.join(
|
||||
ouputFolder,
|
||||
file.name
|
||||
)} -f md > ${path.join(ouputFolder, file.name.replace(".js", ".md"))}`;
|
||||
|
||||
// Run the command
|
||||
const { execSync } = require("child_process");
|
||||
execSync(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// console.error(`exec error: ${error}`);
|
||||
return;
|
||||
}
|
||||
// console.log(`stdout: ${stdout}`);
|
||||
// console.error(`stderr: ${stderr}`);
|
||||
});
|
||||
});
|
||||
// Remove all JS files when all processes are done
|
||||
files.forEach((file) => {
|
||||
fs.unlinkSync(path.join(ouputFolder, file.name));
|
||||
});
|
||||
}
|
||||
|
||||
// Format each md file to remove specific content
|
||||
function mergeMd() {
|
||||
// Get all files from the output folder
|
||||
const files = fs.readdirSync(ouputFolder, { withFileTypes: true });
|
||||
files.forEach((file, id) => {
|
||||
let data = fs.readFileSync(path.join(ouputFolder, file.name), "utf8");
|
||||
// In case we have "[1]:", remove everything after
|
||||
data = data.replace(/\[\d+\]:[\s\S]*?$/g, "");
|
||||
// Remove ### Table of contents
|
||||
data = data.replace("### Table of Contents", "");
|
||||
// Remove everything after the first ## tag
|
||||
const index = data.indexOf("## ");
|
||||
data = data.substring(index);
|
||||
fs.writeFileSync(path.join(ouputFolder, file.name), data, "utf8");
|
||||
});
|
||||
|
||||
// Create order using the tag title path of each file
|
||||
// For example using Form/Input.vue
|
||||
const order = [];
|
||||
files.forEach((file, id) => {
|
||||
// Get the title from first line
|
||||
const data = fs.readFileSync(path.join(ouputFolder, file.name), "utf8");
|
||||
const filePath = data.split("\n")[0].replace("## ", "");
|
||||
order.push({ path: filePath, file: file });
|
||||
});
|
||||
|
||||
// Sort by path
|
||||
order.sort((a, b) => {
|
||||
return a.path.localeCompare(b.path);
|
||||
});
|
||||
|
||||
// Create the md file to merge
|
||||
const merge = path.join(ouputFolder, finalFile);
|
||||
fs.writeFileSync(merge, "", "utf8");
|
||||
// Append each file in order
|
||||
order.forEach((item) => {
|
||||
let data = fs.readFileSync(path.join(ouputFolder, item.file.name), "utf8");
|
||||
fs.appendFileSync(merge, data, "utf8");
|
||||
});
|
||||
}
|
||||
|
||||
// Format merge file
|
||||
function formatMd() {
|
||||
// Create a md file to merge
|
||||
const merge = path.join(ouputFolder, finalFile);
|
||||
|
||||
// Get data from merge
|
||||
let data = fs.readFileSync(merge, "utf8");
|
||||
let isLevel = true;
|
||||
let currAttemps = 0;
|
||||
const maxAttemps = 6;
|
||||
while (isLevel && currAttemps < maxAttemps) {
|
||||
currAttemps++;
|
||||
const titles = [];
|
||||
let tag = "#";
|
||||
for (let i = 0; i < currAttemps; i++) {
|
||||
tag += "#";
|
||||
}
|
||||
tag += " ";
|
||||
|
||||
// Each time, get the first level title and add it to the titles array
|
||||
data.split("\n").forEach((line) => {
|
||||
if (line.startsWith(tag) && line.includes("/")) {
|
||||
const firstLevel = line.split("/")[0];
|
||||
if (!titles.includes(firstLevel.replace(tag, "").trim()))
|
||||
titles.push(firstLevel.replace(tag, ""));
|
||||
}
|
||||
});
|
||||
// Create a top title at the first occurrence
|
||||
// And remove from component the first level string
|
||||
titles.forEach((title) => {
|
||||
let isTitleSet = false;
|
||||
data.split("\n").forEach((line) => {
|
||||
// For title
|
||||
if (line.startsWith(tag) && line.includes("/")) {
|
||||
// Add a top title before the current line
|
||||
if (!isTitleSet && line.includes(`${title}/`)) {
|
||||
data = data.replace(
|
||||
line,
|
||||
`${tag} ${title}\n\n${line
|
||||
.replace(tag, "#" + tag)
|
||||
.replace(`${title}/`, "")}`
|
||||
);
|
||||
isTitleSet = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.includes(`${title}/`)) {
|
||||
data = data.replace(
|
||||
line,
|
||||
line.replace(tag, "#" + tag).replace(`${title}/`, "")
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update the child of .vue component title to keep title levels consistency
|
||||
let componentTag = "";
|
||||
let dataSplit = data.split("\n");
|
||||
data.split("\n").forEach((line, id) => {
|
||||
if (line.startsWith("#") && line.includes(".vue")) {
|
||||
componentTag = line.split(" ")[0];
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(line.startsWith("#") && line.includes("Parameters")) ||
|
||||
(line.startsWith("#") && line.includes("Examples"))
|
||||
) {
|
||||
const elTag = line.split(" ")[0];
|
||||
// get line per id
|
||||
const updateLine = line.replace(elTag, `${componentTag}#`);
|
||||
dataSplit[id] = updateLine;
|
||||
}
|
||||
});
|
||||
// Update the data with split
|
||||
data = dataSplit.join("\n");
|
||||
|
||||
fs.writeFileSync(merge, data, "utf8");
|
||||
}
|
||||
|
||||
vue2js();
|
||||
js2md();
|
||||
mergeMd();
|
||||
formatMd();
|
||||
Loading…
Reference in a new issue