Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored html-tools and htmljs to move ES6 version #385

Open
wants to merge 21 commits into
base: release-3.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions packages/html-tools/charref.js
Original file line number Diff line number Diff line change
Expand Up @@ -2246,18 +2246,18 @@ const getNamedEntityByFirstChar = {};

(function () {
const namedEntitiesByFirstChar = {};
let chr;
let char;

for (const ent in ENTITIES) {
chr = ent.charAt(1);
namedEntitiesByFirstChar[chr] = (namedEntitiesByFirstChar[chr] || []);
namedEntitiesByFirstChar[chr].push(ent.slice(2));
}
Object.getOwnPropertyNames(ENTITIES).forEach(ent => {
char = ent.charAt(1);
namedEntitiesByFirstChar[char] = (namedEntitiesByFirstChar[char] || []);
namedEntitiesByFirstChar[char].push(ent.slice(2));
});

for (chr in namedEntitiesByFirstChar) {
Object.keys(namedEntitiesByFirstChar).forEach(chr => {
const regex = new RegExp(`^&${chr}(?:${namedEntitiesByFirstChar[chr].join('|')})`);
getNamedEntityByFirstChar[chr] = makeRegexMatcher(regex);
}
});
}());

// Run a provided "matcher" function but reset the current position afterwards.
Expand Down Expand Up @@ -2337,9 +2337,9 @@ const BIG_BAD_CODEPOINTS = (function (obj) {
0xDFFFE, 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF,
0x10FFFE, 0x10FFFF];

for (const item of list) {
list.forEach(item => {
obj[item] = true;
}
});

return obj;
}({}));
Expand Down
1 change: 1 addition & 0 deletions packages/html-tools/charref_tests.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question @jankapunkt @guncebektas : Does this have to be included in different files? Isn't there a default .eslintrc or something for blaze which should contain this?

import { HTMLTools } from 'meteor/html-tools';

const { Scanner } = HTMLTools;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think the old way was more straightforward to read - const is fine but in general? Do we need this everywhere?

Expand Down
1 change: 1 addition & 0 deletions packages/html-tools/package.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */
Package.describe({
name: 'html-tools',
summary: 'Standards-compliant HTML tools',
Expand Down
7 changes: 3 additions & 4 deletions packages/html-tools/parse.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 OK... I think this is a blocker - re-sorting / rearranging all the functions makes this one really hard to review... why has this been rearranged @guncebektas ?

Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const parseAttrs = attrs => {
const inValue = attrs[k];
let outParts = [];

for (const token of inValue) {
inValue.forEach(token => {
switch (token.t) {
case 'Chars':
outParts = pushOrAppendString(outParts, token.v);
Expand All @@ -109,7 +109,7 @@ const parseAttrs = attrs => {
outParts.push(token.v);
break;
}
}
});

const outValue = (inValue.length === 0 ? '' : (outParts.length === 1 ? outParts[0] : outParts));
const properKey = properCaseAttributeName(k);
Expand Down Expand Up @@ -285,8 +285,7 @@ export function getContent(scanner, shouldStopFunc) {
if (content == null) content = [];
else if (!HTML.isArray(content)) content = [content];

items.push(HTML.getTag(tagName).apply(
null, (attrs ? [attrs] : []).concat(content)));
items.push(HTML.getTag(tagName).apply(null, (attrs ? [attrs] : []).concat(content)));
}
} else {
scanner.fatal(`Unknown token type: ${token.t}`);
Expand Down
3 changes: 2 additions & 1 deletion packages/html-tools/parse_tests.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the whitespace conversion distracts from the es6 updates unfortunately.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */
import { HTML } from 'meteor/htmljs';
import { HTMLTools } from 'meteor/html-tools';
import { BlazeTools } from 'meteor/blaze-tools';
Expand Down Expand Up @@ -273,7 +274,7 @@ Tinytest.add('html-tools - getTemplateTag', function (test) {
}

const match = mustache.exec(scanner.rest());
if (!match) scanner.fatal('Bad mustache');
if (!match) scanner.fatal(`Bad mustache at ${position}`);

scanner.pos += match[0].length;

Expand Down
17 changes: 3 additions & 14 deletions packages/html-tools/templatetag.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
// _assign is like _.extend or the upcoming Object.assign.
// Copy src's own, enumerable properties onto tgt and return
// tgt.
const _hasOwnProperty = Object.prototype.hasOwnProperty;
const _assign = function (tgt, src) {
for (const k in src) {
if (_hasOwnProperty.call(src, k)) tgt[k] = src[k];
}
return tgt;
};

export function TemplateTag(props) {
if (!(this instanceof TemplateTag)) {
// called without `new`
return new TemplateTag();
}

if (props) _assign(this, props);
if (props) Object.assign(this, props);
}

_assign(TemplateTag.prototype, {
Object.assign(TemplateTag.prototype, {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of like this one, if it does the same thing indeed :D

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructorName: 'TemplateTag',
toJS(visitor) {
return visitor.generateCall(this.constructorName, _assign({}, this));
return visitor.generateCall(this.constructorName, Object.assign({}, this));
},
});
24 changes: 12 additions & 12 deletions packages/html-tools/tokenize.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ export function getComment(scanner) {
}

const skipSpaces = function (scanner) {
while (HTML_SPACE.test(scanner.peek())) scanner.pos++;
while (HTML_SPACE.test(scanner.peek())) {
scanner.pos++;
}
};

const requireSpaces = function (scanner) {
Expand Down Expand Up @@ -228,7 +230,6 @@ const handleEndOfTag = function (scanner, tag) {
return null;
};


// Scan a quoted or unquoted attribute value (omit `quote` for unquoted).
const getAttributeValue = function (scanner, quote) {
if (quote) {
Expand Down Expand Up @@ -261,8 +262,7 @@ const getAttributeValue = function (scanner, quote) {
tokens.push(charRef);
charsTokenToExtend = null;
} else if (scanner.getTemplateTag &&
((templateTag = scanner.getTemplateTag(
scanner, TEMPLATE_TAG_POSITION.IN_ATTRIBUTE)) ||
((templateTag = scanner.getTemplateTag(scanner, TEMPLATE_TAG_POSITION.IN_ATTRIBUTE)) ||
scanner.pos > curPos /* `{{! comment}}` */)) {
if (templateTag) {
tokens.push({
Expand Down Expand Up @@ -332,8 +332,8 @@ export function getTagToken(scanner) {
// first, try for a template tag.
const curPos = scanner.pos;
const templateTag = (scanner.getTemplateTag &&
scanner.getTemplateTag(
scanner, TEMPLATE_TAG_POSITION.IN_START_TAG));
scanner.getTemplateTag(scanner, TEMPLATE_TAG_POSITION.IN_START_TAG));

if (templateTag || (scanner.pos > curPos)) {
if (templateTag) {
if (tag.attrs === nondynamicAttrs) tag.attrs = [nondynamicAttrs];
Expand Down Expand Up @@ -408,16 +408,14 @@ export function getHTMLToken(scanner, dataMode) {
// `getTemplateTag` function. If the function returns `null` but
// consumes characters, it must have parsed a comment or something,
// so we loop and try it again. If it ever returns `null` without
// consuming anything, that means it didn't see anything interesting
// consuming anything, that means it didn't see anything interesting,
// so we look for a normal token. If it returns a truthy value,
// the value must be instanceof HTMLTools.TemplateTag. We wrap it
// in a Special token.
const lastPos = scanner.pos;
result = scanner.getTemplateTag(
scanner,
(dataMode === 'rcdata' ? TEMPLATE_TAG_POSITION.IN_RCDATA :
(dataMode === 'rawtext' ? TEMPLATE_TAG_POSITION.IN_RAWTEXT :
TEMPLATE_TAG_POSITION.ELEMENT)));
const position = (dataMode === 'rcdata' ? TEMPLATE_TAG_POSITION.IN_RCDATA : (dataMode === 'rawtext' ? TEMPLATE_TAG_POSITION.IN_RAWTEXT : TEMPLATE_TAG_POSITION.ELEMENT))

result = scanner.getTemplateTag(scanner, position);

if (result) return { t: 'TemplateTag', v: assertIsTemplateTag(result) };
if (scanner.pos > lastPos) return null;
Expand Down Expand Up @@ -474,12 +472,14 @@ export function isLookingAtEndTag(scanner, tagName) {
const rest = scanner.rest();
let pos = 0; // into rest
const firstPart = /^<\/([a-zA-Z]+)/.exec(rest);

if (firstPart &&
properCaseTagName(firstPart[1]) === tagName) {
// we've seen `</foo`, now see if the end tag continues
pos += firstPart[0].length;
while (pos < rest.length && HTML_SPACE.test(rest.charAt(pos))) pos++;
if (pos < rest.length && rest.charAt(pos) === '>') return true;
}

return false;
}
2 changes: 1 addition & 1 deletion packages/html-tools/tokenize_tests.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */
import { HTMLTools } from 'meteor/html-tools';

const { Scanner } = HTMLTools;
Expand All @@ -19,7 +20,6 @@ const tokenize = function (input) {
return tokens;
};


Tinytest.add('html-tools - comments', function (test) {
const succeed = function (input, content) {
const scanner = new Scanner(input);
Expand Down
40 changes: 19 additions & 21 deletions packages/html-tools/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,6 @@ export function asciiLowerCase (str) {
});
}

const svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' ');

const properAttributeCaseMap = (function (map) {
svgCamelCaseAttributes.forEach(a => {
map[asciiLowerCase(a)] = a;
});
return map;
}({}));

const properTagCaseMap = (function (map) {
const knownElements = HTML.knownElementNames;
knownElements.forEach(a => {
map[asciiLowerCase(a)] = a;
});
return map;
}({}));

// Take a tag name in any case and make it the proper case for HTML.
//
// Modern browsers let you embed SVG in HTML, but SVG elements are special
Expand All @@ -33,14 +16,29 @@ const properTagCaseMap = (function (map) {
// you actually get a `"viewBox"` attribute. Any HTML-parsing toolchain
// must do the same.
export function properCaseTagName (name) {
const _properTagCaseMap = (map => {
const knownElements = HTML.knownElementNames;
knownElements.forEach(a => {
map[asciiLowerCase(a)] = a;
});
return map;
})({});

const lowered = asciiLowerCase(name);
return properTagCaseMap.hasOwnProperty(lowered) ?
properTagCaseMap[lowered] : lowered;
return _properTagCaseMap.hasOwnProperty(lowered) ? _properTagCaseMap[lowered] : lowered;
}

// See docs for properCaseTagName.
export function properCaseAttributeName(name) {
const svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' ');

const _properAttributeCaseMap = (map => {
svgCamelCaseAttributes.forEach(a => {
map[asciiLowerCase(a)] = a;
});
return map;
})({});
guncebektas marked this conversation as resolved.
Show resolved Hide resolved

const lowered = asciiLowerCase(name);
return properAttributeCaseMap.hasOwnProperty(lowered) ?
properAttributeCaseMap[lowered] : lowered;
return _properAttributeCaseMap.hasOwnProperty(lowered) ? _properAttributeCaseMap[lowered] : lowered;
}
33 changes: 15 additions & 18 deletions packages/htmljs/html.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, hard to parse changes

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export const Tag = function () {
Tag.prototype.tagName = ''; // this will be set per Tag subclass
Tag.prototype.attrs = null;
Tag.prototype.children = Object.freeze ? Object.freeze([]) : [];
Tag.prototype.htmljsType = Tag.htmljsType = ['Tag'];
Tag.htmljsType = ['Tag'];
Tag.prototype.htmljsType = Tag.htmljsType;

export function isConstructedObject(x) {
guncebektas marked this conversation as resolved.
Show resolved Hide resolved
// Figure out if `x` is "an instance of some class" or just a plain
Expand Down Expand Up @@ -66,8 +67,10 @@ const makeTagConstructor = function (tagName) {
i++;
} else if (attrs instanceof Attrs) {
const array = attrs.value;

if (array.length === 1) {
instance.attrs = array[0];
const [ attrValue ] = array;
instance.attrs = attrValue;
} else if (array.length > 1) {
instance.attrs = array;
}
Expand Down Expand Up @@ -167,7 +170,8 @@ export function CharRef(attrs) {
this.str = attrs.str;
}

CharRef.prototype.htmljsType = CharRef.htmljsType = ['CharRef'];
CharRef.htmljsType = ['CharRef'];
CharRef.prototype.htmljsType = CharRef.htmljsType;

export function Comment(value) {
if (!(this instanceof Comment)) {
Expand All @@ -185,7 +189,8 @@ export function Comment(value) {
this.sanitizedValue = value.replace(/^-|--+|-$/g, '');
}

Comment.prototype.htmljsType = Comment.htmljsType = ['Comment'];
Comment.htmljsType = ['Comment'];
Comment.prototype.htmljsType = Comment.htmljsType;

export function Raw(value) {
if (!(this instanceof Raw)) {
Expand All @@ -200,7 +205,8 @@ export function Raw(value) {
this.value = value;
}

Raw.prototype.htmljsType = Raw.htmljsType = ['Raw'];
Raw.htmljsType = ['Raw'];
Raw.prototype.htmljsType = Raw.htmljsType;

export function isArray(x) {
return x instanceof Array || Array.isArray(x);
Expand All @@ -212,18 +218,11 @@ export function isNully(node) {
return true;
}

if (isArray(node)) {
// is it an empty array or an array of all nully items?
for (const item of node) if (!isNully(item)) return false;

return true;
}

return false;
return isArray(node) && node.every(isNully);
}

export function isValidAttributeName(name) {
return /^[:_A-Za-z][:_A-Za-z0-9.\-]*/.test(name);
return /^[:_A-Za-z][:_A-Za-z\d.-]*/.test(name);
}

// If `attrs` is an array of attributes dictionaries, combines them
Expand All @@ -240,11 +239,9 @@ export function flattenAttributes(attrs) {
}

const result = {};
const n = (isList ? attrs.length : 1);

let i = 0;
const N = (isList ? attrs.length : 1);

for (; i < N; i++) {
for (let i = 0; i < n; i++) {
const oneAttrs = (isList ? attrs[i] : attrs);

if ((typeof oneAttrs !== 'object') || isConstructedObject(oneAttrs)) {
Expand Down
1 change: 1 addition & 0 deletions packages/htmljs/htmljs_test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */
import { HTML } from 'meteor/htmljs';
guncebektas marked this conversation as resolved.
Show resolved Hide resolved

Tinytest.add('htmljs - getTag', function (test) {
Expand Down
1 change: 1 addition & 0 deletions packages/htmljs/package.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-env meteor */
Package.describe({
guncebektas marked this conversation as resolved.
Show resolved Hide resolved
name: 'htmljs',
summary: 'Small library for expressing HTML trees',
Expand Down
Loading