@4dsas/doc_preprocessing
Version:
Preprocess 4D doc to be built by docusaurus
308 lines (258 loc) • 9.36 kB
JavaScript
var { Instruction, TYPE } = require("./instruction.js")
const { MESSAGE } = require("../log.js")
var fs = require('fs');
class Ref extends Instruction {
constructor(inType) {
super()
this.content = "";
this._id = "";
this._filePath = "";
this._type = inType;
}
getType() {
return TYPE.REF
}
set ID(inID) {
this._id = inID
}
get ID() {
return this._id
}
set filePath(inFilePath) {
this._filePath = inFilePath
}
get filePath() {
return this._filePath
}
resolve(inContent) {
this.content = inContent.substr(this._start, this._end - this._start)
}
equalsTo(inRef) {
return this.content === inRef.content && this._id === inRef._id && this._filePath === inRef._filePath;
}
}
class REFManager {
constructor(logger, inEscapeList) {
this._ref = new Map()
this._logger = logger;
this._escapeList = inEscapeList
}
push(inRef, inUpdate) {
let isNew = false
if (!this._ref.has(inRef.ID)) {
this._ref.set(inRef.ID, inRef)
isNew = true;
}
else {
if (inUpdate) {
if (this._ref.get(inRef.ID).equalsTo(inRef)) {
//log.show(MESSAGE.WARNING, "The reference \'" + inRef.ID + "\' located in " + inRef.filePath + " is already used in " + this._ref.get(inRef.ID).filePath)
}
else {
this._ref.set(inRef.ID, inRef)
isNew = true;
}
}
else {
this._logger(MESSAGE.WARNING, "The reference \'" + inRef.ID + "\' located in " + inRef.filePath + " is already used in " + this._ref.get(inRef.ID).filePath)
}
}
return isNew
}
collectFile(inCurrentPath, inUpdate = false) {
let isNew = false
const content = fs.readFileSync(inCurrentPath, 'utf8');
const re = /(<!--)(.*?)(-->)/g
let stack = []
let match
while ((match = re.exec(content)) != null) {
const keywords = match[2].trim().split(" ")
const type = keywords[0]
switch (type) {
case TYPE.REF:
if (keywords.length > 1) {
let item = new Ref(type)
item.start = match.index + match[0].length
item.ID = Instruction.convertID2Args(keywords)
item.filePath = inCurrentPath;
stack.push(item)
}
else {
this._logger(MESSAGE.ERROR, "Missing id for reference")
}
break
case TYPE.IREF:
{
if (keywords.length > 1) {
let item = new Ref(type)
item.start = match.index + match[0].length
item.end = match.start;
item.ID = Instruction.convertID2Args(keywords)
item.filePath = inCurrentPath;
isNew |= this.push(item, inUpdate)
}
break;
}
case TYPE.END:
if (stack.length > 0) {
let item = stack.pop()
if (item.getType() === TYPE.REF) {
item.end = match.index
item.resolve(content)
isNew |= this.push(item, inUpdate)
}
} else {
this._logger(MESSAGE.ERROR, "Too many 'END' in file " + inCurrentPath)
}
break
}
}
while (stack.length > 0) {
let item = stack.pop()
this._logger(MESSAGE.ERROR, "Missing 'END' for reference " + item.ID + " in " + inCurrentPath)
}
return isNew
}
getContentFromID(inID) {
if (this._ref.has(inID)) {
return { content: this._ref.get(inID).content, found: true, type: this._ref.get(inID)._type }
}
return { content: "", found: false, type: TYPE.UNKNOWN };
}
removeFromPath(inPath) {
for (let entry of this._ref.entries()) {
if (entry[1].filePath === inPath) {
this._ref.delete(entry[0])
}
}
}
_fillAlias(withDelimiter, inHaveCharacter) {
let alias = new Map()
for (let entry of this._ref.entries()) {
let key = entry[0]
let value = entry[1].content;
if (key[0] !== inHaveCharacter) continue
key = key.substring(1)
let splitID = key.split(withDelimiter)
if (splitID.length > 1) {
if (splitID[1] == "_alias_") {
alias.set(splitID[0], value)
}
}
}
return alias;
}
_getRealKey(inAlias, inKey) {
if (inAlias.has(inKey)) {
return inAlias.get(inKey)
}
return inKey
}
_jsonEscape(str) {
str = str.replace(/(<!--)(.*?)(-->)/g, "")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n")
.replace(/\\n/g, "\\n")
.replace(/\\'/g, "\\'")
.replace(/\\"/g, '\\"')
.replace(/\\&/g, "\\&")
.replace(/\\r/g, "\\r")
.replace(/\\t/g, "\\t")
.replace(/\\b/g, "\\b")
.replace(/\\{/g, "{")
.replace(/\\}/g, "}")
.replace(/(\[(.*?)\])\(.*?\)/g, '$2')
.replace(/\\f/g, "\\f");
if (this._escapeList) {
for (let escape of this._escapeList) {
if (escape.from && escape.to)
str = str.replaceAll(escape.from, escape.to);
}
}
if (str[0] == '\n') str = str.substr(1)
if (str.slice(-1) == '\n') str = str.slice(0, -1)
return str
}
_isIDValid(inIDs) {
for (let i = 0; i < inIDs.length; ++i) {
if (inIDs[i] === "_alias_") {
return false
}
}
return true
}
formatIndex(withDelimiter, inHaveCharacter) {
let alias = this._fillAlias(withDelimiter, inHaveCharacter)
let json = {}
for (let entry of this._ref.entries()) {
const key = entry[0]
if (key[0] !== inHaveCharacter) continue
let splitID = key.substring(1).split(withDelimiter)
if (!this._isIDValid(splitID)) continue
if (splitID.length > 1) {
splitID[0] = this._getRealKey(alias, splitID[0])
}
json[splitID.join('.')] = entry[1].filePath
}
return json
}
formatToJSON(withDelimiter, inHaveCharacter) {
let alias = this._fillAlias(withDelimiter, inHaveCharacter)
let json = {}
for (let entry of this._ref.entries()) {
let key = entry[0],
value = entry[1].content;
if (key[0] !== inHaveCharacter) continue
key = key.substring(1)
const type = entry[1]._type
let splitID = key.split(withDelimiter)
if (!this._isIDValid(splitID)) continue
let currentJSON = json;
let lastID = ""
if (splitID.length > 1) {
splitID[0] = this._getRealKey(alias, splitID[0])
}
for (let i = 0; i < splitID.length - 1; i++) {
if (!(splitID[i] in currentJSON)) {
currentJSON[splitID[i]] = {}
}
currentJSON = currentJSON[splitID[i]]
}
lastID = splitID[splitID.length - 1]
let content = this._jsonEscape(value.trim())
if (content[0] == '|') {//It's an array
let lines = content.split('\n')
for (let i = 0; i < lines.length; i++) {
let columns = lines[i].split('|').filter(function (v, index, array) {
return !((index == 0 || index == array.length - 1) && v == '')
})
if (i == 0) {//|title1|title2|
currentJSON[lastID] = []
}
else if (i == 1) { }// |----|----|
else {//Content
let obj = new Array(columns.length)
for (let j = 0; j < columns.length; j++) {
obj[j] = columns[j].trim()
}
currentJSON[lastID].push(obj)
}
}
}
else {
try {
if (type === TYPE.REF) {
currentJSON[lastID] = content
}
else if (type === TYPE.IREF) {
currentJSON[lastID] = true;
}
}
catch (e) { }
}
}
return json
}
}
exports.Ref = Ref
exports.REFManager = REFManager