From d399622de231fccda49925ca3eaeeb2f123e2094 Mon Sep 17 00:00:00 2001 From: ache Date: Tue, 24 Apr 2018 05:16:22 +0200 Subject: Use babel to distribut --- dist/index.js | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 9 ++- 2 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 dist/index.js 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", -- cgit v1.2.3