@discord/intl 0.5.2 → 0.6.1

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/src/message.ts CHANGED
@@ -1,12 +1,20 @@
1
- import { FormatJsNode, FormatJsNodeType, FormatJsTag } from './keyless-json';
1
+ import {
2
+ AstNode,
3
+ FullFormatJsNode,
4
+ FormatJsNodeType,
5
+ compressFormatJsToAst,
6
+ isCompressedAst,
7
+ AstNodeIndices,
8
+ TagNode,
9
+ } from '@discord/intl-ast';
2
10
 
3
11
  export class InternalIntlMessage {
4
12
  locale: string;
5
- ast: FormatJsNode[];
13
+ ast: AstNode[];
6
14
 
7
- constructor(messageOrAst: FormatJsNode[], locale: string) {
15
+ constructor(messageOrAst: AstNode[] | FullFormatJsNode[], locale: string) {
8
16
  this.locale = locale;
9
- this.ast = messageOrAst;
17
+ this.ast = isCompressedAst(messageOrAst) ? messageOrAst : compressFormatJsToAst(messageOrAst);
10
18
  }
11
19
 
12
20
  /**
@@ -15,54 +23,55 @@ export class InternalIntlMessage {
15
23
  */
16
24
  reserialize(): string {
17
25
  const result = { value: '' };
18
- serializeFormatJsAst(this.ast, result);
26
+ serializeAst(this.ast, result);
19
27
  return result.value;
20
28
  }
21
29
  }
22
30
 
23
31
  // Accepting an object as the `result` parameter lets the same string get passed around and
24
32
  // appended, rather than creating a bunch of intermediate strings.
25
- function serializeFormatJsAst(ast: FormatJsNode[], result: { value: string }) {
33
+ function serializeAst(ast: AstNode[], result: { value: string }) {
26
34
  for (const node of ast) {
27
- switch (node.type) {
35
+ switch (node[AstNodeIndices.Type]) {
28
36
  case FormatJsNodeType.Literal:
29
- result.value += node.value;
37
+ result.value += node[AstNodeIndices.Value];
30
38
  return;
31
39
  case FormatJsNodeType.Argument:
32
40
  // Empties are an artifact of our parsing strategy, not necessary here.
33
41
  if (result.value === '$_') return;
34
- result.value += '{' + node.value + '}';
42
+ result.value += '{' + node[AstNodeIndices.Value] + '}';
35
43
  return;
36
44
  case FormatJsNodeType.Date:
37
- result.value += '{' + node.value + ', date';
38
- if (node.style != null) {
39
- result.value += ', ' + node.style;
45
+ result.value += '{' + node[AstNodeIndices.Value] + ', date';
46
+ if (node[AstNodeIndices.Style] != null) {
47
+ result.value += ', ' + node[AstNodeIndices.Style];
40
48
  }
41
49
  result.value += '}';
42
50
  return;
43
51
  case FormatJsNodeType.Time:
44
- result.value += '{' + node.value + ', time';
45
- if (node.style != null) {
46
- result.value += ', ' + node.style;
52
+ result.value += '{' + node[AstNodeIndices.Value] + ', time';
53
+ if (node[AstNodeIndices.Style] != null) {
54
+ result.value += ', ' + node[AstNodeIndices.Style];
47
55
  }
48
56
  result.value += '}';
49
57
  return;
50
58
  case FormatJsNodeType.Number:
51
- result.value += '{' + node.value + ', number';
52
- if (node.style != null) {
53
- result.value += ', ' + node.style;
59
+ result.value += '{' + node[AstNodeIndices.Value] + ', number';
60
+ if (node[AstNodeIndices.Style] != null) {
61
+ result.value += ', ' + node[AstNodeIndices.Style];
54
62
  }
55
63
  result.value += '}';
56
64
  return;
57
65
  case FormatJsNodeType.Plural: {
58
- const pluralType = node.pluralType == 'ordinal' ? 'selectordinal' : 'plural';
59
- result.value += '{' + node.value + ', ' + pluralType + ', ';
60
- if (node.offset) {
61
- result.value += 'offset:' + node.offset;
66
+ const pluralType =
67
+ node[AstNodeIndices.PluralType] == 'ordinal' ? 'selectordinal' : 'plural';
68
+ result.value += '{' + node[AstNodeIndices.Value] + ', ' + pluralType + ', ';
69
+ if (node[AstNodeIndices.Offset]) {
70
+ result.value += 'offset:' + node[AstNodeIndices.Offset];
62
71
  }
63
- for (const [name, arm] of Object.entries(node.options)) {
72
+ for (const [name, arm] of Object.entries(node[AstNodeIndices.Options])) {
64
73
  result.value += ' ' + name + ' {';
65
- serializeFormatJsAst(arm.value, result);
74
+ serializeAst(arm.value, result);
66
75
  result.value += '}';
67
76
  }
68
77
  return;
@@ -71,59 +80,59 @@ function serializeFormatJsAst(ast: FormatJsNode[], result: { value: string }) {
71
80
  result.value += '#';
72
81
  return;
73
82
  case FormatJsNodeType.Select: {
74
- result.value += '{' + node.value + ', select, ';
75
- for (const [name, arm] of Object.entries(node.options)) {
83
+ result.value += '{' + node[AstNodeIndices.Value] + ', select, ';
84
+ for (const [name, arm] of Object.entries(node[AstNodeIndices.Options])) {
76
85
  result.value += ' ' + name + ' {';
77
- serializeFormatJsAst(arm.value, result);
86
+ serializeAst(arm.value, result);
78
87
  result.value += '}';
79
88
  }
80
89
  return;
81
90
  }
82
91
  case FormatJsNodeType.Tag:
83
- serializeFormatJsTag(node, result);
92
+ serializeAstTag(node, result);
84
93
  return;
85
94
  }
86
95
  }
87
96
  }
88
97
 
89
- function serializeFormatJsTag(node: FormatJsTag, result: { value: string }) {
90
- switch (node.value) {
98
+ function serializeAstTag(node: TagNode, result: { value: string }) {
99
+ switch (node[AstNodeIndices.Value]) {
91
100
  case '$b':
92
101
  result.value += '**';
93
- serializeFormatJsAst(node.children, result);
102
+ serializeAst(node[AstNodeIndices.Children], result);
94
103
  result.value += '**';
95
104
  return;
96
105
  case '$i':
97
106
  result.value += '*';
98
- serializeFormatJsAst(node.children, result);
107
+ serializeAst(node[AstNodeIndices.Children], result);
99
108
  result.value += '*';
100
109
  return;
101
110
  case '$code':
102
111
  result.value += '`';
103
- serializeFormatJsAst(node.children, result);
112
+ serializeAst(node[AstNodeIndices.Children], result);
104
113
  result.value += '`';
105
114
  return;
106
115
  case '$p':
107
- serializeFormatJsAst(node.children, result);
116
+ serializeAst(node[AstNodeIndices.Children], result);
108
117
  result.value += '\n\n';
109
118
  return;
110
119
  case '$link':
111
120
  // The target is the first child of the link. We don't have to care if it's a placeholder
112
121
  // or not, because the serialization will automatically remove the extra empty.
113
- const [target, ...children] = node.children;
122
+ const [target, ...children] = node[AstNodeIndices.Children];
114
123
  result.value += '[';
115
- serializeFormatJsAst(children, result);
124
+ serializeAst(children, result);
116
125
  result.value += '](';
117
126
  if (target != null) {
118
- serializeFormatJsAst([target], result);
127
+ serializeAst([target], result);
119
128
  }
120
129
  result.value += ')';
121
130
  return;
122
131
  default:
123
132
  // Any other tag name is a hook, which just adds the `$[` on a link tag.
124
133
  result.value += '$[';
125
- serializeFormatJsAst(node.children, result);
126
- result.value += '](' + node.value + ')';
134
+ serializeAst(node[AstNodeIndices.Children], result);
135
+ result.value += '](' + node[AstNodeIndices.Value] + ')';
127
136
  return;
128
137
  }
129
138
  }
@@ -1,68 +0,0 @@
1
- export declare enum FormatJsNodeType {
2
- Literal = 0,
3
- Argument = 1,
4
- Number = 2,
5
- Date = 3,
6
- Time = 4,
7
- Select = 5,
8
- Plural = 6,
9
- Pound = 7,
10
- Tag = 8
11
- }
12
- export interface FormatJsLiteral {
13
- type: FormatJsNodeType.Literal;
14
- value: string;
15
- }
16
- export interface FormatJsArgument {
17
- type: FormatJsNodeType.Argument;
18
- value: string;
19
- }
20
- export interface FormatJsNumber {
21
- type: FormatJsNodeType.Number;
22
- value: string;
23
- style?: string;
24
- }
25
- export interface FormatJsDate {
26
- type: FormatJsNodeType.Date;
27
- value: string;
28
- style?: string;
29
- }
30
- export interface FormatJsTime {
31
- type: FormatJsNodeType.Time;
32
- value: string;
33
- style?: string;
34
- }
35
- export interface FormatJsSelect {
36
- type: FormatJsNodeType.Select;
37
- value: string;
38
- options: Record<string, {
39
- value: FormatJsNode[];
40
- }>;
41
- }
42
- export type FormatJsPluralType = 'cardinal' | 'ordinal';
43
- export interface FormatJsPlural {
44
- type: FormatJsNodeType.Plural;
45
- value: string;
46
- options: Record<string, {
47
- value: FormatJsNode[];
48
- }>;
49
- offset: number;
50
- pluralType: FormatJsPluralType;
51
- }
52
- export interface FormatJsPound {
53
- type: FormatJsNodeType.Pound;
54
- }
55
- export interface FormatJsTag {
56
- type: FormatJsNodeType.Tag;
57
- value: string;
58
- children: FormatJsNode[];
59
- }
60
- export type FormatJsNode = FormatJsLiteral | FormatJsArgument | FormatJsNumber | FormatJsDate | FormatJsTime | FormatJsSelect | FormatJsPlural | FormatJsPound | FormatJsTag;
61
- export declare const FORMAT_JS_POUND: FormatJsPound;
62
- /**
63
- * Hydrate the given keyless JSON into a FormatJS-compatible AST structure.
64
- * The given object is reused as much as possible, meaning it cannot be safely
65
- * used again after hydrating.
66
- */
67
- export declare function hydrateFormatJsAst(keyless: Array<Array<any>>): FormatJsNode[];
68
- export declare function hydrateMessages(messages: Record<string, any>): Record<string, any>;
@@ -1,107 +0,0 @@
1
- "use strict";
2
- var __read = (this && this.__read) || function (o, n) {
3
- var m = typeof Symbol === "function" && o[Symbol.iterator];
4
- if (!m) return o;
5
- var i = m.call(o), r, ar = [], e;
6
- try {
7
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
- }
9
- catch (error) { e = { error: error }; }
10
- finally {
11
- try {
12
- if (r && !r.done && (m = i["return"])) m.call(i);
13
- }
14
- finally { if (e) throw e.error; }
15
- }
16
- return ar;
17
- };
18
- Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.FORMAT_JS_POUND = exports.FormatJsNodeType = void 0;
20
- exports.hydrateFormatJsAst = hydrateFormatJsAst;
21
- exports.hydrateMessages = hydrateMessages;
22
- var FormatJsNodeType;
23
- (function (FormatJsNodeType) {
24
- FormatJsNodeType[FormatJsNodeType["Literal"] = 0] = "Literal";
25
- FormatJsNodeType[FormatJsNodeType["Argument"] = 1] = "Argument";
26
- FormatJsNodeType[FormatJsNodeType["Number"] = 2] = "Number";
27
- FormatJsNodeType[FormatJsNodeType["Date"] = 3] = "Date";
28
- FormatJsNodeType[FormatJsNodeType["Time"] = 4] = "Time";
29
- FormatJsNodeType[FormatJsNodeType["Select"] = 5] = "Select";
30
- FormatJsNodeType[FormatJsNodeType["Plural"] = 6] = "Plural";
31
- FormatJsNodeType[FormatJsNodeType["Pound"] = 7] = "Pound";
32
- FormatJsNodeType[FormatJsNodeType["Tag"] = 8] = "Tag";
33
- })(FormatJsNodeType || (exports.FormatJsNodeType = FormatJsNodeType = {}));
34
- // Using a static object here ensures there's only one allocation for it, which
35
- // will likely appear in many, many messages. Freezing ensures that the shared
36
- // object can't be accidentally modified if a consumer tries to transform the
37
- // AST.
38
- exports.FORMAT_JS_POUND = Object.freeze({ type: 7 });
39
- function hydrateArray(elements) {
40
- for (var i = 0; i < elements.length; i++) {
41
- elements[i] = hydrateFormatJsAst(elements[i]);
42
- }
43
- }
44
- function hydratePlural(keyless) {
45
- var _a = __read(keyless, 5), type = _a[0], value = _a[1], options = _a[2], offset = _a[3], pluralType = _a[4];
46
- // This tries to be efficient about updating the value for each option
47
- // with a parsed version, reusing the same receiving object, and even
48
- // the inner key within it, just replacing the end value with the
49
- // parsed version.
50
- // This saves multiple allocations compared to building a new object
51
- // from scratch, either for the whole options object or for each value.
52
- for (var key in options) {
53
- hydrateArray(options[key].value);
54
- }
55
- // `pluralType` is technically only valid on `Plural` nodes, even
56
- // though the structure is identical to `Select`.
57
- if (type === FormatJsNodeType.Plural) {
58
- return { type: type, value: value, options: options, offset: offset, pluralType: pluralType };
59
- }
60
- else {
61
- return { type: type, value: value, options: options, offset: offset };
62
- }
63
- }
64
- function hydrateSingle(keyless) {
65
- var _a = __read(keyless, 1), type = _a[0];
66
- switch (type) {
67
- case FormatJsNodeType.Literal:
68
- case FormatJsNodeType.Argument:
69
- return { type: type, value: keyless[1] };
70
- case FormatJsNodeType.Number:
71
- case FormatJsNodeType.Date:
72
- case FormatJsNodeType.Time:
73
- return { type: type, value: keyless[1], style: keyless[2] };
74
- case FormatJsNodeType.Select:
75
- case FormatJsNodeType.Plural:
76
- return hydratePlural(keyless);
77
- case FormatJsNodeType.Pound:
78
- return exports.FORMAT_JS_POUND;
79
- case FormatJsNodeType.Tag: {
80
- var _b = __read(keyless, 3), type_1 = _b[0], value = _b[1], children = _b[2];
81
- hydrateArray(children);
82
- return { type: type_1, value: value, children: children };
83
- }
84
- default:
85
- throw new Error("FormatJS keyless JSON encountered an unknown type: ".concat(type));
86
- }
87
- }
88
- function hydrateFormatJsAst(keyless) {
89
- // If the first element of the array is itself an array, then we have a list
90
- // of elements to parse rather than a single object.
91
- if (Array.isArray(keyless[0])) {
92
- hydrateArray(keyless);
93
- return keyless;
94
- }
95
- else if (keyless.length === 0) {
96
- // Some entries can be empty arrays, like an empty set of children, and those can
97
- // be preserved.
98
- return keyless;
99
- }
100
- return hydrateSingle(keyless);
101
- }
102
- function hydrateMessages(messages) {
103
- for (var key in messages) {
104
- messages[key] = hydrateFormatJsAst(messages[key]);
105
- }
106
- return messages;
107
- }
@@ -1,161 +0,0 @@
1
- export enum FormatJsNodeType {
2
- Literal = 0,
3
- Argument,
4
- Number,
5
- Date,
6
- Time,
7
- Select,
8
- Plural,
9
- Pound,
10
- Tag,
11
- }
12
-
13
- export interface FormatJsLiteral {
14
- type: FormatJsNodeType.Literal;
15
- value: string;
16
- }
17
-
18
- export interface FormatJsArgument {
19
- type: FormatJsNodeType.Argument;
20
- value: string;
21
- }
22
-
23
- export interface FormatJsNumber {
24
- type: FormatJsNodeType.Number;
25
- value: string;
26
- style?: string;
27
- }
28
-
29
- export interface FormatJsDate {
30
- type: FormatJsNodeType.Date;
31
- value: string;
32
- style?: string;
33
- }
34
-
35
- export interface FormatJsTime {
36
- type: FormatJsNodeType.Time;
37
- value: string;
38
- style?: string;
39
- }
40
-
41
- export interface FormatJsSelect {
42
- type: FormatJsNodeType.Select;
43
- value: string;
44
- options: Record<string, { value: FormatJsNode[] }>;
45
- }
46
-
47
- export type FormatJsPluralType = 'cardinal' | 'ordinal';
48
-
49
- export interface FormatJsPlural {
50
- type: FormatJsNodeType.Plural;
51
- value: string;
52
- options: Record<string, { value: FormatJsNode[] }>;
53
- offset: number;
54
- pluralType: FormatJsPluralType;
55
- }
56
-
57
- export interface FormatJsPound {
58
- type: FormatJsNodeType.Pound;
59
- }
60
-
61
- export interface FormatJsTag {
62
- type: FormatJsNodeType.Tag;
63
- value: string;
64
- children: FormatJsNode[];
65
- }
66
-
67
- export type FormatJsNode =
68
- | FormatJsLiteral
69
- | FormatJsArgument
70
- | FormatJsNumber
71
- | FormatJsDate
72
- | FormatJsTime
73
- | FormatJsSelect
74
- | FormatJsPlural
75
- | FormatJsPound
76
- | FormatJsTag;
77
-
78
- // Using a static object here ensures there's only one allocation for it, which
79
- // will likely appear in many, many messages. Freezing ensures that the shared
80
- // object can't be accidentally modified if a consumer tries to transform the
81
- // AST.
82
- export const FORMAT_JS_POUND: FormatJsPound = Object.freeze({ type: 7 });
83
-
84
- function hydrateArray(elements: Array<Array<any>>) {
85
- for (let i = 0; i < elements.length; i++) {
86
- elements[i] = hydrateFormatJsAst(elements[i]);
87
- }
88
- }
89
-
90
- function hydratePlural(keyless: Array<any>): FormatJsNode {
91
- const [type, value, options, offset, pluralType] = keyless;
92
- // This tries to be efficient about updating the value for each option
93
- // with a parsed version, reusing the same receiving object, and even
94
- // the inner key within it, just replacing the end value with the
95
- // parsed version.
96
- // This saves multiple allocations compared to building a new object
97
- // from scratch, either for the whole options object or for each value.
98
- for (const key in options) {
99
- hydrateArray(options[key].value);
100
- }
101
- // `pluralType` is technically only valid on `Plural` nodes, even
102
- // though the structure is identical to `Select`.
103
- if (type === FormatJsNodeType.Plural) {
104
- return { type, value, options, offset, pluralType };
105
- } else {
106
- return { type, value, options, offset };
107
- }
108
- }
109
-
110
- function hydrateSingle(keyless: Array<any>): FormatJsNode {
111
- const [type] = keyless;
112
- switch (type) {
113
- case FormatJsNodeType.Literal:
114
- case FormatJsNodeType.Argument:
115
- return { type, value: keyless[1] };
116
- case FormatJsNodeType.Number:
117
- case FormatJsNodeType.Date:
118
- case FormatJsNodeType.Time:
119
- return { type, value: keyless[1], style: keyless[2] };
120
- case FormatJsNodeType.Select:
121
- case FormatJsNodeType.Plural:
122
- return hydratePlural(keyless);
123
- case FormatJsNodeType.Pound:
124
- return FORMAT_JS_POUND;
125
- case FormatJsNodeType.Tag: {
126
- const [type, value, children] = keyless;
127
- hydrateArray(children);
128
- return { type, value, children: children };
129
- }
130
- default:
131
- throw new Error(`FormatJS keyless JSON encountered an unknown type: ${type}`);
132
- }
133
- }
134
-
135
- /**
136
- * Hydrate the given keyless JSON into a FormatJS-compatible AST structure.
137
- * The given object is reused as much as possible, meaning it cannot be safely
138
- * used again after hydrating.
139
- */
140
- export function hydrateFormatJsAst(keyless: Array<Array<any>>): FormatJsNode[];
141
- export function hydrateFormatJsAst(keyless: Array<any>): FormatJsNode | FormatJsNode[] {
142
- // If the first element of the array is itself an array, then we have a list
143
- // of elements to parse rather than a single object.
144
- if (Array.isArray(keyless[0])) {
145
- hydrateArray(keyless);
146
- return keyless;
147
- } else if (keyless.length === 0) {
148
- // Some entries can be empty arrays, like an empty set of children, and those can
149
- // be preserved.
150
- return keyless;
151
- }
152
-
153
- return hydrateSingle(keyless);
154
- }
155
-
156
- export function hydrateMessages(messages: Record<string, any>) {
157
- for (const key in messages) {
158
- messages[key] = hydrateFormatJsAst(messages[key]);
159
- }
160
- return messages;
161
- }