@saykit/transform-js 0.6.0 → 0.7.0

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.
Files changed (2) hide show
  1. package/dist/parser.mjs +46 -7
  2. package/package.json +2 -2
package/dist/parser.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { AUTO_INCREMENT_IDENTIFIER, ArgumentMessage, ChoiceMessage, CompositeMessage, LiteralMessage } from "@saykit/config/features/messages";
1
+ import { AUTO_INCREMENT_IDENTIFIER, ArgumentMessage, ChoiceMessage, CompositeMessage, LiteralMessage, isArgumentType, validateArgumentStyle, validateBranchIdentifier } from "@saykit/config/features/messages";
2
2
  import * as t from "@babel/types";
3
3
  //#region src/parser.ts
4
4
  const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -20,6 +20,12 @@ function parseCallExpression(call) {
20
20
  const processed = processExpression(call.callee);
21
21
  if (!processed) return null;
22
22
  const [accessor, descriptor, kind] = processed;
23
+ const descriptorId = descriptor ? findPropertyValueIfStringLiteralAsString(descriptor, "id") : void 0;
24
+ const descriptorContext = descriptor ? findPropertyValueIfStringLiteralAsString(descriptor, "context") : void 0;
25
+ const wrap = (children) => new CompositeMessage({
26
+ id: descriptorId,
27
+ context: descriptorContext
28
+ }, [], call.loc ? [`${call.loc.filename}:${call.loc.start.line}`] : [], children, accessor);
23
29
  if (typeof kind === "string" && [
24
30
  "select",
25
31
  "ordinal",
@@ -28,8 +34,11 @@ function parseCallExpression(call) {
28
34
  if (call.arguments.length !== 2) return null;
29
35
  if (!t.isExpression(call.arguments[0])) return null;
30
36
  if (!t.isObjectExpression(call.arguments[1])) return null;
31
- const branches = call.arguments[1].properties.reduce((c, p) => {
37
+ const object = call.arguments[1];
38
+ const offset = kind === "select" ? void 0 : findPluralOffset(object);
39
+ const branches = object.properties.reduce((c, p) => {
32
40
  if (!t.isObjectProperty(p) || !t.isExpression(p.value)) return c;
41
+ if (offset !== void 0 && getPropertyNameAsString(p.key) === "offset") return c;
33
42
  let message = null;
34
43
  if (!message && t.isStringLiteral(p.value)) message = new LiteralMessage(p.value.value);
35
44
  if (!message) message = parseExpression(p.value, true);
@@ -39,15 +48,45 @@ function parseCallExpression(call) {
39
48
  });
40
49
  return c;
41
50
  }, []);
51
+ for (const branch of branches) validateBranchIdentifier(kind, branch.identifier);
42
52
  const [identifier, value] = unwrapPlaceholder(call.arguments[0]);
43
- const choice = new ChoiceMessage(kind, identifier, branches, value);
44
- return new CompositeMessage({
45
- id: descriptor ? findPropertyValueIfStringLiteralAsString(descriptor, "id") : void 0,
46
- context: descriptor ? findPropertyValueIfStringLiteralAsString(descriptor, "context") : void 0
47
- }, [], call.loc ? [`${call.loc.filename}:${call.loc.start.line}`] : [], [choice], accessor);
53
+ return wrap([new ChoiceMessage(kind, identifier, branches, value, offset)]);
54
+ }
55
+ if (typeof kind === "string" && isArgumentType(kind)) {
56
+ if (call.arguments.length < 1 || call.arguments.length > 2) return null;
57
+ if (!t.isExpression(call.arguments[0])) return null;
58
+ let style;
59
+ if (call.arguments.length === 2) {
60
+ if (!t.isObjectExpression(call.arguments[1])) return null;
61
+ style = findPropertyValueIfStringLiteralAsString(call.arguments[1], "style");
62
+ if (style !== void 0) validateArgumentStyle(kind, style);
63
+ }
64
+ const [identifier, value] = unwrapPlaceholder(call.arguments[0]);
65
+ return wrap([new ArgumentMessage(identifier, value, {
66
+ type: kind,
67
+ style
68
+ })]);
48
69
  }
49
70
  return null;
50
71
  }
72
+ /**
73
+ * The `offset` a plural subtracts from its selector before `#` is formatted, so
74
+ * "You and 2 others" can select on a total of three.
75
+ *
76
+ * Read only from an integer literal: the offset is baked into the extracted
77
+ * message, so it has to be known at build time, and a fractional or negative
78
+ * one is not something ICU accepts. Anything else stays an ordinary branch and
79
+ * is validated as the key it looks like.
80
+ */
81
+ function findPluralOffset(object) {
82
+ for (const property of object.properties) {
83
+ if (!t.isObjectProperty(property) || property.computed) continue;
84
+ if (getPropertyNameAsString(property.key) !== "offset") continue;
85
+ if (!t.isNumericLiteral(property.value)) continue;
86
+ if (!Number.isInteger(property.value.value) || property.value.value < 0) continue;
87
+ return property.value.value;
88
+ }
89
+ }
51
90
  function processExpression(expression) {
52
91
  if (t.isIdentifier(expression) && expression.name === "say") return [
53
92
  expression,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saykit/transform-js",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "JavaScript and TypeScript transformer for SayKit",
5
5
  "keywords": [
6
6
  "i18n",
@@ -47,7 +47,7 @@
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.6.0"
50
+ "@saykit/config": "^0.7.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@babel/core": "^7.29.7",