@wiztivi/dana-cli 0.0.7 → 0.0.9

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 (25) hide show
  1. package/{dist/README.md → README.md} +1 -0
  2. package/dist/commands/addDevice/deviceConfig/androidtvConfig.d.ts +0 -4
  3. package/dist/commands/addDevice/deviceConfig/androidtvConfig.js +2 -8
  4. package/dist/commands/addDevice/deviceConfig/configTypes.d.ts +12 -1
  5. package/dist/commands/addDevice/deviceConfig/tizenConfig.js +34 -3
  6. package/dist/commands/addDevice/helper/addDeviceHelper.d.ts +12 -0
  7. package/dist/commands/addDevice/helper/addDeviceHelper.js +19 -0
  8. package/dist/commands/authentication/AuthenticationManager.d.ts +22 -0
  9. package/dist/commands/authentication/AuthenticationManager.js +92 -0
  10. package/dist/commands/authentication/authDefinition.js +1 -2
  11. package/dist/commands/authentication/authHook.d.ts +1 -1
  12. package/dist/commands/authentication/authHook.js +17 -4
  13. package/dist/commands/authentication/commands/login.js +12 -83
  14. package/dist/commands/authentication/helper/CredentialsHelper.d.ts +14 -5
  15. package/dist/commands/authentication/helper/CredentialsHelper.js +25 -5
  16. package/dist/commands/createApp/helpers/CreateAppHelper.d.ts +0 -1
  17. package/dist/common/const/deviceConst.js +1 -1
  18. package/dist/common/helpers/InstallHelper.d.ts +1 -0
  19. package/dist/common/helpers/InstallHelper.js +15 -3
  20. package/dist/common/helpers/UpdateFileHelper.js +11 -3
  21. package/dist/common/helpers/UserInputGetter.js +4 -1
  22. package/dist/common/helpers/stringHelper.d.ts +5 -0
  23. package/dist/common/helpers/stringHelper.js +12 -0
  24. package/package.json +8 -5
  25. package/dist/dana_completion.sh +0 -153
@@ -38,6 +38,7 @@ Use the CLI to:
38
38
  - create a new app project
39
39
  - add device(s) easily to deploy your project on more platforms: `npx @wiztivi/dana-cli add-device`,
40
40
  - add built-in components to your project: eg `npx @wiztivi/dana-cli add-menu`
41
+ - get autocompletion script for dana commands: `npx @wiztivi/dana-cli completion <shell>`
41
42
 
42
43
  To get all available options and more information:`npx @wiztivi/dana-cli help`
43
44
 
@@ -1,10 +1,6 @@
1
1
  import { AndroidConfig } from "./configTypes.js";
2
2
  declare const AndroidtvConfig: {
3
3
  new (): {};
4
- /**
5
- * Get the app icon url
6
- */
7
- readonly _getAppIcon: () => Promise<string>;
8
4
  /**
9
5
  * Ask the appId
10
6
  */
@@ -12,7 +12,6 @@
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import DeviceConfig from "./deviceConfig.js";
15
- import { DEFAULT_IMAGE_URL } from "../../createApp/const/setupConst.js";
16
15
  import ComponentHelper from "../../addComponent/helper/ComponentHelper.js";
17
16
  import * as prompts from "@clack/prompts";
18
17
  import colors from "picocolors";
@@ -24,13 +23,8 @@ import { CredentialsHelper } from "../../authentication/helper/CredentialsHelper
24
23
  import { ListPackageVersionsCommand } from "@aws-sdk/client-codeartifact";
25
24
  import { createCodeArtifactClient } from "../../authentication/helper/CodeArtifactHelper.js";
26
25
  import { DOMAIN, DOMAIN_OWNER } from "../../authentication/authentConst.js";
26
+ import addDeviceHelper from "../helper/addDeviceHelper.js";
27
27
  const AndroidtvConfig = class extends DeviceConfig {
28
- /**
29
- * Get the app icon url
30
- */
31
- static _getAppIcon = () => {
32
- return Promise.resolve(DEFAULT_IMAGE_URL);
33
- };
34
28
  /**
35
29
  * Ask the appId
36
30
  */
@@ -122,7 +116,7 @@ const AndroidtvConfig = class extends DeviceConfig {
122
116
  appName: () => this._askAppName(),
123
117
  appVersion: () => this._askAppVersion(),
124
118
  appVersionCode: () => this._askAppVersionCode(),
125
- icon: () => this._getAppIcon(),
119
+ icon: () => addDeviceHelper.getAppIcon(),
126
120
  wtvAndroidVersion: () => this._getWtvAndroidVersion(),
127
121
  }, {
128
122
  onCancel: () => {
@@ -9,8 +9,19 @@ interface AndroidDevice {
9
9
  interface AndroidConfig {
10
10
  device: AndroidDevice;
11
11
  }
12
+ interface TizenDevice {
13
+ type?: string;
14
+ profileName?: string;
15
+ icon: string;
16
+ application: {
17
+ requiredVersion: string;
18
+ package: string;
19
+ id: string;
20
+ };
21
+ }
12
22
  interface TizenConf {
13
23
  renderers: string[];
24
+ device: TizenDevice;
14
25
  }
15
26
  interface WebosConf {
16
27
  device: WebosDevice;
@@ -24,4 +35,4 @@ interface WebosDevice {
24
35
  largeIcon: string;
25
36
  disableBackHistoryAPI: boolean;
26
37
  }
27
- export { AndroidDevice, AndroidConfig, TizenConf, WebosConf, WebosDevice };
38
+ export { AndroidDevice, AndroidConfig, TizenConf, TizenDevice, WebosConf, WebosDevice };
@@ -13,6 +13,11 @@ import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import DeviceConfig from "./deviceConfig.js";
15
15
  import { LIGHTNING } from "../../../common/const/deviceConst.js";
16
+ import * as prompts from "@clack/prompts";
17
+ import colors from "picocolors";
18
+ import addDeviceHelper from "../helper/addDeviceHelper.js";
19
+ import { validAppVersion, validInputLength } from "../../../common/helpers/InputValidator.js";
20
+ import { clean } from "semver-ts";
16
21
  const TizenConfig = class extends DeviceConfig {
17
22
  static appConfig = {
18
23
  css: {
@@ -34,16 +39,42 @@ const TizenConfig = class extends DeviceConfig {
34
39
  },
35
40
  },
36
41
  };
37
- static getConfig = () => {
38
- const tizenConfig = {
42
+ static getConfig = async () => {
43
+ prompts.log.info(colors.bold("Let's configure Tizen !"));
44
+ const applicationConfig = await prompts.group({
45
+ requiredVersion: async () => {
46
+ const requiredVersion = await addDeviceHelper.promptTextFactory({
47
+ promptMessage: "What is the application requiredVersion ?",
48
+ validationFunction: (value) => validAppVersion(value),
49
+ placeholder: "1.0.0",
50
+ initialValue: "1.0.0",
51
+ });
52
+ return clean(requiredVersion);
53
+ },
54
+ package: async () => addDeviceHelper.promptTextFactory({
55
+ errorMessage: "Package name is required !",
56
+ promptMessage: "What is the package name ? Should be unique.",
57
+ validationFunction: (value) => validInputLength(value, "Package name is required !"),
58
+ }),
59
+ id: async () => addDeviceHelper.promptTextFactory({
60
+ errorMessage: "Package id is required !",
61
+ promptMessage: "What is the package id ?",
62
+ validationFunction: (value) => validInputLength(value, "Package id is required !"),
63
+ }),
64
+ });
65
+ return {
39
66
  renderers: [LIGHTNING],
67
+ device: {
68
+ icon: await addDeviceHelper.getAppIcon(),
69
+ application: { ...applicationConfig },
70
+ },
40
71
  };
41
- return Promise.resolve(tizenConfig);
42
72
  };
43
73
  static setConfig = ({ directory, config }) => {
44
74
  const filePath = path.join(directory, "profiles", "app.config.tizen.json");
45
75
  let jsonData = JSON.parse(fs.readFileSync(filePath).toString());
46
76
  jsonData = this.updateProfileJsonWithRenderers(jsonData, config.renderers);
77
+ jsonData.tizen.device = { ...jsonData.tizen.device, ...config.device };
47
78
  fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 4));
48
79
  return Promise.resolve("Tizen configuration done");
49
80
  };
@@ -1,3 +1,10 @@
1
+ export interface PromptFactoryParams {
2
+ errorMessage?: string;
3
+ promptMessage: string;
4
+ validationFunction: (value: string, errorMessage?: string) => string | undefined;
5
+ placeholder?: string;
6
+ initialValue?: string;
7
+ }
1
8
  declare const AddDeviceHelper: {
2
9
  new (): {};
3
10
  /**
@@ -9,5 +16,10 @@ declare const AddDeviceHelper: {
9
16
  * We use script so we can get renderers for smartTv
10
17
  */
11
18
  readonly getInstalledDevices: (directory: string) => string[];
19
+ /**
20
+ * Get the app icon url
21
+ */
22
+ readonly getAppIcon: () => Promise<string>;
23
+ readonly promptTextFactory: ({ errorMessage, promptMessage, validationFunction, placeholder, initialValue, }: PromptFactoryParams) => Promise<string>;
12
24
  };
13
25
  export default AddDeviceHelper;
@@ -1,6 +1,9 @@
1
1
  import { execSync } from "node:child_process";
2
2
  import { readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
+ import * as prompts from "@clack/prompts";
5
+ import { DEFAULT_IMAGE_URL } from "../../createApp/const/setupConst.js";
6
+ import ComponentHelper from "../../addComponent/helper/ComponentHelper.js";
4
7
  const AddDeviceHelper = class {
5
8
  /**
6
9
  * clean on error
@@ -30,5 +33,21 @@ const AddDeviceHelper = class {
30
33
  }
31
34
  return installedDevices;
32
35
  };
36
+ /**
37
+ * Get the app icon url
38
+ */
39
+ static getAppIcon = () => {
40
+ return Promise.resolve(DEFAULT_IMAGE_URL);
41
+ };
42
+ static promptTextFactory = async ({ errorMessage, promptMessage, validationFunction, placeholder, initialValue, }) => {
43
+ const result = (await prompts.text({
44
+ message: promptMessage,
45
+ placeholder,
46
+ initialValue,
47
+ validate: (value) => validationFunction(value, errorMessage),
48
+ }));
49
+ ComponentHelper.handleCancellation(result);
50
+ return result;
51
+ };
33
52
  };
34
53
  export default AddDeviceHelper;
@@ -0,0 +1,22 @@
1
+ import { AwsCredentials, DanaToken } from "./helper/CredentialsHelper.js";
2
+ import { JwtPayload } from "jsonwebtoken";
3
+ export interface CognitoIdToken extends JwtPayload {
4
+ given_name: string;
5
+ family_name: string;
6
+ email: string;
7
+ }
8
+ export interface AuthCredentials {
9
+ aws: AwsCredentials;
10
+ dana: DanaToken;
11
+ org: string;
12
+ }
13
+ export declare class AuthenticationManager {
14
+ private static getAPIURL;
15
+ static login(organization: string): Promise<AuthCredentials>;
16
+ static refresh(organization: string, token: string): Promise<AuthCredentials>;
17
+ static isTokenExpired(token: string): boolean;
18
+ static loginNPM(organization: string, credentials: AwsCredentials): Promise<{
19
+ token: string;
20
+ expiration?: Date;
21
+ }>;
22
+ }
@@ -0,0 +1,92 @@
1
+ import getPort from "get-port";
2
+ import express from "express";
3
+ import { CredentialsHelper } from "./helper/CredentialsHelper.js";
4
+ import open from "open";
5
+ import jwt from "jsonwebtoken";
6
+ import { createCodeArtifactClient, getRepositoryEndpoint } from "./helper/CodeArtifactHelper.js";
7
+ import { GetAuthorizationTokenCommand } from "@aws-sdk/client-codeartifact";
8
+ import { DOMAIN, DOMAIN_OWNER } from "./authentConst.js";
9
+ import { execSync } from "node:child_process";
10
+ export class AuthenticationManager {
11
+ static getAPIURL(organization) {
12
+ return `https://api.${organization}.dana-framework.com`; // Or http://localhost:3001
13
+ }
14
+ static async login(organization) {
15
+ const app = express();
16
+ app.disable("x-powered-by");
17
+ const promise = new Promise((resolve, reject) => {
18
+ app.get("/callback", (req, res) => {
19
+ try {
20
+ if (req.query.error) {
21
+ res.send("Login failed. Check the CLI for details.");
22
+ return reject(new Error(req.query.error));
23
+ }
24
+ const token = JSON.parse(req.query.token);
25
+ const awsCredentials = JSON.parse(req.query.aws);
26
+ const creds = { aws: awsCredentials, dana: token, org: organization };
27
+ res.send("Login successful! You can close this window.");
28
+ CredentialsHelper.store(creds);
29
+ resolve(creds);
30
+ }
31
+ catch (err) {
32
+ res.send("Login failed. Check the CLI for details.");
33
+ reject(err);
34
+ }
35
+ });
36
+ });
37
+ const port = await getPort();
38
+ const server = app.listen(port);
39
+ await open(`${AuthenticationManager.getAPIURL(organization)}/login?port=${port}`);
40
+ return promise.finally(() => {
41
+ server.closeAllConnections();
42
+ server.close();
43
+ });
44
+ }
45
+ static async refresh(organization, token) {
46
+ const data = await fetch(`${AuthenticationManager.getAPIURL(organization)}/login?refresh_token=${token}`);
47
+ if (!data.ok) {
48
+ return AuthenticationManager.login(organization);
49
+ }
50
+ const refreshedCreds = (await data.json());
51
+ const creds = {
52
+ aws: refreshedCreds.aws,
53
+ dana: {
54
+ ...refreshedCreds.token,
55
+ refresh_token: token,
56
+ },
57
+ org: organization,
58
+ };
59
+ CredentialsHelper.store(creds);
60
+ return creds;
61
+ }
62
+ static isTokenExpired(token) {
63
+ const payload = jwt.decode(token);
64
+ if (!payload.exp) {
65
+ return true;
66
+ }
67
+ const expirationDate = new Date(payload.exp * 1000);
68
+ return expirationDate <= new Date();
69
+ }
70
+ static async loginNPM(organization, credentials) {
71
+ // Create CodeArtifact client with credentials
72
+ const client = createCodeArtifactClient(credentials);
73
+ // Get authorization token
74
+ const tokenCommand = new GetAuthorizationTokenCommand({
75
+ domain: DOMAIN,
76
+ domainOwner: DOMAIN_OWNER,
77
+ });
78
+ const { authorizationToken, expiration } = await client.send(tokenCommand);
79
+ if (!authorizationToken) {
80
+ throw new Error("Authorization token not found");
81
+ }
82
+ // Get repository endpoint
83
+ const repositoryEndpoint = await getRepositoryEndpoint({ organization, credentials, client, format: "npm" });
84
+ // Configure npm with the repository and token
85
+ execSync(`npm config set @dana:registry=${repositoryEndpoint}`);
86
+ execSync(`npm config set //${repositoryEndpoint.replace(/^https?:\/\//, "")}:_authToken=${authorizationToken}`);
87
+ return {
88
+ token: authorizationToken,
89
+ expiration,
90
+ };
91
+ }
92
+ }
@@ -2,12 +2,11 @@ import { Command } from "commander";
2
2
  import translation from "../../common/translation/translation.js";
3
3
  import login from "./commands/login.js";
4
4
  import { statusAction } from "./commands/status.js";
5
- import { DEFAULT_ORG } from "./authentConst.js";
6
5
  const addAuthDefinition = () => {
7
6
  const command = new Command("auth").description(translation["command.auth.description"]);
8
7
  const loginCmd = new Command("login")
9
8
  .description(translation["command.auth.login.description"])
10
- .option("-o, --org <organization>", translation["command.auth.login.option.org"], DEFAULT_ORG)
9
+ .option("-o, --org <organization>", translation["command.auth.login.option.org"])
11
10
  .action(login);
12
11
  command.addCommand(loginCmd);
13
12
  const statusCmd = new Command("status")
@@ -1 +1 @@
1
- export declare const authHook: () => void;
1
+ export declare const authHook: () => Promise<void>;
@@ -9,11 +9,24 @@
9
9
  * This software MAY NOT be used, modified or rewritten without prior written permission from Wiztivi.
10
10
  *
11
11
  */
12
- import { status, LOGIN_STATUS } from "./commands/status.js";
12
+ import * as prompts from "@clack/prompts";
13
+ import { CredentialsHelper } from "./helper/CredentialsHelper.js";
14
+ import { AuthenticationManager } from "./AuthenticationManager.js";
13
15
  import { LOGIN_ERROR } from "../../common/const/exitCodeConst.js";
14
- export const authHook = () => {
15
- const isLogged = status() === LOGIN_STATUS.LOGGED;
16
- if (!isLogged) {
16
+ export const authHook = async () => {
17
+ const credentials = CredentialsHelper.get();
18
+ try {
19
+ if (!credentials.dana?.id_token || !credentials.dana?.refresh_token) {
20
+ await AuthenticationManager.login(credentials.org);
21
+ return;
22
+ }
23
+ if (AuthenticationManager.isTokenExpired(credentials.dana.id_token) ||
24
+ (credentials.aws && new Date(credentials.aws.expiration) < new Date())) {
25
+ await AuthenticationManager.refresh(credentials.org, credentials.dana.refresh_token);
26
+ }
27
+ }
28
+ catch (error) {
29
+ prompts.log.error(error.message);
17
30
  process.exit(LOGIN_ERROR);
18
31
  }
19
32
  };
@@ -1,77 +1,26 @@
1
- import { GetAuthorizationTokenCommand } from "@aws-sdk/client-codeartifact";
2
1
  import * as prompts from "@clack/prompts";
3
- import { execSync } from "node:child_process";
4
- import express from "express";
5
- import getPort from "get-port";
6
- import open from "open";
7
2
  import colors from "picocolors";
8
3
  import { LOGIN_ERROR } from "../../../common/const/exitCodeConst.js";
9
4
  import { CredentialsHelper } from "../helper/CredentialsHelper.js";
10
- import { DEFAULT_ORG, DOMAIN, DOMAIN_OWNER } from "../authentConst.js";
11
- import { getRepositoryEndpoint, createCodeArtifactClient } from "../helper/CodeArtifactHelper.js";
5
+ import { AuthenticationManager } from "../AuthenticationManager.js";
12
6
  /*
13
7
  Login to DANA backend (with aws and cognito) and connect to npm codeartifact
14
8
  More info: https://github.com/wiztivi-rd/dana-cli/wiki/Login
15
9
  */
16
- const login = async ({ org = DEFAULT_ORG }) => {
17
- const apiUrl = `https://api.${org}.dana-framework.com`; // Or http://localhost:3001
18
- const credentials = CredentialsHelper.get();
19
- if (credentials) {
20
- try {
21
- if (new Date(credentials.aws.expiration) < new Date()) {
22
- const data = await fetch(`${apiUrl}/login?refresh_token=${credentials.dana.refresh_token}`);
23
- if (!data.ok) {
24
- throw new Error("Unable to refresh token");
25
- }
26
- const refreshedCreds = (await data.json());
27
- credentials.aws = refreshedCreds.aws;
28
- credentials.dana = {
29
- ...refreshedCreds.token,
30
- refresh_token: credentials.dana.refresh_token,
31
- };
32
- CredentialsHelper.store(credentials);
33
- }
34
- prompts.log.info(`Retrieved credentials for ${org} from local cache`);
35
- await codeartifactLogin(org, credentials.aws);
36
- return;
37
- }
38
- catch (error) {
39
- // Log message and start nominal login process
40
- prompts.log.error(error.message);
41
- }
42
- }
43
- prompts.intro(`Login to ${org}...`);
10
+ const login = async ({ org }) => {
11
+ const credentials = CredentialsHelper.get(org);
44
12
  try {
45
- const port = await getPort();
46
- const app = express();
47
- app.disable("x-powered-by");
48
- const server = app.listen(port);
49
- app.get("/callback", async (req, res) => {
50
- try {
51
- if (req.query.error) {
52
- throw new Error(req.query.error);
53
- }
54
- const token = JSON.parse(req.query.token);
55
- const awsCredentials = JSON.parse(req.query.aws);
56
- CredentialsHelper.store({ aws: awsCredentials, dana: token, org });
57
- // Login to codeartifact repository
58
- await codeartifactLogin(org, awsCredentials);
59
- prompts.outro(colors.green("✓") + ` Login successfull`);
60
- res.send("Login successful! You can close this window.");
61
- }
62
- catch (err) {
63
- console.error(err);
64
- res.send("Login failed. Check the CLI for details.");
65
- handleError(err);
66
- }
67
- finally {
68
- server.close();
69
- }
70
- });
71
- await open(`${apiUrl}/login?port=${port}`);
13
+ prompts.intro(`Login to organization ${credentials.org} ...`);
14
+ const validCredentials = await AuthenticationManager.login(credentials.org);
15
+ prompts.outro(colors.green("") + ` Login successful`);
16
+ const npmToken = await AuthenticationManager.loginNPM(credentials.org, validCredentials.aws);
17
+ let stepText = "Successfully logged in to npm repository.";
18
+ if (npmToken.expiration) {
19
+ stepText += `\nLogin expires in 12 hours at: ${npmToken.expiration.toLocaleString()}`;
20
+ }
21
+ prompts.log.step(stepText);
72
22
  }
73
23
  catch (error) {
74
- console.error(error);
75
24
  handleError(error);
76
25
  }
77
26
  };
@@ -80,24 +29,4 @@ const handleError = (error) => {
80
29
  prompts.outro(colors.red(`An issue occurred :` + message));
81
30
  process.exit(LOGIN_ERROR);
82
31
  };
83
- const codeartifactLogin = async (organization, credentials) => {
84
- // Create CodeArtifact client with credentials
85
- const client = createCodeArtifactClient(credentials);
86
- // Get authorization token
87
- const tokenCommand = new GetAuthorizationTokenCommand({
88
- domain: DOMAIN,
89
- domainOwner: DOMAIN_OWNER,
90
- });
91
- const { authorizationToken, expiration } = await client.send(tokenCommand);
92
- if (!authorizationToken) {
93
- throw new Error("Authorization token not found");
94
- }
95
- // Get repository endpoint
96
- const repositoryEndpoint = await getRepositoryEndpoint({ organization, credentials, client, format: "npm" });
97
- // Configure npm with the repository and token
98
- execSync(`npm config set @dana:registry=${repositoryEndpoint}`);
99
- execSync(`npm config set //${repositoryEndpoint.replace(/^https?:\/\//, "")}:_authToken=${authorizationToken}`);
100
- const expiresAt = expiration ? new Date(expiration).toLocaleString() : "unknown";
101
- prompts.log.step(`Successfully configured npm to use AWS CodeArtifact repository ${repositoryEndpoint}. \nLogin expires in 12 hours at: ${expiresAt}`);
102
- };
103
32
  export default login;
@@ -8,20 +8,29 @@ export interface AwsCredentials {
8
8
  export interface DanaToken {
9
9
  id_token: string;
10
10
  access_token: string;
11
- refresh_token?: string;
11
+ refresh_token: string;
12
12
  expires_in: number;
13
13
  token_type: string;
14
14
  }
15
+ export interface OrgCredentials {
16
+ aws: AwsCredentials;
17
+ dana: DanaToken;
18
+ }
19
+ export interface DanaConfig {
20
+ lastUsedOrg: string;
21
+ credentials: Record<string, OrgCredentials>;
22
+ }
15
23
  export declare class CredentialsHelper {
16
24
  static readonly filePath: string;
25
+ private static getConfig;
17
26
  static store(credentials: {
18
27
  aws: AwsCredentials;
19
28
  dana: DanaToken;
20
29
  org: string;
21
30
  }): void;
22
- static get(): {
23
- aws: AwsCredentials;
24
- dana: DanaToken;
31
+ static get(org?: string): {
32
+ aws?: AwsCredentials;
33
+ dana?: DanaToken;
25
34
  org: string;
26
- } | null;
35
+ };
27
36
  }
@@ -1,15 +1,35 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
+ import { DEFAULT_ORG } from "../authentConst.js";
4
5
  export class CredentialsHelper {
5
6
  static filePath = path.join(homedir(), ".dana.json");
6
- static store(credentials) {
7
- writeFileSync(this.filePath, JSON.stringify(credentials, null, 4));
8
- }
9
- static get() {
7
+ static getConfig() {
10
8
  if (!existsSync(this.filePath)) {
11
9
  return null;
12
10
  }
13
- return JSON.parse(readFileSync(this.filePath).toString());
11
+ return JSON.parse(readFileSync(this.filePath, { encoding: "utf-8" }));
12
+ }
13
+ static store(credentials) {
14
+ let config = this.getConfig();
15
+ config ??= { lastUsedOrg: credentials.org, credentials: {} };
16
+ config.credentials ??= {};
17
+ config.lastUsedOrg = credentials.org;
18
+ config.credentials[credentials.org] = { aws: credentials.aws, dana: credentials.dana };
19
+ writeFileSync(this.filePath, JSON.stringify(config, null, 4));
20
+ }
21
+ static get(org) {
22
+ const config = this.getConfig();
23
+ const organization = org ?? config?.lastUsedOrg ?? DEFAULT_ORG;
24
+ const creds = {
25
+ org: organization,
26
+ };
27
+ if (config?.credentials?.[organization]?.aws) {
28
+ creds.aws = config?.credentials[organization]?.aws;
29
+ }
30
+ if (config?.credentials?.[organization]?.dana) {
31
+ creds.dana = config?.credentials[organization]?.dana;
32
+ }
33
+ return creds;
14
34
  }
15
35
  }
@@ -89,7 +89,6 @@ declare const CreateAppHelper: {
89
89
  updateProfileJsonWithRenderers(jsonData: Record<string, unknown>, renderers: string[]): object;
90
90
  } | {
91
91
  new (): {};
92
- readonly _getAppIcon: () => Promise<string>;
93
92
  readonly _askAppId: () => Promise<string>;
94
93
  readonly _askAppName: () => Promise<string>;
95
94
  readonly _askAppVersion: () => Promise<string>;
@@ -29,7 +29,7 @@ const BASIC_SETUP = {
29
29
  [TIZEN]: {
30
30
  label: "Samsung",
31
31
  package: "@dana/vendor-tizen",
32
- dependencies: ["@dana/vendor-tizen", "@dana/vendor-shaka"],
32
+ dependencies: ["@dana/vendor-tizen", "@dana/vendor-shaka", "@dana/tools-smarttv-grunt"],
33
33
  },
34
34
  };
35
35
  //Devices that need a subscription to be added in project
@@ -11,6 +11,7 @@ declare class InstallHelper {
11
11
  }) => Promise<string>;
12
12
  private static getDependenciesList;
13
13
  private static readonly installSingleDependency;
14
+ private static readonly updateSemver;
14
15
  /**
15
16
  * Get a list of all dependencies needed by the different devices chosen by user
16
17
  */
@@ -12,6 +12,8 @@
12
12
  import { exec } from "node:child_process";
13
13
  import { BASIC_DEPENDENCIES, PLATFORM_CONFIG } from "../const/deviceConst.js";
14
14
  import translation from "../translation/translation.js";
15
+ import path from "node:path";
16
+ import fs from "node:fs";
15
17
  class InstallHelper {
16
18
  /**
17
19
  * Install dependencies with npm
@@ -22,9 +24,9 @@ class InstallHelper {
22
24
  return new Promise((resolve, reject) => {
23
25
  exec(`npm i --prefix ${directory} --silent`, {}, (async () => {
24
26
  try {
25
- for (const depName of devDependencies) {
26
- await InstallHelper.installSingleDependency(depName, directory);
27
- }
27
+ const dependenciesString = devDependencies.join(" ").trim();
28
+ await InstallHelper.installSingleDependency(dependenciesString, directory);
29
+ InstallHelper.updateSemver(directory);
28
30
  resolve("Dependencies updated");
29
31
  }
30
32
  catch (error) {
@@ -53,6 +55,16 @@ class InstallHelper {
53
55
  });
54
56
  });
55
57
  };
58
+ static updateSemver = (directory) => {
59
+ const filePath = path.join(directory, "package.json");
60
+ const packageJson = JSON.parse(fs.readFileSync(filePath).toString());
61
+ packageJson.devDependencies = Object.entries(packageJson.devDependencies).reduce((acc, [key, value]) => {
62
+ acc[key] =
63
+ key.includes("@dana") || key.includes("@wiztivi") ? value.replace(/(\d+\.\d+\.)\d$/, "$1x") : value;
64
+ return acc;
65
+ }, {});
66
+ fs.writeFileSync(filePath, JSON.stringify(packageJson, null, 4));
67
+ };
56
68
  /**
57
69
  * Get a list of all dependencies needed by the different devices chosen by user
58
70
  */
@@ -16,6 +16,7 @@ import { templateGenerator } from "./handlebarsHelper.js";
16
16
  import { CredentialsHelper } from "../../commands/authentication/helper/CredentialsHelper.js";
17
17
  import { getRepositoryEndpoint } from "../../commands/authentication/helper/CodeArtifactHelper.js";
18
18
  import { REGISTRY_KEY } from "../../commands/createApp/const/setupConst.js";
19
+ import { formatToCamelCase } from "./stringHelper.js";
19
20
  class UpdateFileHelper {
20
21
  /**
21
22
  * Update app.Config files with mandatory Data
@@ -54,7 +55,9 @@ class UpdateFileHelper {
54
55
  const filePath = path.join(directory, "package.json");
55
56
  const packageJson = JSON.parse(fs.readFileSync(filePath).toString());
56
57
  if (name) {
57
- packageJson.name = name;
58
+ //https://docs.npmjs.com/cli/v10/configuring-npm/package-json?v=true
59
+ const unsafeCharRegex = /[^A-Za-z0-9._-]/g;
60
+ packageJson.name = name.replaceAll(unsafeCharRegex, "").toLowerCase();
58
61
  }
59
62
  for (const device of selectedDevices) {
60
63
  const deviceConfig = config[device];
@@ -87,7 +90,7 @@ class UpdateFileHelper {
87
90
  };
88
91
  static applyTemplatesToFiles = function ({ directory, name, }) {
89
92
  const routesFilePath = path.join(directory, "scripts", "app");
90
- const appName = `${name[0].toUpperCase()}${name.substring(1)}`;
93
+ const appName = `${formatToCamelCase(name)}`;
91
94
  const filesToUpdate = [
92
95
  {
93
96
  fromPath: path.join(routesFilePath, "TemplateAppRoutes.js.hbs"),
@@ -117,7 +120,12 @@ class UpdateFileHelper {
117
120
  return Promise.resolve("Files templated");
118
121
  };
119
122
  static getConfigFileOutput = function (directory) {
120
- const configFiles = ["gitignoreTemplate.hbs", "editorConfigTemplate.hbs", "eslintrc.jsonTemplate.hbs"];
123
+ const configFiles = [
124
+ "gitignoreTemplate.hbs",
125
+ "editorConfigTemplate.hbs",
126
+ "eslintrc.jsonTemplate.hbs",
127
+ "nvmrcTemplate.hbs",
128
+ ];
121
129
  const filesToUpdate = [];
122
130
  for (const configFile of configFiles) {
123
131
  filesToUpdate.push({
@@ -44,7 +44,7 @@ class UserInputGetter {
44
44
  return UserInputGetter.formatAppName(name);
45
45
  }
46
46
  static formatAppName(input) {
47
- const separatorRegex = /[ \-_.]+/;
47
+ const separatorRegex = /[ /]+/;
48
48
  return input
49
49
  .trim()
50
50
  .split(separatorRegex)
@@ -60,6 +60,8 @@ class UserInputGetter {
60
60
  static getAvailableDevices() {
61
61
  const availableDevices = [];
62
62
  const unavailableDevices = [];
63
+ const spinner = prompts.spinner();
64
+ spinner.start();
63
65
  for (const [key, value] of Object.entries(EXTENDED_SETUP)) {
64
66
  try {
65
67
  execSync(`npm view ${value.package} --silent`, { encoding: "utf-8" });
@@ -69,6 +71,7 @@ class UserInputGetter {
69
71
  unavailableDevices.push(EXTENDED_SETUP[key].label);
70
72
  }
71
73
  }
74
+ spinner.stop("Available devices checked");
72
75
  return Promise.resolve({ availableDevices, unavailableDevices });
73
76
  }
74
77
  /**
@@ -1,3 +1,8 @@
1
1
  import { OptionList } from "../types/helperTypes.js";
2
2
  export declare const capitalizeFirstLetter: (input: string) => string;
3
3
  export declare const optionsListString: (input: OptionList) => string;
4
+ /**
5
+ * Remove separator characters from string and convert it to camelCase
6
+ */
7
+ export declare const formatToCamelCase: (input: string) => string;
8
+ export declare const formatWordToCamelCase: (input: string) => string;
@@ -4,3 +4,15 @@ export const capitalizeFirstLetter = (input) => {
4
4
  export const optionsListString = (input) => {
5
5
  return input.map((option) => option.command).join(", ");
6
6
  };
7
+ /**
8
+ * Remove separator characters from string and convert it to camelCase
9
+ */
10
+ export const formatToCamelCase = (input) => {
11
+ const separatorRegex = /[./_\-\\ ]/g;
12
+ return input
13
+ .trim()
14
+ .split(separatorRegex)
15
+ .map((el, index) => (index === 0 ? el.toLowerCase() : formatWordToCamelCase(el)))
16
+ .join("");
17
+ };
18
+ export const formatWordToCamelCase = (input) => `${input[0].toUpperCase()}${input.substring(1).toLowerCase()}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiztivi/dana-cli",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Dana create app CLI",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",
@@ -14,18 +14,21 @@
14
14
  "lint": "eslint src tests",
15
15
  "test": "vitest",
16
16
  "test:coverage": "vitest run --coverage",
17
- "build": "rimraf ./dist tsconfig.tsbuildinfo && tsc --build && npm run add_files_to_dist",
17
+ "build": "npm run _clean:dist && tsc --build && npm run _add_files:dist",
18
+ "build:watch": "npm run _clean:dist && tsc --build --watch",
18
19
  "ci": "npm run build && npm run lint && npm run test:coverage",
19
- "add_files_to_dist": "cp dana_completion.sh ../../README.md dist/"
20
+ "_add_files:dist": "cp ../../README.md .",
21
+ "_clean:dist": "rimraf ./dist tsconfig.tsbuildinfo"
20
22
  },
21
23
  "files": [
22
24
  "dist",
23
- "dana_completion.sh"
25
+ "dana_completion.sh",
26
+ "README.md"
24
27
  ],
25
28
  "dependencies": {
26
29
  "@aws-sdk/client-codeartifact": "^3.859.0",
27
30
  "@clack/prompts": "0.10.0",
28
- "@wiztivi/dana-templates": "^0.0.7",
31
+ "@wiztivi/dana-templates": "^0.0.9",
29
32
  "child_process": "1.0.x",
30
33
  "command-exists": "1.2.9",
31
34
  "commander": "11.1.x",
@@ -1,153 +0,0 @@
1
- #!/usr/bin/env sh
2
-
3
- # Unified completion script for dana CLI
4
- # Detects shell and loads appropriate completion
5
-
6
- # Shared command list
7
- dana_commands=(
8
- 'add-device'
9
- 'add-menu'
10
- 'add-rail'
11
- 'add-scrollView'
12
- 'add-screen'
13
- 'auth'
14
- 'create-app'
15
- )
16
-
17
- # Shared subcommand declarations
18
- login_sub="login"
19
- logout_sub="logout"
20
- status_sub="status"
21
- # Shared option declarations
22
- direction_opts="-d --direction"
23
- navigation_opts="-n --navigation"
24
- cyclic_opts="-c --cyclic"
25
- margin_opts="-m --itemMargin"
26
- custom_opts="-cm --custom"
27
- item_view_opts="-i --itemView"
28
- org_opts="-o --org"
29
- device_opts="-d --device"
30
- tutorial_opts="-t, --tutorial"
31
-
32
- # Shared option values
33
- direction_values="h v"
34
- navigation_values="fixed lmr lr paginated"
35
- org_values="preprod"
36
- # Detect shell and load appropriate completion
37
- if [ -n "$BASH_VERSION" ]; then
38
- # Bash completion
39
- _dana()
40
- {
41
- local cur prev opts
42
- cur="${COMP_WORDS[COMP_CWORD]}"
43
- prev="${COMP_WORDS[COMP_CWORD-1]}"
44
- # Complete option values
45
- case "$prev" in
46
- -d|--direction)
47
- COMPREPLY=($(compgen -W "$direction_values" -- "$cur"))
48
- return 0
49
- ;;
50
- -n|--navigation)
51
- COMPREPLY=($(compgen -W "$navigation_values" -- "$cur"))
52
- return 0
53
- ;;
54
- -o|--org)
55
- COMPREPLY=($(compgen -W "$org_values" -- "$cur"))
56
- return 0
57
- ;;
58
- esac
59
- # If we're completing the first argument (command)
60
- if [[ $COMP_CWORD -eq 1 ]]; then
61
- COMPREPLY=($(compgen -W "${dana_commands[*]}" -- "$cur"))
62
- return 0
63
- fi
64
- # If we're completing options for specific commands
65
- local cmd="${COMP_WORDS[1]}"
66
- case "$cmd" in
67
- add-rail)
68
- opts="$navigation_opts $cyclic_opts $direction_opts $custom_opts $item_view_opts"
69
- COMPREPLY=($(compgen -W "$opts" -- "$cur"))
70
- ;;
71
- add-menu)
72
- opts="$direction_opts $margin_opts $custom_opts $item_view_opts"
73
- COMPREPLY=($(compgen -W "$opts" -- "$cur"))
74
- ;;
75
- add-scrollView)
76
- opts="$custom_opts $item_view_opts $margin_opts"
77
- COMPREPLY=($(compgen -W "$opts" -- "$cur"))
78
- ;;
79
- auth)
80
- if [[ $COMP_CWORD -eq 2 ]]; then
81
- opts="$login_sub $logout_sub $status_sub"
82
- else
83
- opts="$org_opts"
84
- fi
85
- COMPREPLY=($(compgen -W "$opts" -- "$cur"))
86
- ;;
87
- create-app)
88
- opts="$device_opts $tutorial_opts"
89
- COMPREPLY=($(compgen -W "$opts" -- "$cur"))
90
- ;;
91
- *)
92
- COMPREPLY=()
93
- ;;
94
- esac
95
- }
96
- complete -F _dana dana
97
- elif [ -n "$ZSH_VERSION" ]; then
98
- # Zsh completion
99
- _dana() {
100
- # Main commands
101
- if (( CURRENT == 2 )); then
102
- local -a commands
103
- commands=(${dana_commands[@]})
104
- compadd -a commands
105
- return
106
- fi
107
- # Complete option values
108
- local prev=${words[CURRENT-1]}
109
- case "$prev" in
110
- -d|--direction)
111
- compadd -- ${=direction_values}
112
- return
113
- ;;
114
- -n|--navigation)
115
- compadd -- ${=navigation_values}
116
- return
117
- ;;
118
- -o|--org)
119
- compadd -- ${=org_values}
120
- return
121
- ;;
122
- login|logout|status)
123
- compadd -- ${=org_opts}
124
- return
125
- ;;
126
- esac
127
- # Complete options for commands
128
- local cmd=${words[2]}
129
- case "$cmd" in
130
- add-rail)
131
- compadd -- ${=navigation_opts} ${=cyclic_opts} ${=direction_opts} ${=custom_opts} ${=item_view_opts}
132
- ;;
133
- add-menu)
134
- compadd -- ${=direction_opts} ${=margin_opts} ${=custom_opts} ${=item_view_opts}
135
- ;;
136
- add-scrollView)
137
- compadd -- ${=custom_opts} ${=item_view_opts} ${=margin_opts}
138
- ;;
139
- auth)
140
- if (( CURRENT == 3 )); then
141
- compadd -- ${=login_sub} ${=logout_sub} ${=status_sub}
142
- else
143
- compadd -- ${=org_opts}
144
- fi
145
- ;;
146
- create-app)
147
- compadd -- ${device_opts} $={tutorial_opts}
148
- esac
149
- }
150
-
151
- # Register the completion
152
- compdef _dana dana
153
- fi