mirror of
https://github.com/tenrok/BBob.git
synced 2026-05-15 11:59:37 +03:00
add eslint, travis config, test tasks
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Matches multiple files with brace expansion notation
|
||||||
|
# Set default charset
|
||||||
|
[*.{js,py}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Tab indentation (no size specified)
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# Indentation override for all JS under lib directory
|
||||||
|
[lib/**.js]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "airbnb",
|
||||||
|
"plugins": [
|
||||||
|
"jest"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"jest/globals": true
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
@@ -59,3 +59,4 @@ typings/
|
|||||||
|
|
||||||
# next.js build output
|
# next.js build output
|
||||||
.next
|
.next
|
||||||
|
.idea
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- 'lts/*'
|
||||||
|
|
||||||
|
script:
|
||||||
|
- 'npm run bootstrap'
|
||||||
|
- 'set -e'
|
||||||
|
- 'npm run lint'
|
||||||
|
- 'npm run test'
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- 'npm run cover'
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const parse = require('../index');
|
const parse = require('../packages/bbob-parser/index');
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
closableTags: ['ch', 'syllable', 'tab']
|
closableTags: ['ch', 'syllable', 'tab']
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"lerna": "2.11.0",
|
"lerna": "2.11.0",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/bbob-parser"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "independent"
|
"version": "independent"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+5912
-73
File diff suppressed because it is too large
Load Diff
+22
-3
@@ -1,5 +1,24 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"scripts": {
|
||||||
"lerna": "^2.11.0"
|
"bootstrap": "lerna bootstrap",
|
||||||
}
|
"test": "lerna run test",
|
||||||
|
"cover": "lerna run cover",
|
||||||
|
"lint": "lerna run lint"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Nikolay Kostyurin",
|
||||||
|
"url": "https://artkost.ru/"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^4.19.1",
|
||||||
|
"eslint-config-airbnb": "^16.1.0",
|
||||||
|
"eslint-plugin-import": "^2.12.0",
|
||||||
|
"eslint-plugin-jest": "^21.17.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||||
|
"eslint-plugin-react": "^7.9.1",
|
||||||
|
"jest": "^23.1.0",
|
||||||
|
"lerna": "^2.11.0",
|
||||||
|
"xbbcode-parser": "^0.1.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package-lock.json
|
||||||
|
coverage
|
||||||
|
dist
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package-lock.json
|
||||||
|
coverage
|
||||||
|
src
|
||||||
|
dist
|
||||||
|
!lib
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('../lib/cli');
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const program = require('commander');
|
||||||
|
const version = require('../package.json').version;
|
||||||
|
|
||||||
|
program
|
||||||
|
.version(version)
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
|
function readFile(filename, encoding, callback) {
|
||||||
|
if (options.file === '-') {
|
||||||
|
// read from stdin
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
process.stdin.on('data', function (chunk) { chunks.push(chunk); });
|
||||||
|
|
||||||
|
process.stdin.on('end', function () {
|
||||||
|
return callback(null, Buffer.concat(chunks).toString(encoding));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fs.readFile(filename, encoding, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe('CLI Interface', () => {
|
||||||
|
test('read from file', () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@bbob/cli",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Comand line bbcode parser",
|
||||||
|
"main": "lib/bbob.js",
|
||||||
|
"bin": {
|
||||||
|
"cli": "bbob.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "../../node_modules/.bin/jest --",
|
||||||
|
"cover": "../../node_modules/.bin/jest --coverage"
|
||||||
|
},
|
||||||
|
"author": "Nikolay Kostyurin <jilizart@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@bbob/parser": "^1.0.0",
|
||||||
|
"commander": "^2.15.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"jest": "^23.1.0",
|
||||||
|
"xbbcode-parser": "^0.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package-lock.json
|
||||||
|
coverage
|
||||||
|
dist
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package-lock.json
|
||||||
|
coverage
|
||||||
|
src
|
||||||
|
dist
|
||||||
|
!lib
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# bbob-render
|
||||||
|
Converts bbob-parser AST tree to html
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
function render(tree, options) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function attrs(obj) {
|
||||||
|
let attr = '';
|
||||||
|
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
if (typeof obj[key] === 'boolean' && obj[key]) {
|
||||||
|
attr += ` ${key}`;
|
||||||
|
} else if (typeof obj[key] === 'number') {
|
||||||
|
attr += ` ${key}="${obj[key]}"`;
|
||||||
|
} else if (typeof obj[key] === 'string') {
|
||||||
|
attr += ` ${key}="${obj[key].replace(/"/g, '"')}"`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function traverse(tree, cb) {
|
||||||
|
if (Array.isArray(tree)) {
|
||||||
|
let i = 0,
|
||||||
|
length = tree.length;
|
||||||
|
for (; i < length; i++) {
|
||||||
|
traverse(cb(tree[i]), cb);
|
||||||
|
}
|
||||||
|
} else if (typeof tree === 'object' && tree.hasOwnProperty('content')) {
|
||||||
|
traverse(tree.content, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = render;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
coverage
|
coverage
|
||||||
lib
|
|
||||||
dist
|
dist
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
benchmark
|
package-lock.json
|
||||||
*.test.js
|
coverage
|
||||||
|
src
|
||||||
|
dist
|
||||||
|
!lib
|
||||||
+155
-152
@@ -1,174 +1,177 @@
|
|||||||
const {
|
const {
|
||||||
convertTokenToText,
|
convertTokenToText,
|
||||||
getTagName,
|
getTagName,
|
||||||
getTokenColumn,
|
getTokenColumn,
|
||||||
getTokenLine,
|
getTokenLine,
|
||||||
getTokenValue,
|
getTokenValue,
|
||||||
isAttrNameToken,
|
isAttrNameToken,
|
||||||
isAttrValueToken,
|
isAttrValueToken,
|
||||||
isTagStart,
|
isTagStart,
|
||||||
isTagToken,
|
isTagToken,
|
||||||
isTextToken,
|
isTextToken,
|
||||||
isTagEnd
|
isTagEnd,
|
||||||
} = require("./Tokenizer");
|
} = require('./Tokenizer');
|
||||||
const Tokenizer = require("./Tokenizer");
|
|
||||||
|
const Tokenizer = require('./Tokenizer');
|
||||||
|
|
||||||
const TokenChar = Tokenizer.CHAR;
|
const TokenChar = Tokenizer.CHAR;
|
||||||
const getChar = Tokenizer.getChar;
|
const getChar = Tokenizer.getChar;
|
||||||
|
|
||||||
const createTagNode = (name, attrs = {}, content = []) => ({tag: name, attrs, content});
|
const createTagNode = (name, attrs = {}, content = []) => ({ tag: name, attrs, content });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
{
|
{
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
attrs: {
|
attrs: {
|
||||||
class: 'foo'
|
class: 'foo'
|
||||||
},
|
},
|
||||||
content: ['hello world!']
|
content: ['hello world!']
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
module.exports = class Parser {
|
module.exports = class Parser {
|
||||||
constructor(tokens, options = {}) {
|
constructor(tokens, options = {}) {
|
||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
this.options = options
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse() {
|
parse() {
|
||||||
const tokens = this.tokens;
|
const nodes = [];
|
||||||
const nodes = [];
|
const nestedNodes = [];
|
||||||
const nestedNodes = [];
|
const curTags = [];
|
||||||
const curTags = [];
|
const curTagsAttrName = [];
|
||||||
const curTagsAttrName = [];
|
|
||||||
|
|
||||||
const closableTags = this.findNestedTags(tokens);
|
const closableTags = this.findNestedTags(this.tokens);
|
||||||
|
|
||||||
const isNestedTag = (token) => closableTags.indexOf(getTokenValue(token)) >= 0;
|
const isNestedTag = token => closableTags.indexOf(getTokenValue(token)) >= 0;
|
||||||
|
|
||||||
const getCurTag = () => {
|
const getCurTag = () => {
|
||||||
if (curTags.length) {
|
if (curTags.length) {
|
||||||
return curTags[curTags.length - 1]
|
return curTags[curTags.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCurTag = (token) => {
|
||||||
|
curTags.push(createTagNode(getTokenValue(token)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurTagAttrName = () => {
|
||||||
|
if (curTagsAttrName.length) {
|
||||||
|
return curTagsAttrName[curTagsAttrName.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCurTagAttrName = (token) => {
|
||||||
|
curTagsAttrName.push(getTokenValue(token));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCurTagAttrName = () => {
|
||||||
|
if (curTagsAttrName.length) {
|
||||||
|
curTagsAttrName.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCurTag = () => {
|
||||||
|
if (curTags.length) {
|
||||||
|
curTags.pop();
|
||||||
|
|
||||||
|
clearCurTagAttrName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNodes = () => {
|
||||||
|
if (nestedNodes.length) {
|
||||||
|
const nestedNode = nestedNodes[nestedNodes.length - 1];
|
||||||
|
return nestedNode.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
let token;
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
while (token = this.tokens.shift()) {
|
||||||
|
if (!token) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTagToken(token)) {
|
||||||
|
if (this.isAllowedTag(getTagName(token))) {
|
||||||
|
// [tag]
|
||||||
|
if (isTagStart(token)) {
|
||||||
|
createCurTag(token);
|
||||||
|
|
||||||
|
if (isNestedTag(token)) {
|
||||||
|
nestedNodes.push(getCurTag());
|
||||||
|
} else {
|
||||||
|
getNodes().push(getCurTag());
|
||||||
|
clearCurTag();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
// [/tag]
|
||||||
};
|
if (isTagEnd(token)) {
|
||||||
|
clearCurTag();
|
||||||
|
|
||||||
const createCurTag = (token) => {
|
const lastNestedNode = nestedNodes.pop();
|
||||||
curTags.push(createTagNode(getTokenValue(token)))
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearCurTag = () => {
|
if (lastNestedNode) {
|
||||||
if (curTags.length) {
|
getNodes().push(lastNestedNode);
|
||||||
curTags.pop();
|
} else {
|
||||||
|
console.warn(`Inconsistent tag '${getTokenValue(token)}' on line ${getTokenLine(token)} and column ${getTokenColumn(token)}`);
|
||||||
clearCurTagAttrName()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurTagAttrName = () => {
|
|
||||||
if (curTagsAttrName.length) {
|
|
||||||
return curTagsAttrName[curTagsAttrName.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
};
|
|
||||||
|
|
||||||
const createCurTagAttrName = (token) => {
|
|
||||||
curTagsAttrName.push(getTokenValue(token))
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearCurTagAttrName = () => {
|
|
||||||
if (curTagsAttrName.length) {
|
|
||||||
curTagsAttrName.pop()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNodes = () => {
|
|
||||||
if (nestedNodes.length) {
|
|
||||||
const nestedNode = nestedNodes[nestedNodes.length - 1];
|
|
||||||
return nestedNode.content
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes
|
|
||||||
};
|
|
||||||
|
|
||||||
let token;
|
|
||||||
while (token = tokens.shift()) {
|
|
||||||
if (!token) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTagToken(token)) {
|
|
||||||
if (this.isAllowedTag(getTagName(token))) {
|
|
||||||
// [tag]
|
|
||||||
if (isTagStart(token)) {
|
|
||||||
createCurTag(token);
|
|
||||||
|
|
||||||
if (isNestedTag(token)) {
|
|
||||||
nestedNodes.push(getCurTag())
|
|
||||||
} else {
|
|
||||||
getNodes().push(getCurTag());
|
|
||||||
clearCurTag()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [/tag]
|
|
||||||
if (isTagEnd(token)) {
|
|
||||||
clearCurTag();
|
|
||||||
|
|
||||||
const lastNestedNode = nestedNodes.pop();
|
|
||||||
|
|
||||||
if (lastNestedNode) {
|
|
||||||
getNodes().push(lastNestedNode)
|
|
||||||
} else {
|
|
||||||
console.warn(`Inconsistent tag '${getTokenValue(token)}' on line ${getTokenLine(token)} and column ${getTokenColumn(token)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
getNodes().push(convertTokenToText(token))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getCurTag()) {
|
|
||||||
if (isAttrNameToken(token)) {
|
|
||||||
createCurTagAttrName(token);
|
|
||||||
getCurTag().attrs[getCurTagAttrName()] = null
|
|
||||||
} else if (isAttrValueToken(token)) {
|
|
||||||
getCurTag().attrs[getCurTagAttrName()] = getTokenValue(token);
|
|
||||||
clearCurTagAttrName()
|
|
||||||
} else if (isTextToken(token)) {
|
|
||||||
getCurTag().content.push(getTokenValue(token))
|
|
||||||
}
|
|
||||||
} else if (isTextToken(token)) {
|
|
||||||
getNodes().push(getTokenValue(token))
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getNodes().push(convertTokenToText(token));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nodes
|
if (getCurTag()) {
|
||||||
}
|
if (isAttrNameToken(token)) {
|
||||||
|
createCurTagAttrName(token);
|
||||||
findNestedTags(tokens) {
|
getCurTag().attrs[getCurTagAttrName()] = null;
|
||||||
const tags = tokens.filter(isTagToken).reduce((acc, token) => {
|
} else if (isAttrValueToken(token)) {
|
||||||
acc[getTokenValue(token)] = true;
|
getCurTag().attrs[getCurTagAttrName()] = getTokenValue(token);
|
||||||
|
clearCurTagAttrName();
|
||||||
return acc
|
} else if (isTextToken(token)) {
|
||||||
}, {});
|
getCurTag().content.push(getTokenValue(token));
|
||||||
|
|
||||||
const closeChar = getChar(TokenChar.SLASH);
|
|
||||||
|
|
||||||
return Object.keys(tags).reduce((arr, key) => {
|
|
||||||
if (tags[key] && tags[closeChar + key]) {
|
|
||||||
arr.push(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
isAllowedTag(value) {
|
|
||||||
if (this.options.allowOnlyTags && this.options.allowOnlyTags.length) {
|
|
||||||
return this.options.allowOnlyTags.indexOf(value) >= 0
|
|
||||||
}
|
}
|
||||||
|
} else if (isTextToken(token)) {
|
||||||
return true
|
getNodes().push(getTokenValue(token));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
findNestedTags(tokens) {
|
||||||
|
const tags = tokens.filter(isTagToken).reduce((acc, token) => {
|
||||||
|
acc[getTokenValue(token)] = true;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const closeChar = getChar(TokenChar.SLASH);
|
||||||
|
|
||||||
|
return Object.keys(tags).reduce((arr, key) => {
|
||||||
|
if (tags[key] && tags[closeChar + key]) {
|
||||||
|
arr.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllowedTag(value) {
|
||||||
|
if (this.options.allowOnlyTags && this.options.allowOnlyTags.length) {
|
||||||
|
return this.options.allowOnlyTags.indexOf(value) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
const Parser = require('./Parser');
|
const Parser = require('./Parser');
|
||||||
const TOKEN = require('./token');
|
const TOKEN = require('./token');
|
||||||
|
|
||||||
describe("Parser", () => {
|
describe('Parser', () => {
|
||||||
test("parse paired tags tokens", () => {
|
test('parse paired tags tokens', () => {
|
||||||
const parser = new Parser([
|
const parser = new Parser([
|
||||||
[TOKEN.TYPE_TAG, 'ch'],
|
[TOKEN.TYPE_TAG, 'ch'],
|
||||||
[TOKEN.TYPE_TAG, '/ch']
|
[TOKEN.TYPE_TAG, '/ch'],
|
||||||
]);
|
]);
|
||||||
|
});
|
||||||
})
|
});
|
||||||
});
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Tokenizer {
|
|||||||
this.colPos = 0;
|
this.colPos = 0;
|
||||||
this.rowPos = 0;
|
this.rowPos = 0;
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
|
|
||||||
this.tokenIndex = -1;
|
this.tokenIndex = -1;
|
||||||
this.tokens = [];
|
this.tokens = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,144 +1,145 @@
|
|||||||
const Tokenizer = require('./Tokenizer');
|
const Tokenizer = require('./Tokenizer');
|
||||||
|
|
||||||
const TYPE = Tokenizer.TYPE;
|
const TYPE = Tokenizer.TYPE;
|
||||||
|
|
||||||
describe("Tokenizer", () => {
|
describe('Tokenizer', () => {
|
||||||
test("tokenize single tag", () => {
|
test('tokenize single tag', () => {
|
||||||
const input = `[SingleTag]`;
|
const input = '[SingleTag]';
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
expect(tokens).toEqual([
|
expect(tokens).toEqual([
|
||||||
[TYPE.TAG, 'SingleTag', 0, 0]
|
[TYPE.TAG, 'SingleTag', 0, 0],
|
||||||
])
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("tokenize single tag with spaces", () => {
|
test('tokenize single tag with spaces', () => {
|
||||||
const input = `[Single Tag]`;
|
const input = '[Single Tag]';
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
expect(tokens).toEqual([
|
expect(tokens).toEqual([
|
||||||
[TYPE.TAG, 'Single Tag', 0, 0]
|
[TYPE.TAG, 'Single Tag', 0, 0],
|
||||||
])
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("tokenize tag as param", () => {
|
test('tokenize tag as param', () => {
|
||||||
const input = `[color="#ff0000"]Text[/color]`;
|
const input = '[color="#ff0000"]Text[/color]';
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
expect(tokens).toEqual([
|
expect(tokens).toEqual([
|
||||||
[TYPE.TAG, 'color', 0, 0],
|
[TYPE.TAG, 'color', 0, 0],
|
||||||
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
|
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
|
||||||
[TYPE.WORD, 'Text', 17, 0],
|
[TYPE.WORD, 'Text', 17, 0],
|
||||||
[TYPE.TAG, '/color', 21, 0]
|
[TYPE.TAG, '/color', 21, 0],
|
||||||
])
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("tokenize tag param without quotemarks", () => {
|
test('tokenize tag param without quotemarks', () => {
|
||||||
const input = `[style color=#ff0000]Text[/style]`;
|
const input = '[style color=#ff0000]Text[/style]';
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
expect(tokens).toEqual([
|
expect(tokens).toEqual([
|
||||||
[TYPE.TAG, 'style', 0, 0],
|
[TYPE.TAG, 'style', 0, 0],
|
||||||
[TYPE.ATTR_NAME, 'color', 6, 0],
|
[TYPE.ATTR_NAME, 'color', 6, 0],
|
||||||
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
|
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
|
||||||
[TYPE.WORD, 'Text', 21, 0],
|
[TYPE.WORD, 'Text', 21, 0],
|
||||||
[TYPE.TAG, '/style', 25, 0]
|
[TYPE.TAG, '/style', 25, 0],
|
||||||
])
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("tokenize list tag with items", () => {
|
test('tokenize list tag with items', () => {
|
||||||
const input = `[list]
|
const input = `[list]
|
||||||
[*] Item 1.
|
[*] Item 1.
|
||||||
[*] Item 2.
|
[*] Item 2.
|
||||||
[*] Item 3.
|
[*] Item 3.
|
||||||
[/list]`;
|
[/list]`;
|
||||||
|
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
expect(tokens).toEqual([
|
expect(tokens).toEqual([
|
||||||
[TYPE.TAG, 'list', 0, 0],
|
[TYPE.TAG, 'list', 0, 0],
|
||||||
[TYPE.NEW_LINE, '\n', 6, 0],
|
[TYPE.NEW_LINE, '\n', 6, 0],
|
||||||
[TYPE.SPACE, ' ', 0, 1],
|
[TYPE.SPACE, ' ', 0, 1],
|
||||||
[TYPE.SPACE, ' ', 1, 1],
|
[TYPE.SPACE, ' ', 1, 1],
|
||||||
[TYPE.SPACE, ' ', 2, 1],
|
[TYPE.SPACE, ' ', 2, 1],
|
||||||
[TYPE.TAG, '*', 3, 1],
|
[TYPE.TAG, '*', 3, 1],
|
||||||
[TYPE.SPACE, ' ', 6, 1],
|
[TYPE.SPACE, ' ', 6, 1],
|
||||||
[TYPE.WORD, 'Item', 7, 1],
|
[TYPE.WORD, 'Item', 7, 1],
|
||||||
[TYPE.SPACE, ' ', 11, 1],
|
[TYPE.SPACE, ' ', 11, 1],
|
||||||
[TYPE.WORD, '1.', 11, 1],
|
[TYPE.WORD, '1.', 11, 1],
|
||||||
[TYPE.NEW_LINE, '\n', 14, 1],
|
[TYPE.NEW_LINE, '\n', 14, 1],
|
||||||
[TYPE.SPACE, ' ', 0, 2],
|
[TYPE.SPACE, ' ', 0, 2],
|
||||||
[TYPE.SPACE, ' ', 1, 2],
|
[TYPE.SPACE, ' ', 1, 2],
|
||||||
[TYPE.SPACE, ' ', 2, 2],
|
[TYPE.SPACE, ' ', 2, 2],
|
||||||
[TYPE.TAG, '*', 3, 2],
|
[TYPE.TAG, '*', 3, 2],
|
||||||
[TYPE.SPACE, ' ', 6, 2],
|
[TYPE.SPACE, ' ', 6, 2],
|
||||||
[TYPE.WORD, 'Item', 14, 1],
|
[TYPE.WORD, 'Item', 14, 1],
|
||||||
[TYPE.SPACE, ' ', 11, 2],
|
[TYPE.SPACE, ' ', 11, 2],
|
||||||
[TYPE.WORD, '2.', 11, 2],
|
[TYPE.WORD, '2.', 11, 2],
|
||||||
[TYPE.NEW_LINE, '\n', 14, 2],
|
[TYPE.NEW_LINE, '\n', 14, 2],
|
||||||
[TYPE.SPACE, ' ', 0, 3],
|
[TYPE.SPACE, ' ', 0, 3],
|
||||||
[TYPE.SPACE, ' ', 1, 3],
|
[TYPE.SPACE, ' ', 1, 3],
|
||||||
[TYPE.SPACE, ' ', 2, 3],
|
[TYPE.SPACE, ' ', 2, 3],
|
||||||
[TYPE.TAG, '*', 3, 3],
|
[TYPE.TAG, '*', 3, 3],
|
||||||
[TYPE.SPACE, ' ', 6, 3],
|
[TYPE.SPACE, ' ', 6, 3],
|
||||||
[TYPE.WORD, 'Item', 14, 2],
|
[TYPE.WORD, 'Item', 14, 2],
|
||||||
[TYPE.SPACE, ' ', 11, 3],
|
[TYPE.SPACE, ' ', 11, 3],
|
||||||
[TYPE.WORD, '3.', 11, 3],
|
[TYPE.WORD, '3.', 11, 3],
|
||||||
[TYPE.NEW_LINE, '\n', 14, 3],
|
[TYPE.NEW_LINE, '\n', 14, 3],
|
||||||
[TYPE.TAG, '/list', 0, 4]
|
[TYPE.TAG, '/list', 0, 4],
|
||||||
])
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tokenize bad tags as texts', () => {
|
||||||
|
const inputs = [
|
||||||
|
'[]',
|
||||||
|
'[=]',
|
||||||
|
'',
|
||||||
|
'x html([a. title][, alt][, classes]) x',
|
||||||
|
'[/y]',
|
||||||
|
'[sc',
|
||||||
|
// '[sc / [/sc]',
|
||||||
|
// '[sc arg="val',
|
||||||
|
];
|
||||||
|
|
||||||
|
const asserts = [
|
||||||
|
[[TYPE.WORD, '[]', 0, 0]],
|
||||||
|
[[TYPE.WORD, '[=]', 0, 0]],
|
||||||
|
[
|
||||||
|
[TYPE.WORD, '!', 0, 0],
|
||||||
|
[TYPE.WORD, '[](image.jpg)', 1, 0],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[TYPE.WORD, 'x', 0, 0],
|
||||||
|
[TYPE.SPACE, ' ', 1, 0],
|
||||||
|
[TYPE.WORD, 'html(', 1, 0],
|
||||||
|
[TYPE.TAG, 'a. title', 7, 0],
|
||||||
|
[TYPE.TAG, ', alt', 17, 0],
|
||||||
|
[TYPE.TAG, ', classes', 24, 0],
|
||||||
|
[TYPE.WORD, ')', 7, 0],
|
||||||
|
[TYPE.SPACE, ' ', 36, 0],
|
||||||
|
[TYPE.WORD, 'x', 36, 0],
|
||||||
|
],
|
||||||
|
[[TYPE.TAG, '/y', 0, 0]],
|
||||||
|
[[TYPE.WORD, '[sc', 0, 0]],
|
||||||
|
[
|
||||||
|
[TYPE.WORD, '[sc', 0, 0],
|
||||||
|
[TYPE.SPACE, ' ', 0, 0],
|
||||||
|
[TYPE.WORD, '/', 0, 0],
|
||||||
|
[TYPE.SPACE, ' ', 0, 0],
|
||||||
|
[TYPE.WORD, '[/sc]', 0, 0],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
inputs.forEach((input, idx) => {
|
||||||
|
const tokens = new Tokenizer(input).tokenize();
|
||||||
|
|
||||||
|
expect(tokens).toBeInstanceOf(Array);
|
||||||
|
expect(tokens).toEqual(asserts[idx]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
test("tokenize bad tags as texts", () => {
|
});
|
||||||
const inputs = [
|
|
||||||
'[]',
|
|
||||||
'[=]',
|
|
||||||
'',
|
|
||||||
'x html([a. title][, alt][, classes]) x',
|
|
||||||
'[/y]',
|
|
||||||
'[sc',
|
|
||||||
'[sc / [/sc]',
|
|
||||||
'[sc arg="val',
|
|
||||||
];
|
|
||||||
|
|
||||||
const asserts = [
|
|
||||||
[[TYPE.WORD, '[]', 0, 0]],
|
|
||||||
[[TYPE.WORD, '[=]', 0, 0]],
|
|
||||||
[
|
|
||||||
[TYPE.WORD, '!', 0, 0],
|
|
||||||
[TYPE.WORD, '[](image.jpg)', 1, 0]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[TYPE.WORD, "x", 0, 0],
|
|
||||||
[TYPE.SPACE, " ", 1, 0],
|
|
||||||
[TYPE.WORD, "html(", 1, 0],
|
|
||||||
[TYPE.TAG, "a. title", 7, 0],
|
|
||||||
[TYPE.TAG, ", alt", 17, 0],
|
|
||||||
[TYPE.TAG, ", classes", 24, 0],
|
|
||||||
[TYPE.WORD, ")", 7, 0],
|
|
||||||
[TYPE.SPACE, " ", 36, 0],
|
|
||||||
[TYPE.WORD, "x", 36, 0]
|
|
||||||
],
|
|
||||||
[[TYPE.TAG, "/y", 0, 0]],
|
|
||||||
[[TYPE.WORD, '[sc', 0, 0]],
|
|
||||||
[
|
|
||||||
[TYPE.WORD, '[sc', 0, 0],
|
|
||||||
[TYPE.SPACE, ' ', 0, 0],
|
|
||||||
[TYPE.WORD, '/', 0, 0],
|
|
||||||
[TYPE.SPACE, ' ', 0, 0],
|
|
||||||
[TYPE.WORD, '[/sc]', 0, 0]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
inputs.forEach((input, idx) => {
|
|
||||||
const tokens = new Tokenizer(input).tokenize();
|
|
||||||
|
|
||||||
expect(tokens).toBeInstanceOf(Array);
|
|
||||||
expect(tokens).toEqual(asserts[idx])
|
|
||||||
});
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
const N = "\n".charCodeAt(0);
|
const N = '\n'.charCodeAt(0);
|
||||||
const TAB = "\t".charCodeAt(0);
|
const TAB = '\t'.charCodeAt(0);
|
||||||
const F = "\f".charCodeAt(0);
|
const F = '\f'.charCodeAt(0);
|
||||||
const R = "\r".charCodeAt(0);
|
const R = '\r'.charCodeAt(0);
|
||||||
|
|
||||||
const EQ = "=".charCodeAt(0);
|
const EQ = '='.charCodeAt(0);
|
||||||
const QUOTEMARK = "\"".charCodeAt(0);
|
const QUOTEMARK = '"'.charCodeAt(0);
|
||||||
const SPACE = " ".charCodeAt(0);
|
const SPACE = ' '.charCodeAt(0);
|
||||||
|
|
||||||
const OPEN_BRAKET = "[".charCodeAt(0);
|
const OPEN_BRAKET = '['.charCodeAt(0);
|
||||||
const CLOSE_BRAKET = "]".charCodeAt(0);
|
const CLOSE_BRAKET = ']'.charCodeAt(0);
|
||||||
|
|
||||||
const SLASH = "/".charCodeAt(0);
|
const SLASH = '/'.charCodeAt(0);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
N,
|
N,
|
||||||
F,
|
F,
|
||||||
R,
|
R,
|
||||||
TAB,
|
TAB,
|
||||||
EQ,
|
EQ,
|
||||||
QUOTEMARK,
|
QUOTEMARK,
|
||||||
SPACE,
|
SPACE,
|
||||||
OPEN_BRAKET,
|
OPEN_BRAKET,
|
||||||
CLOSE_BRAKET,
|
CLOSE_BRAKET,
|
||||||
SLASH
|
SLASH,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "bbob-parser",
|
"name": "@bbob/parser",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"description": "Fast BB Code parser written in pure javascript, no dependencies",
|
"description": "Fast BB Code parser written in pure javascript, no dependencies",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
@@ -7,14 +7,12 @@
|
|||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest"
|
"test": "../../node_modules/.bin/jest --",
|
||||||
|
"cover": "../../node_modules/.bin/jest --coverage",
|
||||||
|
"lint": "../../node_modules/.bin/eslint ."
|
||||||
},
|
},
|
||||||
"author": "Nikolay Kostyurin <jilizart@gmail.com>",
|
"author": "Nikolay Kostyurin <jilizart@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
|
||||||
"jest": "^23.1.0",
|
|
||||||
"xbbcode-parser": "^0.1.2"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://registry.npmjs.org/"
|
"registry": "https://registry.npmjs.org/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const Tokenizer = require("./Tokenizer");
|
const Tokenizer = require('./Tokenizer');
|
||||||
const Parser = require("./Parser");
|
const Parser = require('./Parser');
|
||||||
|
|
||||||
module.exports = function parse(input, options) {
|
module.exports = function parse(input, options) {
|
||||||
const tokenizer = new Tokenizer(input);
|
const tokenizer = new Tokenizer(input);
|
||||||
const tokens = tokenizer.tokenize();
|
const tokens = tokenizer.tokenize();
|
||||||
const parser = new Parser(tokens, options);
|
const parser = new Parser(tokens, options);
|
||||||
const ast = parser.parse();
|
const ast = parser.parse();
|
||||||
|
|
||||||
return ast
|
return ast;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
const parse = require('./index');
|
const parse = require('./index');
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
allowOnlyTags: ['ch', 'syllable', 'tab'],
|
allowOnlyTags: ['ch', 'syllable', 'tab'],
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("parse", () => {
|
describe('parse', () => {
|
||||||
test("tag with spaces", () => {
|
test('tag with spaces', () => {
|
||||||
const ast = parse(`[Verse 2]`);
|
const ast = parse('[Verse 2]');
|
||||||
|
|
||||||
expect(ast).toEqual([{tag: 'Verse 2', attrs: {}, content: []}]);
|
expect(ast).toEqual([{ tag: 'Verse 2', attrs: {}, content: [] }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// test("pass invalid tags", () => {
|
// test("pass invalid tags", () => {
|
||||||
// const inputs = [
|
// const inputs = [
|
||||||
// '[]',
|
// '[]',
|
||||||
// '',
|
// '',
|
||||||
// 'x html([a. title][, alt][, classes]) x',
|
// 'x html([a. title][, alt][, classes]) x',
|
||||||
// '[/y]',
|
// '[/y]',
|
||||||
// '[sc',
|
// '[sc',
|
||||||
// '[sc / [/sc]',
|
// '[sc / [/sc]',
|
||||||
// '[sc arg="val',
|
// '[sc arg="val',
|
||||||
// ];
|
// ];
|
||||||
//
|
//
|
||||||
// const ast1 = parse(inputs[0]);
|
// const ast1 = parse(inputs[0]);
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// console.log('ast1', ast1);
|
// console.log('ast1', ast1);
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// expect(ast1).toEqual([
|
// expect(ast1).toEqual([
|
||||||
//
|
//
|
||||||
// ]);
|
// ]);
|
||||||
// })
|
// })
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const parse = require('bbob-html');
|
||||||
|
|
||||||
|
class BBCode extends React.Component {
|
||||||
|
render() {
|
||||||
|
const Container = this.props.container;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
{this.content()}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
if (this.props.source) {
|
||||||
|
return <span dangerouslySetInnerHTML={{ __html: this.renderBBCode(this.props.source) }} />;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return React.Children.map(this.props.children, child => {
|
||||||
|
if (typeof child === 'string') {
|
||||||
|
return <span dangerouslySetInnerHTML={{ __html: this.renderBBCode(child) }} />;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBBCode(source) {
|
||||||
|
return parse(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCode.defaultProps = {
|
||||||
|
container: 'div',
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = BBCode;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
describe('React BBCode', () => {
|
||||||
|
|
||||||
|
test('render markup properly', () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "@bbob/react",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "../../node_modules/.bin/jest --",
|
||||||
|
"cover": "../../node_modules/.bin/jest --coverage",
|
||||||
|
"lint": "../../node_modules/.bin/eslint "
|
||||||
|
},
|
||||||
|
"author": "Nikolay Kostyurin <jilizart@gmail.com>",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
function BBob() {
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user