@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 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://beta.dittowords.com/account/user](https://beta.dittowords.com/account/user) under **API Keys**):
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://beta.dittowords.com/components/all
45
- - NUX Onboarding Flow https://beta.dittowords.com/doc/609e9981c313f8018d0c346a
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
  };
@@ -1,6 +1,10 @@
1
1
  const { collectAndSaveProject } = require("./init/project");
2
2
  const projectsToText = require("./utils/projectsToText");
3
- const getSelectedProjects = require("./utils/getSelectedProjects");
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
- console.log(
16
- `\nYou're currently set up to sync text from the following projects: ${projectsToText(
17
- projects
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(
@@ -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 getSelectedProjects = require("../utils/getSelectedProjects");
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 "ditto_component_library" in the `projects`
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 === "ditto_component_library") {
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(token, projectsAlreadySelected) {
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
- ({ id }) => !projectsAlreadySelected.some((project) => project.id === id)
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 projects = await listProjects(token, projectsAlreadySelected);
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(output.warnText("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"));
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
- : "Add a project",
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://beta.dittowords.com/account/user");
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
- async function downloadAndSaveVariants(projects, format, token) {
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
- async function downloadAndSaveBase(projects, format, token) {
111
- const params = {};
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
- async function downloadAndSave(sourceInformation, token) {
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
- function pull() {
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 = {
@@ -1,12 +1,16 @@
1
1
  const config = require("./config");
2
2
  const consts = require("./consts");
3
3
  const output = require("./output");
4
- const getSelectedProjects = require("./utils/getSelectedProjects");
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
- if (!projects.length) {
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: "Select a project to remove",
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
- module.exports = getSelectedProjects;
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
+ });
@@ -9,7 +9,7 @@ function projectsToText(projects) {
9
9
  "- " +
10
10
  output.info(name) +
11
11
  " " +
12
- output.subtle("https://beta.dittowords.com/doc/" + id)),
12
+ output.subtle("https://app.dittowords.com/doc/" + id)),
13
13
  ""
14
14
  ) + "\n"
15
15
  );
@@ -7,7 +7,7 @@ function formatProjectChoice(project) {
7
7
  project.name +
8
8
  " " +
9
9
  output.subtle(
10
- project.url || `https://beta.dittowords.com/doc/${project.id}`
10
+ project.url || `https://app.dittowords.com/doc/${project.id}`
11
11
  )
12
12
  );
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dittowords/cli",
3
- "version": "2.3.0",
3
+ "version": "2.5.1",
4
4
  "description": "Command Line Interface for Ditto (dittowords.com).",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {