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
|
||||
.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 = {
|
||||
closableTags: ['ch', 'syllable', 'tab']
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"lerna": "2.11.0",
|
||||
"packages": [
|
||||
"packages/bbob-parser"
|
||||
"packages/*"
|
||||
],
|
||||
"version": "independent"
|
||||
}
|
||||
|
||||
Generated
+5912
-73
File diff suppressed because it is too large
Load Diff
+22
-3
@@ -1,5 +1,24 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"lerna": "^2.11.0"
|
||||
}
|
||||
"scripts": {
|
||||
"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
|
||||
coverage
|
||||
lib
|
||||
dist
|
||||
@@ -1,2 +1,5 @@
|
||||
benchmark
|
||||
*.test.js
|
||||
package-lock.json
|
||||
coverage
|
||||
src
|
||||
dist
|
||||
!lib
|
||||
+155
-152
@@ -1,174 +1,177 @@
|
||||
const {
|
||||
convertTokenToText,
|
||||
getTagName,
|
||||
getTokenColumn,
|
||||
getTokenLine,
|
||||
getTokenValue,
|
||||
isAttrNameToken,
|
||||
isAttrValueToken,
|
||||
isTagStart,
|
||||
isTagToken,
|
||||
isTextToken,
|
||||
isTagEnd
|
||||
} = require("./Tokenizer");
|
||||
const Tokenizer = require("./Tokenizer");
|
||||
convertTokenToText,
|
||||
getTagName,
|
||||
getTokenColumn,
|
||||
getTokenLine,
|
||||
getTokenValue,
|
||||
isAttrNameToken,
|
||||
isAttrValueToken,
|
||||
isTagStart,
|
||||
isTagToken,
|
||||
isTextToken,
|
||||
isTagEnd,
|
||||
} = require('./Tokenizer');
|
||||
|
||||
const Tokenizer = require('./Tokenizer');
|
||||
|
||||
const TokenChar = Tokenizer.CHAR;
|
||||
const getChar = Tokenizer.getChar;
|
||||
|
||||
const createTagNode = (name, attrs = {}, content = []) => ({tag: name, attrs, content});
|
||||
const createTagNode = (name, attrs = {}, content = []) => ({ tag: name, attrs, content });
|
||||
|
||||
/**
|
||||
*
|
||||
{
|
||||
tag: 'div',
|
||||
attrs: {
|
||||
class: 'foo'
|
||||
},
|
||||
content: ['hello world!']
|
||||
}
|
||||
{
|
||||
tag: 'div',
|
||||
attrs: {
|
||||
class: 'foo'
|
||||
},
|
||||
content: ['hello world!']
|
||||
}
|
||||
*/
|
||||
module.exports = class Parser {
|
||||
constructor(tokens, options = {}) {
|
||||
this.tokens = tokens;
|
||||
this.options = options
|
||||
}
|
||||
constructor(tokens, options = {}) {
|
||||
this.tokens = tokens;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
parse() {
|
||||
const tokens = this.tokens;
|
||||
const nodes = [];
|
||||
const nestedNodes = [];
|
||||
const curTags = [];
|
||||
const curTagsAttrName = [];
|
||||
parse() {
|
||||
const nodes = [];
|
||||
const nestedNodes = [];
|
||||
const curTags = [];
|
||||
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 = () => {
|
||||
if (curTags.length) {
|
||||
return curTags[curTags.length - 1]
|
||||
const getCurTag = () => {
|
||||
if (curTags.length) {
|
||||
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) => {
|
||||
curTags.push(createTagNode(getTokenValue(token)))
|
||||
};
|
||||
const lastNestedNode = nestedNodes.pop();
|
||||
|
||||
const clearCurTag = () => {
|
||||
if (curTags.length) {
|
||||
curTags.pop();
|
||||
|
||||
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))
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
return true
|
||||
} else if (isTextToken(token)) {
|
||||
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 TOKEN = require('./token');
|
||||
|
||||
describe("Parser", () => {
|
||||
test("parse paired tags tokens", () => {
|
||||
const parser = new Parser([
|
||||
[TOKEN.TYPE_TAG, 'ch'],
|
||||
[TOKEN.TYPE_TAG, '/ch']
|
||||
]);
|
||||
|
||||
})
|
||||
});
|
||||
describe('Parser', () => {
|
||||
test('parse paired tags tokens', () => {
|
||||
const parser = new Parser([
|
||||
[TOKEN.TYPE_TAG, 'ch'],
|
||||
[TOKEN.TYPE_TAG, '/ch'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ class Tokenizer {
|
||||
this.colPos = 0;
|
||||
this.rowPos = 0;
|
||||
this.index = 0;
|
||||
|
||||
|
||||
this.tokenIndex = -1;
|
||||
this.tokens = [];
|
||||
}
|
||||
|
||||
@@ -1,144 +1,145 @@
|
||||
const Tokenizer = require('./Tokenizer');
|
||||
|
||||
const TYPE = Tokenizer.TYPE;
|
||||
|
||||
describe("Tokenizer", () => {
|
||||
test("tokenize single tag", () => {
|
||||
const input = `[SingleTag]`;
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
describe('Tokenizer', () => {
|
||||
test('tokenize single tag', () => {
|
||||
const input = '[SingleTag]';
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'SingleTag', 0, 0]
|
||||
])
|
||||
});
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'SingleTag', 0, 0],
|
||||
]);
|
||||
});
|
||||
|
||||
test("tokenize single tag with spaces", () => {
|
||||
const input = `[Single Tag]`;
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
test('tokenize single tag with spaces', () => {
|
||||
const input = '[Single Tag]';
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'Single Tag', 0, 0]
|
||||
])
|
||||
});
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'Single Tag', 0, 0],
|
||||
]);
|
||||
});
|
||||
|
||||
test("tokenize tag as param", () => {
|
||||
const input = `[color="#ff0000"]Text[/color]`;
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
test('tokenize tag as param', () => {
|
||||
const input = '[color="#ff0000"]Text[/color]';
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'color', 0, 0],
|
||||
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
|
||||
[TYPE.WORD, 'Text', 17, 0],
|
||||
[TYPE.TAG, '/color', 21, 0]
|
||||
])
|
||||
});
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'color', 0, 0],
|
||||
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
|
||||
[TYPE.WORD, 'Text', 17, 0],
|
||||
[TYPE.TAG, '/color', 21, 0],
|
||||
]);
|
||||
});
|
||||
|
||||
test("tokenize tag param without quotemarks", () => {
|
||||
const input = `[style color=#ff0000]Text[/style]`;
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
test('tokenize tag param without quotemarks', () => {
|
||||
const input = '[style color=#ff0000]Text[/style]';
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'style', 0, 0],
|
||||
[TYPE.ATTR_NAME, 'color', 6, 0],
|
||||
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
|
||||
[TYPE.WORD, 'Text', 21, 0],
|
||||
[TYPE.TAG, '/style', 25, 0]
|
||||
])
|
||||
});
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'style', 0, 0],
|
||||
[TYPE.ATTR_NAME, 'color', 6, 0],
|
||||
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
|
||||
[TYPE.WORD, 'Text', 21, 0],
|
||||
[TYPE.TAG, '/style', 25, 0],
|
||||
]);
|
||||
});
|
||||
|
||||
test("tokenize list tag with items", () => {
|
||||
const input = `[list]
|
||||
test('tokenize list tag with items', () => {
|
||||
const input = `[list]
|
||||
[*] Item 1.
|
||||
[*] Item 2.
|
||||
[*] Item 3.
|
||||
[/list]`;
|
||||
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
const tokens = new Tokenizer(input).tokenize();
|
||||
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'list', 0, 0],
|
||||
[TYPE.NEW_LINE, '\n', 6, 0],
|
||||
[TYPE.SPACE, ' ', 0, 1],
|
||||
[TYPE.SPACE, ' ', 1, 1],
|
||||
[TYPE.SPACE, ' ', 2, 1],
|
||||
[TYPE.TAG, '*', 3, 1],
|
||||
[TYPE.SPACE, ' ', 6, 1],
|
||||
[TYPE.WORD, 'Item', 7, 1],
|
||||
[TYPE.SPACE, ' ', 11, 1],
|
||||
[TYPE.WORD, '1.', 11, 1],
|
||||
[TYPE.NEW_LINE, '\n', 14, 1],
|
||||
[TYPE.SPACE, ' ', 0, 2],
|
||||
[TYPE.SPACE, ' ', 1, 2],
|
||||
[TYPE.SPACE, ' ', 2, 2],
|
||||
[TYPE.TAG, '*', 3, 2],
|
||||
[TYPE.SPACE, ' ', 6, 2],
|
||||
[TYPE.WORD, 'Item', 14, 1],
|
||||
[TYPE.SPACE, ' ', 11, 2],
|
||||
[TYPE.WORD, '2.', 11, 2],
|
||||
[TYPE.NEW_LINE, '\n', 14, 2],
|
||||
[TYPE.SPACE, ' ', 0, 3],
|
||||
[TYPE.SPACE, ' ', 1, 3],
|
||||
[TYPE.SPACE, ' ', 2, 3],
|
||||
[TYPE.TAG, '*', 3, 3],
|
||||
[TYPE.SPACE, ' ', 6, 3],
|
||||
[TYPE.WORD, 'Item', 14, 2],
|
||||
[TYPE.SPACE, ' ', 11, 3],
|
||||
[TYPE.WORD, '3.', 11, 3],
|
||||
[TYPE.NEW_LINE, '\n', 14, 3],
|
||||
[TYPE.TAG, '/list', 0, 4]
|
||||
])
|
||||
expect(tokens).toBeInstanceOf(Array);
|
||||
expect(tokens).toEqual([
|
||||
[TYPE.TAG, 'list', 0, 0],
|
||||
[TYPE.NEW_LINE, '\n', 6, 0],
|
||||
[TYPE.SPACE, ' ', 0, 1],
|
||||
[TYPE.SPACE, ' ', 1, 1],
|
||||
[TYPE.SPACE, ' ', 2, 1],
|
||||
[TYPE.TAG, '*', 3, 1],
|
||||
[TYPE.SPACE, ' ', 6, 1],
|
||||
[TYPE.WORD, 'Item', 7, 1],
|
||||
[TYPE.SPACE, ' ', 11, 1],
|
||||
[TYPE.WORD, '1.', 11, 1],
|
||||
[TYPE.NEW_LINE, '\n', 14, 1],
|
||||
[TYPE.SPACE, ' ', 0, 2],
|
||||
[TYPE.SPACE, ' ', 1, 2],
|
||||
[TYPE.SPACE, ' ', 2, 2],
|
||||
[TYPE.TAG, '*', 3, 2],
|
||||
[TYPE.SPACE, ' ', 6, 2],
|
||||
[TYPE.WORD, 'Item', 14, 1],
|
||||
[TYPE.SPACE, ' ', 11, 2],
|
||||
[TYPE.WORD, '2.', 11, 2],
|
||||
[TYPE.NEW_LINE, '\n', 14, 2],
|
||||
[TYPE.SPACE, ' ', 0, 3],
|
||||
[TYPE.SPACE, ' ', 1, 3],
|
||||
[TYPE.SPACE, ' ', 2, 3],
|
||||
[TYPE.TAG, '*', 3, 3],
|
||||
[TYPE.SPACE, ' ', 6, 3],
|
||||
[TYPE.WORD, 'Item', 14, 2],
|
||||
[TYPE.SPACE, ' ', 11, 3],
|
||||
[TYPE.WORD, '3.', 11, 3],
|
||||
[TYPE.NEW_LINE, '\n', 14, 3],
|
||||
[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 TAB = "\t".charCodeAt(0);
|
||||
const F = "\f".charCodeAt(0);
|
||||
const R = "\r".charCodeAt(0);
|
||||
const N = '\n'.charCodeAt(0);
|
||||
const TAB = '\t'.charCodeAt(0);
|
||||
const F = '\f'.charCodeAt(0);
|
||||
const R = '\r'.charCodeAt(0);
|
||||
|
||||
const EQ = "=".charCodeAt(0);
|
||||
const QUOTEMARK = "\"".charCodeAt(0);
|
||||
const SPACE = " ".charCodeAt(0);
|
||||
const EQ = '='.charCodeAt(0);
|
||||
const QUOTEMARK = '"'.charCodeAt(0);
|
||||
const SPACE = ' '.charCodeAt(0);
|
||||
|
||||
const OPEN_BRAKET = "[".charCodeAt(0);
|
||||
const CLOSE_BRAKET = "]".charCodeAt(0);
|
||||
const OPEN_BRAKET = '['.charCodeAt(0);
|
||||
const CLOSE_BRAKET = ']'.charCodeAt(0);
|
||||
|
||||
const SLASH = "/".charCodeAt(0);
|
||||
const SLASH = '/'.charCodeAt(0);
|
||||
|
||||
module.exports = {
|
||||
N,
|
||||
F,
|
||||
R,
|
||||
TAB,
|
||||
EQ,
|
||||
QUOTEMARK,
|
||||
SPACE,
|
||||
OPEN_BRAKET,
|
||||
CLOSE_BRAKET,
|
||||
SLASH
|
||||
};
|
||||
N,
|
||||
F,
|
||||
R,
|
||||
TAB,
|
||||
EQ,
|
||||
QUOTEMARK,
|
||||
SPACE,
|
||||
OPEN_BRAKET,
|
||||
CLOSE_BRAKET,
|
||||
SLASH,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "bbob-parser",
|
||||
"name": "@bbob/parser",
|
||||
"version": "1.0.1",
|
||||
"description": "Fast BB Code parser written in pure javascript, no dependencies",
|
||||
"main": "index.js",
|
||||
@@ -7,14 +7,12 @@
|
||||
"test": "test"
|
||||
},
|
||||
"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>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jest": "^23.1.0",
|
||||
"xbbcode-parser": "^0.1.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const Tokenizer = require("./Tokenizer");
|
||||
const Parser = require("./Parser");
|
||||
const Tokenizer = require('./Tokenizer');
|
||||
const Parser = require('./Parser');
|
||||
|
||||
module.exports = function parse(input, options) {
|
||||
const tokenizer = new Tokenizer(input);
|
||||
const tokens = tokenizer.tokenize();
|
||||
const parser = new Parser(tokens, options);
|
||||
const ast = parser.parse();
|
||||
const tokenizer = new Tokenizer(input);
|
||||
const tokens = tokenizer.tokenize();
|
||||
const parser = new Parser(tokens, options);
|
||||
const ast = parser.parse();
|
||||
|
||||
return ast
|
||||
};
|
||||
return ast;
|
||||
};
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
const parse = require('./index');
|
||||
|
||||
const options = {
|
||||
allowOnlyTags: ['ch', 'syllable', 'tab'],
|
||||
allowOnlyTags: ['ch', 'syllable', 'tab'],
|
||||
};
|
||||
|
||||
describe("parse", () => {
|
||||
test("tag with spaces", () => {
|
||||
const ast = parse(`[Verse 2]`);
|
||||
describe('parse', () => {
|
||||
test('tag with spaces', () => {
|
||||
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", () => {
|
||||
// const inputs = [
|
||||
// '[]',
|
||||
// '',
|
||||
// 'x html([a. title][, alt][, classes]) x',
|
||||
// '[/y]',
|
||||
// '[sc',
|
||||
// '[sc / [/sc]',
|
||||
// '[sc arg="val',
|
||||
// ];
|
||||
//
|
||||
// const ast1 = parse(inputs[0]);
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log('ast1', ast1);
|
||||
//
|
||||
//
|
||||
//
|
||||
// expect(ast1).toEqual([
|
||||
//
|
||||
// ]);
|
||||
// })
|
||||
});
|
||||
// test("pass invalid tags", () => {
|
||||
// const inputs = [
|
||||
// '[]',
|
||||
// '',
|
||||
// 'x html([a. title][, alt][, classes]) x',
|
||||
// '[/y]',
|
||||
// '[sc',
|
||||
// '[sc / [/sc]',
|
||||
// '[sc arg="val',
|
||||
// ];
|
||||
//
|
||||
// const ast1 = parse(inputs[0]);
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log('ast1', ast1);
|
||||
//
|
||||
//
|
||||
//
|
||||
// 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