securemark 0.293.3 → 0.293.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/index.js +94 -103
- package/package.json +1 -1
- package/src/combinator/data/parser.ts +0 -3
- package/src/parser/api/bind.ts +1 -1
- package/src/parser/api/parse.ts +1 -1
- package/src/parser/block/dlist.test.ts +1 -1
- package/src/parser/block/heading.test.ts +1 -0
- package/src/parser/block/olist.test.ts +1 -0
- package/src/parser/block/ulist.test.ts +1 -0
- package/src/parser/inline/emphasis.test.ts +1 -0
- package/src/parser/inline/html.test.ts +3 -3
- package/src/parser/inline/html.ts +9 -9
- package/src/parser/inline/italic.test.ts +1 -0
- package/src/parser/inline/link.test.ts +10 -8
- package/src/parser/inline/link.ts +17 -17
- package/src/parser/inline/mark.test.ts +1 -0
- package/src/parser/inline/media.test.ts +6 -7
- package/src/parser/inline/media.ts +3 -3
- package/src/parser/inline/remark.test.ts +3 -1
- package/src/parser/inline/strong.test.ts +1 -0
- package/src/parser/source/escapable.test.ts +1 -0
- package/src/parser/source/escapable.ts +3 -11
- package/src/parser/source/text.test.ts +5 -4
- package/src/parser/source/text.ts +80 -76
- package/src/parser/source/unescapable.test.ts +1 -0
- package/src/parser/source/unescapable.ts +5 -8
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.293.
|
|
1
|
+
/*! securemark v0.293.4 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
|
|
2
2
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
3
3
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
4
4
|
module.exports = factory(require("Prism"), require("DOMPurify"));
|
|
@@ -6737,17 +6737,17 @@ const attrspecs = {
|
|
|
6737
6737
|
};
|
|
6738
6738
|
Object.setPrototypeOf(attrspecs, null);
|
|
6739
6739
|
Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
|
|
6740
|
-
exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/<[a-z]+(?=[
|
|
6740
|
+
exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/<[a-z]+(?=[ >])/yi, (0, combinator_1.union)([(0, combinator_1.surround)(
|
|
6741
6741
|
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
6742
|
-
(0, source_1.str)(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[
|
|
6742
|
+
(0, source_1.str)(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]]), (0, combinator_1.match)(new RegExp(String.raw`<(${TAGS.join('|')})(?=[^\S\n]|>)`, 'y'), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs]) => [(0, array_1.push)((0, array_1.unshift)(as, bs), cs)], ([as, bs = []]) => [(0, array_1.unshift)(as, bs)]),
|
|
6743
6743
|
// 不可視のHTML構造が可視構造を変化させるべきでない。
|
|
6744
6744
|
// 可視のHTMLは優先度変更を検討する。
|
|
6745
6745
|
// このため<>は将来的に共通構造を変化させる可能性があり
|
|
6746
|
-
//
|
|
6746
|
+
// 共通構造を変化させない非構造文字列としては依然としてエスケープを要する。
|
|
6747
6747
|
(0, combinator_1.precedence)(0, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', `</${tag}>`)), (0, combinator_1.open)('\n', (0, combinator_1.some)(inline_1.inline, `</${tag}>`), true)])))), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], context) => [[elem(tag, true, as, bs, cs, context)]], ([as, bs = []], context) => [[elem(tag, true, as, bs, [], context)]]), ([, tag]) => tag, new Map())), (0, combinator_1.surround)(
|
|
6748
6748
|
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
6749
|
-
(0, source_1.str)(/<[a-z]+(?=[
|
|
6750
|
-
exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/[
|
|
6749
|
+
(0, source_1.str)(/<[a-z]+(?=[ >])/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]])])));
|
|
6750
|
+
exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ >])/yi), (0, source_1.str)(/ [^\s<>]+/y)]);
|
|
6751
6751
|
function elem(tag, content, as, bs, cs, context) {
|
|
6752
6752
|
if (!tags.includes(tag)) return ielem('tag', `Invalid HTML tag name "${tag}"`, context);
|
|
6753
6753
|
if (content) {
|
|
@@ -6916,7 +6916,7 @@ Object.setPrototypeOf(optspec, null);
|
|
|
6916
6916
|
exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */, (0, combinator_1.creation)(10, (0, combinator_1.precedence)(1, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.subsequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, visibility_1.trimBlankStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', [[']', 1]])), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [(0, array_1.push)(ns, ["\u001F" /* Command.Separator */])] : undefined, undefined, [3 | 64 /* Backtrack.bracket */, 3 | 16 /* Backtrack.link */, 2 | 8 /* Backtrack.ruby */])),
|
|
6917
6917
|
// `{ `と`{`で個別にバックトラックが発生し+1nされる。
|
|
6918
6918
|
// 自己再帰的にパースしてもオプションの不要なパースによる計算量の増加により相殺される。
|
|
6919
|
-
(0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /
|
|
6919
|
+
(0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y, false, undefined, ([as, bs], context) => {
|
|
6920
6920
|
if (!bs) return;
|
|
6921
6921
|
const head = context.position - context.range;
|
|
6922
6922
|
(0, combinator_1.setBacktrack)(context, [2 | 16 /* Backtrack.link */], head);
|
|
@@ -6942,10 +6942,10 @@ exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /
|
|
|
6942
6942
|
if (content.length !== 0 && (0, visibility_1.trimBlankNodeEnd)(content).length === 0) return;
|
|
6943
6943
|
return [[parse((0, dom_1.defrag)(content), params, context)]];
|
|
6944
6944
|
}))))));
|
|
6945
|
-
exports.medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */ | 4 /* State.media */, (0, combinator_1.validate)(/[[{]/y, (0, combinator_1.creation)(10, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.union)([inline_1.media, inline_1.shortmedia]), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /
|
|
6946
|
-
exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /
|
|
6947
|
-
exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/
|
|
6948
|
-
exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/
|
|
6945
|
+
exports.medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */ | 4 /* State.media */, (0, combinator_1.validate)(/[[{]/y, (0, combinator_1.creation)(10, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.union)([inline_1.media, inline_1.shortmedia]), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]]))))));
|
|
6946
|
+
exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]])));
|
|
6947
|
+
exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/ /y, (0, source_1.str)(/\S+/y)), (0, source_1.str)(/[^\s{}]+/y)]);
|
|
6948
|
+
exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/ nofollow(?=[ }])/y), () => [` rel="nofollow"`]), (0, source_1.str)(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ }])/yi), (0, source_1.str)(/ [^\s{}]+/y)]);
|
|
6949
6949
|
function parse(content, params, context) {
|
|
6950
6950
|
const INSECURE_URI = params.shift();
|
|
6951
6951
|
let uri;
|
|
@@ -6966,11 +6966,11 @@ function elem(INSECURE_URI, content, uri, origin) {
|
|
|
6966
6966
|
case 'http:':
|
|
6967
6967
|
case 'https:':
|
|
6968
6968
|
switch (true) {
|
|
6969
|
-
case /[
|
|
6969
|
+
case /[0-9a-z]:\S/i.test((0, util_1.stringify)(content)):
|
|
6970
6970
|
type = 'content';
|
|
6971
6971
|
message = 'URI must not be contained';
|
|
6972
6972
|
break;
|
|
6973
|
-
case INSECURE_URI.
|
|
6973
|
+
case INSECURE_URI.startsWith('^/') && /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
|
|
6974
6974
|
type = 'argument';
|
|
6975
6975
|
message = 'Dot-segments cannot be used in subresource paths';
|
|
6976
6976
|
break;
|
|
@@ -7007,13 +7007,13 @@ function elem(INSECURE_URI, content, uri, origin) {
|
|
|
7007
7007
|
}
|
|
7008
7008
|
function resolve(uri, host, source) {
|
|
7009
7009
|
switch (true) {
|
|
7010
|
-
case uri.
|
|
7010
|
+
case uri.startsWith('^/'):
|
|
7011
7011
|
const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
|
|
7012
7012
|
return last.includes('.') // isFile
|
|
7013
7013
|
// Exclude ISO 6709.
|
|
7014
7014
|
&& /^[0-9]*[a-z][0-9a-z]*$/i.test(last.slice(last.lastIndexOf('.') + 1)) ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}` : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
|
|
7015
7015
|
case host.origin === source.origin && host.pathname === source.pathname:
|
|
7016
|
-
case uri.
|
|
7016
|
+
case uri.startsWith('//'):
|
|
7017
7017
|
return uri;
|
|
7018
7018
|
default:
|
|
7019
7019
|
const target = new url_1.ReadonlyURL(uri, source.href);
|
|
@@ -7022,13 +7022,11 @@ function resolve(uri, host, source) {
|
|
|
7022
7022
|
}
|
|
7023
7023
|
exports.resolve = resolve;
|
|
7024
7024
|
function decode(uri) {
|
|
7025
|
-
const
|
|
7025
|
+
const head = /^[a-z]+(?:[.+-][0-9a-z]+)*:\/*[^/?#\s]+/i;
|
|
7026
|
+
const origin = uri.match(head)?.[0] ?? '';
|
|
7026
7027
|
try {
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
path = uri.slice(origin.length);
|
|
7030
|
-
}
|
|
7031
|
-
uri = origin + path;
|
|
7028
|
+
const path = decodeURI(uri.slice(origin.length));
|
|
7029
|
+
uri = !origin && head.test(path) ? uri.slice(origin.length) : origin + path;
|
|
7032
7030
|
} finally {
|
|
7033
7031
|
return uri.replace(/\s+/g, encodeURI);
|
|
7034
7032
|
}
|
|
@@ -7134,7 +7132,7 @@ const optspec = {
|
|
|
7134
7132
|
rel: undefined
|
|
7135
7133
|
};
|
|
7136
7134
|
Object.setPrototypeOf(optspec, null);
|
|
7137
|
-
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* State.media */, (0, combinator_1.creation)(10, (0, combinator_1.open)('!', (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']')), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [ns] : undefined, undefined, [3 | 4 /* Backtrack.escbracket */])), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /
|
|
7135
|
+
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* State.media */, (0, combinator_1.creation)(10, (0, combinator_1.open)('!', (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']')), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [ns] : undefined, undefined, [3 | 4 /* Backtrack.escbracket */])), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), / ?}/y, false, undefined, ([as, bs], context) => {
|
|
7138
7136
|
if (!bs) return;
|
|
7139
7137
|
const head = context.position - context.range;
|
|
7140
7138
|
(0, combinator_1.setBacktrack)(context, [2 | 16 /* Backtrack.link */], head);
|
|
@@ -7183,7 +7181,7 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* S
|
|
|
7183
7181
|
})((0, parser_1.subinput)(`{ ${INSECURE_URI}${linkparams.join('')} }`, context));
|
|
7184
7182
|
})))));
|
|
7185
7183
|
const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ')'), (0, source_1.str)(')'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']'), (0, source_1.str)(']'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), '}'), (0, source_1.str)('}'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, source_1.txt]), '"')), (0, source_1.str)('"'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */])])));
|
|
7186
|
-
const option = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, combinator_1.open)(/
|
|
7184
|
+
const option = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, combinator_1.open)(/ /y, (0, source_1.str)(/[1-9][0-9]*/y)), (0, source_1.str)(/[x:]/y), (0, source_1.str)(/[1-9][0-9]*(?=[ }])/y), false, ([[a], [b], [c]]) => [b === 'x' ? [`width="${a}"`, `height="${c}"`] : [`aspect-ratio="${a}/${c}"`]]), link_1.option]));
|
|
7187
7185
|
function sanitize(target, uri) {
|
|
7188
7186
|
let type;
|
|
7189
7187
|
let message;
|
|
@@ -7994,7 +7992,7 @@ exports.escsource = void 0;
|
|
|
7994
7992
|
const combinator_1 = __webpack_require__(3484);
|
|
7995
7993
|
const text_1 = __webpack_require__(5655);
|
|
7996
7994
|
const dom_1 = __webpack_require__(394);
|
|
7997
|
-
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s
|
|
7995
|
+
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
|
|
7998
7996
|
const escsource = ({
|
|
7999
7997
|
context
|
|
8000
7998
|
}) => {
|
|
@@ -8030,9 +8028,7 @@ const escsource = ({
|
|
|
8030
8028
|
return [[(0, dom_1.html)('br')]];
|
|
8031
8029
|
default:
|
|
8032
8030
|
if (context.sequential) return [[char]];
|
|
8033
|
-
text_1.
|
|
8034
|
-
const b = (0, text_1.isBlank)(source, position);
|
|
8035
|
-
let i = b ? source[position + 1] === '\n' ? position + 1 : text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, delimiter);
|
|
8031
|
+
let i = (0, text_1.next)(source, position, delimiter);
|
|
8036
8032
|
i -= position;
|
|
8037
8033
|
(0, combinator_1.consume)(i - 1, context);
|
|
8038
8034
|
context.position += i - 1;
|
|
@@ -8145,11 +8141,11 @@ exports.strs = strs;
|
|
|
8145
8141
|
Object.defineProperty(exports, "__esModule", ({
|
|
8146
8142
|
value: true
|
|
8147
8143
|
}));
|
|
8148
|
-
exports.
|
|
8144
|
+
exports.backToEmailHead = exports.backToUrlHead = exports.backToWhitespace = exports.next = exports.canSkip = exports.linebreak = exports.txt = exports.text = exports.nonWhitespace = void 0;
|
|
8149
8145
|
const combinator_1 = __webpack_require__(3484);
|
|
8150
8146
|
const dom_1 = __webpack_require__(394);
|
|
8151
8147
|
//const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
|
|
8152
|
-
exports.nonWhitespace = /[\
|
|
8148
|
+
exports.nonWhitespace = /[^ \t ]/g;
|
|
8153
8149
|
const text = input => {
|
|
8154
8150
|
const {
|
|
8155
8151
|
context
|
|
@@ -8184,22 +8180,39 @@ const text = input => {
|
|
|
8184
8180
|
default:
|
|
8185
8181
|
if (context.sequential) return [[char]];
|
|
8186
8182
|
exports.nonWhitespace.lastIndex = position + 1;
|
|
8187
|
-
const
|
|
8188
|
-
let i =
|
|
8189
|
-
const lineend = false ||
|
|
8183
|
+
const s = canSkip(source, position);
|
|
8184
|
+
let i = s ? exports.nonWhitespace.test(source) ? exports.nonWhitespace.lastIndex - 1 : source.length : next(source, position);
|
|
8185
|
+
const lineend = false || s && i === source.length || s && source[i] === '\n';
|
|
8190
8186
|
i -= position;
|
|
8191
|
-
i = lineend ? i : i - +
|
|
8187
|
+
i = lineend ? i : i - +s || 1;
|
|
8192
8188
|
(0, combinator_1.consume)(i - 1, context);
|
|
8193
8189
|
context.position += i - 1;
|
|
8194
8190
|
const linestart = position === 0 || source[position - 1] === '\n';
|
|
8195
|
-
|
|
8196
|
-
i += position;
|
|
8197
|
-
return i === context.position || b && !linestart || lineend ? [[]] : [[source.slice(i, context.position)]];
|
|
8191
|
+
return position === context.position || s && !linestart || lineend ? [[]] : [[source.slice(position, context.position)]];
|
|
8198
8192
|
}
|
|
8199
8193
|
};
|
|
8200
8194
|
exports.text = text;
|
|
8201
8195
|
exports.txt = (0, combinator_1.union)([exports.text]);
|
|
8202
8196
|
exports.linebreak = (0, combinator_1.focus)(/[\r\n]/y, (0, combinator_1.union)([exports.text]));
|
|
8197
|
+
function canSkip(source, position) {
|
|
8198
|
+
if (!isWhitespace(source[position], false)) return false;
|
|
8199
|
+
if (position + 1 === source.length) return true;
|
|
8200
|
+
return isWhitespace(source[position + 1], true);
|
|
8201
|
+
}
|
|
8202
|
+
exports.canSkip = canSkip;
|
|
8203
|
+
function isWhitespace(char, linebreak) {
|
|
8204
|
+
switch (char) {
|
|
8205
|
+
case ' ':
|
|
8206
|
+
case '\t':
|
|
8207
|
+
case ' ':
|
|
8208
|
+
return true;
|
|
8209
|
+
case '\r':
|
|
8210
|
+
case '\n':
|
|
8211
|
+
return linebreak;
|
|
8212
|
+
default:
|
|
8213
|
+
return false;
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8203
8216
|
function next(source, position, delimiter) {
|
|
8204
8217
|
let index;
|
|
8205
8218
|
if (delimiter) {
|
|
@@ -8212,8 +8225,20 @@ function next(source, position, delimiter) {
|
|
|
8212
8225
|
if (index === 0) return source.length;
|
|
8213
8226
|
const char = source[index];
|
|
8214
8227
|
switch (char) {
|
|
8228
|
+
case '$':
|
|
8229
|
+
case '%':
|
|
8230
|
+
case '*':
|
|
8231
|
+
case '+':
|
|
8232
|
+
case '~':
|
|
8233
|
+
case '=':
|
|
8234
|
+
case '/':
|
|
8235
|
+
index = backToWhitespace(source, position, index);
|
|
8236
|
+
break;
|
|
8237
|
+
case '[':
|
|
8238
|
+
index = source[index + 1] === '|' ? backToWhitespace(source, position, index) : index;
|
|
8239
|
+
break;
|
|
8215
8240
|
case ':':
|
|
8216
|
-
index = backToUrlHead(source, position, index);
|
|
8241
|
+
index = source.startsWith('//', index + 1) ? backToUrlHead(source, position, index) : index;
|
|
8217
8242
|
break;
|
|
8218
8243
|
case '@':
|
|
8219
8244
|
index = backToEmailHead(source, position, index);
|
|
@@ -8222,6 +8247,11 @@ function next(source, position, delimiter) {
|
|
|
8222
8247
|
return index;
|
|
8223
8248
|
}
|
|
8224
8249
|
exports.next = next;
|
|
8250
|
+
function backToWhitespace(source, position, index) {
|
|
8251
|
+
const prev = index - 1;
|
|
8252
|
+
return prev > position && /\s/.test(source[prev]) ? prev : index;
|
|
8253
|
+
}
|
|
8254
|
+
exports.backToWhitespace = backToWhitespace;
|
|
8225
8255
|
function backToUrlHead(source, position, index) {
|
|
8226
8256
|
const delim = index;
|
|
8227
8257
|
let state = false;
|
|
@@ -8317,7 +8347,6 @@ function isAlphanumeric(char) {
|
|
|
8317
8347
|
// this[c.charCodeAt(0)] = undefined);
|
|
8318
8348
|
// }
|
|
8319
8349
|
//};
|
|
8320
|
-
const delimiter = /\s(?:\\?(?:$|\s)|[$%])/y;
|
|
8321
8350
|
function seek(source, position) {
|
|
8322
8351
|
for (let i = position + 1; i < source.length; ++i) {
|
|
8323
8352
|
const fst = source[i];
|
|
@@ -8346,7 +8375,6 @@ function seek(source, position) {
|
|
|
8346
8375
|
case '{':
|
|
8347
8376
|
case '}':
|
|
8348
8377
|
case '*':
|
|
8349
|
-
case '%':
|
|
8350
8378
|
case '|':
|
|
8351
8379
|
case '\r':
|
|
8352
8380
|
case '\n':
|
|
@@ -8359,77 +8387,41 @@ function seek(source, position) {
|
|
|
8359
8387
|
case '/':
|
|
8360
8388
|
if (source[i + 1] === fst && source[i + 2] === fst) return i;
|
|
8361
8389
|
continue;
|
|
8390
|
+
case '%':
|
|
8391
|
+
if (source[i + 1] === ']') return i;
|
|
8392
|
+
continue;
|
|
8362
8393
|
case ':':
|
|
8363
8394
|
if (source[i + 1] === '/' && source[i + 2] === '/') return i;
|
|
8364
8395
|
continue;
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
// }
|
|
8389
|
-
// continue;
|
|
8396
|
+
case ' ':
|
|
8397
|
+
case '\t':
|
|
8398
|
+
case ' ':
|
|
8399
|
+
if (i + 1 === source.length) return i;
|
|
8400
|
+
switch (source[i + 1]) {
|
|
8401
|
+
case ' ':
|
|
8402
|
+
case '\t':
|
|
8403
|
+
case '\r':
|
|
8404
|
+
case '\n':
|
|
8405
|
+
case ' ':
|
|
8406
|
+
return i;
|
|
8407
|
+
case '\\':
|
|
8408
|
+
if (i + 2 === source.length) return i;
|
|
8409
|
+
switch (source[i + 2]) {
|
|
8410
|
+
case ' ':
|
|
8411
|
+
case '\t':
|
|
8412
|
+
case '\r':
|
|
8413
|
+
case '\n':
|
|
8414
|
+
case ' ':
|
|
8415
|
+
return i;
|
|
8416
|
+
}
|
|
8417
|
+
}
|
|
8418
|
+
continue;
|
|
8390
8419
|
default:
|
|
8391
|
-
delimiter.lastIndex = i;
|
|
8392
|
-
if (delimiter.test(source)) return i;
|
|
8393
8420
|
continue;
|
|
8394
8421
|
}
|
|
8395
8422
|
}
|
|
8396
8423
|
return source.length;
|
|
8397
8424
|
}
|
|
8398
|
-
const blank = /\s(?:$|\s|\\\n)/y;
|
|
8399
|
-
function isBlank(source, position) {
|
|
8400
|
-
blank.lastIndex = position;
|
|
8401
|
-
return blank.test(source);
|
|
8402
|
-
// removed by dead control flow
|
|
8403
|
-
|
|
8404
|
-
// removed by dead control flow
|
|
8405
|
-
|
|
8406
|
-
// removed by dead control flow
|
|
8407
|
-
|
|
8408
|
-
// removed by dead control flow
|
|
8409
|
-
|
|
8410
|
-
// removed by dead control flow
|
|
8411
|
-
|
|
8412
|
-
// removed by dead control flow
|
|
8413
|
-
|
|
8414
|
-
// removed by dead control flow
|
|
8415
|
-
|
|
8416
|
-
}
|
|
8417
|
-
exports.isBlank = isBlank;
|
|
8418
|
-
const whitespace = /\s/;
|
|
8419
|
-
function isWhitespace(char) {
|
|
8420
|
-
whitespace;
|
|
8421
|
-
switch (char) {
|
|
8422
|
-
case ' ':
|
|
8423
|
-
case '\t':
|
|
8424
|
-
case '\r':
|
|
8425
|
-
case '\n':
|
|
8426
|
-
case ' ':
|
|
8427
|
-
return true;
|
|
8428
|
-
default:
|
|
8429
|
-
return false;
|
|
8430
|
-
}
|
|
8431
|
-
}
|
|
8432
|
-
exports.isWhitespace = isWhitespace;
|
|
8433
8425
|
|
|
8434
8426
|
/***/ },
|
|
8435
8427
|
|
|
@@ -8472,8 +8464,7 @@ const unescsource = ({
|
|
|
8472
8464
|
default:
|
|
8473
8465
|
if (context.sequential) return [[char]];
|
|
8474
8466
|
text_1.nonWhitespace.lastIndex = position + 1;
|
|
8475
|
-
|
|
8476
|
-
let i = b ? source[position + 1] === '\n' ? position + 1 : text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, exports.delimiter);
|
|
8467
|
+
let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, exports.delimiter);
|
|
8477
8468
|
i -= position;
|
|
8478
8469
|
(0, combinator_1.consume)(i - 1, context);
|
|
8479
8470
|
context.position += i - 1;
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Delimiters } from './parser/context/delimiter';
|
|
2
|
-
import { MarkdownParser } from '../../../markdown';
|
|
3
2
|
|
|
4
3
|
export type Parser<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
|
|
5
4
|
= (input: Input<C & Ctx>) => Result<N, C, D>;
|
|
@@ -38,8 +37,6 @@ export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, C
|
|
|
38
37
|
type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
|
|
39
38
|
type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
|
|
40
39
|
|
|
41
|
-
export function input(source: string, context: CtxOptions): Input<Ctx>;
|
|
42
|
-
export function input(source: string, context: MarkdownParser.Options): Input<MarkdownParser.Context>;
|
|
43
40
|
export function input(source: string, context: CtxOptions): Input<Ctx> {
|
|
44
41
|
// @ts-expect-error
|
|
45
42
|
context.source = source;
|
package/src/parser/api/bind.ts
CHANGED
|
@@ -78,7 +78,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
78
78
|
for (; index < sourceSegments.length - last; ++index) {
|
|
79
79
|
assert(rev === revision);
|
|
80
80
|
const seg = sourceSegments[index];
|
|
81
|
-
const es = eval(header(input(seg, { header: index === 0 })) || block(input(seg, context)), []);
|
|
81
|
+
const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []);
|
|
82
82
|
blocks.splice(index, 0, [seg, es, url]);
|
|
83
83
|
if (es.length === 0) continue;
|
|
84
84
|
// All deletion processes always run after all addition processes have done.
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -34,7 +34,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
|
|
|
34
34
|
const node = frag();
|
|
35
35
|
let index = 0;
|
|
36
36
|
for (const seg of segment(source)) {
|
|
37
|
-
node.append(...eval(header(input(seg, { header: index++ === 0 })) || block(input(seg, context)), []));
|
|
37
|
+
node.append(...eval(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []));
|
|
38
38
|
}
|
|
39
39
|
assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
|
|
40
40
|
if (opts.test) return node;
|
|
@@ -69,7 +69,7 @@ describe('Unit: parser/block/dlist', () => {
|
|
|
69
69
|
it('indexer', () => {
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('~ a [|b]'), ctx), [['<dl><dt id="index::b">a<span class="indexer" data-index="b"></span></dt><dd></dd></dl>'], '']);
|
|
71
71
|
assert.deepStrictEqual(inspect(parser('~ a [|b]\\'), ctx), [['<dl><dt id="index::a_[|b]">a <span class="invalid">[|b]</span></dt><dd></dd></dl>'], '']);
|
|
72
|
-
assert.deepStrictEqual(inspect(parser('~
|
|
72
|
+
assert.deepStrictEqual(inspect(parser('~ - [|b]'), ctx), [['<dl><dt id="index::b">-<span class="indexer" data-index="b"></span></dt><dd></dd></dl>'], '']);
|
|
73
73
|
assert.deepStrictEqual(inspect(parser('~ *A*'), ctx), [['<dl><dt id="index::A"><em>A</em></dt><dd></dd></dl>'], '']);
|
|
74
74
|
assert.deepStrictEqual(inspect(parser('~ `A`'), ctx), [['<dl><dt id="index::`A`"><code data-src="`A`">A</code></dt><dd></dd></dl>'], '']);
|
|
75
75
|
assert.deepStrictEqual(inspect(parser('~ ${A}$'), ctx), [['<dl><dt id="index::${A}$"><span class="math" translate="no" data-src="${A}$">${A}$</span></dt><dd></dd></dl>'], '']);
|
|
@@ -79,6 +79,7 @@ describe('Unit: parser/block/heading', () => {
|
|
|
79
79
|
assert.deepStrictEqual(inspect(parser('# a [|b] [|c]'), ctx), [['<h1 id="index::c">a <span class="invalid">[|b]</span><span class="indexer" data-index="c"></span></h1>'], '']);
|
|
80
80
|
assert.deepStrictEqual(inspect(parser('# a [|b] \n'), ctx), [['<h1 id="index::b">a<span class="indexer" data-index="b"></span></h1>'], '']);
|
|
81
81
|
assert.deepStrictEqual(inspect(parser('# a \\[|b]'), ctx), [['<h1 id="index::a_[|b]">a [|b]</h1>'], '']);
|
|
82
|
+
assert.deepStrictEqual(inspect(parser('# - [|b]'), ctx), [['<h1 id="index::b">-<span class="indexer" data-index="b"></span></h1>'], '']);
|
|
82
83
|
assert.deepStrictEqual(inspect(parser('## a [|b] [|c]'), ctx), [['<h2 id="index::c">a <span class="invalid">[|b]</span><span class="indexer" data-index="c"></span></h2>'], '']);
|
|
83
84
|
});
|
|
84
85
|
|
|
@@ -138,6 +138,7 @@ describe('Unit: parser/block/olist', () => {
|
|
|
138
138
|
assert.deepStrictEqual(inspect(parser('1. [|a]'), ctx), [['<ol><li id="index::[|a]"><span class="invalid">[|a]</span></li></ol>'], '']);
|
|
139
139
|
assert.deepStrictEqual(inspect(parser('1. a [|]'), ctx), [['<ol><li>a<span class="indexer" data-index=""></span></li></ol>'], '']);
|
|
140
140
|
assert.deepStrictEqual(inspect(parser('1. a [|b]'), ctx), [['<ol><li id="index::b">a<span class="indexer" data-index="b"></span></li></ol>'], '']);
|
|
141
|
+
assert.deepStrictEqual(inspect(parser('1. - [|]'), ctx), [['<ol><li>-<span class="indexer" data-index=""></span></li></ol>'], '']);
|
|
141
142
|
assert.deepStrictEqual(inspect(parser('1. [ ] [|a]'), ctx), [['<ol class="checklist"><li id="index::[|a]"><span class="checkbox">☐</span><span class="invalid">[|a]</span></li></ol>'], '']);
|
|
142
143
|
assert.deepStrictEqual(inspect(parser('1. [ ] a [|b]'), ctx), [['<ol class="checklist"><li id="index::b"><span class="checkbox">☐</span>a<span class="indexer" data-index="b"></span></li></ol>'], '']);
|
|
143
144
|
assert.deepStrictEqual(inspect(parser('1. a [|]\n 1. c [|d]'), ctx), [['<ol><li>a<span class="indexer" data-index=""></span><ol><li id="index::d">c<span class="indexer" data-index="d"></span></li></ol></li></ol>'], '']);
|
|
@@ -75,6 +75,7 @@ describe('Unit: parser/block/ulist', () => {
|
|
|
75
75
|
assert.deepStrictEqual(inspect(parser('- [|a]'), ctx), [['<ul><li id="index::[|a]"><span class="invalid">[|a]</span></li></ul>'], '']);
|
|
76
76
|
assert.deepStrictEqual(inspect(parser('- a [|]'), ctx), [['<ul><li>a<span class="indexer" data-index=""></span></li></ul>'], '']);
|
|
77
77
|
assert.deepStrictEqual(inspect(parser('- a [|b]'), ctx), [['<ul><li id="index::b">a<span class="indexer" data-index="b"></span></li></ul>'], '']);
|
|
78
|
+
assert.deepStrictEqual(inspect(parser('- - [|b]'), ctx), [['<ul><li id="index::b">-<span class="indexer" data-index="b"></span></li></ul>'], '']);
|
|
78
79
|
assert.deepStrictEqual(inspect(parser('- [ ] [|a]'), ctx), [['<ul class="checklist"><li id="index::[|a]"><span class="checkbox">☐</span><span class="invalid">[|a]</span></li></ul>'], '']);
|
|
79
80
|
assert.deepStrictEqual(inspect(parser('- [ ] a [|b]'), ctx), [['<ul class="checklist"><li id="index::b"><span class="checkbox">☐</span>a<span class="indexer" data-index="b"></span></li></ul>'], '']);
|
|
80
81
|
assert.deepStrictEqual(inspect(parser('- a [|]\n - c [|d]'), ctx), [['<ul><li>a<span class="indexer" data-index=""></span><ul><li id="index::d">c<span class="indexer" data-index="d"></span></li></ul></li></ul>'], '']);
|
|
@@ -40,6 +40,7 @@ describe('Unit: parser/inline/emphasis', () => {
|
|
|
40
40
|
|
|
41
41
|
it('nest', () => {
|
|
42
42
|
assert.deepStrictEqual(inspect(parser('*a *b**'), ctx), [['<em>a <em>b</em></em>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser('*- *b**'), ctx), [['<em>- <em>b</em></em>'], '']);
|
|
43
44
|
assert.deepStrictEqual(inspect(parser('*a **b***'), ctx), [['<em>a <strong>b</strong></em>'], '']);
|
|
44
45
|
assert.deepStrictEqual(inspect(parser('*a\\ *b**'), ctx), [['<em>a <em>b</em></em>'], '']);
|
|
45
46
|
assert.deepStrictEqual(inspect(parser('*a	*b**'), ctx), [['<em>a\t<em>b</em></em>'], '']);
|
|
@@ -67,7 +67,7 @@ describe('Unit: parser/inline/html', () => {
|
|
|
67
67
|
assert.deepStrictEqual(inspect(parser('<wbr >'), ctx), [['<wbr>'], '']);
|
|
68
68
|
assert.deepStrictEqual(inspect(parser('<wbr>a'), ctx), [['<wbr>'], 'a']);
|
|
69
69
|
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<bdi>a</bdi>'], '']);
|
|
70
|
-
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<bdi
|
|
70
|
+
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<span class="invalid"><bdi >a</bdi></span>'], '']);
|
|
71
71
|
assert.deepStrictEqual(inspect(parser('<bdi> a</bdi>'), ctx), [['<bdi> a</bdi>'], '']);
|
|
72
72
|
assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>'), ctx), [['<bdi> a </bdi>'], '']);
|
|
73
73
|
assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>'), ctx), [['<bdi> a </bdi>'], '']);
|
|
@@ -122,8 +122,8 @@ describe('Unit: parser/inline/html', () => {
|
|
|
122
122
|
assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>'), ctx), [['<span class="invalid"><bdo diR="rtl">a</bdo></span>'], '']);
|
|
123
123
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
124
124
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
|
|
125
|
-
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<bdo dir="rtl"
|
|
126
|
-
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<bdo
|
|
125
|
+
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<span class="invalid"><bdo dir="rtl" >a</bdo></span>'], '']);
|
|
126
|
+
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<span class="invalid"><bdo dir="rtl">a</bdo></span>'], '']);
|
|
127
127
|
assert.deepStrictEqual(inspect(parser('<wbr\n>'), ctx), undefined);
|
|
128
128
|
assert.deepStrictEqual(inspect(parser('<wbr \n>'), ctx), [['<span class="invalid"><wbr </span>'], '\n>']);
|
|
129
129
|
assert.deepStrictEqual(inspect(parser('<wbr constructor>'), ctx), [['<span class="invalid"><wbr constructor></span>'], '']);
|
|
@@ -20,13 +20,13 @@ const attrspecs = {
|
|
|
20
20
|
Object.setPrototypeOf(attrspecs, null);
|
|
21
21
|
Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
|
|
22
22
|
|
|
23
|
-
export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[
|
|
23
|
+
export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
|
|
24
24
|
union([
|
|
25
25
|
surround(
|
|
26
26
|
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
27
|
-
str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[
|
|
27
|
+
str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/yi),
|
|
28
28
|
some(union([attribute])),
|
|
29
|
-
open(str(/
|
|
29
|
+
open(str(/ ?/y), str('>'), true),
|
|
30
30
|
true,
|
|
31
31
|
([as, bs = [], cs], context) =>
|
|
32
32
|
[[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
|
|
@@ -38,14 +38,14 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
|
|
|
38
38
|
([, tag]) =>
|
|
39
39
|
surround<HTMLParser.TagParser, string>(
|
|
40
40
|
surround(
|
|
41
|
-
str(`<${tag}`), some(attribute), open(str(/
|
|
41
|
+
str(`<${tag}`), some(attribute), open(str(/ ?/y), str('>'), true),
|
|
42
42
|
true,
|
|
43
43
|
([as, bs = [], cs]) => [push(unshift(as, bs), cs)],
|
|
44
44
|
([as, bs = []]) => [unshift(as, bs)]),
|
|
45
45
|
// 不可視のHTML構造が可視構造を変化させるべきでない。
|
|
46
46
|
// 可視のHTMLは優先度変更を検討する。
|
|
47
47
|
// このため<>は将来的に共通構造を変化させる可能性があり
|
|
48
|
-
//
|
|
48
|
+
// 共通構造を変化させない非構造文字列としては依然としてエスケープを要する。
|
|
49
49
|
precedence(0, recursion(Recursion.inline,
|
|
50
50
|
some(union([
|
|
51
51
|
some(inline, blankWith('\n', `</${tag}>`)),
|
|
@@ -61,9 +61,9 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
|
|
|
61
61
|
new Map())),
|
|
62
62
|
surround(
|
|
63
63
|
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
64
|
-
str(/<[a-z]+(?=[
|
|
64
|
+
str(/<[a-z]+(?=[ >])/yi),
|
|
65
65
|
some(union([attribute])),
|
|
66
|
-
open(str(/
|
|
66
|
+
open(str(/ ?/y), str('>'), true),
|
|
67
67
|
true,
|
|
68
68
|
([as, bs = [], cs], context) =>
|
|
69
69
|
[[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
|
|
@@ -72,8 +72,8 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
|
|
|
72
72
|
])));
|
|
73
73
|
|
|
74
74
|
export const attribute: HTMLParser.AttributeParser = union([
|
|
75
|
-
str(/[
|
|
76
|
-
str(/[^\
|
|
75
|
+
str(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ >])/yi),
|
|
76
|
+
str(/ [^\s<>]+/y),
|
|
77
77
|
]);
|
|
78
78
|
|
|
79
79
|
function elem(tag: string, content: boolean, as: string[], bs: (HTMLElement | string)[], cs: string[], context: Ctx): HTMLElement {
|
|
@@ -58,6 +58,7 @@ describe('Unit: parser/inline/italic', () => {
|
|
|
58
58
|
assert.deepStrictEqual(inspect(parser('//////a//////'), ctx), [['<i><i>a</i></i>'], '']);
|
|
59
59
|
assert.deepStrictEqual(inspect(parser('//////a///b///'), ctx), [['<i><i>a</i>b</i>'], '']);
|
|
60
60
|
assert.deepStrictEqual(inspect(parser('///a ///b//////'), ctx), [['<i>a <i>b</i></i>'], '']);
|
|
61
|
+
assert.deepStrictEqual(inspect(parser('///- ///b//////'), ctx), [['<i>- <i>b</i></i>'], '']);
|
|
61
62
|
assert.deepStrictEqual(inspect(parser('///a\\ ///b//////'), ctx), [['<i>a <i>b</i></i>'], '']);
|
|
62
63
|
assert.deepStrictEqual(inspect(parser('///a //////b/////////'), ctx), [['<i>a <i><i>b</i></i></i>'], '']);
|
|
63
64
|
assert.deepStrictEqual(inspect(parser('///a ///b///c///'), ctx), [['<i>a <i>b</i>c</i>'], '']);
|
|
@@ -63,8 +63,8 @@ describe('Unit: parser/inline/link', () => {
|
|
|
63
63
|
assert.deepStrictEqual(inspect(parser('[]'), ctx), undefined);
|
|
64
64
|
assert.deepStrictEqual(inspect(parser('[]{}'), ctx), undefined);
|
|
65
65
|
assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), [['<span class="invalid">[]{ }</span>'], '']);
|
|
66
|
-
assert.deepStrictEqual(inspect(parser('[]{ }'), ctx),
|
|
67
|
-
assert.deepStrictEqual(inspect(parser('[]{ }'), ctx),
|
|
66
|
+
assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), undefined);
|
|
67
|
+
assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), undefined);
|
|
68
68
|
assert.deepStrictEqual(inspect(parser('[]{{}'), ctx), undefined);
|
|
69
69
|
assert.deepStrictEqual(inspect(parser('[]{}}'), ctx), undefined);
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('[]{{}}'), ctx), undefined);
|
|
@@ -105,12 +105,11 @@ describe('Unit: parser/inline/link', () => {
|
|
|
105
105
|
it('basic', () => {
|
|
106
106
|
assert.deepStrictEqual(inspect(parser('[]{b}'), ctx), [['<a class="url" href="b">b</a>'], '']);
|
|
107
107
|
assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
|
|
108
|
-
assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<
|
|
108
|
+
assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<span class="invalid">[]{b</span>'], ' }']);
|
|
109
109
|
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
|
|
110
|
-
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<
|
|
111
|
-
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx),
|
|
112
|
-
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx),
|
|
113
|
-
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
|
|
110
|
+
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<span class="invalid">[]{ b</span>'], ' }']);
|
|
111
|
+
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), undefined);
|
|
112
|
+
assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), undefined);
|
|
114
113
|
assert.deepStrictEqual(inspect(parser('[]{"}'), ctx), [[`<a class="url" href=""">"</a>`], '']);
|
|
115
114
|
assert.deepStrictEqual(inspect(parser('[]{"}"}'), ctx), [[`<a class="url" href=""">"</a>`], '"}']);
|
|
116
115
|
assert.deepStrictEqual(inspect(parser('[]{\\}'), ctx), [[`<a class="url" href="\\">\\</a>`], '']);
|
|
@@ -209,7 +208,6 @@ describe('Unit: parser/inline/link', () => {
|
|
|
209
208
|
assert.deepStrictEqual(inspect(parser('[]{/ aspect-ratio="4/3"}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
|
|
210
209
|
assert.deepStrictEqual(inspect(parser('[]{/ 4:3}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
|
|
211
210
|
assert.deepStrictEqual(inspect(parser('[]{/ =}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
|
|
212
|
-
assert.deepStrictEqual(inspect(parser('[]{/ name}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
|
|
213
211
|
assert.deepStrictEqual(inspect(parser('[]{/\nname}'), ctx), [['<span class="invalid">[]{/</span>'], '\nname}']);
|
|
214
212
|
});
|
|
215
213
|
|
|
@@ -221,8 +219,12 @@ describe('Unit: parser/inline/link', () => {
|
|
|
221
219
|
assert.deepStrictEqual(inspect(parser('[]{/ nofollow nofollow}'), ctx), [['<a class="invalid" href="/" rel="nofollow">/</a>'], '']);
|
|
222
220
|
assert.deepStrictEqual(inspect(parser('[]{/ nofollow}'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
|
|
223
221
|
assert.deepStrictEqual(inspect(parser('[]{/ nofollow }'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
|
|
222
|
+
assert.deepStrictEqual(inspect(parser('[]{/ nofollow }'), ctx), [['<span class="invalid">[]{/ nofollow</span>'], ' }']);
|
|
223
|
+
assert.deepStrictEqual(inspect(parser('[]{/ nofollow}'), ctx), [['<span class="invalid">[]{/</span>'], ' nofollow}']);
|
|
224
224
|
assert.deepStrictEqual(inspect(parser('[]{ / nofollow}'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
|
|
225
225
|
assert.deepStrictEqual(inspect(parser('[]{ / nofollow }'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
|
|
226
|
+
assert.deepStrictEqual(inspect(parser('[]{ / nofollow }'), ctx), [['<span class="invalid">[]{ / nofollow</span>'], ' }']);
|
|
227
|
+
assert.deepStrictEqual(inspect(parser('[]{ / nofollow}'), ctx), [['<span class="invalid">[]{ /</span>'], ' nofollow}']);
|
|
226
228
|
assert.deepStrictEqual(inspect(parser('[]{http://host nofollow}'), ctx), [['<a class="url" href="http://host" target="_blank" rel="nofollow">http://host</a>'], '']);
|
|
227
229
|
assert.deepStrictEqual(inspect(parser('[!http://host]{http://host nofollow}'), ctx), [['<a class="link" href="http://host" target="_blank" rel="nofollow"><img class="media" data-src="http://host" alt="http://host"></a>'], '']);
|
|
228
230
|
});
|
|
@@ -35,7 +35,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
|
|
|
35
35
|
dup(surround(
|
|
36
36
|
/{(?![{}])/y,
|
|
37
37
|
inits([uri, some(option)]),
|
|
38
|
-
/
|
|
38
|
+
/ ?}/y,
|
|
39
39
|
false,
|
|
40
40
|
undefined,
|
|
41
41
|
([as, bs], context) => {
|
|
@@ -82,7 +82,7 @@ export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State
|
|
|
82
82
|
'[',
|
|
83
83
|
union([media, shortmedia]),
|
|
84
84
|
']')),
|
|
85
|
-
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), /
|
|
85
|
+
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
|
|
86
86
|
])),
|
|
87
87
|
([params, content = []]: [string[], (HTMLElement | string)[]], context) =>
|
|
88
88
|
[[parse(defrag(content), params, context)]]))))));
|
|
@@ -94,20 +94,20 @@ export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
|
|
|
94
94
|
'[',
|
|
95
95
|
some(union([unescsource]), ']'),
|
|
96
96
|
']')),
|
|
97
|
-
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), /
|
|
97
|
+
dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
|
|
98
98
|
])),
|
|
99
99
|
([params, content = []], context) =>
|
|
100
100
|
[[parse(defrag(content), params, context)]])));
|
|
101
101
|
|
|
102
102
|
export const uri: LinkParser.ParameterParser.UriParser = union([
|
|
103
|
-
open(/
|
|
103
|
+
open(/ /y, str(/\S+/y)),
|
|
104
104
|
str(/[^\s{}]+/y),
|
|
105
105
|
]);
|
|
106
106
|
|
|
107
107
|
export const option: LinkParser.ParameterParser.OptionParser = union([
|
|
108
|
-
fmap(str(/
|
|
109
|
-
str(/[
|
|
110
|
-
str(/[^\
|
|
108
|
+
fmap(str(/ nofollow(?=[ }])/y), () => [` rel="nofollow"`]),
|
|
109
|
+
str(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ }])/yi),
|
|
110
|
+
str(/ [^\s{}]+/y),
|
|
111
111
|
]);
|
|
112
112
|
|
|
113
113
|
function parse(
|
|
@@ -155,11 +155,11 @@ function elem(
|
|
|
155
155
|
case 'https:':
|
|
156
156
|
assert(uri.host);
|
|
157
157
|
switch (true) {
|
|
158
|
-
case /[
|
|
158
|
+
case /[0-9a-z]:\S/i.test(stringify(content)):
|
|
159
159
|
type = 'content';
|
|
160
160
|
message = 'URI must not be contained';
|
|
161
161
|
break;
|
|
162
|
-
case INSECURE_URI.
|
|
162
|
+
case INSECURE_URI.startsWith('^/')
|
|
163
163
|
&& /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
|
|
164
164
|
type = 'argument';
|
|
165
165
|
message = 'Dot-segments cannot be used in subresource paths';
|
|
@@ -223,7 +223,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
|
|
|
223
223
|
assert(uri);
|
|
224
224
|
assert(uri === uri.trim());
|
|
225
225
|
switch (true) {
|
|
226
|
-
case uri.
|
|
226
|
+
case uri.startsWith('^/'):
|
|
227
227
|
const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
|
|
228
228
|
return last.includes('.') // isFile
|
|
229
229
|
// Exclude ISO 6709.
|
|
@@ -232,7 +232,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
|
|
|
232
232
|
: `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
|
|
233
233
|
case host.origin === source.origin
|
|
234
234
|
&& host.pathname === source.pathname:
|
|
235
|
-
case uri.
|
|
235
|
+
case uri.startsWith('//'):
|
|
236
236
|
return uri;
|
|
237
237
|
default:
|
|
238
238
|
const target = new ReadonlyURL(uri, source.href);
|
|
@@ -243,13 +243,13 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
export function decode(uri: string): string {
|
|
246
|
-
const
|
|
246
|
+
const head = /^[a-z]+(?:[.+-][0-9a-z]+)*:\/*[^/?#\s]+/i;
|
|
247
|
+
const origin = uri.match(head)?.[0] ?? '';
|
|
247
248
|
try {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
uri = origin + path;
|
|
249
|
+
const path = decodeURI(uri.slice(origin.length));
|
|
250
|
+
uri = !origin && head.test(path)
|
|
251
|
+
? uri.slice(origin.length)
|
|
252
|
+
: origin + path;
|
|
253
253
|
}
|
|
254
254
|
finally {
|
|
255
255
|
return uri.replace(/\s+/g, encodeURI);
|
|
@@ -39,6 +39,7 @@ describe('Unit: parser/inline/mark', () => {
|
|
|
39
39
|
|
|
40
40
|
it('nest', () => {
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('==a ==b===='), ctx), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('==- ==b===='), ctx), [['<mark id="mark::-_b">- <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::-_b"></a>'], '']);
|
|
42
43
|
assert.deepStrictEqual(inspect(parser('==a\\ ==b===='), ctx), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
|
|
43
44
|
assert.deepStrictEqual(inspect(parser('==a	==b===='), ctx), [['<mark id="mark::a_b=33Mw2l">a\t<mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b=33Mw2l"></a>'], '']);
|
|
44
45
|
assert.deepStrictEqual(inspect(parser('==a<wbr>==b===='), ctx), [['<mark id="mark::ab">a<wbr><mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::ab"></a>'], '']);
|
|
@@ -27,8 +27,8 @@ describe('Unit: parser/inline/media', () => {
|
|
|
27
27
|
assert.deepStrictEqual(inspect(parser('![]'), ctx), undefined);
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('![]{}'), ctx), undefined);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), [['<span class="invalid">![]{ }</span>'], '']);
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('![]{ }'), ctx),
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('![]{ }'), ctx),
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), undefined);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), undefined);
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('![]]{/}'), ctx), undefined);
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('![]{{}'), ctx), undefined);
|
|
34
34
|
assert.deepStrictEqual(inspect(parser('![]{}}'), ctx), undefined);
|
|
@@ -66,12 +66,11 @@ describe('Unit: parser/inline/media', () => {
|
|
|
66
66
|
it('basic', () => {
|
|
67
67
|
assert.deepStrictEqual(inspect(parser('![]{b}'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
|
|
68
68
|
assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
|
|
69
|
-
assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<
|
|
69
|
+
assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<span class="invalid">![]{b</span>'], ' }']);
|
|
70
70
|
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
|
|
71
|
-
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<
|
|
72
|
-
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx),
|
|
73
|
-
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx),
|
|
74
|
-
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
|
|
71
|
+
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<span class="invalid">![]{ b</span>'], ' }']);
|
|
72
|
+
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), undefined);
|
|
73
|
+
assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), undefined);
|
|
75
74
|
assert.deepStrictEqual(inspect(parser('![]{"}'), ctx), [['<a href=""" target="_blank"><img class="media" data-src=""" alt="""></a>'], '']);
|
|
76
75
|
assert.deepStrictEqual(inspect(parser('![]{"}"}'), ctx), [['<a href=""" target="_blank"><img class="media" data-src=""" alt="""></a>'], '"}']);
|
|
77
76
|
assert.deepStrictEqual(inspect(parser('![]{\\}'), ctx), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt="\\"></a>'], '']);
|
|
@@ -40,7 +40,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
|
|
|
40
40
|
dup(surround(
|
|
41
41
|
/{(?![{}])/y,
|
|
42
42
|
inits([uri, some(option)]),
|
|
43
|
-
/
|
|
43
|
+
/ ?}/y,
|
|
44
44
|
false,
|
|
45
45
|
undefined,
|
|
46
46
|
([as, bs], context) => {
|
|
@@ -120,9 +120,9 @@ const bracket: MediaParser.TextParser.BracketParser = lazy(() => recursion(Recur
|
|
|
120
120
|
|
|
121
121
|
const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
|
|
122
122
|
surround(
|
|
123
|
-
open(/
|
|
123
|
+
open(/ /y, str(/[1-9][0-9]*/y)),
|
|
124
124
|
str(/[x:]/y),
|
|
125
|
-
str(/[1-9][0-9]*(?=[
|
|
125
|
+
str(/[1-9][0-9]*(?=[ }])/y),
|
|
126
126
|
false,
|
|
127
127
|
([[a], [b], [c]]) => [
|
|
128
128
|
b === 'x'
|
|
@@ -31,7 +31,7 @@ describe('Unit: parser/inline/remark', () => {
|
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('[%\\ a %]'), ctx), undefined);
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('[% a\\ %]'), ctx), [['[%', ' a', ' ', '%', ']'], '']);
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('[% a%]'), ctx), [['[%', ' a', '%', ']'], '']);
|
|
34
|
-
assert.deepStrictEqual(inspect(parser('[% a %%]'), ctx), [['[%', ' a
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('[% a %%]'), ctx), [['[%', ' a %', '%', ']'], '']);
|
|
35
35
|
assert.deepStrictEqual(inspect(parser('[% [%% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% <span class="invalid">[%%</span> %]</span></span>'], '']);
|
|
36
36
|
assert.deepStrictEqual(inspect(parser('[%% [% %%]'), ctx), [['<span class="invalid">[%%</span>'], ' [% %%]']);
|
|
37
37
|
assert.deepStrictEqual(inspect(parser('[%% a %]'), ctx), [['<span class="invalid">[%%</span>'], ' a %]']);
|
|
@@ -43,7 +43,9 @@ describe('Unit: parser/inline/remark', () => {
|
|
|
43
43
|
assert.deepStrictEqual(inspect(parser('[% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% %]</span></span>'], '']);
|
|
44
44
|
assert.deepStrictEqual(inspect(parser('[% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% %]</span></span>'], '']);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('[% a %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], '']);
|
|
46
|
+
assert.deepStrictEqual(inspect(parser('[% - %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% - %]</span></span>'], '']);
|
|
46
47
|
assert.deepStrictEqual(inspect(parser('[% a %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], '']);
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('[% - %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% - %]</span></span>'], '']);
|
|
47
49
|
assert.deepStrictEqual(inspect(parser('[% a b %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a b %]</span></span>'], '']);
|
|
48
50
|
assert.deepStrictEqual(inspect(parser('[% a\nb %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a<br>b %]</span></span>'], '']);
|
|
49
51
|
assert.deepStrictEqual(inspect(parser('[% a %] %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], ' %]']);
|
|
@@ -39,6 +39,7 @@ describe('Unit: parser/inline/strong', () => {
|
|
|
39
39
|
|
|
40
40
|
it('nest', () => {
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('**a *b***'), ctx), [['<strong>a <em>b</em></strong>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('**- *b***'), ctx), [['<strong>- <em>b</em></strong>'], '']);
|
|
42
43
|
assert.deepStrictEqual(inspect(parser('**a **b****'), ctx), [['<strong>a <strong>b</strong></strong>'], '']);
|
|
43
44
|
assert.deepStrictEqual(inspect(parser('**a	**b****'), ctx), [['<strong>a\t<strong>b</strong></strong>'], '']);
|
|
44
45
|
assert.deepStrictEqual(inspect(parser('**a<wbr>**b****'), ctx), [['<strong>a<wbr><strong>b</strong></strong>'], '']);
|
|
@@ -15,6 +15,7 @@ describe('Unit: parser/source/escsource', () => {
|
|
|
15
15
|
it('basic', () => {
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
|
|
18
19
|
assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
|
|
19
20
|
});
|
|
20
21
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { EscapableSourceParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
3
|
import { consume } from '../../combinator';
|
|
4
|
-
import {
|
|
4
|
+
import { next } from './text';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
|
-
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s
|
|
7
|
+
const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
|
|
8
8
|
|
|
9
9
|
export const escsource: EscapableSourceParser = ({ context }) => {
|
|
10
10
|
const { source, position } = context;
|
|
@@ -38,15 +38,7 @@ export const escsource: EscapableSourceParser = ({ context }) => {
|
|
|
38
38
|
default:
|
|
39
39
|
assert(char !== '\n');
|
|
40
40
|
if (context.sequential) return [[char]];
|
|
41
|
-
|
|
42
|
-
const b = isBlank(source, position);
|
|
43
|
-
let i = b
|
|
44
|
-
? source[position + 1] === '\n'
|
|
45
|
-
? position + 1
|
|
46
|
-
: nonWhitespace.test(source)
|
|
47
|
-
? nonWhitespace.lastIndex - 1
|
|
48
|
-
: source.length
|
|
49
|
-
: next(source, position, delimiter);
|
|
41
|
+
let i = next(source, position, delimiter);
|
|
50
42
|
assert(i > position);
|
|
51
43
|
i -= position;
|
|
52
44
|
consume(i - 1, context);
|
|
@@ -15,6 +15,7 @@ describe('Unit: parser/source/text', () => {
|
|
|
15
15
|
it('basic', () => {
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
|
|
18
19
|
assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
|
|
19
20
|
assert.deepStrictEqual(inspect(parser('a\nb'), ctx), [['a', '<br>', 'b'], '']);
|
|
20
21
|
});
|
|
@@ -39,8 +40,8 @@ describe('Unit: parser/source/text', () => {
|
|
|
39
40
|
assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
|
|
40
41
|
assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
|
|
41
42
|
assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', '<br>'], '']);
|
|
44
|
+
assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', ' ', '<br>'], '']);
|
|
44
45
|
assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' a'], '']);
|
|
45
46
|
assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
|
|
46
47
|
assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
|
|
@@ -48,8 +49,8 @@ describe('Unit: parser/source/text', () => {
|
|
|
48
49
|
assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
|
|
49
50
|
assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
|
|
50
51
|
assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
|
|
51
|
-
assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
|
|
52
|
-
assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
|
|
53
|
+
assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
|
|
53
54
|
assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a b'], '']);
|
|
54
55
|
assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
|
|
55
56
|
assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
|
|
@@ -4,7 +4,7 @@ import { union, consume, focus } from '../../combinator';
|
|
|
4
4
|
import { html } from 'typed-dom/dom';
|
|
5
5
|
|
|
6
6
|
//const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
|
|
7
|
-
export const nonWhitespace = /[\
|
|
7
|
+
export const nonWhitespace = /[^ \t ]/g;
|
|
8
8
|
|
|
9
9
|
export const text: TextParser = input => {
|
|
10
10
|
const { context } = input;
|
|
@@ -38,29 +38,24 @@ export const text: TextParser = input => {
|
|
|
38
38
|
assert(char !== '\n');
|
|
39
39
|
if (context.sequential) return [[char]];
|
|
40
40
|
nonWhitespace.lastIndex = position + 1;
|
|
41
|
-
const
|
|
42
|
-
let i =
|
|
43
|
-
? source
|
|
44
|
-
?
|
|
45
|
-
:
|
|
46
|
-
? nonWhitespace.lastIndex - 1
|
|
47
|
-
: source.length
|
|
41
|
+
const s = canSkip(source, position);
|
|
42
|
+
let i = s
|
|
43
|
+
? nonWhitespace.test(source)
|
|
44
|
+
? nonWhitespace.lastIndex - 1
|
|
45
|
+
: source.length
|
|
48
46
|
: next(source, position);
|
|
49
47
|
assert(i > position);
|
|
50
48
|
const lineend = 0
|
|
51
|
-
||
|
|
52
|
-
||
|
|
53
|
-
|| b && source[i] === '\\' && source[i + 1] === '\n';
|
|
49
|
+
|| s && i === source.length
|
|
50
|
+
|| s && source[i] === '\n';
|
|
54
51
|
i -= position;
|
|
55
|
-
i = lineend ? i : i - +
|
|
52
|
+
i = lineend ? i : i - +s || 1;
|
|
56
53
|
consume(i - 1, context);
|
|
57
54
|
context.position += i - 1;
|
|
58
55
|
const linestart = position === 0 || source[position - 1] === '\n';
|
|
59
|
-
|
|
60
|
-
i += position;
|
|
61
|
-
return i === context.position || b && !linestart || lineend
|
|
56
|
+
return position === context.position || s && !linestart || lineend
|
|
62
57
|
? [[]]
|
|
63
|
-
: [[source.slice(
|
|
58
|
+
: [[source.slice(position, context.position)]];
|
|
64
59
|
}
|
|
65
60
|
};
|
|
66
61
|
|
|
@@ -72,6 +67,26 @@ export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
|
|
|
72
67
|
text,
|
|
73
68
|
])) as LinebreakParser;
|
|
74
69
|
|
|
70
|
+
export function canSkip(source: string, position: number): boolean {
|
|
71
|
+
assert(position < source.length);
|
|
72
|
+
if (!isWhitespace(source[position], false)) return false;
|
|
73
|
+
if (position + 1 === source.length) return true;
|
|
74
|
+
return isWhitespace(source[position + 1], true);
|
|
75
|
+
}
|
|
76
|
+
function isWhitespace(char: string, linebreak: boolean): boolean {
|
|
77
|
+
switch (char) {
|
|
78
|
+
case ' ':
|
|
79
|
+
case '\t':
|
|
80
|
+
case ' ':
|
|
81
|
+
return true;
|
|
82
|
+
case '\r':
|
|
83
|
+
case '\n':
|
|
84
|
+
return linebreak;
|
|
85
|
+
default:
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
export function next(source: string, position: number, delimiter?: RegExp): number {
|
|
76
91
|
let index: number;
|
|
77
92
|
if (delimiter) {
|
|
@@ -86,8 +101,24 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
|
|
|
86
101
|
assert(index > position);
|
|
87
102
|
const char = source[index];
|
|
88
103
|
switch (char) {
|
|
104
|
+
case '$':
|
|
105
|
+
case '%':
|
|
106
|
+
case '*':
|
|
107
|
+
case '+':
|
|
108
|
+
case '~':
|
|
109
|
+
case '=':
|
|
110
|
+
case '/':
|
|
111
|
+
index = backToWhitespace(source, position, index);
|
|
112
|
+
break;
|
|
113
|
+
case '[':
|
|
114
|
+
index = source[index + 1] === '|'
|
|
115
|
+
? backToWhitespace(source, position, index)
|
|
116
|
+
: index;
|
|
117
|
+
break;
|
|
89
118
|
case ':':
|
|
90
|
-
index =
|
|
119
|
+
index = source.startsWith('//', index + 1)
|
|
120
|
+
? backToUrlHead(source, position, index)
|
|
121
|
+
: index;
|
|
91
122
|
break;
|
|
92
123
|
case '@':
|
|
93
124
|
index = backToEmailHead(source, position, index);
|
|
@@ -96,6 +127,12 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
|
|
|
96
127
|
assert(index > position);
|
|
97
128
|
return index;
|
|
98
129
|
}
|
|
130
|
+
export function backToWhitespace(source: string, position: number, index: number): number {
|
|
131
|
+
const prev = index - 1;
|
|
132
|
+
return prev > position && /\s/.test(source[prev])
|
|
133
|
+
? prev
|
|
134
|
+
: index;
|
|
135
|
+
}
|
|
99
136
|
export function backToUrlHead(source: string, position: number, index: number): number {
|
|
100
137
|
const delim = index;
|
|
101
138
|
let state = false;
|
|
@@ -151,7 +188,6 @@ export function backToEmailHead(source: string, position: number, index: number)
|
|
|
151
188
|
}
|
|
152
189
|
return index + offset;
|
|
153
190
|
}
|
|
154
|
-
|
|
155
191
|
function isAlphanumeric(char: string): boolean {
|
|
156
192
|
assert(char.length === 1);
|
|
157
193
|
if (char < '0' || '\x7F' < char) return false;
|
|
@@ -195,8 +231,6 @@ function isAlphanumeric(char: string): boolean {
|
|
|
195
231
|
// }
|
|
196
232
|
//};
|
|
197
233
|
|
|
198
|
-
const delimiter = /\s(?:\\?(?:$|\s)|[$%])/y;
|
|
199
|
-
|
|
200
234
|
function seek(source: string, position: number): number {
|
|
201
235
|
for (let i = position + 1; i < source.length; ++i) {
|
|
202
236
|
const fst = source[i];
|
|
@@ -225,7 +259,6 @@ function seek(source: string, position: number): number {
|
|
|
225
259
|
case '{':
|
|
226
260
|
case '}':
|
|
227
261
|
case '*':
|
|
228
|
-
case '%':
|
|
229
262
|
case '|':
|
|
230
263
|
case '\r':
|
|
231
264
|
case '\n':
|
|
@@ -238,68 +271,39 @@ function seek(source: string, position: number): number {
|
|
|
238
271
|
case '/':
|
|
239
272
|
if (source[i + 1] === fst && source[i + 2] === fst) return i;
|
|
240
273
|
continue;
|
|
274
|
+
case '%':
|
|
275
|
+
if (source[i + 1] === ']') return i;
|
|
276
|
+
continue;
|
|
241
277
|
case ':':
|
|
242
278
|
if (source[i + 1] === '/' && source[i + 2] === '/') return i;
|
|
243
279
|
continue;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// }
|
|
268
|
-
// continue;
|
|
280
|
+
case ' ':
|
|
281
|
+
case '\t':
|
|
282
|
+
case ' ':
|
|
283
|
+
if (i + 1 === source.length) return i;
|
|
284
|
+
switch (source[i + 1]) {
|
|
285
|
+
case ' ':
|
|
286
|
+
case '\t':
|
|
287
|
+
case '\r':
|
|
288
|
+
case '\n':
|
|
289
|
+
case ' ':
|
|
290
|
+
return i;
|
|
291
|
+
case '\\':
|
|
292
|
+
if (i + 2 === source.length) return i;
|
|
293
|
+
switch (source[i + 2]) {
|
|
294
|
+
case ' ':
|
|
295
|
+
case '\t':
|
|
296
|
+
case '\r':
|
|
297
|
+
case '\n':
|
|
298
|
+
case ' ':
|
|
299
|
+
return i;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
269
303
|
default:
|
|
270
|
-
delimiter.lastIndex = i;
|
|
271
|
-
if (delimiter.test(source)) return i;
|
|
272
304
|
continue;
|
|
273
305
|
}
|
|
274
306
|
assert(false);
|
|
275
307
|
}
|
|
276
308
|
return source.length;
|
|
277
309
|
}
|
|
278
|
-
|
|
279
|
-
const blank = /\s(?:$|\s|\\\n)/y;
|
|
280
|
-
export function isBlank(source: string, position: number): boolean {
|
|
281
|
-
blank.lastIndex = position;
|
|
282
|
-
return blank.test(source);
|
|
283
|
-
assert(position < source.length);
|
|
284
|
-
if (!isWhitespace(source[position])) return false;
|
|
285
|
-
if (position + 1 === source.length) return true;
|
|
286
|
-
const snd = source[position + 1];
|
|
287
|
-
if (isWhitespace(snd)) return true;
|
|
288
|
-
if (position + 2 === source.length) return false;
|
|
289
|
-
if (snd === '\\' && source[position + 2] === '\n') return true;
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
const whitespace = /\s/;
|
|
293
|
-
export function isWhitespace(char: string): boolean {
|
|
294
|
-
whitespace;
|
|
295
|
-
switch (char) {
|
|
296
|
-
case ' ':
|
|
297
|
-
case '\t':
|
|
298
|
-
case '\r':
|
|
299
|
-
case '\n':
|
|
300
|
-
case ' ':
|
|
301
|
-
return true
|
|
302
|
-
default:
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
@@ -15,6 +15,7 @@ describe('Unit: parser/source/unescapable', () => {
|
|
|
15
15
|
it('basic', () => {
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a', ' b', ' c'], '']);
|
|
18
19
|
assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あいAZaz'], '']);
|
|
19
20
|
});
|
|
20
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UnescapableSourceParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
3
|
import { consume } from '../../combinator';
|
|
4
|
-
import { nonWhitespace,
|
|
4
|
+
import { nonWhitespace, canSkip, next } from './text';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
|
|
@@ -28,13 +28,10 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
|
|
|
28
28
|
assert(char !== '\n');
|
|
29
29
|
if (context.sequential) return [[char]];
|
|
30
30
|
nonWhitespace.lastIndex = position + 1;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: nonWhitespace.test(source)
|
|
36
|
-
? nonWhitespace.lastIndex - 1
|
|
37
|
-
: source.length
|
|
31
|
+
let i = canSkip(source, position)
|
|
32
|
+
? nonWhitespace.test(source)
|
|
33
|
+
? nonWhitespace.lastIndex - 1
|
|
34
|
+
: source.length
|
|
38
35
|
: next(source, position, delimiter);
|
|
39
36
|
assert(i > position);
|
|
40
37
|
i -= position;
|