forked from FINAKON/HelpProject
1. Initial Commit - a boiler plate code and POC to realize the concept of context sensitive help 2. Frontend code written in ReactJS 3. Backend code written in Java, Spring Boot Framework 4. Frontend Start: pre-requisites : node, npm npm run dev ==> to start the frontend vite server 5. Backend Start: pre-requisites : java, mvn mvn spring-boot:run ==> to start the backend server 6. Visit http://localhost:5173/ for basic demo of help, press F1 in textboxes 7. Visit http://localhost:5173/editor and enter "admin123" to add/modify texts. Happy Coding !!! Thank you, Bhargava.
354 lines
8.0 KiB
JavaScript
354 lines
8.0 KiB
JavaScript
'use strict'
|
|
|
|
const DEFAULT_RAW = {
|
|
after: '\n',
|
|
beforeClose: '\n',
|
|
beforeComment: '\n',
|
|
beforeDecl: '\n',
|
|
beforeOpen: ' ',
|
|
beforeRule: '\n',
|
|
colon: ': ',
|
|
commentLeft: ' ',
|
|
commentRight: ' ',
|
|
emptyBody: '',
|
|
indent: ' ',
|
|
semicolon: false
|
|
}
|
|
|
|
function capitalize(str) {
|
|
return str[0].toUpperCase() + str.slice(1)
|
|
}
|
|
|
|
class Stringifier {
|
|
constructor(builder) {
|
|
this.builder = builder
|
|
}
|
|
|
|
atrule(node, semicolon) {
|
|
let name = '@' + node.name
|
|
let params = node.params ? this.rawValue(node, 'params') : ''
|
|
|
|
if (typeof node.raws.afterName !== 'undefined') {
|
|
name += node.raws.afterName
|
|
} else if (params) {
|
|
name += ' '
|
|
}
|
|
|
|
if (node.nodes) {
|
|
this.block(node, name + params)
|
|
} else {
|
|
let end = (node.raws.between || '') + (semicolon ? ';' : '')
|
|
this.builder(name + params + end, node)
|
|
}
|
|
}
|
|
|
|
beforeAfter(node, detect) {
|
|
let value
|
|
if (node.type === 'decl') {
|
|
value = this.raw(node, null, 'beforeDecl')
|
|
} else if (node.type === 'comment') {
|
|
value = this.raw(node, null, 'beforeComment')
|
|
} else if (detect === 'before') {
|
|
value = this.raw(node, null, 'beforeRule')
|
|
} else {
|
|
value = this.raw(node, null, 'beforeClose')
|
|
}
|
|
|
|
let buf = node.parent
|
|
let depth = 0
|
|
while (buf && buf.type !== 'root') {
|
|
depth += 1
|
|
buf = buf.parent
|
|
}
|
|
|
|
if (value.includes('\n')) {
|
|
let indent = this.raw(node, null, 'indent')
|
|
if (indent.length) {
|
|
for (let step = 0; step < depth; step++) value += indent
|
|
}
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
block(node, start) {
|
|
let between = this.raw(node, 'between', 'beforeOpen')
|
|
this.builder(start + between + '{', node, 'start')
|
|
|
|
let after
|
|
if (node.nodes && node.nodes.length) {
|
|
this.body(node)
|
|
after = this.raw(node, 'after')
|
|
} else {
|
|
after = this.raw(node, 'after', 'emptyBody')
|
|
}
|
|
|
|
if (after) this.builder(after)
|
|
this.builder('}', node, 'end')
|
|
}
|
|
|
|
body(node) {
|
|
let last = node.nodes.length - 1
|
|
while (last > 0) {
|
|
if (node.nodes[last].type !== 'comment') break
|
|
last -= 1
|
|
}
|
|
|
|
let semicolon = this.raw(node, 'semicolon')
|
|
for (let i = 0; i < node.nodes.length; i++) {
|
|
let child = node.nodes[i]
|
|
let before = this.raw(child, 'before')
|
|
if (before) this.builder(before)
|
|
this.stringify(child, last !== i || semicolon)
|
|
}
|
|
}
|
|
|
|
comment(node) {
|
|
let left = this.raw(node, 'left', 'commentLeft')
|
|
let right = this.raw(node, 'right', 'commentRight')
|
|
this.builder('/*' + left + node.text + right + '*/', node)
|
|
}
|
|
|
|
decl(node, semicolon) {
|
|
let between = this.raw(node, 'between', 'colon')
|
|
let string = node.prop + between + this.rawValue(node, 'value')
|
|
|
|
if (node.important) {
|
|
string += node.raws.important || ' !important'
|
|
}
|
|
|
|
if (semicolon) string += ';'
|
|
this.builder(string, node)
|
|
}
|
|
|
|
document(node) {
|
|
this.body(node)
|
|
}
|
|
|
|
raw(node, own, detect) {
|
|
let value
|
|
if (!detect) detect = own
|
|
|
|
// Already had
|
|
if (own) {
|
|
value = node.raws[own]
|
|
if (typeof value !== 'undefined') return value
|
|
}
|
|
|
|
let parent = node.parent
|
|
|
|
if (detect === 'before') {
|
|
// Hack for first rule in CSS
|
|
if (!parent || (parent.type === 'root' && parent.first === node)) {
|
|
return ''
|
|
}
|
|
|
|
// `root` nodes in `document` should use only their own raws
|
|
if (parent && parent.type === 'document') {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
// Floating child without parent
|
|
if (!parent) return DEFAULT_RAW[detect]
|
|
|
|
// Detect style by other nodes
|
|
let root = node.root()
|
|
if (!root.rawCache) root.rawCache = {}
|
|
if (typeof root.rawCache[detect] !== 'undefined') {
|
|
return root.rawCache[detect]
|
|
}
|
|
|
|
if (detect === 'before' || detect === 'after') {
|
|
return this.beforeAfter(node, detect)
|
|
} else {
|
|
let method = 'raw' + capitalize(detect)
|
|
if (this[method]) {
|
|
value = this[method](root, node)
|
|
} else {
|
|
root.walk(i => {
|
|
value = i.raws[own]
|
|
if (typeof value !== 'undefined') return false
|
|
})
|
|
}
|
|
}
|
|
|
|
if (typeof value === 'undefined') value = DEFAULT_RAW[detect]
|
|
|
|
root.rawCache[detect] = value
|
|
return value
|
|
}
|
|
|
|
rawBeforeClose(root) {
|
|
let value
|
|
root.walk(i => {
|
|
if (i.nodes && i.nodes.length > 0) {
|
|
if (typeof i.raws.after !== 'undefined') {
|
|
value = i.raws.after
|
|
if (value.includes('\n')) {
|
|
value = value.replace(/[^\n]+$/, '')
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
if (value) value = value.replace(/\S/g, '')
|
|
return value
|
|
}
|
|
|
|
rawBeforeComment(root, node) {
|
|
let value
|
|
root.walkComments(i => {
|
|
if (typeof i.raws.before !== 'undefined') {
|
|
value = i.raws.before
|
|
if (value.includes('\n')) {
|
|
value = value.replace(/[^\n]+$/, '')
|
|
}
|
|
return false
|
|
}
|
|
})
|
|
if (typeof value === 'undefined') {
|
|
value = this.raw(node, null, 'beforeDecl')
|
|
} else if (value) {
|
|
value = value.replace(/\S/g, '')
|
|
}
|
|
return value
|
|
}
|
|
|
|
rawBeforeDecl(root, node) {
|
|
let value
|
|
root.walkDecls(i => {
|
|
if (typeof i.raws.before !== 'undefined') {
|
|
value = i.raws.before
|
|
if (value.includes('\n')) {
|
|
value = value.replace(/[^\n]+$/, '')
|
|
}
|
|
return false
|
|
}
|
|
})
|
|
if (typeof value === 'undefined') {
|
|
value = this.raw(node, null, 'beforeRule')
|
|
} else if (value) {
|
|
value = value.replace(/\S/g, '')
|
|
}
|
|
return value
|
|
}
|
|
|
|
rawBeforeOpen(root) {
|
|
let value
|
|
root.walk(i => {
|
|
if (i.type !== 'decl') {
|
|
value = i.raws.between
|
|
if (typeof value !== 'undefined') return false
|
|
}
|
|
})
|
|
return value
|
|
}
|
|
|
|
rawBeforeRule(root) {
|
|
let value
|
|
root.walk(i => {
|
|
if (i.nodes && (i.parent !== root || root.first !== i)) {
|
|
if (typeof i.raws.before !== 'undefined') {
|
|
value = i.raws.before
|
|
if (value.includes('\n')) {
|
|
value = value.replace(/[^\n]+$/, '')
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
if (value) value = value.replace(/\S/g, '')
|
|
return value
|
|
}
|
|
|
|
rawColon(root) {
|
|
let value
|
|
root.walkDecls(i => {
|
|
if (typeof i.raws.between !== 'undefined') {
|
|
value = i.raws.between.replace(/[^\s:]/g, '')
|
|
return false
|
|
}
|
|
})
|
|
return value
|
|
}
|
|
|
|
rawEmptyBody(root) {
|
|
let value
|
|
root.walk(i => {
|
|
if (i.nodes && i.nodes.length === 0) {
|
|
value = i.raws.after
|
|
if (typeof value !== 'undefined') return false
|
|
}
|
|
})
|
|
return value
|
|
}
|
|
|
|
rawIndent(root) {
|
|
if (root.raws.indent) return root.raws.indent
|
|
let value
|
|
root.walk(i => {
|
|
let p = i.parent
|
|
if (p && p !== root && p.parent && p.parent === root) {
|
|
if (typeof i.raws.before !== 'undefined') {
|
|
let parts = i.raws.before.split('\n')
|
|
value = parts[parts.length - 1]
|
|
value = value.replace(/\S/g, '')
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
return value
|
|
}
|
|
|
|
rawSemicolon(root) {
|
|
let value
|
|
root.walk(i => {
|
|
if (i.nodes && i.nodes.length && i.last.type === 'decl') {
|
|
value = i.raws.semicolon
|
|
if (typeof value !== 'undefined') return false
|
|
}
|
|
})
|
|
return value
|
|
}
|
|
|
|
rawValue(node, prop) {
|
|
let value = node[prop]
|
|
let raw = node.raws[prop]
|
|
if (raw && raw.value === value) {
|
|
return raw.raw
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
root(node) {
|
|
this.body(node)
|
|
if (node.raws.after) this.builder(node.raws.after)
|
|
}
|
|
|
|
rule(node) {
|
|
this.block(node, this.rawValue(node, 'selector'))
|
|
if (node.raws.ownSemicolon) {
|
|
this.builder(node.raws.ownSemicolon, node, 'end')
|
|
}
|
|
}
|
|
|
|
stringify(node, semicolon) {
|
|
/* c8 ignore start */
|
|
if (!this[node.type]) {
|
|
throw new Error(
|
|
'Unknown AST node type ' +
|
|
node.type +
|
|
'. ' +
|
|
'Maybe you need to change PostCSS stringifier.'
|
|
)
|
|
}
|
|
/* c8 ignore stop */
|
|
this[node.type](node, semicolon)
|
|
}
|
|
}
|
|
|
|
module.exports = Stringifier
|
|
Stringifier.default = Stringifier
|