Skip to content

Commit

Permalink
Merge a15a0e8 into e0ff0b1
Browse files Browse the repository at this point in the history
  • Loading branch information
SoftwareAndOutsourcing committed Apr 16, 2021
2 parents e0ff0b1 + a15a0e8 commit 508816c
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 4 deletions.
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -60,6 +60,23 @@ Output:
<p>paragraph with <span class="red">a style me span</span></p>
```

Attribute definition lists supports inheritance based on [kramdown Syntax](https://kramdown.gettalong.org/syntax.html#attribute-list-definitions). `id` attributes are not included.

They require to be preceded and followed by an empty line and other content inside these blocks will not be parsed. `id` attributes are not included:

```md
{ald: #someid .someclass attr="allowed"}
{anotherald: someald #anotherid .anotherclass anotherattr="allowed"}

another text
{anotherald}
```

Output:
```html
<p class="someclass anotherclass" attr="allowed" id="anotherid" anotherattr="allowed">another text</p>
```

## Install

```
Expand Down
3 changes: 3 additions & 0 deletions index.js
@@ -1,6 +1,7 @@
'use strict';

const patternsConfig = require('./patterns.js');
const utils = require('./utils.js');

const defaultOptions = {
leftDelimiter: '{',
Expand All @@ -16,6 +17,8 @@ module.exports = function attributes(md, options_) {

function curlyAttrs(state) {
let tokens = state.tokens;
// clean ALDs for each document
utils.attributeListDefintions.clear();

for (let i = 0; i < tokens.length; i++) {
for (let p = 0; p < patterns.length; p++) {
Expand Down
38 changes: 38 additions & 0 deletions patterns.js
Expand Up @@ -12,7 +12,45 @@ module.exports = options => {
+ '[^' + utils.escapeRegExp(options.rightDelimiter) + ']');

return ([
/**
* {ald1 #id1 .class1 attr1="value1"}
* {ald2 #id2 .class2 attr2="value2"}
* ...
*
*/
{
name: 'attribute list definitions',
tests: [
{
shift: 0,
block: true,
content: utils.hasDelimiters('only', options)
}
],
transform: (tokens, i) => {
const token = tokens[i];

const hasDelimiters = utils.hasDelimiters('only', options);
let hasAlds = false;

for (const child of token.children) {
if (child.type === 'text' && hasDelimiters(child.content)) {
const childStart =
child.content.lastIndexOf(options.leftDelimiter);
const attrs = utils.getAttrs(child.content, childStart, options);
if (attrs.length && attrs[0][0] === 'attributeListDefinition') {
utils.attributeListDefintions.set(attrs[0][1], attrs.slice(1));
hasAlds = true;
}
}
}

if (hasAlds) {
// remove surrounding paragraph
tokens.splice(i - 1, 3);
}
}
}, {
/**
* ```python {.cls}
* for i in range(10):
Expand Down
25 changes: 25 additions & 0 deletions test.js
Expand Up @@ -381,6 +381,31 @@ function describeTestsWithOptions(options, postText) {
expected = '<p class="someclass" attr="allowed">text</p>\n';
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});

it('should support attribute list definitions', () => {
src = '{someald: #someid .someclass attr="allowed"}\n\n';
src += 'some text\n';
src += '{someald}';
expected = '<p id="someid" class="someclass" attr="allowed">some text</p>\n';
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});

it('should support single attribute list definitions and references', () => {
src = '{someald: #someid .someclass attr="allowed"}\n\n';
src += 'some text\n';
src += '{someald}';
expected = '<p id="someid" class="someclass" attr="allowed">some text</p>\n';
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});

it('should support multiple attribute list definitions with inheritance and references', () => {
src = '{someald: #someid .someclass attr="allowed"}\n';
src += '{anotherald: someald #anotherid .anotherclass anotherattr="allowed"}\n\n';
src += 'another text\n';
src += '{anotherald}';
expected = '<p class="someclass anotherclass" attr="allowed" id="anotherid" anotherattr="allowed">another text</p>\n';
assert.equal(md.render(replaceDelimiters(src, options)), expected);
});
});
}

Expand Down
41 changes: 37 additions & 4 deletions utils.js
@@ -1,4 +1,8 @@
'use strict';

const attributeListDefintions = new Map();
exports.attributeListDefintions = attributeListDefintions;

/**
* parse {.class #id key=val} strings
* @param {string} str: string to parse
Expand All @@ -12,6 +16,7 @@ exports.getAttrs = function (str, start, options) {
const keySeparator = '=';
const classChar = '.';
const idChar = '#';
const attributeListReferenceChar = ':';

const attrs = [];
let key = '';
Expand All @@ -24,7 +29,15 @@ exports.getAttrs = function (str, start, options) {
// breaks when } is found or end of string
for (let i = start + options.leftDelimiter.length; i < str.length; i++) {
if (str.slice(i, i + options.rightDelimiter.length) === options.rightDelimiter) {
if (key !== '') { attrs.push([key, value]); }
if (key !== '') {
if (attributeListDefintions.has(key)
&& value === ''
&& !['css-module', 'class', 'id'].includes(key)) {
attrs.push(['attributeListReference', key]);
} else {
attrs.push([key, value]);
}
}
break;
}
let char_ = str.charAt(i);
Expand Down Expand Up @@ -70,7 +83,23 @@ exports.getAttrs = function (str, start, options) {
// beginning or ending space: { .red } vs {.red}
continue;
}
attrs.push([key, value]);

if (key.slice(-1) === attributeListReferenceChar) {
value = key.slice(0,-1);
key = 'attributeListDefinition';
}

if (value === '' && attributeListDefintions.has(key)) {
// attribute list definition import
for (const attr of attributeListDefintions.get(key)) {
if (attr[0] !== 'id') {
attrs.push(attr);
}
}
} else {
attrs.push([key, value]);
}

key = '';
value = '';
parsingKey = true;
Expand All @@ -96,7 +125,7 @@ exports.getAttrs = function (str, start, options) {
return attrs.filter(function (attrPair) {
let attr = attrPair[0];

function isAllowedAttribute (allowedAttribute) {
function isAllowedAttribute(allowedAttribute) {
return (attr === allowedAttribute
|| (allowedAttribute instanceof RegExp && allowedAttribute.test(attr))
);
Expand All @@ -123,6 +152,10 @@ exports.addAttrs = function (attrs, token) {
token.attrJoin('class', attrs[j][1]);
} else if (key === 'css-module') {
token.attrJoin('css-module', attrs[j][1]);
} else if (key === 'attributeListReference') {
if (attributeListDefintions.has(attrs[j][1])) {
exports.addAttrs(attributeListDefintions.get(attrs[j][1]), token);
}
} else {
token.attrPush(attrs[j]);
}
Expand Down Expand Up @@ -223,7 +256,7 @@ exports.removeDelimiter = function (str, options) {
* @param {string} s Regex string.
* @return {string} Escaped string.
*/
function escapeRegExp (s) {
function escapeRegExp(s) {
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
exports.escapeRegExp = escapeRegExp;
Expand Down

0 comments on commit 508816c

Please sign in to comment.