@yozora/tokenizer-image 2.0.4 → 2.0.5
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/lib/cjs/{index.js → index.cjs} +5 -6
- package/lib/esm/{index.js → index.mjs} +4 -5
- package/lib/types/index.d.ts +4 -4
- package/package.json +19 -15
- package/src/index.ts +10 -0
- package/src/match.ts +211 -0
- package/src/parse.ts +50 -0
- package/src/tokenizer.ts +32 -0
- package/src/types.ts +45 -0
- package/src/util.ts +18 -0
|
@@ -142,19 +142,18 @@ const uniqueName = '@yozora/tokenizer-image';
|
|
|
142
142
|
|
|
143
143
|
class ImageTokenizer extends coreTokenizer.BaseInlineTokenizer {
|
|
144
144
|
constructor(props = {}) {
|
|
145
|
-
var _a, _b;
|
|
146
145
|
super({
|
|
147
|
-
name:
|
|
148
|
-
priority:
|
|
146
|
+
name: props.name ?? uniqueName,
|
|
147
|
+
priority: props.priority ?? coreTokenizer.TokenizerPriority.LINKS,
|
|
149
148
|
});
|
|
150
|
-
this.match = match;
|
|
151
|
-
this.parse = parse;
|
|
152
149
|
}
|
|
150
|
+
match = match;
|
|
151
|
+
parse = parse;
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
exports.ImageTokenizer = ImageTokenizer;
|
|
156
155
|
exports.ImageTokenizerName = uniqueName;
|
|
157
156
|
exports.calcImageAlt = calcImageAlt;
|
|
158
|
-
exports
|
|
157
|
+
exports.default = ImageTokenizer;
|
|
159
158
|
exports.imageMatch = match;
|
|
160
159
|
exports.imageParse = parse;
|
|
@@ -138,14 +138,13 @@ const uniqueName = '@yozora/tokenizer-image';
|
|
|
138
138
|
|
|
139
139
|
class ImageTokenizer extends BaseInlineTokenizer {
|
|
140
140
|
constructor(props = {}) {
|
|
141
|
-
var _a, _b;
|
|
142
141
|
super({
|
|
143
|
-
name:
|
|
144
|
-
priority:
|
|
142
|
+
name: props.name ?? uniqueName,
|
|
143
|
+
priority: props.priority ?? TokenizerPriority.LINKS,
|
|
145
144
|
});
|
|
146
|
-
this.match = match;
|
|
147
|
-
this.parse = parse;
|
|
148
145
|
}
|
|
146
|
+
match = match;
|
|
147
|
+
parse = parse;
|
|
149
148
|
}
|
|
150
149
|
|
|
151
150
|
export { ImageTokenizer, uniqueName as ImageTokenizerName, calcImageAlt, ImageTokenizer as default, match as imageMatch, parse as imageParse };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ import { INodeInterval } from '@yozora/character';
|
|
|
10
10
|
*/
|
|
11
11
|
declare function calcImageAlt(nodes: ReadonlyArray<Node>): string;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
type T = ImageType;
|
|
14
|
+
type INode = Image;
|
|
15
15
|
declare const uniqueName = "@yozora/tokenizer-image";
|
|
16
16
|
/**
|
|
17
17
|
* An image token.
|
|
@@ -40,8 +40,8 @@ interface IDelimiter extends IYastTokenDelimiter {
|
|
|
40
40
|
*/
|
|
41
41
|
titleContent?: INodeInterval;
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
type IThis = ITokenizer;
|
|
44
|
+
type ITokenizerProps = Partial<IBaseInlineTokenizerProps>;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Syntax for images is like the syntax for links, with one difference.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yozora/tokenizer-image",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "guanghechen",
|
|
6
6
|
"url": "https://github.com/guanghechen/"
|
|
@@ -11,34 +11,38 @@
|
|
|
11
11
|
"directory": "tokenizers/image"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/yozorajs/yozora/tree/release-2.x.x/tokenizers/image",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
"types": "./lib/types/index.d.ts",
|
|
17
|
+
"import": "./lib/esm/index.mjs",
|
|
18
|
+
"require": "./lib/cjs/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"source": "./src/index.ts",
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"main": "./lib/cjs/index.cjs",
|
|
23
|
+
"module": "./lib/esm/index.mjs",
|
|
18
24
|
"license": "MIT",
|
|
19
25
|
"engines": {
|
|
20
26
|
"node": ">= 16.0.0"
|
|
21
27
|
},
|
|
22
28
|
"files": [
|
|
23
29
|
"lib/",
|
|
24
|
-
"
|
|
25
|
-
"!lib/**/*.d.ts.map",
|
|
30
|
+
"src/",
|
|
26
31
|
"package.json",
|
|
27
32
|
"CHANGELOG.md",
|
|
28
33
|
"LICENSE",
|
|
29
34
|
"README.md"
|
|
30
35
|
],
|
|
31
36
|
"scripts": {
|
|
32
|
-
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.
|
|
33
|
-
"prebuild": "rimraf lib/",
|
|
37
|
+
"build": "rimraf lib/ && cross-env NODE_ENV=production rollup -c ../../rollup.config.mjs",
|
|
34
38
|
"prepublishOnly": "cross-env ROLLUP_SHOULD_SOURCEMAP=false yarn build",
|
|
35
|
-
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.
|
|
39
|
+
"test": "cross-env TS_NODE_FILES=true NODE_OPTIONS=--experimental-vm-modules jest --config ../../jest.config.mjs --rootDir ."
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
|
-
"@yozora/ast": "^2.0.
|
|
39
|
-
"@yozora/character": "^2.0.
|
|
40
|
-
"@yozora/core-tokenizer": "^2.0.
|
|
41
|
-
"@yozora/tokenizer-link": "^2.0.
|
|
42
|
+
"@yozora/ast": "^2.0.5",
|
|
43
|
+
"@yozora/character": "^2.0.5",
|
|
44
|
+
"@yozora/core-tokenizer": "^2.0.5",
|
|
45
|
+
"@yozora/tokenizer-link": "^2.0.5"
|
|
42
46
|
},
|
|
43
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "7ba3bab49fe65cf2f57082c0503af73da9356cf0"
|
|
44
48
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './util'
|
|
2
|
+
export { match as imageMatch } from './match'
|
|
3
|
+
export { parse as imageParse } from './parse'
|
|
4
|
+
export { ImageTokenizer, ImageTokenizer as default } from './tokenizer'
|
|
5
|
+
export { uniqueName as ImageTokenizerName } from './types'
|
|
6
|
+
export type {
|
|
7
|
+
IThis as IImageHookContext,
|
|
8
|
+
IToken as IImageToken,
|
|
9
|
+
ITokenizerProps as IImageTokenizerProps,
|
|
10
|
+
} from './types'
|
package/src/match.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { ImageType } from '@yozora/ast'
|
|
2
|
+
import type { INodePoint } from '@yozora/character'
|
|
3
|
+
import { AsciiCodePoint } from '@yozora/character'
|
|
4
|
+
import type {
|
|
5
|
+
IMatchInlineHookCreator,
|
|
6
|
+
IResultOfIsDelimiterPair,
|
|
7
|
+
IResultOfProcessDelimiterPair,
|
|
8
|
+
IYastInlineToken,
|
|
9
|
+
} from '@yozora/core-tokenizer'
|
|
10
|
+
import { eatOptionalWhitespaces, genFindDelimiter } from '@yozora/core-tokenizer'
|
|
11
|
+
import {
|
|
12
|
+
checkBalancedBracketsStatus,
|
|
13
|
+
eatLinkDestination,
|
|
14
|
+
eatLinkTitle,
|
|
15
|
+
} from '@yozora/tokenizer-link'
|
|
16
|
+
import type { IDelimiter, IThis, IToken, T } from './types'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Syntax for images is like the syntax for links, with one difference.
|
|
20
|
+
* Instead of link text, we have an image description.
|
|
21
|
+
* The rules for this are the same as for link text, except that
|
|
22
|
+
*
|
|
23
|
+
* a) an image description starts with ''
|
|
38
|
+
* - '](url "title")'
|
|
39
|
+
* - '](<url>)'
|
|
40
|
+
* - '](<url> "title")'
|
|
41
|
+
*
|
|
42
|
+
* @see https://github.com/syntax-tree/mdast#image
|
|
43
|
+
* @see https://github.github.com/gfm/#images
|
|
44
|
+
*/
|
|
45
|
+
export const match: IMatchInlineHookCreator<T, IDelimiter, IToken, IThis> = function (api) {
|
|
46
|
+
return {
|
|
47
|
+
findDelimiter: () => genFindDelimiter<IDelimiter>(_findDelimiter),
|
|
48
|
+
isDelimiterPair,
|
|
49
|
+
processDelimiterPair,
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Images can contains Images, so implement an algorithm similar to bracket
|
|
53
|
+
* matching, pushing all opener delimiters onto the stack
|
|
54
|
+
*
|
|
55
|
+
* The rules for this are the same as for link text, except that
|
|
56
|
+
* (a) an image description starts with '![' rather than '[', and
|
|
57
|
+
* (b) an image description may contain links. An image description has
|
|
58
|
+
* inline elements as its contents. When an image is rendered to HTML,
|
|
59
|
+
* this is standardly used as the image’s alt attribute
|
|
60
|
+
*
|
|
61
|
+
* @see https://github.github.com/gfm/#inline-link
|
|
62
|
+
* @see https://github.github.com/gfm/#example-582
|
|
63
|
+
*
|
|
64
|
+
* @override
|
|
65
|
+
* @see IMatchInlineHook
|
|
66
|
+
*/
|
|
67
|
+
function _findDelimiter(startIndex: number, endIndex: number): IDelimiter | null {
|
|
68
|
+
const nodePoints: ReadonlyArray<INodePoint> = api.getNodePoints()
|
|
69
|
+
const blockEndIndex = api.getBlockEndIndex()
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* FIXME:
|
|
73
|
+
*
|
|
74
|
+
* This is a hack method to fix the situation where a higher priority token
|
|
75
|
+
* is embedded in the delimiter, at this time, ignore the tokens that have
|
|
76
|
+
* been parsed, and continue to match the content until the delimiter meets
|
|
77
|
+
* its own definition or reaches the right boundary of the block content.
|
|
78
|
+
*
|
|
79
|
+
* This algorithm has not been strictly logically verified, but I think it
|
|
80
|
+
* can work well in most cases. After all, it has passed many test cases.
|
|
81
|
+
* @see https://github.github.com/gfm/#example-588
|
|
82
|
+
*/
|
|
83
|
+
for (let i = startIndex; i < endIndex; ++i) {
|
|
84
|
+
const c = nodePoints[i].codePoint
|
|
85
|
+
switch (c) {
|
|
86
|
+
case AsciiCodePoint.BACKSLASH:
|
|
87
|
+
i += 1
|
|
88
|
+
break
|
|
89
|
+
case AsciiCodePoint.EXCLAMATION_MARK: {
|
|
90
|
+
if (i + 1 < endIndex && nodePoints[i + 1].codePoint === AsciiCodePoint.OPEN_BRACKET) {
|
|
91
|
+
return {
|
|
92
|
+
type: 'opener',
|
|
93
|
+
startIndex: i,
|
|
94
|
+
endIndex: i + 2,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* An inline link consists of a link text followed immediately by a
|
|
101
|
+
* left parenthesis '(', ..., and a right parenthesis ')'
|
|
102
|
+
* @see https://github.github.com/gfm/#inline-link
|
|
103
|
+
*/
|
|
104
|
+
case AsciiCodePoint.CLOSE_BRACKET: {
|
|
105
|
+
/**
|
|
106
|
+
* An inline link consists of a link text followed immediately by a
|
|
107
|
+
* left parenthesis '('
|
|
108
|
+
* @see https://github.github.com/gfm/#inline-link
|
|
109
|
+
*/
|
|
110
|
+
if (
|
|
111
|
+
i + 1 >= endIndex ||
|
|
112
|
+
nodePoints[i + 1].codePoint !== AsciiCodePoint.OPEN_PARENTHESIS
|
|
113
|
+
) {
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// try to match link destination
|
|
118
|
+
const destinationStartIndex = eatOptionalWhitespaces(nodePoints, i + 2, blockEndIndex)
|
|
119
|
+
const destinationEndIndex = eatLinkDestination(
|
|
120
|
+
nodePoints,
|
|
121
|
+
destinationStartIndex,
|
|
122
|
+
blockEndIndex,
|
|
123
|
+
)
|
|
124
|
+
if (destinationEndIndex < 0) break
|
|
125
|
+
|
|
126
|
+
// try to match link title
|
|
127
|
+
const titleStartIndex = eatOptionalWhitespaces(
|
|
128
|
+
nodePoints,
|
|
129
|
+
destinationEndIndex,
|
|
130
|
+
blockEndIndex,
|
|
131
|
+
)
|
|
132
|
+
const titleEndIndex = eatLinkTitle(nodePoints, titleStartIndex, blockEndIndex)
|
|
133
|
+
if (titleEndIndex < 0) break
|
|
134
|
+
|
|
135
|
+
const _startIndex = i
|
|
136
|
+
const _endIndex = eatOptionalWhitespaces(nodePoints, titleEndIndex, blockEndIndex) + 1
|
|
137
|
+
if (
|
|
138
|
+
_endIndex > blockEndIndex ||
|
|
139
|
+
nodePoints[_endIndex - 1].codePoint !== AsciiCodePoint.CLOSE_PARENTHESIS
|
|
140
|
+
) {
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Both the title and the destination may be omitted
|
|
146
|
+
* @see https://github.github.com/gfm/#example-495
|
|
147
|
+
*/
|
|
148
|
+
return {
|
|
149
|
+
type: 'closer',
|
|
150
|
+
startIndex: _startIndex,
|
|
151
|
+
endIndex: _endIndex,
|
|
152
|
+
destinationContent:
|
|
153
|
+
destinationStartIndex < destinationEndIndex
|
|
154
|
+
? {
|
|
155
|
+
startIndex: destinationStartIndex,
|
|
156
|
+
endIndex: destinationEndIndex,
|
|
157
|
+
}
|
|
158
|
+
: undefined,
|
|
159
|
+
titleContent:
|
|
160
|
+
titleStartIndex < titleEndIndex
|
|
161
|
+
? { startIndex: titleStartIndex, endIndex: titleEndIndex }
|
|
162
|
+
: undefined,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isDelimiterPair(
|
|
171
|
+
openerDelimiter: IDelimiter,
|
|
172
|
+
closerDelimiter: IDelimiter,
|
|
173
|
+
internalTokens: ReadonlyArray<IYastInlineToken>,
|
|
174
|
+
): IResultOfIsDelimiterPair {
|
|
175
|
+
const nodePoints: ReadonlyArray<INodePoint> = api.getNodePoints()
|
|
176
|
+
const balancedBracketsStatus: -1 | 0 | 1 = checkBalancedBracketsStatus(
|
|
177
|
+
openerDelimiter.endIndex,
|
|
178
|
+
closerDelimiter.startIndex,
|
|
179
|
+
internalTokens,
|
|
180
|
+
nodePoints,
|
|
181
|
+
)
|
|
182
|
+
switch (balancedBracketsStatus) {
|
|
183
|
+
case -1:
|
|
184
|
+
return { paired: false, opener: false, closer: true }
|
|
185
|
+
case 0:
|
|
186
|
+
return { paired: true }
|
|
187
|
+
case 1:
|
|
188
|
+
return { paired: false, opener: true, closer: false }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function processDelimiterPair(
|
|
193
|
+
openerDelimiter: IDelimiter,
|
|
194
|
+
closerDelimiter: IDelimiter,
|
|
195
|
+
internalTokens: ReadonlyArray<IYastInlineToken>,
|
|
196
|
+
): IResultOfProcessDelimiterPair<T, IToken, IDelimiter> {
|
|
197
|
+
const token: IToken = {
|
|
198
|
+
nodeType: ImageType,
|
|
199
|
+
startIndex: openerDelimiter.startIndex,
|
|
200
|
+
endIndex: closerDelimiter.endIndex,
|
|
201
|
+
destinationContent: closerDelimiter.destinationContent,
|
|
202
|
+
titleContent: closerDelimiter.titleContent,
|
|
203
|
+
children: api.resolveInternalTokens(
|
|
204
|
+
internalTokens,
|
|
205
|
+
openerDelimiter.endIndex,
|
|
206
|
+
closerDelimiter.startIndex,
|
|
207
|
+
),
|
|
208
|
+
}
|
|
209
|
+
return { tokens: [token] }
|
|
210
|
+
}
|
|
211
|
+
}
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Node } from '@yozora/ast'
|
|
2
|
+
import { ImageType } from '@yozora/ast'
|
|
3
|
+
import type { INodePoint } from '@yozora/character'
|
|
4
|
+
import { AsciiCodePoint, calcEscapedStringFromNodePoints } from '@yozora/character'
|
|
5
|
+
import type { IParseInlineHookCreator } from '@yozora/core-tokenizer'
|
|
6
|
+
import { encodeLinkDestination } from '@yozora/core-tokenizer'
|
|
7
|
+
import type { INode, IThis, IToken, T } from './types'
|
|
8
|
+
import { calcImageAlt } from './util'
|
|
9
|
+
|
|
10
|
+
export const parse: IParseInlineHookCreator<T, IToken, INode, IThis> = function (api) {
|
|
11
|
+
return {
|
|
12
|
+
parse: tokens =>
|
|
13
|
+
tokens.map(token => {
|
|
14
|
+
const nodePoints: ReadonlyArray<INodePoint> = api.getNodePoints()
|
|
15
|
+
|
|
16
|
+
// calc url
|
|
17
|
+
let url = ''
|
|
18
|
+
if (token.destinationContent != null) {
|
|
19
|
+
let { startIndex, endIndex } = token.destinationContent
|
|
20
|
+
if (nodePoints[startIndex].codePoint === AsciiCodePoint.OPEN_ANGLE) {
|
|
21
|
+
startIndex += 1
|
|
22
|
+
endIndex -= 1
|
|
23
|
+
}
|
|
24
|
+
const destination = calcEscapedStringFromNodePoints(
|
|
25
|
+
nodePoints,
|
|
26
|
+
startIndex,
|
|
27
|
+
endIndex,
|
|
28
|
+
true,
|
|
29
|
+
)
|
|
30
|
+
url = encodeLinkDestination(destination)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// calc alt
|
|
34
|
+
const children: Node[] = api.parseInlineTokens(token.children)
|
|
35
|
+
const alt = calcImageAlt(children)
|
|
36
|
+
|
|
37
|
+
// calc title
|
|
38
|
+
let title: string | undefined
|
|
39
|
+
if (token.titleContent != null) {
|
|
40
|
+
const { startIndex, endIndex } = token.titleContent
|
|
41
|
+
title = calcEscapedStringFromNodePoints(nodePoints, startIndex + 1, endIndex - 1)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const node: INode = api.shouldReservePosition
|
|
45
|
+
? { type: ImageType, position: api.calcPosition(token), url, alt, title }
|
|
46
|
+
: { type: ImageType, url, alt, title }
|
|
47
|
+
return node
|
|
48
|
+
}),
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/tokenizer.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IInlineTokenizer,
|
|
3
|
+
IMatchInlineHookCreator,
|
|
4
|
+
IParseInlineHookCreator,
|
|
5
|
+
} from '@yozora/core-tokenizer'
|
|
6
|
+
import { BaseInlineTokenizer, TokenizerPriority } from '@yozora/core-tokenizer'
|
|
7
|
+
import { match } from './match'
|
|
8
|
+
import { parse } from './parse'
|
|
9
|
+
import type { IDelimiter, INode, IThis, IToken, ITokenizerProps, T } from './types'
|
|
10
|
+
import { uniqueName } from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lexical Analyzer for InlineImage.
|
|
14
|
+
* @see https://github.com/syntax-tree/mdast#image
|
|
15
|
+
* @see https://github.github.com/gfm/#images
|
|
16
|
+
*/
|
|
17
|
+
export class ImageTokenizer
|
|
18
|
+
extends BaseInlineTokenizer<T, IDelimiter, IToken, INode, IThis>
|
|
19
|
+
implements IInlineTokenizer<T, IDelimiter, IToken, INode, IThis>
|
|
20
|
+
{
|
|
21
|
+
/* istanbul ignore next */
|
|
22
|
+
constructor(props: ITokenizerProps = {}) {
|
|
23
|
+
super({
|
|
24
|
+
name: props.name ?? uniqueName,
|
|
25
|
+
priority: props.priority ?? TokenizerPriority.LINKS,
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public override readonly match: IMatchInlineHookCreator<T, IDelimiter, IToken, IThis> = match
|
|
30
|
+
|
|
31
|
+
public override readonly parse: IParseInlineHookCreator<T, IToken, INode, IThis> = parse
|
|
32
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Image, ImageType } from '@yozora/ast'
|
|
2
|
+
import type { INodeInterval } from '@yozora/character'
|
|
3
|
+
import type {
|
|
4
|
+
IBaseInlineTokenizerProps,
|
|
5
|
+
IPartialYastInlineToken,
|
|
6
|
+
ITokenizer,
|
|
7
|
+
IYastTokenDelimiter,
|
|
8
|
+
} from '@yozora/core-tokenizer'
|
|
9
|
+
|
|
10
|
+
export type T = ImageType
|
|
11
|
+
export type INode = Image
|
|
12
|
+
export const uniqueName = '@yozora/tokenizer-image'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* An image token.
|
|
16
|
+
*/
|
|
17
|
+
export interface IToken extends IPartialYastInlineToken<T> {
|
|
18
|
+
/**
|
|
19
|
+
* Link destination interval.
|
|
20
|
+
*/
|
|
21
|
+
destinationContent?: INodeInterval
|
|
22
|
+
/**
|
|
23
|
+
* Link title interval.
|
|
24
|
+
*/
|
|
25
|
+
titleContent?: INodeInterval
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface IDelimiter extends IYastTokenDelimiter {
|
|
29
|
+
/**
|
|
30
|
+
* IDelimiter type.
|
|
31
|
+
*/
|
|
32
|
+
type: 'opener' | 'closer'
|
|
33
|
+
/**
|
|
34
|
+
* link destination
|
|
35
|
+
*/
|
|
36
|
+
destinationContent?: INodeInterval
|
|
37
|
+
/**
|
|
38
|
+
* link title
|
|
39
|
+
*/
|
|
40
|
+
titleContent?: INodeInterval
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type IThis = ITokenizer
|
|
44
|
+
|
|
45
|
+
export type ITokenizerProps = Partial<IBaseInlineTokenizerProps>
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Alternative, Literal, Node, Parent } from '@yozora/ast'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* calc alt
|
|
5
|
+
* An image description has inline elements as its contents. When an image
|
|
6
|
+
* is rendered to HTML, this is standardly used as the image’s alt attribute
|
|
7
|
+
* @see https://github.github.com/gfm/#example-582
|
|
8
|
+
*/
|
|
9
|
+
export function calcImageAlt(nodes: ReadonlyArray<Node>): string {
|
|
10
|
+
return (nodes as ReadonlyArray<Node & Alternative & Literal & Parent>)
|
|
11
|
+
.map((o): string => {
|
|
12
|
+
if (o.value != null) return o.value
|
|
13
|
+
if (o.alt != null) return o.alt
|
|
14
|
+
if (o.children != null) return calcImageAlt(o.children)
|
|
15
|
+
return ''
|
|
16
|
+
})
|
|
17
|
+
.join('')
|
|
18
|
+
}
|