@messagevisor/core 0.0.1 → 0.1.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 (211) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +7 -0
  4. package/jest.config.js +8 -0
  5. package/lib/benchmark/index.d.ts +2 -0
  6. package/lib/benchmark/index.js +417 -0
  7. package/lib/benchmark/index.js.map +1 -0
  8. package/lib/builder/index.d.ts +70 -0
  9. package/lib/builder/index.js +831 -0
  10. package/lib/builder/index.js.map +1 -0
  11. package/lib/cli/index.d.ts +28 -0
  12. package/lib/cli/index.js +182 -0
  13. package/lib/cli/index.js.map +1 -0
  14. package/lib/config/index.d.ts +61 -0
  15. package/lib/config/index.js +255 -0
  16. package/lib/config/index.js.map +1 -0
  17. package/lib/create/index.d.ts +2 -0
  18. package/lib/create/index.js +405 -0
  19. package/lib/create/index.js.map +1 -0
  20. package/lib/datasource/filesystemAdapter.d.ts +44 -0
  21. package/lib/datasource/filesystemAdapter.js +424 -0
  22. package/lib/datasource/filesystemAdapter.js.map +1 -0
  23. package/lib/datasource/index.d.ts +39 -0
  24. package/lib/datasource/index.js +96 -0
  25. package/lib/datasource/index.js.map +1 -0
  26. package/lib/error.d.ts +6 -0
  27. package/lib/error.js +49 -0
  28. package/lib/error.js.map +1 -0
  29. package/lib/evaluate/cli.d.ts +8 -0
  30. package/lib/evaluate/cli.js +179 -0
  31. package/lib/evaluate/cli.js.map +1 -0
  32. package/lib/evaluate/index.d.ts +10 -0
  33. package/lib/evaluate/index.js +131 -0
  34. package/lib/evaluate/index.js.map +1 -0
  35. package/lib/examples/coerceExampleIsoDates.d.ts +12 -0
  36. package/lib/examples/coerceExampleIsoDates.js +81 -0
  37. package/lib/examples/coerceExampleIsoDates.js.map +1 -0
  38. package/lib/examples/index.d.ts +63 -0
  39. package/lib/examples/index.js +713 -0
  40. package/lib/examples/index.js.map +1 -0
  41. package/lib/exporter/index.d.ts +60 -0
  42. package/lib/exporter/index.js +610 -0
  43. package/lib/exporter/index.js.map +1 -0
  44. package/lib/find-duplicates/index.d.ts +41 -0
  45. package/lib/find-duplicates/index.js +297 -0
  46. package/lib/find-duplicates/index.js.map +1 -0
  47. package/lib/generate-code/index.d.ts +11 -0
  48. package/lib/generate-code/index.js +157 -0
  49. package/lib/generate-code/index.js.map +1 -0
  50. package/lib/generate-code/typescript.d.ts +14 -0
  51. package/lib/generate-code/typescript.js +307 -0
  52. package/lib/generate-code/typescript.js.map +1 -0
  53. package/lib/importer/index.d.ts +64 -0
  54. package/lib/importer/index.js +1092 -0
  55. package/lib/importer/index.js.map +1 -0
  56. package/lib/index.d.ts +18 -0
  57. package/lib/index.js +35 -0
  58. package/lib/index.js.map +1 -0
  59. package/lib/info/index.d.ts +17 -0
  60. package/lib/info/index.js +132 -0
  61. package/lib/info/index.js.map +1 -0
  62. package/lib/init/index.d.ts +30 -0
  63. package/lib/init/index.js +348 -0
  64. package/lib/init/index.js.map +1 -0
  65. package/lib/lint/index.d.ts +1 -0
  66. package/lib/lint/index.js +6 -0
  67. package/lib/lint/index.js.map +1 -0
  68. package/lib/linter/attributeSchema.d.ts +7 -0
  69. package/lib/linter/attributeSchema.js +36 -0
  70. package/lib/linter/attributeSchema.js.map +1 -0
  71. package/lib/linter/checkLocaleCircularDependency.d.ts +7 -0
  72. package/lib/linter/checkLocaleCircularDependency.js +42 -0
  73. package/lib/linter/checkLocaleCircularDependency.js.map +1 -0
  74. package/lib/linter/conditionSchema.d.ts +3 -0
  75. package/lib/linter/conditionSchema.js +283 -0
  76. package/lib/linter/conditionSchema.js.map +1 -0
  77. package/lib/linter/formatSchema.d.ts +325 -0
  78. package/lib/linter/formatSchema.js +165 -0
  79. package/lib/linter/formatSchema.js.map +1 -0
  80. package/lib/linter/icuStyleLint.d.ts +6 -0
  81. package/lib/linter/icuStyleLint.js +226 -0
  82. package/lib/linter/icuStyleLint.js.map +1 -0
  83. package/lib/linter/index.d.ts +34 -0
  84. package/lib/linter/index.js +557 -0
  85. package/lib/linter/index.js.map +1 -0
  86. package/lib/linter/localeSchema.d.ts +672 -0
  87. package/lib/linter/localeSchema.js +50 -0
  88. package/lib/linter/localeSchema.js.map +1 -0
  89. package/lib/linter/messageSchema.d.ts +35 -0
  90. package/lib/linter/messageSchema.js +115 -0
  91. package/lib/linter/messageSchema.js.map +1 -0
  92. package/lib/linter/printError.d.ts +8 -0
  93. package/lib/linter/printError.js +41 -0
  94. package/lib/linter/printError.js.map +1 -0
  95. package/lib/linter/schema.d.ts +33 -0
  96. package/lib/linter/schema.js +192 -0
  97. package/lib/linter/schema.js.map +1 -0
  98. package/lib/linter/segmentSchema.d.ts +8 -0
  99. package/lib/linter/segmentSchema.js +18 -0
  100. package/lib/linter/segmentSchema.js.map +1 -0
  101. package/lib/linter/targetSchema.d.ts +337 -0
  102. package/lib/linter/targetSchema.js +39 -0
  103. package/lib/linter/targetSchema.js.map +1 -0
  104. package/lib/linter/testSchema.d.ts +71 -0
  105. package/lib/linter/testSchema.js +165 -0
  106. package/lib/linter/testSchema.js.map +1 -0
  107. package/lib/linter/zodHelpers.d.ts +2 -0
  108. package/lib/linter/zodHelpers.js +15 -0
  109. package/lib/linter/zodHelpers.js.map +1 -0
  110. package/lib/list/index.d.ts +8 -0
  111. package/lib/list/index.js +524 -0
  112. package/lib/list/index.js.map +1 -0
  113. package/lib/matrix.d.ts +4 -0
  114. package/lib/matrix.js +66 -0
  115. package/lib/matrix.js.map +1 -0
  116. package/lib/promoter/index.d.ts +65 -0
  117. package/lib/promoter/index.js +1208 -0
  118. package/lib/promoter/index.js.map +1 -0
  119. package/lib/prune/index.d.ts +37 -0
  120. package/lib/prune/index.js +673 -0
  121. package/lib/prune/index.js.map +1 -0
  122. package/lib/sets.d.ts +10 -0
  123. package/lib/sets.js +120 -0
  124. package/lib/sets.js.map +1 -0
  125. package/lib/tester/cliFormat.d.ts +8 -0
  126. package/lib/tester/cliFormat.js +15 -0
  127. package/lib/tester/cliFormat.js.map +1 -0
  128. package/lib/tester/index.d.ts +35 -0
  129. package/lib/tester/index.js +713 -0
  130. package/lib/tester/index.js.map +1 -0
  131. package/lib/tester/matrix.d.ts +14 -0
  132. package/lib/tester/matrix.js +76 -0
  133. package/lib/tester/matrix.js.map +1 -0
  134. package/lib/tester/prettyDuration.d.ts +1 -0
  135. package/lib/tester/prettyDuration.js +30 -0
  136. package/lib/tester/prettyDuration.js.map +1 -0
  137. package/lib/tester/printTestResult.d.ts +2 -0
  138. package/lib/tester/printTestResult.js +32 -0
  139. package/lib/tester/printTestResult.js.map +1 -0
  140. package/lib/tester/types.d.ts +29 -0
  141. package/lib/tester/types.js +3 -0
  142. package/lib/tester/types.js.map +1 -0
  143. package/package.json +41 -13
  144. package/src/benchmark/index.spec.ts +375 -0
  145. package/src/benchmark/index.ts +433 -0
  146. package/src/builder/index.spec.ts +822 -0
  147. package/src/builder/index.ts +920 -0
  148. package/src/cli/index.spec.ts +54 -0
  149. package/src/cli/index.ts +150 -0
  150. package/src/config/index.spec.ts +70 -0
  151. package/src/config/index.ts +259 -0
  152. package/src/create/index.spec.ts +272 -0
  153. package/src/create/index.ts +295 -0
  154. package/src/datasource/filesystemAdapter.ts +313 -0
  155. package/src/datasource/index.ts +135 -0
  156. package/src/error.ts +33 -0
  157. package/src/evaluate/cli.spec.ts +368 -0
  158. package/src/evaluate/cli.ts +130 -0
  159. package/src/evaluate/index.ts +161 -0
  160. package/src/examples/coerceExampleIsoDates.spec.ts +81 -0
  161. package/src/examples/coerceExampleIsoDates.ts +98 -0
  162. package/src/examples/index.spec.ts +453 -0
  163. package/src/examples/index.ts +854 -0
  164. package/src/exporter/index.spec.ts +443 -0
  165. package/src/exporter/index.ts +643 -0
  166. package/src/find-duplicates/index.spec.ts +289 -0
  167. package/src/find-duplicates/index.ts +314 -0
  168. package/src/generate-code/index.ts +92 -0
  169. package/src/generate-code/typescript.spec.ts +241 -0
  170. package/src/generate-code/typescript.ts +284 -0
  171. package/src/importer/index.spec.ts +1101 -0
  172. package/src/importer/index.ts +1190 -0
  173. package/src/index.ts +18 -0
  174. package/src/info/index.ts +67 -0
  175. package/src/init/index.spec.ts +279 -0
  176. package/src/init/index.ts +292 -0
  177. package/src/lint/index.ts +1 -0
  178. package/src/linter/attributeSchema.ts +38 -0
  179. package/src/linter/checkLocaleCircularDependency.ts +51 -0
  180. package/src/linter/conditionSchema.ts +386 -0
  181. package/src/linter/formatSchema.ts +170 -0
  182. package/src/linter/icuStyleLint.ts +312 -0
  183. package/src/linter/index.spec.ts +824 -0
  184. package/src/linter/index.ts +460 -0
  185. package/src/linter/localeSchema.ts +70 -0
  186. package/src/linter/messageSchema.ts +152 -0
  187. package/src/linter/printError.ts +52 -0
  188. package/src/linter/schema.ts +230 -0
  189. package/src/linter/segmentSchema.ts +15 -0
  190. package/src/linter/targetSchema.ts +50 -0
  191. package/src/linter/testSchema.spec.ts +405 -0
  192. package/src/linter/testSchema.ts +239 -0
  193. package/src/linter/zodHelpers.ts +16 -0
  194. package/src/list/index.spec.ts +431 -0
  195. package/src/list/index.ts +463 -0
  196. package/src/matrix.ts +69 -0
  197. package/src/promoter/index.spec.ts +584 -0
  198. package/src/promoter/index.ts +1267 -0
  199. package/src/prune/index.spec.ts +418 -0
  200. package/src/prune/index.ts +693 -0
  201. package/src/sets.ts +74 -0
  202. package/src/tester/cliFormat.ts +11 -0
  203. package/src/tester/featurevisorIntegration.spec.ts +101 -0
  204. package/src/tester/index.spec.ts +577 -0
  205. package/src/tester/index.ts +679 -0
  206. package/src/tester/matrix.ts +106 -0
  207. package/src/tester/prettyDuration.ts +34 -0
  208. package/src/tester/printTestResult.ts +40 -0
  209. package/src/tester/types.ts +32 -0
  210. package/tsconfig.cjs.json +11 -0
  211. package/tsconfig.typecheck.json +4 -0
@@ -0,0 +1,418 @@
1
+ import * as fs from "fs";
2
+ import * as os from "os";
3
+ import * as path from "path";
4
+
5
+ import { getProjectConfig } from "../config";
6
+ import { Datasource } from "../datasource";
7
+
8
+ import { prunePlugin, pruneProject } from "./index";
9
+
10
+ async function writeFile(root: string, relativePath: string, content: string) {
11
+ const filePath = path.join(root, relativePath);
12
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
13
+ await fs.promises.writeFile(filePath, content);
14
+ }
15
+
16
+ async function createProject() {
17
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-prune-"));
18
+
19
+ await writeFile(root, "messagevisor.config.js", "module.exports = {};\n");
20
+ await writeFile(
21
+ root,
22
+ "locales/en.yml",
23
+ [
24
+ "description: English",
25
+ "formats:",
26
+ " number:",
27
+ " currency:",
28
+ " style: currency",
29
+ " currency: USD",
30
+ " date:",
31
+ " short:",
32
+ " year: numeric",
33
+ "",
34
+ ].join("\n"),
35
+ );
36
+ await writeFile(
37
+ root,
38
+ "locales/en-US.yml",
39
+ [
40
+ "description: English US",
41
+ "inheritTranslationsFrom: en",
42
+ "inheritFormatsFrom: en",
43
+ "formats:",
44
+ " number:",
45
+ " currency:",
46
+ " style: currency",
47
+ " currency: USD",
48
+ " currencyCode:",
49
+ " style: currency",
50
+ " currency: USD",
51
+ " currencyDisplay: code",
52
+ " date:",
53
+ " short:",
54
+ " year: numeric",
55
+ "",
56
+ ].join("\n"),
57
+ );
58
+ await writeFile(
59
+ root,
60
+ "locales/en-AU.yml",
61
+ [
62
+ "description: English AU",
63
+ "inheritTranslationsFrom: en-US",
64
+ "inheritFormatsFrom: en-US",
65
+ "",
66
+ ].join("\n"),
67
+ );
68
+ await writeFile(
69
+ root,
70
+ "targets/web.yml",
71
+ ["description: Web", "includeMessages:", " - common*", "locales:", " - en-US", ""].join("\n"),
72
+ );
73
+ await writeFile(
74
+ root,
75
+ "messages/common/welcome.yml",
76
+ [
77
+ "description: Welcome",
78
+ "translations:",
79
+ " en: Welcome",
80
+ " en-US: Welcome",
81
+ " en-AU: Welcome",
82
+ "overrides:",
83
+ " - key: pro",
84
+ " segments: '*'",
85
+ " translations:",
86
+ " en: Welcome pro",
87
+ " en-US: Welcome pro",
88
+ "",
89
+ ].join("\n"),
90
+ );
91
+ await writeFile(
92
+ root,
93
+ "messages/common/goodbye.yml",
94
+ ["description: Goodbye", "translations:", " en: Goodbye", " en-US: Different", ""].join("\n"),
95
+ );
96
+ await writeFile(
97
+ root,
98
+ "messages/admin/secret.yml",
99
+ ["description: Secret", "translations:", " en: Secret", " en-US: Secret", ""].join("\n"),
100
+ );
101
+
102
+ return root;
103
+ }
104
+
105
+ async function createSetsProject() {
106
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-prune-sets-"));
107
+ await writeFile(root, "messagevisor.config.js", "module.exports = { sets: true };\n");
108
+ await writeFile(root, "sets/dev/locales/en.yml", "description: English\n");
109
+ await writeFile(
110
+ root,
111
+ "sets/dev/locales/en-US.yml",
112
+ ["description: English US", "inheritTranslationsFrom: en", ""].join("\n"),
113
+ );
114
+ await writeFile(
115
+ root,
116
+ "sets/dev/messages/common/welcome.yml",
117
+ "description: Welcome\ntranslations:\n en: Welcome\n en-US: Welcome\n",
118
+ );
119
+ await writeFile(root, "sets/staging/locales/en.yml", "description: English\n");
120
+ await writeFile(
121
+ root,
122
+ "sets/staging/locales/en-US.yml",
123
+ ["description: English US", "inheritTranslationsFrom: en", ""].join("\n"),
124
+ );
125
+ await writeFile(
126
+ root,
127
+ "sets/staging/messages/common/welcome.yml",
128
+ "description: Welcome\ntranslations:\n en: Welcome\n en-US: Welcome\n",
129
+ );
130
+
131
+ return root;
132
+ }
133
+
134
+ async function createRecursiveFormatsProject() {
135
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "messagevisor-prune-formats-"));
136
+
137
+ await writeFile(root, "messagevisor.config.js", "module.exports = {};\n");
138
+ await writeFile(
139
+ root,
140
+ "locales/en.yml",
141
+ [
142
+ "description: English",
143
+ "formats:",
144
+ " number:",
145
+ " money:",
146
+ " style: currency",
147
+ " currency: USD",
148
+ "",
149
+ ].join("\n"),
150
+ );
151
+ await writeFile(
152
+ root,
153
+ "locales/en-GB.yml",
154
+ [
155
+ "description: English GB",
156
+ "inheritFormatsFrom: en",
157
+ "formats:",
158
+ " number:",
159
+ " money:",
160
+ " currency: GBP",
161
+ "",
162
+ ].join("\n"),
163
+ );
164
+ await writeFile(
165
+ root,
166
+ "locales/en-GB-x-test.yml",
167
+ [
168
+ "description: English GB test",
169
+ "inheritFormatsFrom: en-GB",
170
+ "formats:",
171
+ " number:",
172
+ " money:",
173
+ " currency: GBP",
174
+ "",
175
+ ].join("\n"),
176
+ );
177
+
178
+ return root;
179
+ }
180
+
181
+ function getDatasource(root: string) {
182
+ const projectConfig = getProjectConfig(root);
183
+ const datasource = new Datasource(projectConfig, root);
184
+
185
+ return { projectConfig, datasource };
186
+ }
187
+
188
+ describe("pruneProject", function () {
189
+ it("reports and applies prune-able inherited message and override translations", async function () {
190
+ const root = await createProject();
191
+ const { projectConfig, datasource } = getDatasource(root);
192
+
193
+ const preview = await pruneProject(projectConfig, datasource, {
194
+ pruneMode: "translations",
195
+ target: "web",
196
+ });
197
+
198
+ expect(preview.changedFiles).toEqual([]);
199
+ expect(preview.entries).toEqual([
200
+ expect.objectContaining({
201
+ kind: "message",
202
+ key: "common.welcome",
203
+ locale: "en-US",
204
+ inheritedFrom: "en",
205
+ }),
206
+ expect.objectContaining({
207
+ kind: "override",
208
+ key: "common.welcome",
209
+ overrideKey: "pro",
210
+ locale: "en-US",
211
+ inheritedFrom: "en",
212
+ }),
213
+ ]);
214
+
215
+ const applied = await pruneProject(projectConfig, datasource, {
216
+ pruneMode: "translations",
217
+ target: "web",
218
+ apply: true,
219
+ });
220
+
221
+ expect(applied.changedFiles).toHaveLength(1);
222
+
223
+ const updated = await datasource.readMessage("common.welcome");
224
+
225
+ expect(updated.translations.en).toEqual("Welcome");
226
+ expect(updated.translations["en-US"]).toBeUndefined();
227
+ expect(updated.translations["en-AU"]).toEqual("Welcome");
228
+ expect(updated.overrides?.[0].translations.en).toEqual("Welcome pro");
229
+ expect(updated.overrides?.[0].translations["en-US"]).toBeUndefined();
230
+ });
231
+
232
+ it("prunes deep duplicate locale formats while preserving true overrides", async function () {
233
+ const root = await createProject();
234
+ const { projectConfig, datasource } = getDatasource(root);
235
+
236
+ const preview = await pruneProject(projectConfig, datasource, {
237
+ pruneMode: "formats",
238
+ target: "web",
239
+ });
240
+
241
+ expect(preview.entries).toEqual(
242
+ expect.arrayContaining([
243
+ expect.objectContaining({
244
+ kind: "locale",
245
+ key: "en-US",
246
+ formatPath: "date.short.year",
247
+ inheritedFrom: "en",
248
+ }),
249
+ expect.objectContaining({
250
+ kind: "locale",
251
+ key: "en-US",
252
+ formatPath: "number.currency.currency",
253
+ inheritedFrom: "en",
254
+ }),
255
+ expect.objectContaining({
256
+ kind: "locale",
257
+ key: "en-US",
258
+ formatPath: "number.currency.style",
259
+ inheritedFrom: "en",
260
+ }),
261
+ ]),
262
+ );
263
+
264
+ await pruneProject(projectConfig, datasource, {
265
+ pruneMode: "formats",
266
+ target: "web",
267
+ apply: true,
268
+ });
269
+
270
+ const updated = await datasource.readLocale("en-US");
271
+
272
+ expect(updated.formats).toEqual({
273
+ number: {
274
+ currencyCode: {
275
+ style: "currency",
276
+ currency: "USD",
277
+ currencyDisplay: "code",
278
+ },
279
+ },
280
+ });
281
+ });
282
+
283
+ it("respects nearest-parent precedence for recursive inherited formats", async function () {
284
+ const root = await createRecursiveFormatsProject();
285
+ const { projectConfig, datasource } = getDatasource(root);
286
+
287
+ const preview = await pruneProject(projectConfig, datasource, {
288
+ pruneMode: "formats",
289
+ locale: "en-GB-x-test",
290
+ });
291
+
292
+ expect(preview.entries).toEqual([
293
+ expect.objectContaining({
294
+ kind: "locale",
295
+ key: "en-GB-x-test",
296
+ formatPath: "number.money.currency",
297
+ inheritedFrom: "en-GB",
298
+ }),
299
+ ]);
300
+
301
+ await pruneProject(projectConfig, datasource, {
302
+ pruneMode: "formats",
303
+ locale: "en-GB-x-test",
304
+ apply: true,
305
+ });
306
+
307
+ const updated = await datasource.readLocale("en-GB-x-test");
308
+
309
+ expect(updated.formats).toBeUndefined();
310
+ });
311
+ });
312
+
313
+ describe("prunePlugin", function () {
314
+ it("requires exactly one target", async function () {
315
+ const root = await createProject();
316
+ const { projectConfig, datasource } = getDatasource(root);
317
+
318
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => undefined);
319
+
320
+ try {
321
+ await expect(
322
+ prunePlugin.handler({
323
+ projectConfig,
324
+ datasource,
325
+ parsed: {},
326
+ }),
327
+ ).resolves.toBe(false);
328
+
329
+ await expect(
330
+ prunePlugin.handler({
331
+ projectConfig,
332
+ datasource,
333
+ parsed: {
334
+ translations: true,
335
+ formats: true,
336
+ },
337
+ }),
338
+ ).resolves.toBe(false);
339
+
340
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
341
+ "Pass exactly one of --translations or --formats.",
342
+ );
343
+ } finally {
344
+ consoleErrorSpy.mockRestore();
345
+ }
346
+ });
347
+
348
+ it("runs across all sets by default in sets-enabled projects", async function () {
349
+ const root = await createSetsProject();
350
+ const { projectConfig, datasource } = getDatasource(root);
351
+ const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => undefined);
352
+
353
+ try {
354
+ await prunePlugin.handler({
355
+ projectConfig,
356
+ datasource,
357
+ parsed: {
358
+ translations: true,
359
+ },
360
+ });
361
+
362
+ const output = consoleLogSpy.mock.calls.flat().join("\n");
363
+
364
+ expect(output).toContain('Set "dev":');
365
+ expect(output).toContain('Set "staging":');
366
+ } finally {
367
+ consoleLogSpy.mockRestore();
368
+ }
369
+ });
370
+
371
+ it("runs for the selected set only when --set is provided", async function () {
372
+ const root = await createSetsProject();
373
+ const { projectConfig, datasource } = getDatasource(root);
374
+
375
+ await prunePlugin.handler({
376
+ projectConfig,
377
+ datasource,
378
+ parsed: {
379
+ translations: true,
380
+ set: "dev",
381
+ apply: true,
382
+ },
383
+ });
384
+
385
+ const devDatasource = datasource.forSet("dev");
386
+ const stagingDatasource = datasource.forSet("staging");
387
+ const devMessage = await devDatasource.readMessage("common.welcome");
388
+ const stagingMessage = await stagingDatasource.readMessage("common.welcome");
389
+
390
+ expect(devMessage.translations["en-US"]).toBeUndefined();
391
+ expect(stagingMessage.translations["en-US"]).toEqual("Welcome");
392
+ });
393
+
394
+ it("prints preview output with inherited source information", async function () {
395
+ const root = await createProject();
396
+ const { projectConfig, datasource } = getDatasource(root);
397
+ const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => undefined);
398
+
399
+ try {
400
+ await prunePlugin.handler({
401
+ projectConfig,
402
+ datasource,
403
+ parsed: {
404
+ translations: true,
405
+ target: "web",
406
+ },
407
+ });
408
+
409
+ const output = consoleLogSpy.mock.calls.flat().join("\n");
410
+
411
+ expect(output).toContain("Prune Messagevisor project");
412
+ expect(output).toContain("common.welcome");
413
+ expect(output).toContain("locale en-US duplicates inherited value from en");
414
+ } finally {
415
+ consoleLogSpy.mockRestore();
416
+ }
417
+ });
418
+ });