@dittowords/cli 4.0.0 → 4.1.0-alpha
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/README.md +29 -364
- package/bin/config.js +5 -3
- package/bin/config.js.map +1 -1
- package/bin/generate-swift-struct.js +6 -0
- package/bin/generate-swift-struct.js.map +1 -0
- package/bin/http/fetchComponentFolders.js +3 -3
- package/bin/http/fetchComponentFolders.js.map +1 -1
- package/bin/http/fetchComponents.js +13 -5
- package/bin/http/fetchComponents.js.map +1 -1
- package/bin/http/fetchVariants.js +3 -3
- package/bin/http/fetchVariants.js.map +1 -1
- package/bin/init/project.js +3 -3
- package/bin/init/project.js.map +1 -1
- package/bin/pull.js +82 -38
- package/bin/pull.js.map +1 -1
- package/bin/pull.test.js +26 -24
- package/bin/pull.test.js.map +1 -1
- package/bin/types.js +2 -2
- package/bin/types.js.map +1 -1
- package/bin/utils/determineModuleType.js +80 -0
- package/bin/utils/determineModuleType.js.map +1 -0
- package/bin/utils/generateIOSBundles.js +147 -0
- package/bin/utils/generateIOSBundles.js.map +1 -0
- package/bin/utils/generateJsDriver.js +117 -58
- package/bin/utils/generateJsDriver.js.map +1 -1
- package/bin/utils/generateJsDriverTypeFile.js +105 -0
- package/bin/utils/generateJsDriverTypeFile.js.map +1 -0
- package/bin/utils/generateSwiftDriver.js +93 -0
- package/bin/utils/generateSwiftDriver.js.map +1 -0
- package/lib/config.ts +4 -0
- package/lib/http/fetchComponentFolders.ts +1 -1
- package/lib/http/fetchComponents.ts +14 -9
- package/lib/http/fetchVariants.ts +1 -1
- package/lib/init/project.ts +1 -1
- package/lib/pull.test.ts +24 -22
- package/lib/pull.ts +106 -55
- package/lib/types.ts +4 -0
- package/lib/utils/determineModuleType.ts +57 -0
- package/lib/utils/generateIOSBundles.ts +122 -0
- package/lib/utils/generateJsDriver.ts +156 -51
- package/lib/utils/generateJsDriverTypeFile.ts +75 -0
- package/lib/utils/generateSwiftDriver.ts +48 -0
- package/package.json +1 -1
|
@@ -4,6 +4,9 @@ import consts from "../consts";
|
|
|
4
4
|
import output from "../output";
|
|
5
5
|
import { Source } from "../types";
|
|
6
6
|
import { cleanFileName } from "./cleanFileName";
|
|
7
|
+
import { ModuleType, determineModuleType } from "./determineModuleType";
|
|
8
|
+
import { generateJsDriverTypeFile } from "./generateJsDriverTypeFile";
|
|
9
|
+
import { JSONFormat } from "../pull";
|
|
7
10
|
|
|
8
11
|
// compatability with legacy method of specifying project ids
|
|
9
12
|
// that is still used by the default format
|
|
@@ -20,77 +23,67 @@ const stringifySourceId = (projectId: string) =>
|
|
|
20
23
|
* The generated file will have a unified format
|
|
21
24
|
* independent of the CLI configuration used to fetch
|
|
22
25
|
* data from Ditto.
|
|
26
|
+
*
|
|
23
27
|
*/
|
|
28
|
+
type DriverFile = Record<string, Record<string, string | object>>;
|
|
29
|
+
export function generateJsDriver(sources: Source[], format: JSONFormat) {
|
|
30
|
+
const moduleType = determineModuleType();
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
export function generateJsDriver(sources: Source[]) {
|
|
27
|
-
const sourceIdsByName: Record<string, string> = sources.reduce(
|
|
28
|
-
(agg, source) => {
|
|
29
|
-
if (source.fileName) {
|
|
30
|
-
return { ...agg, [cleanFileName(source.fileName)]: source.id };
|
|
31
|
-
}
|
|
32
|
+
const fullyQualifiedSources = getFullyQualifiedJSONSources(sources);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{}
|
|
36
|
-
);
|
|
34
|
+
const variableNameGenerator = createVariableNameGenerator();
|
|
35
|
+
const importStatements: string[] = [];
|
|
37
36
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
.filter(
|
|
41
|
-
(fileName) => /\.json$/.test(fileName) && !/^components__/.test(fileName)
|
|
42
|
-
);
|
|
37
|
+
const data: DriverFile = {};
|
|
38
|
+
const dataComponents: Record<string, string[]> = {};
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
(
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
fullyQualifiedSources.forEach((source) => {
|
|
41
|
+
let variableName: string;
|
|
42
|
+
if (source.type === "components") {
|
|
43
|
+
variableName = variableNameGenerator.generate(
|
|
44
|
+
source.fileName.split(".")[0]
|
|
45
|
+
);
|
|
46
|
+
} else {
|
|
47
|
+
const fileNameWithoutExtension = source.fileName.split(".")[0];
|
|
48
|
+
variableName = variableNameGenerator.generate(
|
|
49
|
+
fileNameWithoutExtension.split("__")[0]
|
|
50
|
+
);
|
|
51
|
+
}
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
importStatements.push(
|
|
54
|
+
getImportStatement(source.fileName, variableName, moduleType)
|
|
55
|
+
);
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
if (source.type === "project") {
|
|
58
|
+
const { variantApiId } = source;
|
|
59
|
+
const projectId = stringifySourceId(source.projectId);
|
|
60
|
+
data[projectId] ??= {};
|
|
61
|
+
data[projectId][variantApiId] = `{...${variableName}}`;
|
|
62
|
+
} else {
|
|
63
|
+
dataComponents[source.variantApiId] ??= [];
|
|
64
|
+
dataComponents[source.variantApiId].push(`...${variableName}`);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
56
67
|
|
|
57
|
-
obj[projectIdStr][variantApiId] = `require('./${fileName}')`;
|
|
58
|
-
return obj;
|
|
59
|
-
},
|
|
60
|
-
{}
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Create arrays of stringified "...require()" statements,
|
|
64
|
-
// each of which corresponds to one of the component files
|
|
65
|
-
// (which are created on a per-component-folder basis)
|
|
66
|
-
const componentData: Record<string, string[]> = {};
|
|
67
|
-
sources
|
|
68
|
-
.filter((s) => s.type === "components")
|
|
69
|
-
.forEach((componentSource) => {
|
|
70
|
-
if (componentSource.type !== "components") return;
|
|
71
|
-
componentData[componentSource.variant] ??= [];
|
|
72
|
-
componentData[componentSource.variant].push(
|
|
73
|
-
`...require('./${componentSource.fileName}')`
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
68
|
// Convert each array of stringified "...require()" statements
|
|
77
69
|
// into a unified string, and set it on the final data object
|
|
78
70
|
// that will be written to the driver file
|
|
79
|
-
Object.keys(
|
|
71
|
+
Object.keys(dataComponents).forEach((key) => {
|
|
80
72
|
data.ditto_component_library ??= {};
|
|
81
73
|
|
|
82
74
|
let str = "{";
|
|
83
|
-
|
|
75
|
+
dataComponents[key].forEach((k: any, i: any) => {
|
|
84
76
|
str += k;
|
|
85
|
-
if (i <
|
|
77
|
+
if (i < dataComponents[key].length - 1) str += ", ";
|
|
86
78
|
});
|
|
87
79
|
str += "}";
|
|
88
80
|
data.ditto_component_library[key] = str;
|
|
89
81
|
});
|
|
90
82
|
|
|
91
|
-
let dataString =
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
let dataString = "";
|
|
84
|
+
dataString += importStatements.join("\n") + "\n\n";
|
|
85
|
+
dataString += `${getExportPrefix(moduleType)}`;
|
|
86
|
+
dataString += `${JSON.stringify(data, null, 2)}`
|
|
94
87
|
// remove quotes around opening & closing curlies
|
|
95
88
|
.replace(/"\{/g, "{")
|
|
96
89
|
.replace(/\}"/g, "}");
|
|
@@ -98,5 +91,117 @@ export function generateJsDriver(sources: Source[]) {
|
|
|
98
91
|
const filePath = path.resolve(consts.TEXT_DIR, "index.js");
|
|
99
92
|
fs.writeFileSync(filePath, dataString, { encoding: "utf8" });
|
|
100
93
|
|
|
94
|
+
generateJsDriverTypeFile({
|
|
95
|
+
format,
|
|
96
|
+
moduleType,
|
|
97
|
+
});
|
|
98
|
+
|
|
101
99
|
return `Generated .js SDK driver at ${output.info(filePath)}`;
|
|
102
100
|
}
|
|
101
|
+
|
|
102
|
+
type IFullyQualifiedJSONSource =
|
|
103
|
+
| {
|
|
104
|
+
type: "components";
|
|
105
|
+
variantApiId: string;
|
|
106
|
+
folderApiId: string;
|
|
107
|
+
fileName: string;
|
|
108
|
+
}
|
|
109
|
+
| {
|
|
110
|
+
type: "project";
|
|
111
|
+
projectId: string;
|
|
112
|
+
projectName: string;
|
|
113
|
+
variantApiId: string;
|
|
114
|
+
fileName: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Upstream source data is a mess - this function is an attempt at cleaning it up
|
|
119
|
+
* so that it can be used in a more straightforward way to generate the driver file.
|
|
120
|
+
*/
|
|
121
|
+
function getFullyQualifiedJSONSources(
|
|
122
|
+
sources: Source[]
|
|
123
|
+
): IFullyQualifiedJSONSource[] {
|
|
124
|
+
const projectIdsByCleanedFileName = new Map<string, string>();
|
|
125
|
+
sources.forEach((source) => {
|
|
126
|
+
if (!source.fileName || source.type === "components") {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
projectIdsByCleanedFileName.set(cleanFileName(source.fileName), source.id);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const fileNames = fs.readdirSync(consts.TEXT_DIR);
|
|
133
|
+
return fileNames
|
|
134
|
+
.filter((f) => path.extname(f) === ".json")
|
|
135
|
+
.map((fileName) => {
|
|
136
|
+
const parts = fileName.split("__");
|
|
137
|
+
|
|
138
|
+
if (parts.length === 3) {
|
|
139
|
+
const [, folderApiId, rest] = parts;
|
|
140
|
+
const [variantApiId] = rest.split(".");
|
|
141
|
+
return {
|
|
142
|
+
type: "components",
|
|
143
|
+
variantApiId,
|
|
144
|
+
folderApiId,
|
|
145
|
+
fileName,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (parts.length === 2) {
|
|
150
|
+
const [projectName, rest] = parts;
|
|
151
|
+
const [variantApiId] = rest.split(".");
|
|
152
|
+
const key = cleanFileName(fileName.split("__")[0]);
|
|
153
|
+
const projectId = projectIdsByCleanedFileName.get(key) || "";
|
|
154
|
+
return {
|
|
155
|
+
type: "project",
|
|
156
|
+
projectId,
|
|
157
|
+
projectName,
|
|
158
|
+
variantApiId,
|
|
159
|
+
fileName,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
throw new Error("Invalid JSON file generated: " + fileName);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function createVariableNameGenerator() {
|
|
168
|
+
const variableNames = new Set<string>();
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
generate: (str: string) => {
|
|
172
|
+
const baseName = str.replace(/(\W|-)/g, "_");
|
|
173
|
+
let name = baseName;
|
|
174
|
+
let i = 1;
|
|
175
|
+
while (variableNames.has(name)) {
|
|
176
|
+
name = `${baseName}${i}`;
|
|
177
|
+
i++;
|
|
178
|
+
}
|
|
179
|
+
variableNames.add(name);
|
|
180
|
+
return name;
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getExportPrefix(moduleType: ModuleType) {
|
|
186
|
+
if (moduleType === "commonjs") {
|
|
187
|
+
return "module.exports = ";
|
|
188
|
+
}
|
|
189
|
+
if (moduleType === "module") {
|
|
190
|
+
return "export default ";
|
|
191
|
+
}
|
|
192
|
+
throw new Error("Unknown module type: " + moduleType);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getImportStatement(
|
|
196
|
+
fileName: string,
|
|
197
|
+
variableName: string,
|
|
198
|
+
moduleType: ModuleType
|
|
199
|
+
) {
|
|
200
|
+
if (moduleType === "commonjs") {
|
|
201
|
+
return `const ${variableName} = require('./${fileName}');`;
|
|
202
|
+
}
|
|
203
|
+
if (moduleType === "module") {
|
|
204
|
+
return `import ${variableName} from './${fileName}';`;
|
|
205
|
+
}
|
|
206
|
+
throw new Error("Unknown module type: " + moduleType);
|
|
207
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { ModuleType } from "./determineModuleType";
|
|
4
|
+
import { JSONFormat } from "../pull";
|
|
5
|
+
import consts from "../consts";
|
|
6
|
+
|
|
7
|
+
function getFormatString(format: JSONFormat) {
|
|
8
|
+
switch (format) {
|
|
9
|
+
case "flat":
|
|
10
|
+
return "IJSONFlat";
|
|
11
|
+
case "nested":
|
|
12
|
+
return "IJSONNested";
|
|
13
|
+
case "structured":
|
|
14
|
+
return "IJSONStructured";
|
|
15
|
+
case "icu":
|
|
16
|
+
return "IJSONICU";
|
|
17
|
+
default:
|
|
18
|
+
return "_JSON";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getExportString(exportedValue: string, moduleType: ModuleType) {
|
|
23
|
+
if (moduleType === "commonjs") {
|
|
24
|
+
return `export = ${exportedValue};`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return `export default ${exportedValue};`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getTypesString(options: IOptions) {
|
|
31
|
+
return `
|
|
32
|
+
interface IJSONFlat {
|
|
33
|
+
[key: string]: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IJSONStructured {
|
|
37
|
+
[key: string]: {
|
|
38
|
+
text: string;
|
|
39
|
+
status?: string;
|
|
40
|
+
notes?: string;
|
|
41
|
+
[property: string]: any;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface IJSONNested {
|
|
46
|
+
[key: string]: string | IJSONNested;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type _JSON = IJSONFlat | IJSONStructured | IJSONNested;
|
|
50
|
+
|
|
51
|
+
interface IDriverFile {
|
|
52
|
+
[sourceKey: string]: {
|
|
53
|
+
[variantKey: string]: ${getFormatString(options.format)};
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare const driver: IDriverFile;
|
|
58
|
+
|
|
59
|
+
${getExportString("driver", options.moduleType)}
|
|
60
|
+
`.trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface IOptions {
|
|
64
|
+
format: JSONFormat;
|
|
65
|
+
moduleType: ModuleType;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function generateJsDriverTypeFile(options: IOptions) {
|
|
69
|
+
const typeFileString = getTypesString(options);
|
|
70
|
+
fs.writeFileSync(
|
|
71
|
+
path.resolve(consts.TEXT_DIR, "index.d.ts"),
|
|
72
|
+
typeFileString + "\n",
|
|
73
|
+
"utf8"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { writeFile } from "../pull";
|
|
3
|
+
import { SourceInformation } from "../types";
|
|
4
|
+
import { createApiClient } from "../api";
|
|
5
|
+
import consts from "../consts";
|
|
6
|
+
import output from "../output";
|
|
7
|
+
|
|
8
|
+
interface IArg {
|
|
9
|
+
variants: boolean;
|
|
10
|
+
components?:
|
|
11
|
+
| boolean
|
|
12
|
+
| {
|
|
13
|
+
root?: boolean | { status?: string };
|
|
14
|
+
folders?: string[] | { id: string | null; status?: string }[];
|
|
15
|
+
};
|
|
16
|
+
projects?: string[] | { id: string; status?: string }[];
|
|
17
|
+
localeByVariantId?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function generateSwiftDriver(source: SourceInformation) {
|
|
21
|
+
const client = createApiClient();
|
|
22
|
+
|
|
23
|
+
const body: IArg = {
|
|
24
|
+
variants: source.variants,
|
|
25
|
+
localeByVariantId: source.localeByVariantApiId,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (source.componentFolders || source.componentRoot) {
|
|
29
|
+
body.components = {};
|
|
30
|
+
if (source.componentFolders) {
|
|
31
|
+
body.components.folders = source.componentFolders;
|
|
32
|
+
}
|
|
33
|
+
if (source.componentRoot) {
|
|
34
|
+
body.components.root = source.componentRoot;
|
|
35
|
+
}
|
|
36
|
+
} else if (source.shouldFetchComponentLibrary) {
|
|
37
|
+
body.components = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (source.validProjects) body.projects = source.validProjects;
|
|
41
|
+
|
|
42
|
+
const { data } = await client.post<string>("/v1/ios/swift-driver", body);
|
|
43
|
+
|
|
44
|
+
const filePath = path.join(consts.TEXT_DIR, "Ditto.swift");
|
|
45
|
+
await writeFile(filePath, data);
|
|
46
|
+
|
|
47
|
+
return `Successfully saved Swift driver to ${output.info("Ditto.swift")}`;
|
|
48
|
+
}
|