@yozora/tokenizer-image 2.1.3 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yozora/tokenizer-image",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "author": {
5
5
  "name": "guanghechen",
6
6
  "url": "https://github.com/guanghechen/"
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "files": [
29
29
  "lib/",
30
- "src/",
30
+ "lib/**/*.map",
31
31
  "package.json",
32
32
  "CHANGELOG.md",
33
33
  "LICENSE",
@@ -39,10 +39,10 @@
39
39
  "test": "cross-env TS_NODE_FILES=true NODE_OPTIONS=--experimental-vm-modules jest --config ../../jest.config.mjs --rootDir ."
40
40
  },
41
41
  "dependencies": {
42
- "@yozora/ast": "^2.1.3",
43
- "@yozora/character": "^2.1.3",
44
- "@yozora/core-tokenizer": "^2.1.3",
45
- "@yozora/tokenizer-link": "^2.1.3"
42
+ "@yozora/ast": "^2.1.4",
43
+ "@yozora/character": "^2.1.4",
44
+ "@yozora/core-tokenizer": "^2.1.4",
45
+ "@yozora/tokenizer-link": "^2.1.4"
46
46
  },
47
- "gitHead": "9abaaff74a12d0bae0f645813241ff947a0d828c"
47
+ "gitHead": "aa464ed1e3cd84892773a833910cfc53a556bf5f"
48
48
  }
package/src/index.ts DELETED
@@ -1,10 +0,0 @@
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 DELETED
@@ -1,211 +0,0 @@
1
- import { ImageType } from '@yozora/ast'
2
- import type { INodePoint } from '@yozora/character'
3
- import { AsciiCodePoint } from '@yozora/character'
4
- import type {
5
- IInlineToken,
6
- IMatchInlineHookCreator,
7
- IResultOfIsDelimiterPair,
8
- IResultOfProcessDelimiterPair,
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 '![' rather than '[', and
24
- * b) an image description may contain links.
25
- *
26
- * An image description has inline elements as its contents. When an image is
27
- * rendered to HTML, this is standardly used as the image’s alt attribute.
28
- *
29
- * ------
30
- *
31
- * A 'opener' type delimiter is one of the following forms:
32
- *
33
- * - '!['
34
- *
35
- * A 'closer' type delimiter is one of the following forms:
36
- *
37
- * - '](url)'
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<IInlineToken>,
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<IInlineToken>,
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 DELETED
@@ -1,50 +0,0 @@
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 DELETED
@@ -1,32 +0,0 @@
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 DELETED
@@ -1,45 +0,0 @@
1
- import type { Image, ImageType } from '@yozora/ast'
2
- import type { INodeInterval } from '@yozora/character'
3
- import type {
4
- IBaseInlineTokenizerProps,
5
- IPartialInlineToken,
6
- ITokenDelimiter,
7
- ITokenizer,
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 IPartialInlineToken<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 ITokenDelimiter {
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 DELETED
@@ -1,18 +0,0 @@
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
- }