@dittowords/cli 4.4.0 → 4.5.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 (87) hide show
  1. package/.github/actions/install-node-dependencies/action.yml +24 -0
  2. package/.github/workflows/required-checks.yml +24 -0
  3. package/__mocks__/fs.js +2 -0
  4. package/bin/__mocks__/api.js +48 -0
  5. package/bin/__mocks__/api.js.map +1 -0
  6. package/bin/config.js +6 -4
  7. package/bin/config.js.map +1 -1
  8. package/bin/config.test.js +4 -3
  9. package/bin/config.test.js.map +1 -1
  10. package/bin/consts.js +19 -29
  11. package/bin/consts.js.map +1 -1
  12. package/bin/generate-suggestions.js +68 -58
  13. package/bin/generate-suggestions.js.map +1 -1
  14. package/bin/generate-suggestions.test.js +24 -13
  15. package/bin/generate-suggestions.test.js.map +1 -1
  16. package/bin/http/__mocks__/fetchComponentFolders.js +71 -0
  17. package/bin/http/__mocks__/fetchComponentFolders.js.map +1 -0
  18. package/bin/http/__mocks__/fetchComponents.js +73 -0
  19. package/bin/http/__mocks__/fetchComponents.js.map +1 -0
  20. package/bin/http/__mocks__/fetchVariants.js +71 -0
  21. package/bin/http/__mocks__/fetchVariants.js.map +1 -0
  22. package/bin/http/fetchComponentFolders.js +4 -4
  23. package/bin/http/fetchComponentFolders.js.map +1 -1
  24. package/bin/http/fetchComponents.js +4 -4
  25. package/bin/http/fetchComponents.js.map +1 -1
  26. package/bin/http/fetchVariants.js +2 -2
  27. package/bin/http/fetchVariants.js.map +1 -1
  28. package/bin/http/http.test.js +159 -0
  29. package/bin/http/http.test.js.map +1 -0
  30. package/bin/http/importComponents.js +9 -2
  31. package/bin/http/importComponents.js.map +1 -1
  32. package/bin/init/project.test.js +5 -28
  33. package/bin/init/project.test.js.map +1 -1
  34. package/bin/init/token.js +72 -27
  35. package/bin/init/token.js.map +1 -1
  36. package/bin/init/token.test.js +87 -9
  37. package/bin/init/token.test.js.map +1 -1
  38. package/bin/pull-lib.test.js +379 -0
  39. package/bin/pull-lib.test.js.map +1 -0
  40. package/bin/pull.js +13 -5
  41. package/bin/pull.js.map +1 -1
  42. package/bin/pull.test.js +100 -289
  43. package/bin/pull.test.js.map +1 -1
  44. package/bin/replace.js +22 -6
  45. package/bin/replace.js.map +1 -1
  46. package/bin/replace.test.js +53 -11
  47. package/bin/replace.test.js.map +1 -1
  48. package/bin/types.js +2 -2
  49. package/bin/types.js.map +1 -1
  50. package/bin/utils/determineModuleType.js +6 -7
  51. package/bin/utils/determineModuleType.js.map +1 -1
  52. package/bin/utils/determineModuleType.test.js +60 -0
  53. package/bin/utils/determineModuleType.test.js.map +1 -0
  54. package/bin/utils/getSelectedProjects.js +5 -5
  55. package/bin/utils/getSelectedProjects.js.map +1 -1
  56. package/bin/utils/quit.js +3 -3
  57. package/bin/utils/quit.js.map +1 -1
  58. package/jest.config.ts +16 -0
  59. package/lib/__mocks__/api.ts +12 -0
  60. package/lib/config.test.ts +3 -1
  61. package/lib/config.ts +4 -2
  62. package/lib/consts.ts +19 -17
  63. package/lib/generate-suggestions.test.ts +23 -11
  64. package/lib/generate-suggestions.ts +89 -79
  65. package/lib/http/__mocks__/fetchComponentFolders.ts +23 -0
  66. package/lib/http/__mocks__/fetchComponents.ts +24 -0
  67. package/lib/http/__mocks__/fetchVariants.ts +21 -0
  68. package/lib/http/fetchComponentFolders.ts +6 -4
  69. package/lib/http/fetchComponents.ts +5 -3
  70. package/lib/http/fetchVariants.ts +14 -3
  71. package/lib/http/http.test.ts +122 -0
  72. package/lib/http/importComponents.ts +8 -0
  73. package/lib/init/project.test.ts +4 -27
  74. package/lib/init/token.test.ts +55 -7
  75. package/lib/init/token.ts +76 -27
  76. package/lib/pull-lib.test.ts +367 -0
  77. package/lib/pull.test.ts +97 -310
  78. package/lib/pull.ts +11 -3
  79. package/lib/replace.test.ts +46 -10
  80. package/lib/replace.ts +20 -3
  81. package/lib/types.ts +5 -0
  82. package/lib/utils/determineModuleType.test.ts +48 -0
  83. package/lib/utils/determineModuleType.ts +4 -6
  84. package/lib/utils/getSelectedProjects.ts +3 -3
  85. package/lib/utils/quit.ts +1 -1
  86. package/package.json +4 -3
  87. package/jest.config.js +0 -6
package/bin/pull.test.js CHANGED
@@ -1,12 +1,26 @@
1
1
  "use strict";
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3d53a7f0-4cb6-57db-948f-db309cf2611a")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4ca59558-b2d6-536a-a5a5-bd049d48303f")}catch(e){}}();
3
3
 
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
9
  var __getProtoOf = Object.getPrototypeOf;
9
10
  var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __spreadValues = (a, b) => {
14
+ for (var prop in b || (b = {}))
15
+ if (__hasOwnProp.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ if (__getOwnPropSymbols)
18
+ for (var prop of __getOwnPropSymbols(b)) {
19
+ if (__propIsEnum.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ }
22
+ return a;
23
+ };
10
24
  var __copyProps = (to, from, except, desc) => {
11
25
  if (from && typeof from === "object" || typeof from === "function") {
12
26
  for (let key of __getOwnPropNames(from))
@@ -43,298 +57,95 @@ var __async = (__this, __arguments, generator) => {
43
57
  step((generator = generator.apply(__this, __arguments)).next());
44
58
  });
45
59
  };
46
- var import_fs = __toESM(require("fs"));
47
- var import_path = __toESM(require("path"));
48
- var import_api = require("./api");
60
+ var import_pull = require("./pull");
61
+ var import_memfs = require("memfs");
49
62
  var import_consts = __toESM(require("./consts"));
50
- var import_pull = __toESM(require("./pull"));
51
- jest.mock("./api", () => ({
52
- createApiClient: jest.fn()
53
- // this needs to be mocked in each test that requires it
54
- }));
55
- const testProjects = [
56
- {
57
- id: "1",
58
- name: "Project 1",
59
- fileName: "Project 1"
60
- },
61
- { id: "2", name: "Project 2", fileName: "Project 2" }
62
- ];
63
- const mockApi = (0, import_api.createApiClient)();
64
- jest.mock("./consts", () => ({
65
- TEXT_DIR: ".testing",
66
- API_HOST: "https://api.dittowords.com",
67
- CONFIG_FILE: ".testing/ditto",
68
- PROJECT_CONFIG_FILE: ".testing/config.yml",
69
- TEXT_FILE: ".testing/text.json"
70
- }));
71
- const {
72
- _testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase }
73
- } = import_pull.default;
74
- const variant = "english";
75
- const cleanOutputDir = () => {
76
- if (import_fs.default.existsSync(import_consts.default.TEXT_DIR))
77
- import_fs.default.rmSync(import_consts.default.TEXT_DIR, { recursive: true, force: true });
78
- import_fs.default.mkdirSync(import_consts.default.TEXT_DIR);
79
- };
80
- afterAll(() => {
81
- import_fs.default.rmSync(import_consts.default.TEXT_DIR, { force: true, recursive: true });
82
- });
83
- describe("cleanOutputFiles", () => {
84
- it("removes .js, .json, .xml, .strings, .stringsdict files", () => {
85
- cleanOutputDir();
86
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.json"), "test");
87
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.js"), "test");
88
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.xml"), "test");
89
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.strings"), "test");
90
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.stringsdict"), "test");
91
- import_fs.default.writeFileSync(import_path.default.resolve(import_consts.default.TEXT_DIR, "test.txt"), "test");
92
- expect(import_fs.default.readdirSync(import_consts.default.TEXT_DIR).length).toEqual(6);
93
- cleanOutputFiles();
94
- expect(import_fs.default.readdirSync(import_consts.default.TEXT_DIR).length).toEqual(1);
95
- });
63
+ var import_globals = require("@jest/globals");
64
+ var import_axios = __toESM(require("axios"));
65
+ var import_fs = __toESM(require("fs"));
66
+ const axiosMock = import_globals.jest.mocked(import_axios.default);
67
+ import_globals.jest.mock("fs");
68
+ import_globals.jest.mock("./api");
69
+ import_globals.jest.mock("./http/fetchComponentFolders");
70
+ import_globals.jest.mock("./http/fetchComponents");
71
+ import_globals.jest.mock("./http/fetchVariants");
72
+ const defaultEnv = __spreadValues({}, process.env);
73
+ beforeEach(() => {
74
+ import_memfs.vol.reset();
75
+ process.env = __spreadValues({}, defaultEnv);
96
76
  });
97
- describe("downloadAndSaveBase", () => {
98
- beforeAll(() => {
99
- if (!import_fs.default.existsSync(import_consts.default.TEXT_DIR)) {
100
- import_fs.default.mkdirSync(import_consts.default.TEXT_DIR);
101
- }
102
- });
103
- beforeEach(() => {
104
- cleanOutputDir();
105
- });
106
- const mockDataFlat = { hello: "world" };
107
- const mockDataNested = { hello: { text: "world" } };
108
- const mockDataStructured = { hello: { text: "world" } };
109
- const mockDataIcu = { hello: "world" };
110
- const mockDataAndroid = `
111
- <?xml version="1.0" encoding="utf-8"?>
112
- <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
113
- <string name="hello-world" ditto_api_id="hello-world">Hello World</string>
114
- </resources>
115
- `;
116
- const mockDataIosStrings = `
117
- "hello" = "world";
118
- `;
119
- const mockDataIosStringsDict = `
120
- <?xml version="1.0" encoding="utf-8"?>
121
- <plist version="1.0">
122
- <dict>
123
- <key>hello-world</key>
124
- <dict>
125
- <key>NSStringLocalizedFormatKey</key>
126
- <string>%1$#@count@</string>
127
- <key>count</key>
128
- <dict>
129
- <key>NSStringFormatSpecTypeKey</key>
130
- <string>NSStringPluralRuleType</string>
131
- <key>NSStringFormatValueTypeKey</key>
132
- <string>d</string>
133
- <key>other</key>
134
- <string>espanol</string>
135
- </dict>
136
- </dict>
137
- </dict>
138
- </plist>
139
- `;
140
- const formats = [
141
- { format: "flat", data: mockDataFlat, ext: ".json" },
142
- { format: "nested", data: mockDataNested, ext: ".json" },
143
- { format: "structured", data: mockDataStructured, ext: ".json" },
144
- { format: "icu", data: mockDataIcu, ext: ".json" },
145
- { format: "android", data: mockDataAndroid, ext: ".xml" },
146
- { format: "ios-strings", data: mockDataIosStrings, ext: ".strings" },
147
- {
148
- format: "ios-stringsdict",
149
- data: mockDataIosStringsDict,
150
- ext: ".stringsdict"
151
- }
152
- ];
153
- const mockApiCall = (data) => {
154
- import_api.createApiClient.mockImplementation(() => ({
155
- get: () => ({ data })
156
- }));
157
- };
158
- const verifySavedData = (format, data, ext) => __async(exports, null, function* () {
159
- const output = yield downloadAndSaveBase({
160
- projects: testProjects,
161
- format
77
+ const mockGlobalConfigFile = `
78
+ api.dittowords.com:
79
+ - token: xxx-xxx-xxx
80
+ `;
81
+ const mockProjectConfigFile = `
82
+ sources:
83
+ components: true
84
+ projects:
85
+ - id: project-id-1
86
+ name: Test Project
87
+ variants: true
88
+ `;
89
+ describe("pull", () => {
90
+ it("correctly writes files to disk per source for basic config", () => __async(exports, null, function* () {
91
+ process.env.DITTO_TEXT_DIR = "/ditto";
92
+ process.env.DITTO_PROJECT_CONFIG_FILE = "/ditto/config.yml";
93
+ axiosMock.get.mockImplementation(
94
+ () => Promise.resolve({ data: "data" })
95
+ );
96
+ import_memfs.vol.fromJSON({
97
+ [import_consts.default.CONFIG_FILE]: mockGlobalConfigFile,
98
+ [import_consts.default.PROJECT_CONFIG_FILE]: mockProjectConfigFile
99
+ });
100
+ yield (0, import_pull.pull)();
101
+ const filesOnDiskExpected = /* @__PURE__ */ new Set([
102
+ "components__example-folder__base.json",
103
+ "components__example-folder__example-variant-1.json",
104
+ "components__example-folder__example-variant-2.json",
105
+ "components__root__base.json",
106
+ "components__root__example-variant-1.json",
107
+ "components__root__example-variant-2.json",
108
+ "test-project__base.json",
109
+ "test-project__example-variant-1.json",
110
+ "test-project__example-variant-2.json",
111
+ "index.d.ts",
112
+ "index.js"
113
+ ]);
114
+ const filesOnDisk = import_fs.default.readdirSync("/ditto");
115
+ filesOnDisk.forEach((file) => {
116
+ filesOnDiskExpected.delete(file);
162
117
  });
163
- expect(/successfully saved/i.test(output)).toEqual(true);
164
- const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
165
- expect(directoryContents.length).toEqual(testProjects.length);
166
- expect(directoryContents.every((f) => f.endsWith(ext))).toBe(true);
167
- const fileDataString = import_fs.default.readFileSync(
168
- import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
169
- "utf8"
118
+ expect(filesOnDiskExpected.size).toBe(0);
119
+ }));
120
+ it("correctly does not write index.js or index.d.ts when `disableJsDriver: true` is specified", () => __async(exports, null, function* () {
121
+ process.env.DITTO_TEXT_DIR = "/ditto";
122
+ process.env.DITTO_PROJECT_CONFIG_FILE = "/ditto/config.yml";
123
+ axiosMock.get.mockImplementation(
124
+ () => Promise.resolve({ data: "data" })
170
125
  );
171
- switch (format) {
172
- case "android":
173
- case "ios-strings":
174
- case "ios-stringsdict":
175
- expect(typeof data).toBe("string");
176
- expect(fileDataString.replace(/\s/g, "")).toEqual(
177
- data.replace(/\s/g, "")
178
- );
179
- break;
180
- case "flat":
181
- case "nested":
182
- case "structured":
183
- case "icu":
184
- default:
185
- expect(JSON.parse(fileDataString)).toEqual(data);
186
- break;
187
- }
188
- });
189
- formats.forEach(({ format, data, ext }) => {
190
- it(`writes the ${format} format to disk`, () => __async(exports, null, function* () {
191
- mockApiCall(data);
192
- yield verifySavedData(format, data, ext);
193
- }));
194
- });
195
- });
196
- describe("getFormatDataIsValid", () => {
197
- it("handles flat format appropriately", () => {
198
- expect(import_pull.getFormatDataIsValid.flat("{}")).toBe(false);
199
- expect(import_pull.getFormatDataIsValid.flat(`{ "hello": "world" }`)).toBe(true);
200
- expect(
201
- import_pull.getFormatDataIsValid.flat(`{
202
- "__variant-name": "English",
203
- "__variant-description": ""
204
- }`)
205
- ).toBe(false);
206
- expect(
207
- import_pull.getFormatDataIsValid.flat(`{
208
- "__variant-name": "English",
209
- "__variant-description": "",
210
- "hello": "world"
211
- }`)
212
- ).toBe(true);
213
- });
214
- it("handles structured format appropriately", () => {
215
- expect(import_pull.getFormatDataIsValid.structured("{}")).toBe(false);
216
- expect(
217
- import_pull.getFormatDataIsValid.structured(`{ "hello": { "text": "world" } }`)
218
- ).toBe(true);
219
- expect(
220
- import_pull.getFormatDataIsValid.structured(`{
221
- "__variant-name": "English",
222
- "__variant-description": ""
223
- }`)
224
- ).toBe(false);
225
- expect(
226
- import_pull.getFormatDataIsValid.structured(`{
227
- "__variant-name": "English",
228
- "__variant-description": "",
229
- "hello": { "text": "world" }
230
- }`)
231
- ).toBe(true);
232
- });
233
- it("handles icu format appropriately", () => {
234
- expect(import_pull.getFormatDataIsValid.icu("{}")).toBe(false);
235
- expect(import_pull.getFormatDataIsValid.icu(`{ "hello": "world" }`)).toBe(true);
236
- expect(
237
- import_pull.getFormatDataIsValid.icu(`{
238
- "__variant-name": "English",
239
- "__variant-description": ""
240
- }`)
241
- ).toBe(false);
242
- expect(
243
- import_pull.getFormatDataIsValid.icu(`{
244
- "__variant-name": "English",
245
- "__variant-description": "",
246
- "hello": "world"
247
- }`)
248
- ).toBe(true);
249
- });
250
- it("handles android format appropriately", () => {
251
- expect(
252
- import_pull.getFormatDataIsValid.android(`
253
- <?xml version="1.0" encoding="utf-8"?>
254
- <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"/>
255
- `)
256
- ).toBe(false);
257
- expect(
258
- import_pull.getFormatDataIsValid.android(`
259
- <?xml version="1.0" encoding="utf-8"?>
260
- <!--Variant Name: English-->
261
- <!--Variant Description: -->
262
- <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"/>
263
- `)
264
- ).toBe(false);
265
- expect(
266
- import_pull.getFormatDataIsValid.android(`
267
- <?xml version="1.0" encoding="utf-8"?>
268
- <!--Variant Name: English-->
269
- <!--Variant Description: -->
270
- <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
271
- <string name="hello-world" ditto_api_id="hello-world">Hello World</string>
272
- </resources>
273
- `)
274
- ).toBe(true);
275
- });
276
- it("handles ios-strings format appropriately", () => {
277
- expect(import_pull.getFormatDataIsValid["ios-strings"]("")).toBe(false);
278
- expect(
279
- import_pull.getFormatDataIsValid["ios-strings"](`
280
- /* Variant Name: English */
281
- /* Variant Description: */
282
- `)
283
- ).toBe(false);
284
- expect(
285
- import_pull.getFormatDataIsValid["ios-strings"](`
286
- /* Variant Name: English */
287
- /* Variant Description: */
288
- "Hello" = "World";
289
- `)
290
- ).toBe(true);
291
- });
292
- it("handles ios-stringsdict format appropriately", () => {
293
- expect(
294
- import_pull.getFormatDataIsValid["ios-stringsdict"](`
295
- <?xml version="1.0" encoding="utf-8"?>
296
- <plist version="1.0">
297
- <dict/>
298
- </plist>
299
- `)
300
- ).toBe(false);
301
- expect(
302
- import_pull.getFormatDataIsValid["ios-stringsdict"](`
303
- <?xml version="1.0" encoding="utf-8"?>
304
- <!--Variant Name: English-->
305
- <!--Variant Description: -->
306
- <plist version="1.0">
307
- <dict/>
308
- </plist>
309
- `)
310
- ).toBe(false);
311
- expect(
312
- import_pull.getFormatDataIsValid["ios-stringsdict"](`
313
- <?xml version="1.0" encoding="utf-8"?>
314
- <!--Variant Name: English-->
315
- <!--Variant Description: -->
316
- <plist version="1.0">
317
- <dict>
318
- <key>Hello World</key>
319
- <dict>
320
- <key>NSStringLocalizedFormatKey</key>
321
- <string>%1$#@count@</string>
322
- <key>count</key>
323
- <dict>
324
- <key>NSStringFormatSpecTypeKey</key>
325
- <string>NSStringPluralRuleType</string>
326
- <key>NSStringFormatValueTypeKey</key>
327
- <string>d</string>
328
- <key>other</key>
329
- <string>espanol</string>
330
- </dict>
331
- </dict>
332
- </dict>
333
- </plist>
334
- `)
335
- ).toBe(true);
336
- });
126
+ import_memfs.vol.fromJSON({
127
+ [import_consts.default.CONFIG_FILE]: mockGlobalConfigFile,
128
+ [import_consts.default.PROJECT_CONFIG_FILE]: mockProjectConfigFile + "\ndisableJsDriver: true"
129
+ });
130
+ yield (0, import_pull.pull)();
131
+ const filesOnDiskExpected = /* @__PURE__ */ new Set([
132
+ "components__example-folder__base.json",
133
+ "components__example-folder__example-variant-1.json",
134
+ "components__example-folder__example-variant-2.json",
135
+ "components__root__base.json",
136
+ "components__root__example-variant-1.json",
137
+ "components__root__example-variant-2.json",
138
+ "test-project__base.json",
139
+ "test-project__example-variant-1.json",
140
+ "test-project__example-variant-2.json"
141
+ ]);
142
+ const filesOnDisk = import_fs.default.readdirSync("/ditto");
143
+ filesOnDisk.forEach((file) => {
144
+ filesOnDiskExpected.delete(file);
145
+ });
146
+ expect(filesOnDiskExpected.size).toBe(0);
147
+ }));
337
148
  });
338
149
  //# sourceMappingURL=pull.test.js.map
339
150
 
340
- //# debugId=3d53a7f0-4cb6-57db-948f-db309cf2611a
151
+ //# debugId=4ca59558-b2d6-536a-a5a5-bd049d48303f
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/pull.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\njest.mock(\"./api\", () => ({\n createApiClient: jest.fn(), // this needs to be mocked in each test that requires it\n}));\nimport { createApiClient } from \"./api\";\n\nconst testProjects: Project[] = [\n {\n id: \"1\",\n name: \"Project 1\",\n fileName: \"Project 1\",\n },\n { id: \"2\", name: \"Project 2\", fileName: \"Project 2\" },\n];\n\n// TODO: all tests in this file currently failing because we're re-instantiating the api client\n// everywhere and are unable to mock the return type separately for each instance of usage.\n// We need to refactor to share one api client everywhere instead of always re-creating it.\nconst mockApi = createApiClient() as any as jest.Mocked<\n ReturnType<typeof createApiClient>\n>;\n\njest.mock(\"./consts\", () => ({\n TEXT_DIR: \".testing\",\n API_HOST: \"https://api.dittowords.com\",\n CONFIG_FILE: \".testing/ditto\",\n PROJECT_CONFIG_FILE: \".testing/config.yml\",\n TEXT_FILE: \".testing/text.json\",\n}));\n\nimport consts from \"./consts\";\nimport allPull, { getFormatDataIsValid } from \"./pull\";\nimport { Project, SupportedExtension, SupportedFormat } from \"./types\";\n\nconst {\n _testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase },\n} = allPull;\nconst variant = \"english\";\n\nconst cleanOutputDir = () => {\n if (fs.existsSync(consts.TEXT_DIR))\n fs.rmSync(consts.TEXT_DIR, { recursive: true, force: true });\n\n fs.mkdirSync(consts.TEXT_DIR);\n};\n\nafterAll(() => {\n fs.rmSync(consts.TEXT_DIR, { force: true, recursive: true });\n});\n\ndescribe(\"cleanOutputFiles\", () => {\n it(\"removes .js, .json, .xml, .strings, .stringsdict files\", () => {\n cleanOutputDir();\n\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.json\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.js\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.xml\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.strings\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.stringsdict\"), \"test\");\n // this file shouldn't be deleted\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.txt\"), \"test\");\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(6);\n\n cleanOutputFiles();\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(1);\n });\n});\n\ndescribe(\"downloadAndSaveBase\", () => {\n beforeAll(() => {\n if (!fs.existsSync(consts.TEXT_DIR)) {\n fs.mkdirSync(consts.TEXT_DIR);\n }\n });\n\n beforeEach(() => {\n cleanOutputDir();\n });\n\n const mockDataFlat = { hello: \"world\" };\n const mockDataNested = { hello: { text: \"world\" } };\n const mockDataStructured = { hello: { text: \"world\" } };\n const mockDataIcu = { hello: \"world\" };\n const mockDataAndroid = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `;\n const mockDataIosStrings = `\n \"hello\" = \"world\"; \n `;\n const mockDataIosStringsDict = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict>\n <key>hello-world</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `;\n\n const formats: Array<{\n format: SupportedFormat;\n data: Object;\n ext: SupportedExtension;\n }> = [\n { format: \"flat\", data: mockDataFlat, ext: \".json\" },\n { format: \"nested\", data: mockDataNested, ext: \".json\" },\n { format: \"structured\", data: mockDataStructured, ext: \".json\" },\n { format: \"icu\", data: mockDataIcu, ext: \".json\" },\n { format: \"android\", data: mockDataAndroid, ext: \".xml\" },\n { format: \"ios-strings\", data: mockDataIosStrings, ext: \".strings\" },\n {\n format: \"ios-stringsdict\",\n data: mockDataIosStringsDict,\n ext: \".stringsdict\",\n },\n ];\n\n const mockApiCall = (data: unknown) => {\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data }),\n }));\n };\n\n const verifySavedData = async (\n format: SupportedFormat,\n data: unknown,\n ext: SupportedExtension\n ) => {\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: format,\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(ext))).toBe(true);\n const fileDataString = fs.readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n );\n\n switch (format) {\n case \"android\":\n case \"ios-strings\":\n case \"ios-stringsdict\":\n expect(typeof data).toBe(\"string\");\n expect(fileDataString.replace(/\\s/g, \"\")).toEqual(\n (data as string).replace(/\\s/g, \"\")\n );\n break;\n\n case \"flat\":\n case \"nested\":\n case \"structured\":\n case \"icu\":\n default:\n expect(JSON.parse(fileDataString)).toEqual(data);\n break;\n }\n };\n\n formats.forEach(({ format, data, ext }) => {\n it(`writes the ${format} format to disk`, async () => {\n mockApiCall(data);\n await verifySavedData(format, data, ext);\n });\n });\n});\n\ndescribe(\"getFormatDataIsValid\", () => {\n it(\"handles flat format appropriately\", () => {\n expect(getFormatDataIsValid.flat(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.flat(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles structured format appropriately\", () => {\n expect(getFormatDataIsValid.structured(\"{}\")).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{ \"hello\": { \"text\": \"world\" } }`)\n ).toBe(true);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": { \"text\": \"world\" }\n }`)\n ).toBe(true);\n });\n it(\"handles icu format appropriately\", () => {\n expect(getFormatDataIsValid.icu(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.icu(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles android format appropriately\", () => {\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `)\n ).toBe(true);\n });\n it(\"handles ios-strings format appropriately\", () => {\n expect(getFormatDataIsValid[\"ios-strings\"](\"\")).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n \"Hello\" = \"World\";\n `)\n ).toBe(true);\n });\n it(\"handles ios-stringsdict format appropriately\", () => {\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict>\n <key>Hello World</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `)\n ).toBe(true);\n });\n});\n"],"names":["allPull","fs","consts","path"],"mappings":"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","debug_id":"3d53a7f0-4cb6-57db-948f-db309cf2611a"}
1
+ {"version":3,"sources":["../lib/pull.test.ts"],"sourcesContent":["import { pull } from \"./pull\";\nimport { vol } from \"memfs\";\nimport consts from \"./consts\";\nimport { jest } from \"@jest/globals\";\nimport axios from \"axios\";\nconst axiosMock = jest.mocked(axios);\nimport fs from \"fs\";\n\njest.mock(\"fs\");\njest.mock(\"./api\");\n\njest.mock(\"./http/fetchComponentFolders\");\njest.mock(\"./http/fetchComponents\");\njest.mock(\"./http/fetchVariants\");\n\nconst defaultEnv = { ...process.env };\n\nbeforeEach(() => {\n vol.reset();\n process.env = { ...defaultEnv };\n});\n\nconst mockGlobalConfigFile = `\napi.dittowords.com:\n - token: xxx-xxx-xxx\n`;\nconst mockProjectConfigFile = `\nsources:\n components: true\n projects:\n - id: project-id-1\n name: Test Project\nvariants: true\n`;\n\ndescribe(\"pull\", () => {\n it(\"correctly writes files to disk per source for basic config\", async () => {\n process.env.DITTO_TEXT_DIR = \"/ditto\";\n process.env.DITTO_PROJECT_CONFIG_FILE = \"/ditto/config.yml\";\n\n // we need to manually mock responses for the http calls that happen\n // directly within the pull function; we don't need to mock the http\n // calls that happen by way of http/* function calls since those have\n // their own mocks already.\n axiosMock.get.mockImplementation(\n (): Promise<any> => Promise.resolve({ data: \"data\" })\n );\n\n vol.fromJSON({\n [consts.CONFIG_FILE]: mockGlobalConfigFile,\n [consts.PROJECT_CONFIG_FILE]: mockProjectConfigFile,\n });\n\n await pull();\n\n const filesOnDiskExpected = new Set([\n \"components__example-folder__base.json\",\n \"components__example-folder__example-variant-1.json\",\n \"components__example-folder__example-variant-2.json\",\n \"components__root__base.json\",\n \"components__root__example-variant-1.json\",\n \"components__root__example-variant-2.json\",\n \"test-project__base.json\",\n \"test-project__example-variant-1.json\",\n \"test-project__example-variant-2.json\",\n \"index.d.ts\",\n \"index.js\",\n ]);\n\n const filesOnDisk = fs.readdirSync(\"/ditto\");\n filesOnDisk.forEach((file) => {\n filesOnDiskExpected.delete(file);\n });\n\n expect(filesOnDiskExpected.size).toBe(0);\n });\n\n it(\"correctly does not write index.js or index.d.ts when `disableJsDriver: true` is specified\", async () => {\n process.env.DITTO_TEXT_DIR = \"/ditto\";\n process.env.DITTO_PROJECT_CONFIG_FILE = \"/ditto/config.yml\";\n\n // we need to manually mock responses for the http calls that happen\n // directly within the pull function; we don't need to mock the http\n // calls that happen by way of http/* function calls since those have\n // their own mocks already.\n axiosMock.get.mockImplementation(\n (): Promise<any> => Promise.resolve({ data: \"data\" })\n );\n\n vol.fromJSON({\n [consts.CONFIG_FILE]: mockGlobalConfigFile,\n [consts.PROJECT_CONFIG_FILE]:\n mockProjectConfigFile + \"\\n\" + \"disableJsDriver: true\",\n });\n\n await pull();\n\n const filesOnDiskExpected = new Set([\n \"components__example-folder__base.json\",\n \"components__example-folder__example-variant-1.json\",\n \"components__example-folder__example-variant-2.json\",\n \"components__root__base.json\",\n \"components__root__example-variant-1.json\",\n \"components__root__example-variant-2.json\",\n \"test-project__base.json\",\n \"test-project__example-variant-1.json\",\n \"test-project__example-variant-2.json\",\n ]);\n\n const filesOnDisk = fs.readdirSync(\"/ditto\");\n filesOnDisk.forEach((file) => {\n filesOnDiskExpected.delete(file);\n });\n\n expect(filesOnDiskExpected.size).toBe(0);\n });\n});\n"],"names":["axios","consts","fs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kBAAqB;AACrB,mBAAoB;AACpB,oBAAmB;AACnB,qBAAqB;AACrB,mBAAkB;AAElB,gBAAe;AADf,MAAM,YAAY,oBAAK,OAAO,aAAAA,OAAK;AAGnC,oBAAK,KAAK,IAAI;AACd,oBAAK,KAAK,OAAO;AAEjB,oBAAK,KAAK,8BAA8B;AACxC,oBAAK,KAAK,wBAAwB;AAClC,oBAAK,KAAK,sBAAsB;AAEhC,MAAM,aAAa,mBAAK,QAAQ;AAEhC,WAAW,MAAM;AACf,mBAAI,MAAM;AACV,UAAQ,MAAM,mBAAK;AACrB,CAAC;AAED,MAAM,uBAAuB;AAAA;AAAA;AAAA;AAI7B,MAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS9B,SAAS,QAAQ,MAAM;AACrB,KAAG,8DAA8D,MAAY;AAC3E,YAAQ,IAAI,iBAAiB;AAC7B,YAAQ,IAAI,4BAA4B;AAMxC,cAAU,IAAI;AAAA,MACZ,MAAoB,QAAQ,QAAQ,EAAE,MAAM,OAAO,CAAC;AAAA,IACtD;AAEA,qBAAI,SAAS;AAAA,MACX,CAAC,cAAAC,QAAO,WAAW,GAAG;AAAA,MACtB,CAAC,cAAAA,QAAO,mBAAmB,GAAG;AAAA,IAChC,CAAC;AAED,cAAM,kBAAK;AAEX,UAAM,sBAAsB,oBAAI,IAAI;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,cAAc,UAAAC,QAAG,YAAY,QAAQ;AAC3C,gBAAY,QAAQ,CAAC,SAAS;AAC5B,0BAAoB,OAAO,IAAI;AAAA,IACjC,CAAC;AAED,WAAO,oBAAoB,IAAI,EAAE,KAAK,CAAC;AAAA,EACzC,EAAC;AAED,KAAG,6FAA6F,MAAY;AAC1G,YAAQ,IAAI,iBAAiB;AAC7B,YAAQ,IAAI,4BAA4B;AAMxC,cAAU,IAAI;AAAA,MACZ,MAAoB,QAAQ,QAAQ,EAAE,MAAM,OAAO,CAAC;AAAA,IACtD;AAEA,qBAAI,SAAS;AAAA,MACX,CAAC,cAAAD,QAAO,WAAW,GAAG;AAAA,MACtB,CAAC,cAAAA,QAAO,mBAAmB,GACzB,wBAAwB;AAAA,IAC5B,CAAC;AAED,cAAM,kBAAK;AAEX,UAAM,sBAAsB,oBAAI,IAAI;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,cAAc,UAAAC,QAAG,YAAY,QAAQ;AAC3C,gBAAY,QAAQ,CAAC,SAAS;AAC5B,0BAAoB,OAAO,IAAI;AAAA,IACjC,CAAC;AAED,WAAO,oBAAoB,IAAI,EAAE,KAAK,CAAC;AAAA,EACzC,EAAC;AACH,CAAC","debug_id":"4ca59558-b2d6-536a-a5a5-bd049d48303f"}
package/bin/replace.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="68dcdcf7-5081-5b09-9769-c28ddf977785")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="76d5bf23-fec4-53e2-9669-a83a306dc53e")}catch(e){}}();
3
3
 
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -55,14 +55,22 @@ __export(replace_exports, {
55
55
  replaceJSXTextInFile: () => replaceJSXTextInFile
56
56
  });
57
57
  module.exports = __toCommonJS(replace_exports);
58
- var import_fs_extra = __toESM(require("fs-extra"));
58
+ var import_fs = __toESM(require("fs"));
59
59
  var import_parser = require("@babel/parser");
60
60
  var import_traverse = __toESM(require("@babel/traverse"));
61
61
  var t = __toESM(require("@babel/types"));
62
62
  var import_core = require("@babel/core");
63
63
  function replaceJSXTextInFile(filePath, replacement, flags) {
64
64
  return __async(this, null, function* () {
65
- const code = yield import_fs_extra.default.readFile(filePath, "utf-8");
65
+ const code = yield new Promise(
66
+ (resolve, reject) => import_fs.default.readFile(filePath, "utf-8", (err, data) => {
67
+ if (err) {
68
+ reject(err);
69
+ } else {
70
+ resolve(data);
71
+ }
72
+ })
73
+ );
66
74
  const ast = (0, import_parser.parse)(code, {
67
75
  sourceType: "module",
68
76
  plugins: ["jsx", "typescript"]
@@ -107,7 +115,15 @@ function replaceJSXTextInFile(filePath, replacement, flags) {
107
115
  /* @ts-ignore */
108
116
  configFile: false
109
117
  });
110
- import_fs_extra.default.writeFile(filePath, transformedCode);
118
+ yield new Promise(
119
+ (resolve, reject) => import_fs.default.writeFile(filePath, transformedCode, (err) => {
120
+ if (err) {
121
+ reject(err);
122
+ } else {
123
+ resolve(null);
124
+ }
125
+ })
126
+ );
111
127
  });
112
128
  }
113
129
  function splitByCaseInsensitive(str, delimiter) {
@@ -138,7 +154,7 @@ function parseOptions(options) {
138
154
  );
139
155
  }
140
156
  const filePath = options[0];
141
- const isFilePathValid = import_fs_extra.default.existsSync(filePath) && import_fs_extra.default.lstatSync(filePath).isFile();
157
+ const isFilePathValid = import_fs.default.existsSync(filePath) && import_fs.default.lstatSync(filePath).isFile();
142
158
  if (!isFilePathValid) {
143
159
  throw new Error(`${filePath} is not a valid file path.`);
144
160
  }
@@ -152,4 +168,4 @@ function parseOptions(options) {
152
168
  });
153
169
  //# sourceMappingURL=replace.js.map
154
170
 
155
- //# debugId=68dcdcf7-5081-5b09-9769-c28ddf977785
171
+ //# debugId=76d5bf23-fec4-53e2-9669-a83a306dc53e
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/replace.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport { parse } from \"@babel/parser\";\nimport traverse from \"@babel/traverse\";\nimport * as t from \"@babel/types\";\nimport { transformFromAst } from \"@babel/core\";\n\nasync function replaceJSXTextInFile(\n filePath: string,\n replacement: { searchString: string; replaceWith: string },\n flags: {\n lineNumbers?: number[];\n }\n) {\n const code = await fs.readFile(filePath, \"utf-8\");\n const ast = parse(code, {\n sourceType: \"module\",\n plugins: [\"jsx\", \"typescript\"],\n });\n\n traverse(ast, {\n JSXText(path) {\n const { searchString, replaceWith } = replacement;\n\n const searchStringEscaped = searchString.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n \"\\\\$&\"\n );\n const regex = new RegExp(searchStringEscaped, \"gi\");\n if (regex.test(path.node.value)) {\n // Ignore if not on a line number that we want to replace.\n if (\n flags.lineNumbers &&\n path.node.loc &&\n !flags.lineNumbers.includes(path.node.loc.start.line)\n ) {\n return;\n }\n\n const splitValues = splitByCaseInsensitive(\n path.node.value,\n searchStringEscaped\n );\n const nodes: (t.JSXElement | t.JSXText)[] = [];\n\n splitValues.forEach((splitValue) => {\n if (splitValue.toLowerCase() === searchString.toLowerCase()) {\n const identifier = t.jsxIdentifier(\"DittoComponent\");\n const componentId = t.jsxAttribute(\n t.jsxIdentifier(\"componentId\"),\n t.stringLiteral(replaceWith)\n );\n const o = t.jsxOpeningElement(identifier, [componentId], true);\n const jsxElement = t.jsxElement(o, undefined, [], true);\n nodes.push(jsxElement);\n } else {\n nodes.push(t.jsxText(splitValue));\n }\n });\n\n path.replaceWithMultiple(nodes);\n }\n },\n });\n\n // transfromFromAst types are wrong?\n /* @ts-ignore */\n const { code: transformedCode } = transformFromAst(ast, code, {\n // Don't let this codebase's Babel config affect the code we're transforming.\n /* @ts-ignore */\n configFile: false,\n });\n fs.writeFile(filePath, transformedCode);\n}\n\nfunction splitByCaseInsensitive(str: string, delimiter: string) {\n return str.split(new RegExp(`(${delimiter})`, \"gi\")).filter((s) => s !== \"\");\n}\n\nfunction replace(options: string[], flags: { lineNumbers?: number[] }) {\n let filePath: string;\n let searchString: string;\n let replaceWith: string;\n\n try {\n const parsedOptions = parseOptions(options);\n filePath = parsedOptions.filePath;\n searchString = parsedOptions.searchString;\n replaceWith = parsedOptions.replaceWith;\n } catch (e) {\n console.error(e);\n console.error(\n \"Usage for replace: ditto-cli replace <file path> <search string> <replace with>\"\n );\n return;\n }\n\n replaceJSXTextInFile(filePath, { searchString, replaceWith }, flags);\n}\n\nfunction parseOptions(options: string[]): {\n filePath: string;\n searchString: string;\n replaceWith: string;\n} {\n if (options.length !== 3) {\n throw new Error(\n \"The options array must contain <file path> <search string> <replace with>.\"\n );\n }\n\n const filePath = options[0];\n // Check if the file path exists and points to a regular file (not a directory or other file system object).\n const isFilePathValid =\n fs.existsSync(filePath) && fs.lstatSync(filePath).isFile();\n\n if (!isFilePathValid) {\n throw new Error(`${filePath} is not a valid file path.`);\n }\n\n return { filePath, searchString: options[1], replaceWith: options[2] };\n}\n\nexport { replace, parseOptions, replaceJSXTextInFile };\n"],"names":["fs","traverse"],"mappings":"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","debug_id":"68dcdcf7-5081-5b09-9769-c28ddf977785"}
1
+ {"version":3,"sources":["../lib/replace.ts"],"sourcesContent":["import fs from \"fs\";\nimport { parse } from \"@babel/parser\";\nimport traverse from \"@babel/traverse\";\nimport * as t from \"@babel/types\";\nimport { transformFromAst } from \"@babel/core\";\n\nasync function replaceJSXTextInFile(\n filePath: string,\n replacement: { searchString: string; replaceWith: string },\n flags: {\n lineNumbers?: number[];\n }\n) {\n const code = await new Promise<string>((resolve, reject) =>\n fs.readFile(filePath, \"utf-8\", (err, data) => {\n if (err) {\n reject(err);\n } else {\n resolve(data);\n }\n })\n );\n const ast = parse(code, {\n sourceType: \"module\",\n plugins: [\"jsx\", \"typescript\"],\n });\n\n traverse(ast, {\n JSXText(path) {\n const { searchString, replaceWith } = replacement;\n\n const searchStringEscaped = searchString.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n \"\\\\$&\"\n );\n const regex = new RegExp(searchStringEscaped, \"gi\");\n if (regex.test(path.node.value)) {\n // Ignore if not on a line number that we want to replace.\n if (\n flags.lineNumbers &&\n path.node.loc &&\n !flags.lineNumbers.includes(path.node.loc.start.line)\n ) {\n return;\n }\n\n const splitValues = splitByCaseInsensitive(\n path.node.value,\n searchStringEscaped\n );\n const nodes: (t.JSXElement | t.JSXText)[] = [];\n\n splitValues.forEach((splitValue) => {\n if (splitValue.toLowerCase() === searchString.toLowerCase()) {\n const identifier = t.jsxIdentifier(\"DittoComponent\");\n const componentId = t.jsxAttribute(\n t.jsxIdentifier(\"componentId\"),\n t.stringLiteral(replaceWith)\n );\n const o = t.jsxOpeningElement(identifier, [componentId], true);\n const jsxElement = t.jsxElement(o, undefined, [], true);\n nodes.push(jsxElement);\n } else {\n nodes.push(t.jsxText(splitValue));\n }\n });\n\n path.replaceWithMultiple(nodes);\n }\n },\n });\n\n // transfromFromAst types are wrong?\n /* @ts-ignore */\n const { code: transformedCode } = transformFromAst(ast, code, {\n // Don't let this codebase's Babel config affect the code we're transforming.\n /* @ts-ignore */\n configFile: false,\n });\n\n await new Promise((resolve, reject) =>\n fs.writeFile(filePath, transformedCode, (err) => {\n if (err) {\n reject(err);\n } else {\n resolve(null);\n }\n })\n );\n}\n\nfunction splitByCaseInsensitive(str: string, delimiter: string) {\n return str.split(new RegExp(`(${delimiter})`, \"gi\")).filter((s) => s !== \"\");\n}\n\nfunction replace(options: string[], flags: { lineNumbers?: number[] }) {\n let filePath: string;\n let searchString: string;\n let replaceWith: string;\n\n try {\n const parsedOptions = parseOptions(options);\n filePath = parsedOptions.filePath;\n searchString = parsedOptions.searchString;\n replaceWith = parsedOptions.replaceWith;\n } catch (e) {\n console.error(e);\n console.error(\n \"Usage for replace: ditto-cli replace <file path> <search string> <replace with>\"\n );\n return;\n }\n\n replaceJSXTextInFile(filePath, { searchString, replaceWith }, flags);\n}\n\nfunction parseOptions(options: string[]): {\n filePath: string;\n searchString: string;\n replaceWith: string;\n} {\n if (options.length !== 3) {\n throw new Error(\n \"The options array must contain <file path> <search string> <replace with>.\"\n );\n }\n\n const filePath = options[0];\n // Check if the file path exists and points to a regular file (not a directory or other file system object).\n const isFilePathValid =\n fs.existsSync(filePath) && fs.lstatSync(filePath).isFile();\n\n if (!isFilePathValid) {\n throw new Error(`${filePath} is not a valid file path.`);\n }\n\n return { filePath, searchString: options[1], replaceWith: options[2] };\n}\n\nexport { replace, parseOptions, replaceJSXTextInFile };\n"],"names":["fs","traverse"],"mappings":"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","debug_id":"76d5bf23-fec4-53e2-9669-a83a306dc53e"}