@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.
- package/{dist/README.md → README.md} +1 -0
- package/dist/commands/addDevice/deviceConfig/androidtvConfig.d.ts +0 -4
- package/dist/commands/addDevice/deviceConfig/androidtvConfig.js +2 -8
- package/dist/commands/addDevice/deviceConfig/configTypes.d.ts +12 -1
- package/dist/commands/addDevice/deviceConfig/tizenConfig.js +34 -3
- package/dist/commands/addDevice/helper/addDeviceHelper.d.ts +12 -0
- package/dist/commands/addDevice/helper/addDeviceHelper.js +19 -0
- package/dist/commands/authentication/AuthenticationManager.d.ts +22 -0
- package/dist/commands/authentication/AuthenticationManager.js +92 -0
- package/dist/commands/authentication/authDefinition.js +1 -2
- package/dist/commands/authentication/authHook.d.ts +1 -1
- package/dist/commands/authentication/authHook.js +17 -4
- package/dist/commands/authentication/commands/login.js +12 -83
- package/dist/commands/authentication/helper/CredentialsHelper.d.ts +14 -5
- package/dist/commands/authentication/helper/CredentialsHelper.js +25 -5
- package/dist/commands/createApp/helpers/CreateAppHelper.d.ts +0 -1
- package/dist/common/const/deviceConst.js +1 -1
- package/dist/common/helpers/InstallHelper.d.ts +1 -0
- package/dist/common/helpers/InstallHelper.js +15 -3
- package/dist/common/helpers/UpdateFileHelper.js +11 -3
- package/dist/common/helpers/UserInputGetter.js +4 -1
- package/dist/common/helpers/stringHelper.d.ts +5 -0
- package/dist/common/helpers/stringHelper.js +12 -0
- package/package.json +8 -5
- 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
|
|
|
@@ -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: () =>
|
|
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
|
-
|
|
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"]
|
|
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
|
|
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
|
|
16
|
-
|
|
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 {
|
|
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
|
|
17
|
-
const
|
|
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
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
|
24
|
-
dana
|
|
31
|
+
static get(org?: string): {
|
|
32
|
+
aws?: AwsCredentials;
|
|
33
|
+
dana?: DanaToken;
|
|
25
34
|
org: string;
|
|
26
|
-
}
|
|
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
|
|
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)
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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 = `${
|
|
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 = [
|
|
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.
|
|
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": "
|
|
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
|
-
"
|
|
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.
|
|
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",
|
package/dist/dana_completion.sh
DELETED
|
@@ -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
|