aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorache <ache@ache.one>2018-04-24 05:16:22 +0200
committerache <ache@ache.one>2018-04-24 05:17:56 +0200
commitd399622de231fccda49925ca3eaeeb2f123e2094 (patch)
tree16a5f40e850708000c119d358a9cedcb7b878e8f
parentUpdate comments (diff)
Use babel to distribut
-rw-r--r--dist/index.js255
-rw-r--r--package.json9
2 files changed, 262 insertions, 2 deletions
diff --git a/dist/index.js b/dist/index.js
new file mode 100644
index 0000000..aa0a693
--- /dev/null
+++ b/dist/index.js
@@ -0,0 +1,255 @@
+'use strict';
+
+// A valid output which mean nothing has been parsed.
+// Used as error return / invalid output
+
+const nothingHappend = {
+ prop: {
+ key: undefined,
+ class: undefined,
+ id: undefined
+ },
+ eaten: ''
+};
+
+// Main function
+function parse(value, indexNext = 0) {
+ let letsEat = '';
+ let stopOnBrace = false;
+ let errorDetected = false;
+
+ const prop = { key: undefined /* {} */, class: undefined /* [] */, id: undefined };
+
+ /* They is at leat one label and at best two */
+ /* ekqsdf <- one label
+ * qsdfqsfd=qsdfqsdf <- two */
+ let labelFirst = '';
+ let labelSecond;
+
+ /* 3 types :
+ * .azcv <- class
+ * #poi <- id
+ * dfgh=zert <- key
+ * jkj <- this is also a key but with a undefined value
+ * jkj= <- this is also a key but with a empty value
+ */
+ let type;
+ const forbidenCharacters = '\n\r{}';
+
+ // A function that detect if it's time to end the parsing
+ const shouldStop = () => {
+ if (indexNext >= value.length || forbidenCharacters.indexOf(value[indexNext]) > -1) {
+ if (stopOnBrace && value[indexNext] !== '}') {
+ errorDetected = true;
+ }
+ return true;
+ }
+ return value[indexNext] === '}' && stopOnBrace;
+ };
+
+ let eaten = '';
+ // Couple of functions that parse same kinds of characters
+ // Used to parse spaces or identifiers
+ const eat = chars => {
+ eaten = '';
+
+ while (indexNext < value.length && forbidenCharacters.indexOf(value.charAt(indexNext)) < 0 && chars.indexOf(value.charAt(indexNext)) >= 0) {
+ letsEat += value.charAt(indexNext);
+ eaten += value.charAt(indexNext);
+ indexNext++;
+ }
+
+ return shouldStop();
+ };
+ const eatUntil = chars => {
+ eaten = '';
+
+ while (indexNext < value.length && forbidenCharacters.indexOf(value.charAt(indexNext)) < 0 && chars.indexOf(value.charAt(indexNext)) < 0) {
+ letsEat += value.charAt(indexNext);
+ eaten += value.charAt(indexNext);
+ indexNext++;
+ }
+
+ // Ugly but keep the main loop readable
+ // Set the label it should set
+ if (labelFirst) {
+ labelSecond = eaten;
+ } else {
+ labelFirst = eaten;
+ }
+
+ return shouldStop();
+ };
+
+ // In quote, every character is valid exept the unescaped quotes and CR or LF
+ // Same function for single and double quote
+ const eatInQuote = quote => {
+ eaten = '';
+ // First check so value[indexNext-1] will always be valid
+ if (value[indexNext] === quote) {
+ return;
+ }
+
+ while (indexNext < value.length && !(quote === value[indexNext] && value[indexNext - 1] !== '\\') && value[indexNext] !== '\n' && value[indexNext] !== '\r') {
+ letsEat += value.charAt(indexNext);
+ eaten += value.charAt(indexNext);
+ indexNext++;
+ }
+ // If we encounter an EOL, there is an error
+ // We are waiting for a quote
+ if (value[indexNext] === '\n' || value[indexNext] === '\r' || indexNext >= value.length) {
+ errorDetected = true;
+ return true;
+ }
+
+ // Ugly but keep the main loop readable
+ if (labelFirst) {
+ labelSecond = eaten.replace(/\\"/g, '"');
+ } else {
+ labelFirst = eaten.replace(/\\"/g, '"');
+ }
+
+ return shouldStop();
+ };
+
+ // It's realy commun to eat only one character so let's make it a function
+ const eatOne = c => {
+ // Miam !
+ letsEat += c;
+ indexNext++;
+
+ return shouldStop();
+ };
+
+ const addAttribute = () => {
+ switch (type) {
+ case 'id':
+ // ID
+ prop.id = prop.id || labelFirst;
+ break;
+ case 'class':
+ if (!prop.class) {
+ prop.class = [];
+ }
+
+ if (prop.class.indexOf(labelFirst) < 0) {
+ prop.class.push(labelFirst);
+ }
+
+ break;
+ case 'key':
+ if (!labelFirst) {
+ return nothingHappend;
+ }
+ if (labelFirst !== 'id' && labelFirst !== 'class') {
+ prop[labelFirst] = labelSecond;
+ }
+ break;
+ default:
+ }
+ type = undefined;
+ labelFirst = '';
+ labelSecond = undefined;
+ };
+
+ /** *********************** Start parsing ************************ */
+
+ // Let's check for trelling spaces first
+ eat(' \t\v');
+
+ if (value[indexNext] === '{') {
+ eatOne('{');
+ stopOnBrace = true;
+ }
+
+ while (!shouldStop()) {
+ if (eat(' \t\v')) {
+ break;
+ }
+
+ if (value.charAt(indexNext) === '.') {
+ // Classes
+ type = 'class';
+ if (eatOne('.')) {
+ errorDetected = true;
+ break;
+ }
+ } else if (value.charAt(indexNext) === '#') {
+ // ID
+ type = 'id';
+ if (eatOne('#')) {
+ errorDetected = true;
+ break;
+ }
+ } else {
+ // Key
+ type = 'key';
+ }
+
+ // Extract name
+ if (eatUntil('=\t\b\v  ') || !labelFirst) {
+ break;
+ }
+ if (value.charAt(indexNext) === '=' && type === 'key') {
+ // Set labelSecond
+ if (eatOne('=')) {
+ break;
+ }
+
+ if (value.charAt(indexNext) === '"') {
+ if (eatOne('"')) {
+ break;
+ }
+
+ if (eatInQuote('"')) {
+ break;
+ }
+
+ if (value.charAt(indexNext) === '"') {
+ if (eatOne('"')) {
+ break;
+ }
+ } else {
+ return nothingHappend;
+ }
+ } else if (value.charAt(indexNext) === '\'') {
+ if (eatOne('\'')) {
+ break;
+ }
+ if (eatInQuote('\'')) {
+ break;
+ }
+
+ if (value.charAt(indexNext) === '\'') {
+ if (eatOne('\'')) {
+ break;
+ }
+ } else {
+ return nothingHappend;
+ }
+ } else if (eatUntil(' \t\n\r\v=}')) {
+ break;
+ }
+ }
+
+ // Add the parsed attribute to the output prop with the ad hoc type
+ addAttribute();
+ }
+ addAttribute();
+ if (stopOnBrace) {
+ if (indexNext < value.length && value[indexNext] === '}') {
+ stopOnBrace = false;
+ eatOne('}');
+ } else {
+ return nothingHappend;
+ }
+ }
+
+ if (errorDetected) {
+ return nothingHappend;
+ }
+
+ return { prop, eaten: letsEat };
+}
+
+module.exports = parse; \ No newline at end of file
diff --git a/package.json b/package.json
index 8c5c310..4168dd6 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,11 @@
{
"name": "md-attr-parser",
- "version": "1.1.0",
+ "version": "1.1.1",
"description": "A parser for markdown's attributes",
- "main": "src/index.js",
+ "main": "dist/index.js",
"scripts": {
"pretest": "xo",
+ "prepare": "del-cli dist && cross-env BABEL_ENV=production babel src --out-dir dist",
"test": "ava"
},
"repository": {
@@ -17,6 +18,10 @@
],
"devDependencies": {
"ava": "^0.25.0",
+ "babel-cli": "^6.26.0",
+ "babel-preset-env": "^1.6.1",
+ "cross-env": "^5.1.4",
+ "del-cli": "^1.1.0",
"xo": "^0.18.2"
},
"license": "GPL-3.0",