@discord/intl 0.9.0 → 0.10.0-canary.1576ad1
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/dist/format.d.ts +1 -1
- package/dist/format.js +18 -17
- package/dist/formatters/ast.js +3 -4
- package/dist/formatters/markdown.js +3 -4
- package/dist/formatters/react.js +5 -7
- package/dist/formatters/string.d.ts +1 -1
- package/dist/formatters/string.js +6 -8
- package/dist/message.js +26 -29
- package/dist/types.d.ts +3 -3
- package/package.json +4 -4
- package/src/format.ts +20 -6
- package/src/formatters/ast.ts +3 -4
- package/src/formatters/markdown.ts +3 -4
- package/src/formatters/react.ts +9 -7
- package/src/formatters/string.ts +6 -8
- package/src/intl-manager.ts +0 -1
- package/src/message.ts +0 -2
- package/src/types.d.ts +3 -3
package/dist/format.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { Formatters } from 'intl-messageformat';
|
|
|
21
21
|
import type { FormatConfig } from './format-config';
|
|
22
22
|
import type { RichTextTagNames } from './types';
|
|
23
23
|
export declare abstract class FormatBuilder<Result> {
|
|
24
|
-
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[]): void;
|
|
24
|
+
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[], control: Result[]): void;
|
|
25
25
|
abstract pushLiteralText(text: string): void;
|
|
26
26
|
abstract pushObject(value: object): void;
|
|
27
27
|
abstract finish(): Result[];
|
package/dist/format.js
CHANGED
|
@@ -46,7 +46,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
46
46
|
builder.pushLiteralText(node);
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
|
-
const nodeType = node[
|
|
49
|
+
const nodeType = node[0 /* AstNodeIndices.Type */];
|
|
50
50
|
switch (nodeType) {
|
|
51
51
|
case intl_ast_1.FormatJsNodeType.Pound:
|
|
52
52
|
// Replace `#` in plural rules with the actual numeric value. Only
|
|
@@ -58,7 +58,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
58
58
|
}
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
61
|
-
const variableName = node[
|
|
61
|
+
const variableName = node[1 /* AstNodeIndices.Value */];
|
|
62
62
|
// Enforce that all required values are provided by the caller, even if the
|
|
63
63
|
// actual value is falsy/undefined.
|
|
64
64
|
if (!(variableName in values) && !isRichTextTag(variableName)) {
|
|
@@ -67,10 +67,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
67
67
|
const value = values[variableName];
|
|
68
68
|
switch (nodeType) {
|
|
69
69
|
case intl_ast_1.FormatJsNodeType.Argument:
|
|
70
|
-
|
|
71
|
-
if (variableName == '$_')
|
|
72
|
-
break;
|
|
73
|
-
if (typeof value === 'object') {
|
|
70
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
74
71
|
builder.pushObject(value);
|
|
75
72
|
}
|
|
76
73
|
else {
|
|
@@ -80,7 +77,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
80
77
|
}
|
|
81
78
|
break;
|
|
82
79
|
case intl_ast_1.FormatJsNodeType.Date: {
|
|
83
|
-
const nodeStyle = node[
|
|
80
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
84
81
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
85
82
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
86
83
|
const style = nodeStyle in formatConfig.date
|
|
@@ -93,7 +90,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
93
90
|
break;
|
|
94
91
|
}
|
|
95
92
|
case intl_ast_1.FormatJsNodeType.Time: {
|
|
96
|
-
const nodeStyle = node[
|
|
93
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
97
94
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
98
95
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
99
96
|
const style = nodeStyle in formatConfig.time
|
|
@@ -107,7 +104,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
107
104
|
break;
|
|
108
105
|
}
|
|
109
106
|
case intl_ast_1.FormatJsNodeType.Number: {
|
|
110
|
-
const nodeStyle = node[
|
|
107
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
111
108
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
112
109
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
113
110
|
const style = nodeStyle in formatConfig.number
|
|
@@ -122,10 +119,14 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
122
119
|
break;
|
|
123
120
|
}
|
|
124
121
|
case intl_ast_1.FormatJsNodeType.Tag: {
|
|
125
|
-
const children = node[
|
|
122
|
+
const children = node[2 /* AstNodeIndices.Children */];
|
|
123
|
+
const control = node[3 /* AstNodeIndices.Control */];
|
|
126
124
|
const appliedChildren = bindFormatValues(builder.constructor, children, locales, formatters, formatConfig, values, currentPluralValue);
|
|
125
|
+
const appliedControl = control != null
|
|
126
|
+
? bindFormatValues(builder.constructor, control, locales, formatters, formatConfig, values, currentPluralValue)
|
|
127
|
+
: [];
|
|
127
128
|
if (isRichTextTag(variableName)) {
|
|
128
|
-
builder.pushRichTextTag(variableName, appliedChildren);
|
|
129
|
+
builder.pushRichTextTag(variableName, appliedChildren, appliedControl);
|
|
129
130
|
}
|
|
130
131
|
else {
|
|
131
132
|
if (typeof value !== 'function') {
|
|
@@ -145,8 +146,8 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
145
146
|
break;
|
|
146
147
|
}
|
|
147
148
|
case intl_ast_1.FormatJsNodeType.Select: {
|
|
148
|
-
const value = node[
|
|
149
|
-
const options = node[
|
|
149
|
+
const value = node[1 /* AstNodeIndices.Value */];
|
|
150
|
+
const options = node[2 /* AstNodeIndices.Options */];
|
|
150
151
|
const option = value in options ? options[value] : options.other;
|
|
151
152
|
if (option == null) {
|
|
152
153
|
throw `${value} is not a known option for select value ${variableName}. Valid options are ${Object.keys(options).join(', ')}`;
|
|
@@ -155,10 +156,10 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
155
156
|
break;
|
|
156
157
|
}
|
|
157
158
|
case intl_ast_1.FormatJsNodeType.Plural: {
|
|
158
|
-
const value = node[
|
|
159
|
-
const options = node[
|
|
160
|
-
const offset = node[
|
|
161
|
-
const pluralType = node[
|
|
159
|
+
const value = node[1 /* AstNodeIndices.Value */];
|
|
160
|
+
const options = node[2 /* AstNodeIndices.Options */];
|
|
161
|
+
const offset = node[3 /* AstNodeIndices.Offset */];
|
|
162
|
+
const pluralType = node[4 /* AstNodeIndices.PluralType */];
|
|
162
163
|
const option = (() => {
|
|
163
164
|
var _a;
|
|
164
165
|
const exactSelector = `=${value}`;
|
package/dist/formatters/ast.js
CHANGED
|
@@ -27,12 +27,11 @@ var RichTextNodeType;
|
|
|
27
27
|
RichTextNodeType["Object"] = "object";
|
|
28
28
|
})(RichTextNodeType || (exports.RichTextNodeType = RichTextNodeType = {}));
|
|
29
29
|
const AST_RICH_TEXT_ELEMENTS = {
|
|
30
|
-
$_: () => ({ type: RichTextNodeType.Text, content: '' }),
|
|
31
30
|
$b: (content) => ({ type: RichTextNodeType.Strong, content }),
|
|
32
31
|
$i: (content) => ({ type: RichTextNodeType.Emphasis, content }),
|
|
33
32
|
$del: (content) => ({ type: RichTextNodeType.Strikethrough, content }),
|
|
34
33
|
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
35
|
-
$link: ([target
|
|
34
|
+
$link: (content, _, [target]) => ({
|
|
36
35
|
type: RichTextNodeType.Link,
|
|
37
36
|
target: target.content,
|
|
38
37
|
content,
|
|
@@ -44,11 +43,11 @@ class AstBuilder extends format_1.FormatBuilder {
|
|
|
44
43
|
super(...arguments);
|
|
45
44
|
this.result = [];
|
|
46
45
|
}
|
|
47
|
-
pushRichTextTag(tag, children) {
|
|
46
|
+
pushRichTextTag(tag, children, control) {
|
|
48
47
|
if (!(tag in AST_RICH_TEXT_ELEMENTS)) {
|
|
49
48
|
throw `${tag} is not a known rich text formatting tag`;
|
|
50
49
|
}
|
|
51
|
-
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
50
|
+
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
52
51
|
if (Array.isArray(result)) {
|
|
53
52
|
this.result.push(...result);
|
|
54
53
|
}
|
|
@@ -4,12 +4,11 @@ exports.markdownFormatter = void 0;
|
|
|
4
4
|
exports.formatToMarkdownString = formatToMarkdownString;
|
|
5
5
|
const string_1 = require("./string");
|
|
6
6
|
const MARKDOWN_RICH_TEXT_ELEMENTS = {
|
|
7
|
-
$_: () => '',
|
|
8
7
|
$b: (content) => '**' + content.join('') + '**',
|
|
9
8
|
$i: (content) => '*' + content.join('') + '*',
|
|
10
9
|
$del: (content) => '~~' + content.join('') + '~~',
|
|
11
10
|
$code: (content) => '`' + content.join('') + '`',
|
|
12
|
-
$link: ([target
|
|
11
|
+
$link: (content, _, [target]) => '[' + content.join('') + '](' + target + ')',
|
|
13
12
|
$p: (content) => content.join('') + '\n\n',
|
|
14
13
|
};
|
|
15
14
|
class MarkdownBuilder extends string_1.StringBuilder {
|
|
@@ -17,8 +16,8 @@ class MarkdownBuilder extends string_1.StringBuilder {
|
|
|
17
16
|
super(...arguments);
|
|
18
17
|
this.result = '';
|
|
19
18
|
}
|
|
20
|
-
pushRichTextTag(tag, children) {
|
|
21
|
-
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
19
|
+
pushRichTextTag(tag, children, control) {
|
|
20
|
+
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
function formatToMarkdownString(message, values, Builder = MarkdownBuilder) {
|
package/dist/formatters/react.js
CHANGED
|
@@ -7,13 +7,11 @@ const React = require("react");
|
|
|
7
7
|
const format_1 = require("../format");
|
|
8
8
|
const h = React.createElement;
|
|
9
9
|
exports.DEFAULT_REACT_RICH_TEXT_ELEMENTS = {
|
|
10
|
-
$_: () => '',
|
|
11
10
|
$b: (content, key) => h('strong', { key }, content),
|
|
12
11
|
$i: (content, key) => h('em', { key }, content),
|
|
13
12
|
$del: (content, key) => h('del', { key }, content),
|
|
14
13
|
$code: (content, key) => h('code', { key }, content),
|
|
15
|
-
|
|
16
|
-
$link: ([href, ...content], key) => h('a', { href, key }, content),
|
|
14
|
+
$link: (content, key, [href]) => h('a', { href, key }, content),
|
|
17
15
|
$p: (content, key) => h('p', { key }, content),
|
|
18
16
|
};
|
|
19
17
|
/**
|
|
@@ -30,8 +28,8 @@ function createReactBuilder(richTextElements) {
|
|
|
30
28
|
this._nodeKey = 0;
|
|
31
29
|
this.result = [];
|
|
32
30
|
}
|
|
33
|
-
pushRichTextTag(tag, children) {
|
|
34
|
-
this.result.push(richTextElements[tag](children, `${this._nodeKey++}
|
|
31
|
+
pushRichTextTag(tag, children, control) {
|
|
32
|
+
this.result.push(richTextElements[tag](children, `${this._nodeKey++}`, control));
|
|
35
33
|
}
|
|
36
34
|
pushLiteralText(text) {
|
|
37
35
|
const last = this.result[this.result.length - 1];
|
|
@@ -43,8 +41,8 @@ function createReactBuilder(richTextElements) {
|
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
pushObject(value) {
|
|
46
|
-
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
47
|
-
// an object, it'll be acting as some form of ReactNode.
|
|
44
|
+
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
45
|
+
// returns an object, it'll be acting as some form of ReactNode.
|
|
48
46
|
this.result.push(value);
|
|
49
47
|
}
|
|
50
48
|
finish() {
|
|
@@ -12,7 +12,7 @@ import { IntlManager } from '../intl-manager';
|
|
|
12
12
|
export type StringFunctionTypes = FunctionTypes<string>;
|
|
13
13
|
export declare class StringBuilder extends FormatBuilder<string> {
|
|
14
14
|
result: string;
|
|
15
|
-
pushRichTextTag(
|
|
15
|
+
pushRichTextTag(_tag: RichTextTagNames, children: string[], _control: string[]): void;
|
|
16
16
|
pushLiteralText(text: string): void;
|
|
17
17
|
pushObject(value: object): void;
|
|
18
18
|
finish(): string[];
|
|
@@ -8,16 +8,14 @@ class StringBuilder extends format_1.FormatBuilder {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.result = '';
|
|
10
10
|
}
|
|
11
|
-
pushRichTextTag(
|
|
11
|
+
pushRichTextTag(_tag, children, _control) {
|
|
12
12
|
// Plain string formatting ignores rich text tags and just takes the
|
|
13
|
-
// visible content from the children. This means the
|
|
14
|
-
//
|
|
15
|
-
// together
|
|
16
|
-
|
|
17
|
-
this.result +=
|
|
18
|
-
return;
|
|
13
|
+
// visible content from the children. This means the control element is not
|
|
14
|
+
// important for string rendering, so the result is always just the
|
|
15
|
+
// children joined together.
|
|
16
|
+
for (const child of children) {
|
|
17
|
+
this.result += child;
|
|
19
18
|
}
|
|
20
|
-
this.result += children.join('');
|
|
21
19
|
}
|
|
22
20
|
pushLiteralText(text) {
|
|
23
21
|
this.result += text;
|
package/dist/message.js
CHANGED
|
@@ -26,41 +26,38 @@ function serializeAst(ast, result) {
|
|
|
26
26
|
result.value += node[0];
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
switch (node[
|
|
29
|
+
switch (node[0 /* AstNodeIndices.Type */]) {
|
|
30
30
|
case intl_ast_1.FormatJsNodeType.Argument:
|
|
31
|
-
|
|
32
|
-
if (result.value === '$_')
|
|
33
|
-
return;
|
|
34
|
-
result.value += '{' + node[intl_ast_1.AstNodeIndices.Value] + '}';
|
|
31
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + '}';
|
|
35
32
|
return;
|
|
36
33
|
case intl_ast_1.FormatJsNodeType.Date:
|
|
37
|
-
result.value += '{' + node[
|
|
38
|
-
if (node[
|
|
39
|
-
result.value += ', ' + node[
|
|
34
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', date';
|
|
35
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
36
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
40
37
|
}
|
|
41
38
|
result.value += '}';
|
|
42
39
|
return;
|
|
43
40
|
case intl_ast_1.FormatJsNodeType.Time:
|
|
44
|
-
result.value += '{' + node[
|
|
45
|
-
if (node[
|
|
46
|
-
result.value += ', ' + node[
|
|
41
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', time';
|
|
42
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
43
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
47
44
|
}
|
|
48
45
|
result.value += '}';
|
|
49
46
|
return;
|
|
50
47
|
case intl_ast_1.FormatJsNodeType.Number:
|
|
51
|
-
result.value += '{' + node[
|
|
52
|
-
if (node[
|
|
53
|
-
result.value += ', ' + node[
|
|
48
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', number';
|
|
49
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
50
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
54
51
|
}
|
|
55
52
|
result.value += '}';
|
|
56
53
|
return;
|
|
57
54
|
case intl_ast_1.FormatJsNodeType.Plural: {
|
|
58
|
-
const pluralType = node[
|
|
59
|
-
result.value += '{' + node[
|
|
60
|
-
if (node[
|
|
61
|
-
result.value += 'offset:' + node[
|
|
55
|
+
const pluralType = node[4 /* AstNodeIndices.PluralType */] == 'ordinal' ? 'selectordinal' : 'plural';
|
|
56
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', ' + pluralType + ', ';
|
|
57
|
+
if (node[3 /* AstNodeIndices.Offset */]) {
|
|
58
|
+
result.value += 'offset:' + node[3 /* AstNodeIndices.Offset */];
|
|
62
59
|
}
|
|
63
|
-
for (const [name, arm] of Object.entries(node[
|
|
60
|
+
for (const [name, arm] of Object.entries(node[2 /* AstNodeIndices.Options */])) {
|
|
64
61
|
result.value += ' ' + name + ' {';
|
|
65
62
|
serializeAst(arm, result);
|
|
66
63
|
result.value += '}';
|
|
@@ -71,8 +68,8 @@ function serializeAst(ast, result) {
|
|
|
71
68
|
result.value += '#';
|
|
72
69
|
return;
|
|
73
70
|
case intl_ast_1.FormatJsNodeType.Select: {
|
|
74
|
-
result.value += '{' + node[
|
|
75
|
-
for (const [name, arm] of Object.entries(node[
|
|
71
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', select, ';
|
|
72
|
+
for (const [name, arm] of Object.entries(node[2 /* AstNodeIndices.Options */])) {
|
|
76
73
|
result.value += ' ' + name + ' {';
|
|
77
74
|
serializeAst(arm, result);
|
|
78
75
|
result.value += '}';
|
|
@@ -86,30 +83,30 @@ function serializeAst(ast, result) {
|
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
function serializeAstTag(node, result) {
|
|
89
|
-
switch (node[
|
|
86
|
+
switch (node[1 /* AstNodeIndices.Value */]) {
|
|
90
87
|
case '$b':
|
|
91
88
|
result.value += '**';
|
|
92
|
-
serializeAst(node[
|
|
89
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
93
90
|
result.value += '**';
|
|
94
91
|
return;
|
|
95
92
|
case '$i':
|
|
96
93
|
result.value += '*';
|
|
97
|
-
serializeAst(node[
|
|
94
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
98
95
|
result.value += '*';
|
|
99
96
|
return;
|
|
100
97
|
case '$code':
|
|
101
98
|
result.value += '`';
|
|
102
|
-
serializeAst(node[
|
|
99
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
103
100
|
result.value += '`';
|
|
104
101
|
return;
|
|
105
102
|
case '$p':
|
|
106
|
-
serializeAst(node[
|
|
103
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
107
104
|
result.value += '\n\n';
|
|
108
105
|
return;
|
|
109
106
|
case '$link':
|
|
110
107
|
// The target is the first child of the link. We don't have to care if it's a placeholder
|
|
111
108
|
// or not, because the serialization will automatically remove the extra empty.
|
|
112
|
-
const [target, ...children] = node[
|
|
109
|
+
const [target, ...children] = node[2 /* AstNodeIndices.Children */];
|
|
113
110
|
result.value += '[';
|
|
114
111
|
serializeAst(children, result);
|
|
115
112
|
result.value += '](';
|
|
@@ -121,8 +118,8 @@ function serializeAstTag(node, result) {
|
|
|
121
118
|
default:
|
|
122
119
|
// Any other tag name is a hook, which just adds the `$[` on a link tag.
|
|
123
120
|
result.value += '$[';
|
|
124
|
-
serializeAst(node[
|
|
125
|
-
result.value += '](' + node[
|
|
121
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
122
|
+
result.value += '](' + node[1 /* AstNodeIndices.Value */] + ')';
|
|
126
123
|
return;
|
|
127
124
|
}
|
|
128
125
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -193,8 +193,9 @@ export interface FunctionTypeMap {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
export interface FunctionTypes<Result, HandlerType = () => void> {
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
// TODO: Figure out a nicer way to type `control` here.
|
|
197
|
+
link: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
|
+
hook: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
199
|
handler: undefined | HandlerType;
|
|
199
200
|
}
|
|
200
201
|
|
|
@@ -265,7 +266,6 @@ export type RequiredFormatValues<
|
|
|
265
266
|
>;
|
|
266
267
|
|
|
267
268
|
export interface RichTextFormattingMap<T = any> {
|
|
268
|
-
$_: T;
|
|
269
269
|
$b: T;
|
|
270
270
|
$i: T;
|
|
271
271
|
$p: T;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/intl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-canary.1576ad1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Client runtime for managing messages and translations in a React project.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@formatjs/intl": "^2.10.1",
|
|
20
20
|
"@intrnl/xxhash64": "^0.1.2",
|
|
21
21
|
"intl-messageformat": "^10.5.11",
|
|
22
|
-
"@discord/intl-ast": "0.
|
|
22
|
+
"@discord/intl-ast": "0.10.0-canary.1576ad1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@swc/cli": "^0.3.12",
|
package/src/format.ts
CHANGED
|
@@ -37,7 +37,7 @@ function isRichTextTag(name: string) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export abstract class FormatBuilder<Result> {
|
|
40
|
-
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[]): void;
|
|
40
|
+
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[], control: Result[]): void;
|
|
41
41
|
abstract pushLiteralText(text: string): void;
|
|
42
42
|
abstract pushObject(value: object): void;
|
|
43
43
|
abstract finish(): Result[];
|
|
@@ -89,10 +89,7 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
89
89
|
const value = values[variableName];
|
|
90
90
|
switch (nodeType) {
|
|
91
91
|
case FormatJsNodeType.Argument:
|
|
92
|
-
|
|
93
|
-
if (variableName == '$_') break;
|
|
94
|
-
|
|
95
|
-
if (typeof value === 'object') {
|
|
92
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
96
93
|
builder.pushObject(value);
|
|
97
94
|
} else {
|
|
98
95
|
// Taken from FormatJS: non-objects (strings, numbers, and falsy
|
|
@@ -149,6 +146,7 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
149
146
|
|
|
150
147
|
case FormatJsNodeType.Tag: {
|
|
151
148
|
const children = node[AstNodeIndices.Children];
|
|
149
|
+
const control = node[AstNodeIndices.Control];
|
|
152
150
|
const appliedChildren = bindFormatValues(
|
|
153
151
|
builder.constructor as FormatBuilderConstructor<T>,
|
|
154
152
|
children,
|
|
@@ -158,8 +156,24 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
158
156
|
values,
|
|
159
157
|
currentPluralValue,
|
|
160
158
|
);
|
|
159
|
+
const appliedControl =
|
|
160
|
+
control != null
|
|
161
|
+
? bindFormatValues(
|
|
162
|
+
builder.constructor as FormatBuilderConstructor<T>,
|
|
163
|
+
control,
|
|
164
|
+
locales,
|
|
165
|
+
formatters,
|
|
166
|
+
formatConfig,
|
|
167
|
+
values,
|
|
168
|
+
currentPluralValue,
|
|
169
|
+
)
|
|
170
|
+
: [];
|
|
161
171
|
if (isRichTextTag(variableName)) {
|
|
162
|
-
builder.pushRichTextTag(
|
|
172
|
+
builder.pushRichTextTag(
|
|
173
|
+
variableName as RichTextTagNames,
|
|
174
|
+
appliedChildren,
|
|
175
|
+
appliedControl,
|
|
176
|
+
);
|
|
163
177
|
} else {
|
|
164
178
|
if (typeof value !== 'function') {
|
|
165
179
|
throw `expected a function type for a Tag formatting value, ${variableName}. got ${typeof value}: ${value}`;
|
package/src/formatters/ast.ts
CHANGED
|
@@ -78,12 +78,11 @@ export type RichTextNode =
|
|
|
78
78
|
| RichTextObjectNode;
|
|
79
79
|
|
|
80
80
|
const AST_RICH_TEXT_ELEMENTS: RichTextFormattingMap<AstFunctionTypes['hook']> = {
|
|
81
|
-
$_: () => ({ type: RichTextNodeType.Text, content: '' }),
|
|
82
81
|
$b: (content) => ({ type: RichTextNodeType.Strong, content }),
|
|
83
82
|
$i: (content) => ({ type: RichTextNodeType.Emphasis, content }),
|
|
84
83
|
$del: (content) => ({ type: RichTextNodeType.Strikethrough, content }),
|
|
85
84
|
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
86
|
-
$link: ([target
|
|
85
|
+
$link: (content, _, [target]) => ({
|
|
87
86
|
type: RichTextNodeType.Link,
|
|
88
87
|
target: target.content as string,
|
|
89
88
|
content,
|
|
@@ -94,11 +93,11 @@ const AST_RICH_TEXT_ELEMENTS: RichTextFormattingMap<AstFunctionTypes['hook']> =
|
|
|
94
93
|
class AstBuilder extends FormatBuilder<RichTextNode> {
|
|
95
94
|
result: RichTextNode[] = [];
|
|
96
95
|
|
|
97
|
-
pushRichTextTag(tag: RichTextTagNames, children: RichTextNode[]) {
|
|
96
|
+
pushRichTextTag(tag: RichTextTagNames, children: RichTextNode[], control: RichTextNode[]) {
|
|
98
97
|
if (!(tag in AST_RICH_TEXT_ELEMENTS)) {
|
|
99
98
|
throw `${tag} is not a known rich text formatting tag`;
|
|
100
99
|
}
|
|
101
|
-
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
100
|
+
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
102
101
|
if (Array.isArray(result)) {
|
|
103
102
|
this.result.push(...result);
|
|
104
103
|
} else {
|
|
@@ -19,20 +19,19 @@ import { StringBuilder } from './string';
|
|
|
19
19
|
export type MarkdownFunctionTypes = FunctionTypes<string>;
|
|
20
20
|
|
|
21
21
|
const MARKDOWN_RICH_TEXT_ELEMENTS: RichTextFormattingMap<MarkdownFunctionTypes['hook']> = {
|
|
22
|
-
$_: () => '',
|
|
23
22
|
$b: (content) => '**' + content.join('') + '**',
|
|
24
23
|
$i: (content) => '*' + content.join('') + '*',
|
|
25
24
|
$del: (content) => '~~' + content.join('') + '~~',
|
|
26
25
|
$code: (content) => '`' + content.join('') + '`',
|
|
27
|
-
$link: ([target
|
|
26
|
+
$link: (content, _, [target]) => '[' + content.join('') + '](' + target + ')',
|
|
28
27
|
$p: (content) => content.join('') + '\n\n',
|
|
29
28
|
};
|
|
30
29
|
|
|
31
30
|
class MarkdownBuilder extends StringBuilder {
|
|
32
31
|
result: string = '';
|
|
33
32
|
|
|
34
|
-
pushRichTextTag(tag: RichTextTagNames, children: string[]) {
|
|
35
|
-
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
33
|
+
pushRichTextTag(tag: RichTextTagNames, children: string[], control: string[]) {
|
|
34
|
+
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
|
package/src/formatters/react.ts
CHANGED
|
@@ -27,13 +27,11 @@ export type ReactFunctionTypes = FunctionTypes<
|
|
|
27
27
|
|
|
28
28
|
const h = React.createElement;
|
|
29
29
|
export const DEFAULT_REACT_RICH_TEXT_ELEMENTS: RichTextFormattingMap<ReactFunctionTypes['hook']> = {
|
|
30
|
-
$_: () => '',
|
|
31
30
|
$b: (content, key) => h('strong', { key }, content),
|
|
32
31
|
$i: (content, key) => h('em', { key }, content),
|
|
33
32
|
$del: (content, key) => h('del', { key }, content),
|
|
34
33
|
$code: (content, key) => h('code', { key }, content),
|
|
35
|
-
|
|
36
|
-
$link: ([href, ...content], key) => h('a', { href, key }, content),
|
|
34
|
+
$link: (content, key, [href]) => h('a', { href, key }, content),
|
|
37
35
|
$p: (content, key) => h('p', { key }, content),
|
|
38
36
|
};
|
|
39
37
|
|
|
@@ -51,8 +49,12 @@ function createReactBuilder(richTextElements: RichTextFormattingMap<ReactFunctio
|
|
|
51
49
|
_nodeKey: number = 0;
|
|
52
50
|
result: React.ReactNode[] = [];
|
|
53
51
|
|
|
54
|
-
pushRichTextTag(
|
|
55
|
-
|
|
52
|
+
pushRichTextTag(
|
|
53
|
+
tag: RichTextTagNames,
|
|
54
|
+
children: React.ReactNode[],
|
|
55
|
+
control: React.ReactNode[],
|
|
56
|
+
) {
|
|
57
|
+
this.result.push(richTextElements[tag](children, `${this._nodeKey++}`, control));
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
pushLiteralText(text: string) {
|
|
@@ -65,8 +67,8 @@ function createReactBuilder(richTextElements: RichTextFormattingMap<ReactFunctio
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
pushObject(value: object) {
|
|
68
|
-
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
69
|
-
// an object, it'll be acting as some form of ReactNode.
|
|
70
|
+
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
71
|
+
// returns an object, it'll be acting as some form of ReactNode.
|
|
70
72
|
this.result.push(value);
|
|
71
73
|
}
|
|
72
74
|
|
package/src/formatters/string.ts
CHANGED
|
@@ -15,16 +15,14 @@ export type StringFunctionTypes = FunctionTypes<string>;
|
|
|
15
15
|
export class StringBuilder extends FormatBuilder<string> {
|
|
16
16
|
result: string = '';
|
|
17
17
|
|
|
18
|
-
pushRichTextTag(
|
|
18
|
+
pushRichTextTag(_tag: RichTextTagNames, children: string[], _control: string[]) {
|
|
19
19
|
// Plain string formatting ignores rich text tags and just takes the
|
|
20
|
-
// visible content from the children. This means the
|
|
21
|
-
//
|
|
22
|
-
// together
|
|
23
|
-
|
|
24
|
-
this.result +=
|
|
25
|
-
return;
|
|
20
|
+
// visible content from the children. This means the control element is not
|
|
21
|
+
// important for string rendering, so the result is always just the
|
|
22
|
+
// children joined together.
|
|
23
|
+
for (const child of children) {
|
|
24
|
+
this.result += child;
|
|
26
25
|
}
|
|
27
|
-
this.result += children.join('');
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
pushLiteralText(text: string) {
|
package/src/intl-manager.ts
CHANGED
package/src/message.ts
CHANGED
|
@@ -39,8 +39,6 @@ function serializeAst(ast: AstNode[], result: { value: string }) {
|
|
|
39
39
|
|
|
40
40
|
switch (node[AstNodeIndices.Type]) {
|
|
41
41
|
case FormatJsNodeType.Argument:
|
|
42
|
-
// Empties are an artifact of our parsing strategy, not necessary here.
|
|
43
|
-
if (result.value === '$_') return;
|
|
44
42
|
result.value += '{' + node[AstNodeIndices.Value] + '}';
|
|
45
43
|
return;
|
|
46
44
|
case FormatJsNodeType.Date:
|
package/src/types.d.ts
CHANGED
|
@@ -193,8 +193,9 @@ export interface FunctionTypeMap {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
export interface FunctionTypes<Result, HandlerType = () => void> {
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
// TODO: Figure out a nicer way to type `control` here.
|
|
197
|
+
link: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
|
+
hook: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
199
|
handler: undefined | HandlerType;
|
|
199
200
|
}
|
|
200
201
|
|
|
@@ -265,7 +266,6 @@ export type RequiredFormatValues<
|
|
|
265
266
|
>;
|
|
266
267
|
|
|
267
268
|
export interface RichTextFormattingMap<T = any> {
|
|
268
|
-
$_: T;
|
|
269
269
|
$b: T;
|
|
270
270
|
$i: T;
|
|
271
271
|
$p: T;
|