@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.
- package/LICENSE +21 -0
- package/README.md +11 -2
- package/bin/lib/add-project.js +36 -0
- package/bin/lib/add-project.js.map +1 -0
- package/bin/lib/api.js +20 -0
- package/bin/lib/api.js.map +1 -0
- package/bin/lib/config.js +202 -0
- package/bin/lib/config.js.map +1 -0
- package/bin/lib/consts.js +21 -0
- package/bin/lib/consts.js.map +1 -0
- package/bin/lib/ditto.js +121 -0
- package/bin/lib/ditto.js.map +1 -0
- package/bin/lib/generate-suggestions.js +71 -0
- package/bin/lib/generate-suggestions.js.map +1 -0
- package/bin/lib/http/fetchComponents.js +13 -0
- package/bin/lib/http/fetchComponents.js.map +1 -0
- package/bin/lib/http/fetchVariants.js +26 -0
- package/bin/lib/http/fetchVariants.js.map +1 -0
- package/bin/lib/init/init.js +50 -0
- package/bin/lib/init/init.js.map +1 -0
- package/bin/lib/init/project.js +108 -0
- package/bin/lib/init/project.js.map +1 -0
- package/bin/lib/init/token.js +91 -0
- package/bin/lib/init/token.js.map +1 -0
- package/bin/lib/output.js +34 -0
- package/bin/lib/output.js.map +1 -0
- package/bin/lib/pull.js +264 -0
- package/bin/lib/pull.js.map +1 -0
- package/bin/lib/remove-project.js +35 -0
- package/bin/lib/remove-project.js.map +1 -0
- package/bin/lib/replace.js +107 -0
- package/bin/lib/replace.js.map +1 -0
- package/bin/lib/types.js +3 -0
- package/bin/lib/types.js.map +1 -0
- package/bin/lib/utils/cleanFileName.js +11 -0
- package/bin/lib/utils/cleanFileName.js.map +1 -0
- package/bin/lib/utils/generateJsDriver.js +56 -0
- package/bin/lib/utils/generateJsDriver.js.map +1 -0
- package/bin/lib/utils/getSelectedProjects.js +61 -0
- package/bin/lib/utils/getSelectedProjects.js.map +1 -0
- package/bin/lib/utils/processMetaOption.js +15 -0
- package/bin/lib/utils/processMetaOption.js.map +1 -0
- package/bin/lib/utils/projectsToText.js +25 -0
- package/bin/lib/utils/projectsToText.js.map +1 -0
- package/bin/lib/utils/promptForProject.js +43 -0
- package/bin/lib/utils/promptForProject.js.map +1 -0
- package/bin/lib/utils/quit.js +10 -0
- package/bin/lib/utils/quit.js.map +1 -0
- package/bin/lib/utils/sourcesToText.js +25 -0
- package/bin/lib/utils/sourcesToText.js.map +1 -0
- package/bin/package.json +76 -0
- package/bin/pull.js +31 -12
- package/bin/pull.js.map +1 -1
- package/lib/pull.test.ts +289 -101
- package/lib/pull.ts +46 -14
- package/lib/types.ts +1 -1
- package/package.json +2 -1
- package/.idea/cli.iml +0 -13
- package/.idea/codeStyles/Project.xml +0 -65
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -6
- package/.idea/vcs.xml +0 -6
- 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
|
-
|
|
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
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
|
141
|
-
|
|
142
|
-
expect(
|
|
143
|
-
expect(
|
|
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
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
157
|
-
expect(
|
|
158
|
-
expect(
|
|
159
|
-
expect(
|
|
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
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
173
|
-
expect(
|
|
174
|
-
expect(
|
|
175
|
-
expect(
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
189
|
-
expect(
|
|
190
|
-
expect(
|
|
191
|
-
expect(
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
expect(
|
|
207
|
-
expect(
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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) =>
|
|
54
|
+
"ios-strings": (data: string) => data.includes(`" = "`),
|
|
43
55
|
"ios-stringsdict": (data: string) => data.includes("<key>"),
|
|
44
56
|
};
|
|
45
57
|
|
|
46
|
-
const getFormat = (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
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>
|