securemark 0.229.0 → 0.231.0

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.
@@ -6,6 +6,6 @@ import { html } from 'typed-dom';
6
6
  // https://html.spec.whatwg.org/multipage/input.html
7
7
 
8
8
  export const email: AutolinkParser.EmailParser = creator(rewrite(verify(
9
- str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*@[0-9A-Za-z](?:[0-9A-Za-z-]{0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:[0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*/),
9
+ str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*@[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*(?![0-9A-Za-z])/),
10
10
  ([source]) => source.indexOf('@') <= 64 && source.length <= 255),
11
11
  source => [[html('a', { class: 'email', href: `mailto:${source}` }, source)], '']));
@@ -35,6 +35,7 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
35
35
  assert.deepStrictEqual(inspect(parser('あ#1')), [['あ#1'], '']);
36
36
  assert.deepStrictEqual(inspect(parser(' #1')), undefined);
37
37
  assert.deepStrictEqual(inspect(parser('#12345678901234567')), [['#12345678901234567'], '']);
38
+ assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(128)}a`)), [[`#${'1'.repeat(128)}a`], '']);
38
39
  });
39
40
 
40
41
  it('valid', () => {
@@ -1,11 +1,12 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { union, rewrite, context, open, convert, fmap, lazy } from '../../../combinator';
3
3
  import { link } from '../link';
4
+ import { emoji } from './hashtag';
4
5
  import { str } from '../../source';
5
6
  import { define } from 'typed-dom';
6
7
 
7
8
  export const hashnum: AutolinkParser.HashnumParser = lazy(() => fmap(rewrite(
8
- open('#', str(/^[0-9]{1,16}(?![0-9A-Za-z'_]|[^\x00-\x7F\s])/)),
9
+ open('#', str(new RegExp(String.raw`^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|${emoji}|['_])`, 'u'))),
9
10
  context({ syntax: { inline: {
10
11
  link: true,
11
12
  autolink: false,
@@ -23,9 +23,6 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
23
23
  assert.deepStrictEqual(inspect(parser(`#'`)), [[`#'`], '']);
24
24
  assert.deepStrictEqual(inspect(parser(`#a''`)), [[`#a''`], '']);
25
25
  assert.deepStrictEqual(inspect(parser('#_')), [['#_'], '']);
26
- assert.deepStrictEqual(inspect(parser('#a_')), [['#a_'], '']);
27
- assert.deepStrictEqual(inspect(parser('#a__b')), [['#a__b'], '']);
28
- assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`#a_'b`], '']);
29
26
  assert.deepStrictEqual(inspect(parser('#(a)')), [['#'], '(a)']);
30
27
  assert.deepStrictEqual(inspect(parser('#{}')), [['#'], '{}']);
31
28
  assert.deepStrictEqual(inspect(parser('#{{}')), [['#'], '{{}']);
@@ -38,6 +35,9 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
38
35
  assert.deepStrictEqual(inspect(parser('a##1')), [['a##1'], '']);
39
36
  assert.deepStrictEqual(inspect(parser('a##b')), [['a##b'], '']);
40
37
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
38
+ assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_`)), [[`#${'1'.repeat(127)}_`], '']);
39
+ assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_a`)), [[`#${'1'.repeat(127)}_a`], '']);
40
+ assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_'a`)), [[`#${'1'.repeat(127)}_'a`], '']);
41
41
  assert.deepStrictEqual(inspect(parser(' #a')), undefined);
42
42
  });
43
43
 
@@ -50,21 +50,22 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
50
50
  assert.deepStrictEqual(inspect(parser('#a\\\n')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '\\\n']);
51
51
  assert.deepStrictEqual(inspect(parser('#a)')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], ')']);
52
52
  assert.deepStrictEqual(inspect(parser('#a(b')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '(b']);
53
- assert.deepStrictEqual(inspect(parser('#a(b)')), [['<a href="/hashtags/a(b)" class="hashtag">#a(b)</a>'], '']);
54
- assert.deepStrictEqual(inspect(parser('#a((b))')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '((b))']);
55
- assert.deepStrictEqual(inspect(parser(`#a'`)), [[`<a href="/hashtags/a'" class="hashtag">#a'</a>`], '']);
56
- assert.deepStrictEqual(inspect(parser(`#a(b')`)), [[`<a href="/hashtags/a(b')" class="hashtag">#a(b')</a>`], '']);
57
- assert.deepStrictEqual(inspect(parser(`#a(b'')`)), [[`<a href="/hashtags/a" class="hashtag">#a</a>`], `(b'')`]);
58
- assert.deepStrictEqual(inspect(parser(`#a('b)`)), [['<a href="/hashtags/a" class="hashtag">#a</a>'], `('b)`]);
53
+ assert.deepStrictEqual(inspect(parser('#a(b)')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '(b)']);
54
+ assert.deepStrictEqual(inspect(parser('#a_')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '_']);
59
55
  assert.deepStrictEqual(inspect(parser('#a_b')), [['<a href="/hashtags/a_b" class="hashtag">#a_b</a>'], '']);
60
- assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`<a href="/hashtags/a'_b" class="hashtag">#a'_b</a>`], '']);
61
- assert.deepStrictEqual(inspect(parser('#a(b_c)')), [['<a href="/hashtags/a(b_c)" class="hashtag">#a(b_c)</a>'], '']);
62
- assert.deepStrictEqual(inspect(parser('#a(b__c)')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '(b__c)']);
56
+ assert.deepStrictEqual(inspect(parser(`#a_'b`)), [['<a href="/hashtags/a" class="hashtag">#a</a>'], `_'b`]);
57
+ assert.deepStrictEqual(inspect(parser('#a__b')), [['<a href="/hashtags/a" class="hashtag">#a</a>'], '__b']);
63
58
  assert.deepStrictEqual(inspect(parser('#あ')), [['<a href="/hashtags/あ" class="hashtag">#あ</a>'], '']);
59
+ assert.deepStrictEqual(inspect(parser('#👩')), [['<a href="/hashtags/👩" class="hashtag">#👩</a>'], '']);
64
60
  assert.deepStrictEqual(inspect(parser('#1a')), [['<a href="/hashtags/1a" class="hashtag">#1a</a>'], '']);
65
61
  assert.deepStrictEqual(inspect(parser('#1あ')), [['<a href="/hashtags/1あ" class="hashtag">#1あ</a>'], '']);
62
+ assert.deepStrictEqual(inspect(parser('#1👩')), [['<a href="/hashtags/1👩" class="hashtag">#1👩</a>'], '']);
66
63
  assert.deepStrictEqual(inspect(parser('#domain/a')), [['<a href="https://domain/hashtags/a" target="_blank" class="hashtag">#domain/a</a>'], '']);
67
64
  assert.deepStrictEqual(inspect(parser('#domain.co.jp/a')), [['<a href="https://domain.co.jp/hashtags/a" target="_blank" class="hashtag">#domain.co.jp/a</a>'], '']);
65
+ // Reserved
66
+ assert.deepStrictEqual(inspect(parser(`#a'`)), [[`#a'`], '']);
67
+ assert.deepStrictEqual(inspect(parser(`#a'b`)), [[`#a'b`], '']);
68
+ assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`#a'_b`], '']);
68
69
  });
69
70
 
70
71
  });
@@ -6,15 +6,22 @@ import { define } from 'typed-dom';
6
6
 
7
7
  // https://example/hashtags/a must be a hashtag page or a redirect page going there.
8
8
 
9
+ // https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
10
+ export const emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
11
+
9
12
  export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
10
13
  open(
11
14
  '#',
12
15
  tails([
13
16
  verify(
14
- str(/^[0-9A-Za-z](?:[0-9A-Za-z-]{0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:[0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*\//),
17
+ str(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//),
15
18
  ([source]) => source.length <= 253 + 1),
16
19
  verify(
17
- str(/^(?=(?:[0-9]{1,127}_?)?(?:[A-Za-z]|[^\x00-\x7F\s]))(?:[0-9A-Za-z]|[^\x00-\x7F\s]|'(?!')|_(?=[0-9A-Za-z]|[^\x00-\x7F\s])){1,128}(?:_?\((?=(?:[0-9]{1,127}_?)?(?:[A-Za-z]|[^\x00-\x7F\s]))(?:[0-9A-Za-z]|[^\x00-\x7F\s]|'(?!')|_(?=[0-9A-Za-z]|[^\x00-\x7F\s])){1,125}\))?(?![0-9A-Za-z'_]|[^\x00-\x7F\s])/),
20
+ str(new RegExp(['^',
21
+ String.raw`(?=[0-9]{0,127}_?(?:[^\d\p{C}\p{S}\p{P}\s]|${emoji}))`,
22
+ String.raw`(?:[^\p{C}\p{S}\p{P}\s]|${emoji}|_(?=[^\p{C}\p{S}\p{P}\s]|${emoji})){1,128}`,
23
+ String.raw`(?!_?(?:[^\p{C}\p{S}\p{P}\s]|${emoji})|')`,
24
+ ].join(''), 'u')),
18
25
  ([source]) => source.length <= 128),
19
26
  ])),
20
27
  context({ syntax: { inline: {
@@ -4,33 +4,30 @@ import { url } from './autolink/url';
4
4
  import { email } from './autolink/email';
5
5
  import { channel } from './autolink/channel';
6
6
  import { account } from './autolink/account';
7
- import { hashtag } from './autolink/hashtag';
7
+ import { hashtag, emoji } from './autolink/hashtag';
8
8
  import { hashnum } from './autolink/hashnum';
9
9
  import { anchor } from './autolink/anchor';
10
10
  import { str } from '../source';
11
11
  import { stringify } from '../util';
12
12
 
13
13
  export const autolink: AutolinkParser = fmap(
14
- validate(/^(?:[@#>0-9A-Za-z]|[^\x00-\x7F\s])/,
14
+ validate(/^(?:[@#>0-9A-Za-z]|\S#)/,
15
15
  guard(context => context.syntax?.inline?.autolink ?? true,
16
16
  some(union([
17
17
  url,
18
18
  email,
19
19
  // Escape unmatched email-like strings.
20
20
  str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)+/),
21
- // Escape repeated symbols.
22
- str(/^@+(?![0-9A-Za-z]|[^\x00-\x7F\s])/),
23
- str(/^#+(?![0-9A-Za-z'_]|[^\x00-\x7F\s])/),
24
21
  channel,
25
22
  account,
26
23
  // Escape unmatched account-like strings.
27
- str(/^@[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*/),
24
+ str(/^@+[0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/),
28
25
  // Escape invalid leading characters.
29
- str(/^[0-9A-Za-z]+(?=#)|^[^\x00-\x7F\s]+(?=#)/),
26
+ str(new RegExp(String.raw`^(?:[^\p{C}\p{S}\p{P}\s]|${emoji}|['_])(?=#)`, 'u')),
30
27
  hashtag,
31
28
  hashnum,
32
29
  // Escape unmatched hashtag-like strings.
33
- str(/^#(?:[0-9A-Za-z'_]|[^\x00-\x7F\s])+/),
30
+ str(new RegExp(String.raw`^#+(?:[^\p{C}\p{S}\p{P}\s]|${emoji}|['_])*`, 'u')),
34
31
  anchor,
35
32
  ])))),
36
33
  ns => ns.length === 1 ? ns : [stringify(ns)]);
@@ -83,7 +83,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
83
83
  case uri.slice(0, 2) === '^/':
84
84
  const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
85
85
  return last.includes('.') // isFile
86
- && /^[0-9]*[a-z][0-9a-z]*$/i.test(last.slice(last.lastIndexOf('.') + 1))
86
+ && /^[0-9]*[A-Za-z][0-9A-Za-z]*$/.test(last.slice(last.lastIndexOf('.') + 1))
87
87
  ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}`
88
88
  : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
89
89
  case host.origin === source.origin
@@ -168,11 +168,12 @@ describe('Unit: parser/inline', () => {
168
168
  assert.deepStrictEqual(inspect(parser('#a\nb\n#c\n[#d]')), [['<a href="/hashtags/a" class="hashtag">#a</a>', '<br>', 'b', '<br>', '<a href="/hashtags/c" class="hashtag">#c</a>', '<br>', '<a class="index" href="#index:d">d</a>'], '']);
169
169
  assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
170
170
  assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
171
- assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
171
+ assert.deepStrictEqual(inspect(parser('0a#b')), [['0', 'a#b'], '']);
172
172
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
173
- assert.deepStrictEqual(inspect(parser('あい#b')), [['あい#b'], '']);
173
+ assert.deepStrictEqual(inspect(parser('あい#b')), [['あ', 'い#b'], '']);
174
174
  assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0a', 'あ#b'], '']);
175
- assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あい#b'], '']);
175
+ assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
176
+ assert.deepStrictEqual(inspect(parser('「#あ」')), [['「', '<a href="/hashtags/あ" class="hashtag">#あ</a>', '」'], '']);
176
177
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
177
178
  assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '<span class="linebreak"> </span>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
178
179
  assert.deepStrictEqual(inspect(parser('*a*#b')), [['<em>a</em>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
@@ -186,6 +187,7 @@ describe('Unit: parser/inline', () => {
186
187
  it('hashnum', () => {
187
188
  assert.deepStrictEqual(inspect(parser('#1')), [['<a class="hashnum">#1</a>'], '']);
188
189
  assert.deepStrictEqual(inspect(parser('#12345678901234567@a')), [['#12345678901234567@a'], '']);
190
+ assert.deepStrictEqual(inspect(parser('「#1」')), [['「', '<a class="hashnum">#1</a>', '」'], '']);
189
191
  });
190
192
 
191
193
  });
@@ -4,9 +4,9 @@ import { union, focus, creator } from '../../combinator';
4
4
  import { str } from './str';
5
5
  import { html } from 'typed-dom';
6
6
 
7
- export const separator = /[\s\x00-\x7F]|[、。!?][^\S\n]*(?=\\\n)/;
7
+ export const separator = /[\s\x00-\x7F]|\S#|[、。!?][^\S\n]*(?=\\\n)/;
8
8
  export const nonWhitespace = /[\S\n]|$/;
9
- export const nonAlphanumeric = /[^0-9A-Za-z]|$/;
9
+ export const nonAlphanumeric = /[^0-9A-Za-z]|\S#|$/;
10
10
  const repeat = str(/^(.)\1*/);
11
11
 
12
12
  export const text: TextParser = creator((source, context) => {
@@ -59,7 +59,7 @@ export const text: TextParser = creator((source, context) => {
59
59
  assert(source[0] !== '\n');
60
60
  const b = source[0].trimStart() === '';
61
61
  const i = b || isAlphanumeric(source[0])
62
- ? source.search(b ? nonWhitespace : nonAlphanumeric)
62
+ ? source.search(b ? nonWhitespace : nonAlphanumeric) || 1
63
63
  : 1;
64
64
  assert(i > 0);
65
65
  assert(!['\\', '\n'].includes(source[0]));
@@ -12,7 +12,7 @@ export const unescsource: UnescapableSourceParser = creator(source => {
12
12
  case 0: {
13
13
  const b = source[0] !== '\n' && source[0].trimStart() === '';
14
14
  const i = b || isAlphanumeric(source[0])
15
- ? source.search(b ? nonWhitespace : nonAlphanumeric)
15
+ ? source.search(b ? nonWhitespace : nonAlphanumeric) || 1
16
16
  : 1;
17
17
  assert(i > 0);
18
18
  return [[source.slice(0, i)], source.slice(i)];