@dittowords/cli 2.3.0 → 2.5.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/README.md +6 -3
- package/bin/ditto.js +10 -1
- package/lib/add-project.js +27 -6
- package/lib/init/project.js +35 -11
- package/lib/init/token.js +1 -1
- package/lib/pull.js +29 -8
- package/lib/remove-project.js +19 -4
- package/lib/utils/getSelectedProjects.js +10 -1
- package/lib/utils/processMetaOption.js +16 -0
- package/lib/utils/processMetaOption.test.js +16 -0
- package/lib/utils/projectsToText.js +1 -1
- package/lib/utils/promptForProject.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install --global @dittowords/cli
|
|
|
16
16
|
|
|
17
17
|
The installed binary is named `ditto-cli`. You can execute it directly in `node_modules/.bin/ditto-cli` or using [npx](https://www.npmjs.com/package/npx) (with or without installation) like `npx @dittowords/cli`.
|
|
18
18
|
|
|
19
|
-
The first time you run the CLI, you'll be asked to provide an API key (found at [https://
|
|
19
|
+
The first time you run the CLI, you'll be asked to provide an API key (found at [https://app.dittowords.com/account/user](https://app.dittowords.com/account/user) under **API Keys**):
|
|
20
20
|
|
|
21
21
|
```
|
|
22
22
|
$ npx @dittowords/cli
|
|
@@ -41,8 +41,8 @@ Once you've successfully authenticated, you'll be asked to configure the CLI wit
|
|
|
41
41
|
Looks like there are no Ditto projects selected for your current directory.
|
|
42
42
|
|
|
43
43
|
? Choose the project you'd like to sync text from:
|
|
44
|
-
- Ditto Component Library https://
|
|
45
|
-
- NUX Onboarding Flow https://
|
|
44
|
+
- Ditto Component Library https://app.dittowords.com/components/all
|
|
45
|
+
- NUX Onboarding Flow https://app.dittowords.com/doc/609e9981c313f8018d0c346a
|
|
46
46
|
...
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -85,6 +85,9 @@ For more details on files generated by the `pull` command, see [JSON Files](#jso
|
|
|
85
85
|
|
|
86
86
|
**Action:** Removes a project from the list of Ditto projects stored in `config.yml`.
|
|
87
87
|
|
|
88
|
+
## Options
|
|
89
|
+
`-m, --meta <data...>` Optional metadata to send arbitrary data to the backend. Ex: `-m githubActionRequest:true trigger:manual`. Currently, this option should only be utilized when the CLI is ran within a GitHub action (see [Ditto GitHub Action](https://github.com/dittowords/ditto-github-action)) via `githubActionRequest:true`.
|
|
90
|
+
|
|
88
91
|
## Files
|
|
89
92
|
|
|
90
93
|
### `ditto/`
|
package/bin/ditto.js
CHANGED
|
@@ -9,6 +9,7 @@ const { pull } = require("../lib/pull");
|
|
|
9
9
|
|
|
10
10
|
const addProject = require("../lib/add-project");
|
|
11
11
|
const removeProject = require("../lib/remove-project");
|
|
12
|
+
const processMetaOption = require("../lib/utils/processMetaOption");
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Catch and report unexpected error.
|
|
@@ -45,6 +46,12 @@ const setupCommands = () => {
|
|
|
45
46
|
.action(() => checkInit("project remove"));
|
|
46
47
|
};
|
|
47
48
|
|
|
49
|
+
const setupOptions = () => {
|
|
50
|
+
program
|
|
51
|
+
.option('-m, --meta <data...>',
|
|
52
|
+
'Optional metadata for this command to send arbitrary data to the backend. Ex: -m githubActionRequest:true trigger:manual');
|
|
53
|
+
};
|
|
54
|
+
|
|
48
55
|
const checkInit = async (command) => {
|
|
49
56
|
if (needsInit() && command !== "project remove") {
|
|
50
57
|
try {
|
|
@@ -54,9 +61,10 @@ const checkInit = async (command) => {
|
|
|
54
61
|
quit();
|
|
55
62
|
}
|
|
56
63
|
} else {
|
|
64
|
+
const { meta } = program.opts();
|
|
57
65
|
switch (command) {
|
|
58
66
|
case "pull":
|
|
59
|
-
pull();
|
|
67
|
+
pull({ meta: processMetaOption(meta) });
|
|
60
68
|
break;
|
|
61
69
|
case "project":
|
|
62
70
|
case "project add":
|
|
@@ -80,6 +88,7 @@ const main = async () => {
|
|
|
80
88
|
await checkInit("none");
|
|
81
89
|
} else {
|
|
82
90
|
setupCommands();
|
|
91
|
+
setupOptions();
|
|
83
92
|
}
|
|
84
93
|
program.parse(process.argv);
|
|
85
94
|
};
|
package/lib/add-project.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const { collectAndSaveProject } = require("./init/project");
|
|
2
2
|
const projectsToText = require("./utils/projectsToText");
|
|
3
|
-
const
|
|
3
|
+
const {
|
|
4
|
+
getSelectedProjects,
|
|
5
|
+
getIsUsingComponents,
|
|
6
|
+
} = require("./utils/getSelectedProjects");
|
|
7
|
+
const output = require("./output");
|
|
4
8
|
|
|
5
9
|
function quit(exitCode = 2) {
|
|
6
10
|
console.log("Project selection was not updated.");
|
|
@@ -10,13 +14,30 @@ function quit(exitCode = 2) {
|
|
|
10
14
|
|
|
11
15
|
const addProject = async () => {
|
|
12
16
|
const projects = getSelectedProjects();
|
|
17
|
+
const usingComponents = getIsUsingComponents();
|
|
13
18
|
|
|
14
19
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
if (usingComponents) {
|
|
21
|
+
if (projects.length) {
|
|
22
|
+
console.log(
|
|
23
|
+
`\nYou're currently set up to sync text from the ${output.info(
|
|
24
|
+
"Component Library"
|
|
25
|
+
)} and from the following projects: ${projectsToText(projects)}`
|
|
26
|
+
);
|
|
27
|
+
} else {
|
|
28
|
+
console.log(
|
|
29
|
+
`\nYou're currently only set up to sync text from the ${output.info(
|
|
30
|
+
"Component Library"
|
|
31
|
+
)}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
} else if (projects.length) {
|
|
35
|
+
console.log(
|
|
36
|
+
`\nYou're currently set up to sync text from the following projects: ${projectsToText(
|
|
37
|
+
projects
|
|
38
|
+
)}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
20
41
|
await collectAndSaveProject(false);
|
|
21
42
|
} catch (error) {
|
|
22
43
|
console.log(
|
package/lib/init/project.js
CHANGED
|
@@ -5,7 +5,10 @@ const config = require("../config");
|
|
|
5
5
|
const consts = require("../consts");
|
|
6
6
|
const output = require("../output");
|
|
7
7
|
const { collectAndSaveToken } = require("../init/token");
|
|
8
|
-
const
|
|
8
|
+
const {
|
|
9
|
+
getSelectedProjects,
|
|
10
|
+
getIsUsingComponents,
|
|
11
|
+
} = require("../utils/getSelectedProjects");
|
|
9
12
|
const promptForProject = require("../utils/promptForProject");
|
|
10
13
|
|
|
11
14
|
function quit(exitCode = 2) {
|
|
@@ -15,10 +18,10 @@ function quit(exitCode = 2) {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function saveProject(file, name, id) {
|
|
18
|
-
// old functionality included "
|
|
21
|
+
// old functionality included "components" in the `projects`
|
|
19
22
|
// array, but we want to always treat the component library as a separate
|
|
20
23
|
// entity and use the new notation of a top-level `components` key
|
|
21
|
-
if (id === "
|
|
24
|
+
if (id === "components") {
|
|
22
25
|
config.writeData(file, { components: true });
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
@@ -39,7 +42,11 @@ async function askForAnotherToken() {
|
|
|
39
42
|
await collectAndSaveToken(message);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
async function listProjects(
|
|
45
|
+
async function listProjects(
|
|
46
|
+
token,
|
|
47
|
+
projectsAlreadySelected,
|
|
48
|
+
componentsSelected
|
|
49
|
+
) {
|
|
43
50
|
const spinner = ora("Fetching projects in your workspace...");
|
|
44
51
|
spinner.start();
|
|
45
52
|
|
|
@@ -58,9 +65,13 @@ async function listProjects(token, projectsAlreadySelected) {
|
|
|
58
65
|
|
|
59
66
|
spinner.stop();
|
|
60
67
|
|
|
61
|
-
return projects.data.filter(
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
return projects.data.filter(({ id }) => {
|
|
69
|
+
if (id === "ditto_component_library") {
|
|
70
|
+
return !componentsSelected;
|
|
71
|
+
} else {
|
|
72
|
+
return !projectsAlreadySelected.some((project) => project.id === id);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
async function collectProject(token, initialize) {
|
|
@@ -74,19 +85,32 @@ async function collectProject(token, initialize) {
|
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
const projectsAlreadySelected = getSelectedProjects();
|
|
77
|
-
const
|
|
88
|
+
const usingComponents = getIsUsingComponents();
|
|
89
|
+
const projects = await listProjects(
|
|
90
|
+
token,
|
|
91
|
+
projectsAlreadySelected,
|
|
92
|
+
usingComponents
|
|
93
|
+
);
|
|
78
94
|
|
|
79
95
|
if (!(projects && projects.length)) {
|
|
80
96
|
console.log("You're currently syncing all projects in your workspace.");
|
|
81
|
-
console.log(
|
|
97
|
+
console.log(
|
|
98
|
+
output.warnText(
|
|
99
|
+
"Not seeing a project that you were expecting? Verify that developer mode is enabled on that project. More info: https://www.dittowords.com/docs/ditto-developer-mode"
|
|
100
|
+
)
|
|
101
|
+
);
|
|
82
102
|
return null;
|
|
83
103
|
}
|
|
84
104
|
|
|
105
|
+
const nonInitPrompt = usingComponents
|
|
106
|
+
? "Add a project"
|
|
107
|
+
: "Add a project or library";
|
|
108
|
+
|
|
85
109
|
return promptForProject({
|
|
86
110
|
projects,
|
|
87
111
|
message: initialize
|
|
88
|
-
? "Choose the project you'd like to sync text from"
|
|
89
|
-
:
|
|
112
|
+
? "Choose the project or library you'd like to sync text from"
|
|
113
|
+
: nonInitPrompt,
|
|
90
114
|
});
|
|
91
115
|
}
|
|
92
116
|
|
package/lib/init/token.js
CHANGED
|
@@ -58,7 +58,7 @@ async function checkToken(token) {
|
|
|
58
58
|
|
|
59
59
|
async function collectToken(message) {
|
|
60
60
|
const blue = output.info;
|
|
61
|
-
const apiUrl = output.url("https://
|
|
61
|
+
const apiUrl = output.url("https://app.dittowords.com/account/user");
|
|
62
62
|
const breadcrumbs = `${blue("User")}`;
|
|
63
63
|
const tokenDescription =
|
|
64
64
|
message ||
|
package/lib/pull.js
CHANGED
|
@@ -89,9 +89,15 @@ async function downloadAndSaveVariant(variantApiId, projects, format, token) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @param {{ meta: Object.<string, string> }} options
|
|
94
|
+
*/
|
|
95
|
+
async function downloadAndSaveVariants(projects, format, token, options) {
|
|
96
|
+
const meta = options ? options.meta : {};
|
|
97
|
+
|
|
93
98
|
const { data: variants } = await api.get("/variants", {
|
|
94
99
|
params: {
|
|
100
|
+
...meta,
|
|
95
101
|
projectIds: projects.map(({ id }) => id),
|
|
96
102
|
},
|
|
97
103
|
headers: { Authorization: `token ${token}` },
|
|
@@ -107,8 +113,15 @@ async function downloadAndSaveVariants(projects, format, token) {
|
|
|
107
113
|
return messages.join("");
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
/**
|
|
117
|
+
@param {{ meta: Object.<string, string> }} options
|
|
118
|
+
*/
|
|
119
|
+
async function downloadAndSaveBase(projects, format, token, options) {
|
|
120
|
+
const meta = options ? options.meta : {};
|
|
121
|
+
|
|
122
|
+
const params = {
|
|
123
|
+
...meta,
|
|
124
|
+
};
|
|
112
125
|
if (format) {
|
|
113
126
|
params.format = format;
|
|
114
127
|
}
|
|
@@ -261,7 +274,10 @@ function generateJsDriver(projects, variants, format) {
|
|
|
261
274
|
return `Generated .js SDK driver at ${output.info(filePath)}`;
|
|
262
275
|
}
|
|
263
276
|
|
|
264
|
-
|
|
277
|
+
/**
|
|
278
|
+
* @param {{ meta: Object.<string, string> }} options
|
|
279
|
+
*/
|
|
280
|
+
async function downloadAndSave(sourceInformation, token, options) {
|
|
265
281
|
const { validProjects, variants, format, shouldFetchComponentLibrary } =
|
|
266
282
|
sourceInformation;
|
|
267
283
|
|
|
@@ -287,9 +303,10 @@ async function downloadAndSave(sourceInformation, token) {
|
|
|
287
303
|
try {
|
|
288
304
|
msg += cleanOutputFiles();
|
|
289
305
|
|
|
306
|
+
const meta = options ? options.meta : {};
|
|
290
307
|
msg += variants
|
|
291
|
-
? await downloadAndSaveVariants(validProjects, format, token)
|
|
292
|
-
: await downloadAndSaveBase(validProjects, format, token);
|
|
308
|
+
? await downloadAndSaveVariants(validProjects, format, token, { meta })
|
|
309
|
+
: await downloadAndSaveBase(validProjects, format, token, { meta });
|
|
293
310
|
|
|
294
311
|
msg += generateJsDriver(validProjects, variants, format);
|
|
295
312
|
|
|
@@ -334,11 +351,15 @@ async function downloadAndSave(sourceInformation, token) {
|
|
|
334
351
|
}
|
|
335
352
|
}
|
|
336
353
|
|
|
337
|
-
|
|
354
|
+
/**
|
|
355
|
+
* @param {{ meta: Object.<string, string> }} options
|
|
356
|
+
*/
|
|
357
|
+
function pull(options) {
|
|
358
|
+
const meta = options ? options.meta : {};
|
|
338
359
|
const token = config.getToken(consts.CONFIG_FILE, consts.API_HOST);
|
|
339
360
|
const sourceInformation = config.parseSourceInformation();
|
|
340
361
|
|
|
341
|
-
return downloadAndSave(sourceInformation, token);
|
|
362
|
+
return downloadAndSave(sourceInformation, token, { meta });
|
|
342
363
|
}
|
|
343
364
|
|
|
344
365
|
module.exports = {
|
package/lib/remove-project.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
const config = require("./config");
|
|
2
2
|
const consts = require("./consts");
|
|
3
3
|
const output = require("./output");
|
|
4
|
-
const
|
|
4
|
+
const {
|
|
5
|
+
getSelectedProjects,
|
|
6
|
+
getIsUsingComponents,
|
|
7
|
+
} = require("./utils/getSelectedProjects");
|
|
5
8
|
const promptForProject = require("./utils/promptForProject");
|
|
6
9
|
|
|
7
10
|
async function removeProject() {
|
|
8
11
|
const projects = getSelectedProjects();
|
|
9
|
-
|
|
12
|
+
const isUsingComponents = getIsUsingComponents();
|
|
13
|
+
if (!projects.length && !isUsingComponents) {
|
|
10
14
|
console.log(
|
|
11
15
|
"\n" +
|
|
12
16
|
"No projects found in your workspace.\n" +
|
|
@@ -15,13 +19,24 @@ async function removeProject() {
|
|
|
15
19
|
return;
|
|
16
20
|
}
|
|
17
21
|
|
|
22
|
+
const allProjects = isUsingComponents
|
|
23
|
+
? [
|
|
24
|
+
{ id: "ditto_component_library", name: "Ditto Component Library" },
|
|
25
|
+
...projects,
|
|
26
|
+
]
|
|
27
|
+
: projects;
|
|
28
|
+
|
|
18
29
|
const projectToRemove = await promptForProject({
|
|
19
|
-
projects,
|
|
20
|
-
message:
|
|
30
|
+
projects: allProjects,
|
|
31
|
+
message: isUsingComponents
|
|
32
|
+
? "Select a project or library to remove"
|
|
33
|
+
: "Select a project to remove",
|
|
21
34
|
});
|
|
22
35
|
if (!projectToRemove) return;
|
|
23
36
|
|
|
24
37
|
config.writeData(consts.PROJECT_CONFIG_FILE, {
|
|
38
|
+
components:
|
|
39
|
+
isUsingComponents && projectToRemove.id !== "ditto_component_library",
|
|
25
40
|
projects: projects.filter(({ id }) => id !== projectToRemove.id),
|
|
26
41
|
});
|
|
27
42
|
|
|
@@ -32,4 +32,13 @@ function getSelectedProjects(configFile = PROJECT_CONFIG_FILE) {
|
|
|
32
32
|
return contentJson.projects.filter(({ name, id }) => name && id);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
const getIsUsingComponents = (configFile = PROJECT_CONFIG_FILE) => {
|
|
36
|
+
if (!fs.existsSync(configFile)) return [];
|
|
37
|
+
|
|
38
|
+
const contentYaml = fs.readFileSync(configFile, "utf8");
|
|
39
|
+
const contentJson = yamlToJson(contentYaml);
|
|
40
|
+
|
|
41
|
+
return contentJson && contentJson.components;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports = { getSelectedProjects, getIsUsingComponents };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const processMetaOption = (inputArr) => {
|
|
2
|
+
const res = {};
|
|
3
|
+
|
|
4
|
+
if (!Array.isArray(inputArr)) {
|
|
5
|
+
return res;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
inputArr.forEach((element) => {
|
|
9
|
+
const [key, value] = element.split(':');
|
|
10
|
+
res[key] = value;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return res;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = processMetaOption;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const processMetaOption = require('./processMetaOption');
|
|
2
|
+
|
|
3
|
+
describe('processMetaOption tests', () => {
|
|
4
|
+
it('It parses correctly', () => {
|
|
5
|
+
expect(processMetaOption(['githubActionRequest:true', 'trigger:manual'])).toEqual({
|
|
6
|
+
githubActionRequest: 'true',
|
|
7
|
+
trigger: 'manual',
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
it('Malformed doesnt crash', () => {
|
|
11
|
+
expect(processMetaOption(['context:github-action', 'trigger'])).toEqual({
|
|
12
|
+
context: 'github-action',
|
|
13
|
+
trigger: undefined,
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|