@dittowords/cli 3.9.0 → 3.10.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -2
  3. package/bin/lib/add-project.js +36 -0
  4. package/bin/lib/add-project.js.map +1 -0
  5. package/bin/lib/api.js +20 -0
  6. package/bin/lib/api.js.map +1 -0
  7. package/bin/lib/config.js +202 -0
  8. package/bin/lib/config.js.map +1 -0
  9. package/bin/lib/consts.js +21 -0
  10. package/bin/lib/consts.js.map +1 -0
  11. package/bin/lib/ditto.js +121 -0
  12. package/bin/lib/ditto.js.map +1 -0
  13. package/bin/lib/generate-suggestions.js +71 -0
  14. package/bin/lib/generate-suggestions.js.map +1 -0
  15. package/bin/lib/http/fetchComponents.js +13 -0
  16. package/bin/lib/http/fetchComponents.js.map +1 -0
  17. package/bin/lib/http/fetchVariants.js +26 -0
  18. package/bin/lib/http/fetchVariants.js.map +1 -0
  19. package/bin/lib/init/init.js +50 -0
  20. package/bin/lib/init/init.js.map +1 -0
  21. package/bin/lib/init/project.js +108 -0
  22. package/bin/lib/init/project.js.map +1 -0
  23. package/bin/lib/init/token.js +91 -0
  24. package/bin/lib/init/token.js.map +1 -0
  25. package/bin/lib/output.js +34 -0
  26. package/bin/lib/output.js.map +1 -0
  27. package/bin/lib/pull.js +264 -0
  28. package/bin/lib/pull.js.map +1 -0
  29. package/bin/lib/remove-project.js +35 -0
  30. package/bin/lib/remove-project.js.map +1 -0
  31. package/bin/lib/replace.js +107 -0
  32. package/bin/lib/replace.js.map +1 -0
  33. package/bin/lib/types.js +3 -0
  34. package/bin/lib/types.js.map +1 -0
  35. package/bin/lib/utils/cleanFileName.js +11 -0
  36. package/bin/lib/utils/cleanFileName.js.map +1 -0
  37. package/bin/lib/utils/generateJsDriver.js +56 -0
  38. package/bin/lib/utils/generateJsDriver.js.map +1 -0
  39. package/bin/lib/utils/getSelectedProjects.js +61 -0
  40. package/bin/lib/utils/getSelectedProjects.js.map +1 -0
  41. package/bin/lib/utils/processMetaOption.js +15 -0
  42. package/bin/lib/utils/processMetaOption.js.map +1 -0
  43. package/bin/lib/utils/projectsToText.js +25 -0
  44. package/bin/lib/utils/projectsToText.js.map +1 -0
  45. package/bin/lib/utils/promptForProject.js +43 -0
  46. package/bin/lib/utils/promptForProject.js.map +1 -0
  47. package/bin/lib/utils/quit.js +10 -0
  48. package/bin/lib/utils/quit.js.map +1 -0
  49. package/bin/lib/utils/sourcesToText.js +25 -0
  50. package/bin/lib/utils/sourcesToText.js.map +1 -0
  51. package/bin/package.json +76 -0
  52. package/bin/pull.js +31 -12
  53. package/bin/pull.js.map +1 -1
  54. package/lib/pull.test.ts +289 -101
  55. package/lib/pull.ts +46 -14
  56. package/lib/types.ts +1 -1
  57. package/package.json +2 -1
  58. package/.idea/cli.iml +0 -13
  59. package/.idea/codeStyles/Project.xml +0 -65
  60. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  61. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  62. package/.idea/modules.xml +0 -8
  63. package/.idea/prettier.xml +0 -6
  64. package/.idea/vcs.xml +0 -6
  65. package/.idea/workspace.xml +0 -220
package/lib/pull.test.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+
4
+ jest.mock("./api", () => ({
5
+ createApiClient: jest.fn(), // this needs to be mocked in each test that requires it
6
+ }));
3
7
  import { createApiClient } from "./api";
4
8
 
5
- const testProjects = [
9
+ const testProjects: Project[] = [
6
10
  {
7
11
  id: "1",
8
12
  name: "Project 1",
@@ -11,8 +15,6 @@ const testProjects = [
11
15
  { id: "2", name: "Project 2", fileName: "Project 2" },
12
16
  ];
13
17
 
14
- jest.mock("./api");
15
-
16
18
  // TODO: all tests in this file currently failing because we're re-instantiating the api client
17
19
  // everywhere and are unable to mock the return type separately for each instance of usage.
18
20
  // We need to refactor to share one api client everywhere instead of always re-creating it.
@@ -29,7 +31,8 @@ jest.mock("./consts", () => ({
29
31
  }));
30
32
 
31
33
  import consts from "./consts";
32
- import allPull from "./pull";
34
+ import allPull, { getFormatDataIsValid } from "./pull";
35
+ import { Project } from "./types";
33
36
 
34
37
  const {
35
38
  _testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase },
@@ -67,49 +70,6 @@ describe("cleanOutputFiles", () => {
67
70
  });
68
71
  });
69
72
 
70
- // describe("downloadAndSaveVariant", () => {
71
- // beforeAll(() => {
72
- // if (!fs.existsSync(consts.TEXT_DIR)) {
73
- // fs.mkdirSync(consts.TEXT_DIR);
74
- // }
75
- // });
76
-
77
- // it("writes a single file for default format", async () => {
78
- // cleanOutputDir();
79
-
80
- // const output = await downloadAndSaveVariant(variant, testProjects, "");
81
- // expect(/saved to.*english\.json/.test(output)).toEqual(true);
82
- // expect(output.match(/saved to/g)?.length).toEqual(1);
83
- // expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(1);
84
- // });
85
-
86
- // it("writes multiple files for flat format", async () => {
87
- // cleanOutputDir();
88
-
89
- // const output = await downloadAndSaveVariant(variant, testProjects, "flat");
90
-
91
- // expect(/saved to.*Project 1__english\.json/.test(output)).toEqual(true);
92
- // expect(/saved to.*Project 2__english\.json/.test(output)).toEqual(true);
93
- // expect(output.match(/saved to/g)?.length).toEqual(2);
94
- // expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
95
- // });
96
-
97
- // it("writes multiple files for structured format", async () => {
98
- // cleanOutputDir();
99
-
100
- // const output = await downloadAndSaveVariant(
101
- // variant,
102
- // testProjects,
103
- // "structured"
104
- // );
105
-
106
- // expect(/saved to.*Project 1__english\.json/.test(output)).toEqual(true);
107
- // expect(/saved to.*Project 2__english\.json/.test(output)).toEqual(true);
108
- // expect(output.match(/saved to/g)?.length).toEqual(2);
109
- // expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
110
- // });
111
- // });
112
-
113
73
  describe("downloadAndSaveBase", () => {
114
74
  beforeAll(() => {
115
75
  if (!fs.existsSync(consts.TEXT_DIR)) {
@@ -117,93 +77,321 @@ describe("downloadAndSaveBase", () => {
117
77
  }
118
78
  });
119
79
 
120
- it.only("writes to text.json for default format", async () => {
80
+ beforeEach(() => {
121
81
  cleanOutputDir();
122
-
123
- mockApi.get.mockResolvedValueOnce({ data: [] });
124
- const output = await downloadAndSaveBase(
125
- testProjects,
126
- "" as any,
127
- undefined
128
- );
129
-
130
- expect(/saved to.*text\.json/.test(output)).toEqual(true);
131
- expect(output.match(/saved to/g)?.length).toEqual(1);
132
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(1);
133
82
  });
134
83
 
135
- it("writes multiple files for flat format", async () => {
136
- cleanOutputDir();
137
-
138
- mockApi.get.mockResolvedValue({ data: [] });
84
+ it("writes the flat format to disk", async () => {
85
+ const mockData = {
86
+ hello: "world",
87
+ };
88
+ (createApiClient as any).mockImplementation(() => ({
89
+ get: () => ({ data: mockData }),
90
+ }));
139
91
  const output = await downloadAndSaveBase(testProjects, "flat", undefined);
140
- expect(/saved to.*Project 1\.json/.test(output)).toEqual(true);
141
- expect(/saved to.*Project 2\.json/.test(output)).toEqual(true);
142
- expect(output.match(/saved to/g)?.length).toEqual(2);
143
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
92
+ expect(/successfully saved/i.test(output)).toEqual(true);
93
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
94
+ expect(directoryContents.length).toEqual(testProjects.length);
95
+ expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
96
+ expect(
97
+ JSON.parse(
98
+ fs.readFileSync(
99
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
100
+ "utf8"
101
+ )
102
+ )
103
+ ).toEqual(mockData);
144
104
  });
145
105
 
146
- it("writes multiple files for structured format", async () => {
147
- cleanOutputDir();
148
-
149
- mockApi.get.mockResolvedValue({ data: [] });
106
+ it("writes the structured format to disk", async () => {
107
+ const mockData = {
108
+ hello: { text: "world" },
109
+ };
110
+ (createApiClient as any).mockImplementation(() => ({
111
+ get: () => ({ data: mockData }),
112
+ }));
150
113
  const output = await downloadAndSaveBase(
151
114
  testProjects,
152
115
  "structured",
153
116
  undefined
154
117
  );
155
-
156
- expect(/saved to.*Project 1\.json/.test(output)).toEqual(true);
157
- expect(/saved to.*Project 2\.json/.test(output)).toEqual(true);
158
- expect(output.match(/saved to/g)?.length).toEqual(2);
159
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
118
+ expect(/successfully saved/i.test(output)).toEqual(true);
119
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
120
+ expect(directoryContents.length).toEqual(testProjects.length);
121
+ expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
122
+ expect(
123
+ JSON.parse(
124
+ fs.readFileSync(
125
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
126
+ "utf8"
127
+ )
128
+ )
129
+ ).toEqual(mockData);
160
130
  });
161
131
 
162
- it("writes .xml files for android", async () => {
163
- cleanOutputDir();
132
+ it("writes the icu format to disk", async () => {
133
+ const mockData = {
134
+ hello: "world",
135
+ };
136
+ (createApiClient as any).mockImplementation(() => ({
137
+ get: () => ({ data: mockData }),
138
+ }));
139
+ const output = await downloadAndSaveBase(testProjects, "icu", undefined);
140
+ expect(/successfully saved/i.test(output)).toEqual(true);
141
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
142
+ expect(directoryContents.length).toEqual(testProjects.length);
143
+ expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
144
+ expect(
145
+ JSON.parse(
146
+ fs.readFileSync(
147
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
148
+ "utf8"
149
+ )
150
+ )
151
+ ).toEqual(mockData);
152
+ });
164
153
 
165
- mockApi.get.mockResolvedValue({ data: "hello" });
154
+ it("writes the android format to disk", async () => {
155
+ const mockData = `
156
+ <?xml version="1.0" encoding="utf-8"?>
157
+ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
158
+ <string name="hello-world" ditto_api_id="hello-world">Hello World</string>
159
+ </resources>
160
+ `;
161
+ (createApiClient as any).mockImplementation(() => ({
162
+ get: () => ({ data: mockData }),
163
+ }));
166
164
  const output = await downloadAndSaveBase(
167
165
  testProjects,
168
166
  "android",
169
167
  undefined
170
168
  );
171
-
172
- expect(/saved to.*Project 1\.xml/.test(output)).toEqual(true);
173
- expect(/saved to.*Project 2\.xml/.test(output)).toEqual(true);
174
- expect(output.match(/saved to/g)?.length).toEqual(2);
175
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
169
+ expect(/successfully saved/i.test(output)).toEqual(true);
170
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
171
+ expect(directoryContents.length).toEqual(testProjects.length);
172
+ expect(directoryContents.every((f) => f.endsWith(".xml"))).toBe(true);
173
+ expect(
174
+ fs
175
+ .readFileSync(
176
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
177
+ "utf8"
178
+ )
179
+ .replace(/\s/g, "")
180
+ ).toEqual(mockData.replace(/\s/g, ""));
176
181
  });
177
182
 
178
- it("writes .strings files for ios-strings", async () => {
179
- cleanOutputDir();
180
-
181
- mockApi.get.mockResolvedValue({ data: "hello" });
183
+ it("writes the ios-strings format to disk", async () => {
184
+ const mockData = `
185
+ "hello" = "world";
186
+ `;
187
+ (createApiClient as any).mockImplementation(() => ({
188
+ get: () => ({ data: mockData }),
189
+ }));
182
190
  const output = await downloadAndSaveBase(
183
191
  testProjects,
184
192
  "ios-strings",
185
193
  undefined
186
194
  );
187
-
188
- expect(/saved to.*Project 1\.strings/.test(output)).toEqual(true);
189
- expect(/saved to.*Project 2\.strings/.test(output)).toEqual(true);
190
- expect(output.match(/saved to/g)?.length).toEqual(2);
191
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
195
+ expect(/successfully saved/i.test(output)).toEqual(true);
196
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
197
+ expect(directoryContents.length).toEqual(testProjects.length);
198
+ expect(directoryContents.every((f) => f.endsWith(".strings"))).toBe(true);
199
+ expect(
200
+ fs
201
+ .readFileSync(
202
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
203
+ "utf8"
204
+ )
205
+ .replace(/\s/g, "")
206
+ ).toEqual(mockData.replace(/\s/g, ""));
192
207
  });
193
208
 
194
- it("writes .stringsdict files for ios-stringsdict", async () => {
195
- cleanOutputDir();
196
-
197
- mockApi.get.mockResolvedValue({ data: "hello" });
209
+ it("writes the ios-stringsdict format to disk", async () => {
210
+ const mockData = `
211
+ <?xml version="1.0" encoding="utf-8"?>
212
+ <plist version="1.0">
213
+ <dict>
214
+ <key>hello-world</key>
215
+ <dict>
216
+ <key>NSStringLocalizedFormatKey</key>
217
+ <string>%1$#@count@</string>
218
+ <key>count</key>
219
+ <dict>
220
+ <key>NSStringFormatSpecTypeKey</key>
221
+ <string>NSStringPluralRuleType</string>
222
+ <key>NSStringFormatValueTypeKey</key>
223
+ <string>d</string>
224
+ <key>other</key>
225
+ <string>espanol</string>
226
+ </dict>
227
+ </dict>
228
+ </dict>
229
+ </plist>
230
+ `;
231
+ (createApiClient as any).mockImplementation(() => ({
232
+ get: () => ({ data: mockData }),
233
+ }));
198
234
  const output = await downloadAndSaveBase(
199
235
  testProjects,
200
236
  "ios-stringsdict",
201
237
  undefined
202
238
  );
239
+ expect(/successfully saved/i.test(output)).toEqual(true);
240
+ const directoryContents = fs.readdirSync(consts.TEXT_DIR);
241
+ expect(directoryContents.length).toEqual(testProjects.length);
242
+ expect(directoryContents.every((f) => f.endsWith(".stringsdict"))).toBe(
243
+ true
244
+ );
245
+ expect(
246
+ fs
247
+ .readFileSync(
248
+ path.resolve(consts.TEXT_DIR, directoryContents[0]),
249
+ "utf8"
250
+ )
251
+ .replace(/\s/g, "")
252
+ ).toEqual(mockData.replace(/\s/g, ""));
253
+ });
254
+ });
203
255
 
204
- expect(/saved to.*Project 1\.stringsdict/.test(output)).toEqual(true);
205
- expect(/saved to.*Project 2\.stringsdict/.test(output)).toEqual(true);
206
- expect(output.match(/saved to/g)?.length).toEqual(2);
207
- expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(2);
256
+ describe("getFormatDataIsValid", () => {
257
+ it("handles flat format appropriately", () => {
258
+ expect(getFormatDataIsValid.flat("{}")).toBe(false);
259
+ expect(getFormatDataIsValid.flat(`{ "hello": "world" }`)).toBe(true);
260
+ expect(
261
+ getFormatDataIsValid.flat(`{
262
+ "__variant-name": "English",
263
+ "__variant-description": ""
264
+ }`)
265
+ ).toBe(false);
266
+ expect(
267
+ getFormatDataIsValid.flat(`{
268
+ "__variant-name": "English",
269
+ "__variant-description": "",
270
+ "hello": "world"
271
+ }`)
272
+ ).toBe(true);
273
+ });
274
+ it("handles structured format appropriately", () => {
275
+ expect(getFormatDataIsValid.structured("{}")).toBe(false);
276
+ expect(
277
+ getFormatDataIsValid.structured(`{ "hello": { "text": "world" } }`)
278
+ ).toBe(true);
279
+ expect(
280
+ getFormatDataIsValid.structured(`{
281
+ "__variant-name": "English",
282
+ "__variant-description": ""
283
+ }`)
284
+ ).toBe(false);
285
+ expect(
286
+ getFormatDataIsValid.structured(`{
287
+ "__variant-name": "English",
288
+ "__variant-description": "",
289
+ "hello": { "text": "world" }
290
+ }`)
291
+ ).toBe(true);
292
+ });
293
+ it("handles icu format appropriately", () => {
294
+ expect(getFormatDataIsValid.icu("{}")).toBe(false);
295
+ expect(getFormatDataIsValid.icu(`{ "hello": "world" }`)).toBe(true);
296
+ expect(
297
+ getFormatDataIsValid.icu(`{
298
+ "__variant-name": "English",
299
+ "__variant-description": ""
300
+ }`)
301
+ ).toBe(false);
302
+ expect(
303
+ getFormatDataIsValid.icu(`{
304
+ "__variant-name": "English",
305
+ "__variant-description": "",
306
+ "hello": "world"
307
+ }`)
308
+ ).toBe(true);
309
+ });
310
+ it("handles android format appropriately", () => {
311
+ expect(
312
+ getFormatDataIsValid.android(`
313
+ <?xml version="1.0" encoding="utf-8"?>
314
+ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"/>
315
+ `)
316
+ ).toBe(false);
317
+ expect(
318
+ getFormatDataIsValid.android(`
319
+ <?xml version="1.0" encoding="utf-8"?>
320
+ <!--Variant Name: English-->
321
+ <!--Variant Description: -->
322
+ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"/>
323
+ `)
324
+ ).toBe(false);
325
+ expect(
326
+ getFormatDataIsValid.android(`
327
+ <?xml version="1.0" encoding="utf-8"?>
328
+ <!--Variant Name: English-->
329
+ <!--Variant Description: -->
330
+ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
331
+ <string name="hello-world" ditto_api_id="hello-world">Hello World</string>
332
+ </resources>
333
+ `)
334
+ ).toBe(true);
335
+ });
336
+ it("handles ios-strings format appropriately", () => {
337
+ expect(getFormatDataIsValid["ios-strings"]("")).toBe(false);
338
+ expect(
339
+ getFormatDataIsValid["ios-strings"](`
340
+ /* Variant Name: English */
341
+ /* Variant Description: */
342
+ `)
343
+ ).toBe(false);
344
+ expect(
345
+ getFormatDataIsValid["ios-strings"](`
346
+ /* Variant Name: English */
347
+ /* Variant Description: */
348
+ "Hello" = "World";
349
+ `)
350
+ ).toBe(true);
351
+ });
352
+ it("handles ios-stringsdict format appropriately", () => {
353
+ expect(
354
+ getFormatDataIsValid["ios-stringsdict"](`
355
+ <?xml version="1.0" encoding="utf-8"?>
356
+ <plist version="1.0">
357
+ <dict/>
358
+ </plist>
359
+ `)
360
+ ).toBe(false);
361
+ expect(
362
+ getFormatDataIsValid["ios-stringsdict"](`
363
+ <?xml version="1.0" encoding="utf-8"?>
364
+ <!--Variant Name: English-->
365
+ <!--Variant Description: -->
366
+ <plist version="1.0">
367
+ <dict/>
368
+ </plist>
369
+ `)
370
+ ).toBe(false);
371
+ expect(
372
+ getFormatDataIsValid["ios-stringsdict"](`
373
+ <?xml version="1.0" encoding="utf-8"?>
374
+ <!--Variant Name: English-->
375
+ <!--Variant Description: -->
376
+ <plist version="1.0">
377
+ <dict>
378
+ <key>Hello World</key>
379
+ <dict>
380
+ <key>NSStringLocalizedFormatKey</key>
381
+ <string>%1$#@count@</string>
382
+ <key>count</key>
383
+ <dict>
384
+ <key>NSStringFormatSpecTypeKey</key>
385
+ <string>NSStringPluralRuleType</string>
386
+ <key>NSStringFormatValueTypeKey</key>
387
+ <string>d</string>
388
+ <key>other</key>
389
+ <string>espanol</string>
390
+ </dict>
391
+ </dict>
392
+ </dict>
393
+ </plist>
394
+ `)
395
+ ).toBe(true);
208
396
  });
209
397
  });
package/lib/pull.ts CHANGED
@@ -13,6 +13,7 @@ import { generateJsDriver } from "./utils/generateJsDriver";
13
13
  import { cleanFileName } from "./utils/cleanFileName";
14
14
  import { SourceInformation, Token, Project, SupportedFormat } from "./types";
15
15
  import { fetchVariants } from "./http/fetchVariants";
16
+ import { kMaxLength } from "buffer";
16
17
 
17
18
  const SUPPORTED_FORMATS: SupportedFormat[] = [
18
19
  "flat",
@@ -34,22 +35,40 @@ const FORMAT_EXTENSIONS = {
34
35
  icu: ".json",
35
36
  };
36
37
 
37
- const getFormatDataIsValid = {
38
- flat: (data: string) => data !== "{}",
39
- structured: (data: string) => data !== "{}",
40
- icu: (data: string) => data !== "{}",
38
+ const getJsonFormatIsValid = (data: string) => {
39
+ try {
40
+ return Object.keys(JSON.parse(data)).some(
41
+ (k) => !k.startsWith("__variant")
42
+ );
43
+ } catch {
44
+ return false;
45
+ }
46
+ };
47
+
48
+ // exported for test usage only
49
+ export const getFormatDataIsValid = {
50
+ flat: getJsonFormatIsValid,
51
+ structured: getJsonFormatIsValid,
52
+ icu: getJsonFormatIsValid,
41
53
  android: (data: string) => data.includes("<string"),
42
- "ios-strings": (data: string) => !!data,
54
+ "ios-strings": (data: string) => data.includes(`" = "`),
43
55
  "ios-stringsdict": (data: string) => data.includes("<key>"),
44
56
  };
45
57
 
46
- const getFormat = (formatFromSource: string | undefined): SupportedFormat => {
47
- const f = formatFromSource as SupportedFormat | undefined;
48
- if (f && SUPPORTED_FORMATS.includes(f)) {
49
- return f;
58
+ const getFormat = (
59
+ formatFromSource: string | string[] | undefined
60
+ ): SupportedFormat[] => {
61
+ const formats = (
62
+ Array.isArray(formatFromSource) ? formatFromSource : [formatFromSource]
63
+ ).filter((format) =>
64
+ SUPPORTED_FORMATS.includes(format as SupportedFormat)
65
+ ) as SupportedFormat[];
66
+
67
+ if (formats.length) {
68
+ return formats;
50
69
  }
51
70
 
52
- return "flat";
71
+ return ["flat"];
53
72
  };
54
73
 
55
74
  const getFormatExtension = (format: SupportedFormat) => {
@@ -226,7 +245,7 @@ async function downloadAndSave(
226
245
  componentFolders,
227
246
  } = source;
228
247
 
229
- const format = getFormat(formatFromSource);
248
+ const formats = getFormat(formatFromSource);
230
249
 
231
250
  let msg = "";
232
251
  const spinner = ora(msg);
@@ -243,7 +262,7 @@ async function downloadAndSave(
243
262
 
244
263
  const meta = options ? options.meta : {};
245
264
 
246
- if (shouldFetchComponentLibrary) {
265
+ async function fetchComponentLibrary(format: SupportedFormat) {
247
266
  // Always include a variant with an apiID of undefined to ensure that we
248
267
  // fetch the base text for the component library.
249
268
  const componentVariants = [{ apiID: undefined }, ...(variants || [])];
@@ -290,7 +309,13 @@ async function downloadAndSave(
290
309
  msg += messages.join("");
291
310
  }
292
311
 
293
- if (validProjects.length) {
312
+ if (shouldFetchComponentLibrary) {
313
+ for (const format of formats) {
314
+ await fetchComponentLibrary(format);
315
+ }
316
+ }
317
+
318
+ async function fetchProjects(format: SupportedFormat) {
294
319
  msg += variants
295
320
  ? await downloadAndSaveVariants(
296
321
  variants,
@@ -312,6 +337,12 @@ async function downloadAndSave(
312
337
  );
313
338
  }
314
339
 
340
+ if (validProjects.length) {
341
+ for (const format of formats) {
342
+ await fetchProjects(format);
343
+ }
344
+ }
345
+
315
346
  const sources = [...validProjects];
316
347
  if (shouldFetchComponentLibrary) {
317
348
  sources.push({
@@ -321,7 +352,8 @@ async function downloadAndSave(
321
352
  });
322
353
  }
323
354
 
324
- if (JSON_FORMATS.includes(format)) msg += generateJsDriver(sources);
355
+ if (formats.some((f) => JSON_FORMATS.includes(f)))
356
+ msg += generateJsDriver(sources);
325
357
 
326
358
  msg += `\n${output.success("Done")}!`;
327
359
 
package/lib/types.ts CHANGED
@@ -45,7 +45,7 @@ export interface SourceInformation {
45
45
  validProjects: Project[];
46
46
  shouldFetchComponentLibrary: boolean;
47
47
  variants: boolean;
48
- format: string | undefined;
48
+ format: string | string[] | undefined;
49
49
  status: string | undefined;
50
50
  richText: boolean | undefined;
51
51
  componentFolders: ComponentFolder[] | null;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@dittowords/cli",
3
- "version": "3.9.0",
3
+ "version": "3.10.1",
4
4
  "description": "Command Line Interface for Ditto (dittowords.com).",
5
+ "license": "MIT",
5
6
  "main": "bin/index.js",
6
7
  "scripts": {
7
8
  "prepublish": "tsc",
package/.idea/cli.iml DELETED
@@ -1,13 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$">
5
- <excludeFolder url="file://$MODULE_DIR$/temp" />
6
- <excludeFolder url="file://$MODULE_DIR$/.tmp" />
7
- <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
- <excludeFolder url="file://$MODULE_DIR$/bin" />
9
- </content>
10
- <orderEntry type="inheritedJdk" />
11
- <orderEntry type="sourceFolder" forTests="false" />
12
- </component>
13
- </module>
@@ -1,65 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <code_scheme name="Project" version="173">
3
- <HTMLCodeStyleSettings>
4
- <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
5
- <option name="HTML_ENFORCE_QUOTES" value="true" />
6
- </HTMLCodeStyleSettings>
7
- <JSCodeStyleSettings version="0">
8
- <option name="FORCE_SEMICOLON_STYLE" value="true" />
9
- <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
10
- <option name="FORCE_QUOTE_STYlE" value="true" />
11
- <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
12
- <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
13
- <option name="SPACES_WITHIN_IMPORTS" value="true" />
14
- </JSCodeStyleSettings>
15
- <TypeScriptCodeStyleSettings version="0">
16
- <option name="FORCE_SEMICOLON_STYLE" value="true" />
17
- <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
18
- <option name="FORCE_QUOTE_STYlE" value="true" />
19
- <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
20
- <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
21
- <option name="SPACES_WITHIN_IMPORTS" value="true" />
22
- </TypeScriptCodeStyleSettings>
23
- <VueCodeStyleSettings>
24
- <option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
25
- <option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
26
- </VueCodeStyleSettings>
27
- <codeStyleSettings language="CSS">
28
- <indentOptions>
29
- <option name="INDENT_SIZE" value="2" />
30
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
31
- <option name="TAB_SIZE" value="2" />
32
- </indentOptions>
33
- </codeStyleSettings>
34
- <codeStyleSettings language="HTML">
35
- <option name="SOFT_MARGINS" value="80" />
36
- <indentOptions>
37
- <option name="INDENT_SIZE" value="2" />
38
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
39
- <option name="TAB_SIZE" value="2" />
40
- </indentOptions>
41
- </codeStyleSettings>
42
- <codeStyleSettings language="JavaScript">
43
- <option name="SOFT_MARGINS" value="80" />
44
- <indentOptions>
45
- <option name="INDENT_SIZE" value="2" />
46
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
47
- <option name="TAB_SIZE" value="2" />
48
- </indentOptions>
49
- </codeStyleSettings>
50
- <codeStyleSettings language="TypeScript">
51
- <option name="SOFT_MARGINS" value="80" />
52
- <indentOptions>
53
- <option name="INDENT_SIZE" value="2" />
54
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
55
- <option name="TAB_SIZE" value="2" />
56
- </indentOptions>
57
- </codeStyleSettings>
58
- <codeStyleSettings language="Vue">
59
- <option name="SOFT_MARGINS" value="80" />
60
- <indentOptions>
61
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
62
- </indentOptions>
63
- </codeStyleSettings>
64
- </code_scheme>
65
- </component>
@@ -1,5 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <state>
3
- <option name="USE_PER_PROJECT_SETTINGS" value="true" />
4
- </state>
5
- </component>