@saykit/transform-jsx 0.8.0 → 0.9.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/dist/index.mjs +3 -5
- package/dist/parser.d.mts +6 -1
- package/dist/parser.mjs +5 -43
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import * as parser from "@babel/parser";
|
|
|
5
5
|
import traverse_ from "@babel/traverse";
|
|
6
6
|
import { assignSequenceIdentifiers } from "@saykit/config/features/messages";
|
|
7
7
|
import { generateSayCallExpression } from "@saykit/transform-js/generator";
|
|
8
|
-
import { isEquivalentPlaceholder, parseExpression } from "@saykit/transform-js/parser";
|
|
8
|
+
import { collectLeadingComments, isEquivalentPlaceholder, parseExpression } from "@saykit/transform-js/parser";
|
|
9
9
|
//#region src/index.ts
|
|
10
10
|
const traverse = traverse_.default || traverse_;
|
|
11
11
|
function createProgram(code, id) {
|
|
@@ -37,7 +37,7 @@ function createJsxTransformer() {
|
|
|
37
37
|
const messages = [];
|
|
38
38
|
traverse(program, {
|
|
39
39
|
Expression(path) {
|
|
40
|
-
path.node.leadingComments = path
|
|
40
|
+
path.node.leadingComments = collectLeadingComments(path);
|
|
41
41
|
const message = parseExpression(path.node);
|
|
42
42
|
if (message) {
|
|
43
43
|
assignSequenceIdentifiers(message, { current: 0 }, isEquivalentPlaceholder);
|
|
@@ -46,7 +46,7 @@ function createJsxTransformer() {
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
JSXElement(path) {
|
|
49
|
-
path.node.leadingComments = path
|
|
49
|
+
path.node.leadingComments = collectLeadingComments(path);
|
|
50
50
|
const message = parseJSXElement(path.node);
|
|
51
51
|
if (message) {
|
|
52
52
|
assignSequenceIdentifiers(message, { current: 0 }, isEquivalentPlaceholder);
|
|
@@ -68,7 +68,6 @@ function createJsxTransformer() {
|
|
|
68
68
|
const program = createProgram(code, id);
|
|
69
69
|
traverse(program, {
|
|
70
70
|
Expression(path) {
|
|
71
|
-
path.node.leadingComments = path.node.leadingComments ?? [];
|
|
72
71
|
const message = parseExpression(path.node);
|
|
73
72
|
if (message) {
|
|
74
73
|
assignSequenceIdentifiers(message, { current: 0 }, isEquivalentPlaceholder);
|
|
@@ -78,7 +77,6 @@ function createJsxTransformer() {
|
|
|
78
77
|
}
|
|
79
78
|
},
|
|
80
79
|
JSXElement(path) {
|
|
81
|
-
path.node.leadingComments = path.node.leadingComments ?? [];
|
|
82
80
|
const message = parseJSXElement(path.node);
|
|
83
81
|
if (message) {
|
|
84
82
|
assignSequenceIdentifiers(message, { current: 0 }, isEquivalentPlaceholder);
|
package/dist/parser.d.mts
CHANGED
|
@@ -2,7 +2,12 @@ import { CompositeMessage, ElementMessage } from "@saykit/config/features/messag
|
|
|
2
2
|
import * as t from "@babel/types";
|
|
3
3
|
//#region src/parser.d.ts
|
|
4
4
|
declare function parseJSXContainerElement(element: t.JSXElement): CompositeMessage | null;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* A self-closing `Say` element. Comments are passed in rather than read off the
|
|
7
|
+
* node, since they were written in front of the element as a whole and Babel
|
|
8
|
+
* attaches them there.
|
|
9
|
+
*/
|
|
10
|
+
declare function parseJSXOpeningElement(element: t.JSXOpeningElement, comments?: readonly t.Comment[] | null): CompositeMessage | null;
|
|
6
11
|
declare function parseJSXElement(element: t.JSXElement, fallback?: false): CompositeMessage | null;
|
|
7
12
|
declare function parseJSXElement(element: t.JSXElement, fallback: true): CompositeMessage | ElementMessage;
|
|
8
13
|
//#endregion
|
package/dist/parser.mjs
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { AUTO_INCREMENT_IDENTIFIER, ArgumentMessage, ChoiceMessage, CompositeMessage, ElementMessage, LiteralMessage, isArgumentType, validateArgumentStyle, validateBranchIdentifier } from "@saykit/config/features/messages";
|
|
2
|
-
import { unwrapPlaceholder } from "@saykit/transform-js/parser";
|
|
2
|
+
import { getTranslatorComments, unwrapPlaceholder } from "@saykit/transform-js/parser";
|
|
3
3
|
import * as t from "@babel/types";
|
|
4
4
|
//#region src/parser.ts
|
|
5
|
-
/**
|
|
6
|
-
* Attribute used to name the ICU tag an embedded element extracts as, e.g.
|
|
7
|
-
* `<a say-tag="link">` becomes `<link></link>` rather than `<0></0>`. It never
|
|
8
|
-
* reaches the real element.
|
|
9
|
-
*/
|
|
10
5
|
const TAG_ATTRIBUTE = "say-tag";
|
|
11
6
|
const TAG_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
12
7
|
function parseJSXContainerElement(element) {
|
|
@@ -21,9 +16,9 @@ function parseJSXContainerElement(element) {
|
|
|
21
16
|
return new CompositeMessage({
|
|
22
17
|
id: descriptorId,
|
|
23
18
|
context: descriptorContext
|
|
24
|
-
},
|
|
19
|
+
}, getTranslatorComments(element.leadingComments), getReferences(element), children, accessor, whitespace);
|
|
25
20
|
}
|
|
26
|
-
function parseJSXOpeningElement(element) {
|
|
21
|
+
function parseJSXOpeningElement(element, comments) {
|
|
27
22
|
if (!element.selfClosing) return null;
|
|
28
23
|
const processed = processJSXOpeningElement(element);
|
|
29
24
|
if (!processed) return null;
|
|
@@ -33,7 +28,7 @@ function parseJSXOpeningElement(element) {
|
|
|
33
28
|
const wrap = (children) => new CompositeMessage({
|
|
34
29
|
id: descriptorId,
|
|
35
30
|
context: descriptorContext
|
|
36
|
-
},
|
|
31
|
+
}, getTranslatorComments(comments), getReferences(element), children, accessor);
|
|
37
32
|
if (typeof kind === "string" && [
|
|
38
33
|
"select",
|
|
39
34
|
"ordinal",
|
|
@@ -90,11 +85,6 @@ function parseJSXOpeningElement(element) {
|
|
|
90
85
|
}
|
|
91
86
|
return null;
|
|
92
87
|
}
|
|
93
|
-
/**
|
|
94
|
-
* The `offset` a plural subtracts from its selector before `#` is formatted.
|
|
95
|
-
* See the JS parser's counterpart — the constraints are the same, only the
|
|
96
|
-
* syntax carrying it differs.
|
|
97
|
-
*/
|
|
98
88
|
function findPluralOffset(attributes) {
|
|
99
89
|
for (const attribute of attributes) {
|
|
100
90
|
if (!t.isJSXAttribute(attribute)) continue;
|
|
@@ -106,14 +96,6 @@ function findPluralOffset(attributes) {
|
|
|
106
96
|
return expression.value;
|
|
107
97
|
}
|
|
108
98
|
}
|
|
109
|
-
/**
|
|
110
|
-
* The children the JSX transform itself would compile, so a message extracts as
|
|
111
|
-
* the text that renders. Text children come back with their whitespace
|
|
112
|
-
* collapsed the way JSX collapses it — a line break and the indentation around
|
|
113
|
-
* it are layout and disappear, while two lines that both hold text rejoin with
|
|
114
|
-
* the single space that separated the words — every expression container comes
|
|
115
|
-
* back unwrapped, and a comment child comes back as nothing.
|
|
116
|
-
*/
|
|
117
99
|
function buildMessageChildren(element, into = []) {
|
|
118
100
|
return t.react.buildChildren(element).reduce((p, c) => {
|
|
119
101
|
const literal = getExpressionAsLiteralText(c);
|
|
@@ -127,26 +109,12 @@ function buildMessageChildren(element, into = []) {
|
|
|
127
109
|
return p;
|
|
128
110
|
}, into);
|
|
129
111
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Add text to the message, folding it into the literal in front of it when
|
|
132
|
-
* there is one, so `Hello{' '}world` is three children in the source and one
|
|
133
|
-
* run of text in the catalogue.
|
|
134
|
-
*/
|
|
135
112
|
function pushLiteral(messages, text) {
|
|
136
113
|
if (!text) return;
|
|
137
114
|
const last = messages.at(-1);
|
|
138
115
|
if (last instanceof LiteralMessage) messages[messages.length - 1] = new LiteralMessage(last.text + text);
|
|
139
116
|
else messages.push(new LiteralMessage(text));
|
|
140
117
|
}
|
|
141
|
-
/**
|
|
142
|
-
* The text a child renders as, when that is knowable at build time — a text
|
|
143
|
-
* child, which `buildChildren` has already collapsed into a string, or an
|
|
144
|
-
* expression holding a literal with nothing interpolated into it.
|
|
145
|
-
*
|
|
146
|
-
* The second is what makes `{' '}` the way to write whitespace that has to
|
|
147
|
-
* survive a line break: it extracts as the space it renders as, rather than as
|
|
148
|
-
* a placeholder a translator can neither see nor move.
|
|
149
|
-
*/
|
|
150
118
|
function getExpressionAsLiteralText(expression) {
|
|
151
119
|
if (t.isStringLiteral(expression)) return expression.value;
|
|
152
120
|
if (t.isNumericLiteral(expression)) return String(expression.value);
|
|
@@ -164,19 +132,13 @@ function processJSXOpeningElement(element) {
|
|
|
164
132
|
return null;
|
|
165
133
|
}
|
|
166
134
|
function parseJSXElement(element, fallback) {
|
|
167
|
-
const message = element.openingElement.selfClosing ? parseJSXOpeningElement(element.openingElement) : parseJSXContainerElement(element);
|
|
135
|
+
const message = element.openingElement.selfClosing ? parseJSXOpeningElement(element.openingElement, element.leadingComments) : parseJSXContainerElement(element);
|
|
168
136
|
if (message) return message;
|
|
169
137
|
if (!fallback) return null;
|
|
170
138
|
const identifier = takeElementTag(element) ?? AUTO_INCREMENT_IDENTIFIER;
|
|
171
139
|
if (element.openingElement.selfClosing) return new ElementMessage(identifier, [], element);
|
|
172
140
|
return new ElementMessage(identifier, [parseJSXElement(t.jsxElement(t.jsxOpeningElement(t.jsxIdentifier("Say"), []), t.jsxClosingElement(t.jsxIdentifier("Say")), element.children), true)], element);
|
|
173
141
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Read the tag name off an embedded element and remove the attribute so it
|
|
176
|
-
* never reaches the rendered element. Only static strings name a tag; anything
|
|
177
|
-
* else is left in place and the element falls back to an auto-incremented
|
|
178
|
-
* identifier.
|
|
179
|
-
*/
|
|
180
142
|
function takeElementTag(element) {
|
|
181
143
|
const attributes = element.openingElement.attributes;
|
|
182
144
|
const index = attributes.findIndex((a) => t.isJSXAttribute(a) && getAttributeNameAsString(a) === TAG_ATTRIBUTE);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saykit/transform-jsx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "JSX and TSX source transformer for SayKit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"@babel/parser": "^7.29.7",
|
|
48
48
|
"@babel/traverse": "^7.29.7",
|
|
49
49
|
"@babel/types": "^7.29.7",
|
|
50
|
-
"@saykit/config": "^0.
|
|
51
|
-
"@saykit/transform-js": "^0.
|
|
50
|
+
"@saykit/config": "^0.9.1",
|
|
51
|
+
"@saykit/transform-js": "^0.9.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@babel/core": "^7.29.7",
|