@eik/cli 3.1.4 → 3.1.5

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.
@@ -1,63 +1,63 @@
1
1
  import { join } from "path";
2
- import ora from "ora";
3
2
  import chalk from "chalk";
4
3
  import PublishPackage from "../classes/publish/package/index.js";
5
- import { logger, getDefaults, typeSlug, typeTitle } from "../utils/index.js";
4
+ import { typeSlug, typeTitle } from "../utils/index.js";
6
5
  import { Artifact } from "../formatters/index.js";
6
+ import { EikCliError, errors } from "../utils/error.js";
7
+ import { commandHandler } from "../utils/command-handler.js";
7
8
 
8
9
  export const command = "publish";
9
10
 
10
11
  export const aliases = ["pkg", "package", "pub"];
11
12
 
12
- export const describe = `Publish an app package to an Eik server. Reads configuration from eik.json or package.json files. See https://eik.dev for more details.`;
13
+ export const describe = "Publish an app package to an Eik server";
13
14
 
15
+ /** @type {import('yargs').CommandBuilder} */
14
16
  export const builder = (yargs) => {
15
- const defaults = getDefaults(yargs.argv.config || yargs.argv.cwd);
16
-
17
- yargs.options({
18
- dryRun: {
19
- alias: "d",
20
- describe:
21
- "Terminates the publish early (before upload) and provides information about created bundles for inspection.",
22
- default: false,
23
- type: "boolean",
24
- },
25
- token: {
26
- describe: `Provide a jwt token to be used to authenticate with the Eik server. Automatically determined if authenticated (via eik login)`,
27
- type: "string",
28
- alias: "t",
29
- },
30
- });
31
-
32
- // @ts-expect-error
33
- yargs.default("token", defaults.token, defaults.token ? "######" : "");
34
-
35
- yargs.example(`eik publish`);
36
- yargs.example(`eik package`);
37
- yargs.example(`eik pub --dry-run`);
38
- yargs.example(`eik pkg --token ######`);
39
- yargs.example(`eik pkg --debug`);
17
+ return yargs
18
+ .options({
19
+ dryRun: {
20
+ alias: "d",
21
+ describe: "Log details about the operation and skip upload",
22
+ type: "boolean",
23
+ },
24
+ token: {
25
+ describe: "JWT used for authentication, if not using eik login",
26
+ type: "string",
27
+ alias: "t",
28
+ },
29
+ })
30
+ .example("eik publish")
31
+ .example("eik publish --dry-run")
32
+ .example("eik publish --token yourtoken");
40
33
  };
41
34
 
42
- export const handler = async (argv) => {
43
- const spinner = ora({ stream: process.stdout }).start("working...");
44
- const { debug, dryRun, cwd, token, config } = argv;
45
- // @ts-expect-error
46
- const { name, version, server, map, out, files, type } = getDefaults(
47
- config || cwd,
48
- );
49
-
50
- if (type === "map") {
51
- spinner.warn(
52
- '"type" is set to "map", which is not supported by the publish command. Please use the "eik map" command instead',
53
- );
54
- process.stdout.write("\n");
55
- process.exit(0);
56
- }
57
-
58
- try {
35
+ export const handler = commandHandler(
36
+ { command, options: ["server"] },
37
+ async (argv, log, spinner) => {
38
+ const {
39
+ debug,
40
+ dryRun,
41
+ cwd,
42
+ token,
43
+ name,
44
+ version,
45
+ server,
46
+ map,
47
+ out,
48
+ files,
49
+ type,
50
+ } = argv;
51
+
52
+ if (type === "map") {
53
+ throw new EikCliError(
54
+ errors.ERR_WRONG_TYPE,
55
+ '"type" is set to "map", which is not supported by the publish command. Please use the "eik map" command instead',
56
+ );
57
+ }
58
+
59
59
  const options = {
60
- logger: logger(spinner, debug),
60
+ logger: log,
61
61
  cwd,
62
62
  token,
63
63
  dryRun,
@@ -74,11 +74,10 @@ export const handler = async (argv) => {
74
74
  const publish = await new PublishPackage(options).run();
75
75
 
76
76
  if (!publish) {
77
- spinner.warn(
77
+ throw new EikCliError(
78
+ errors.ERR_VERSION_EXISTS,
78
79
  "Version in eik.json has not changed since last publish, publishing is not necessary",
79
80
  );
80
- process.stdout.write("\n");
81
- process.exit(0);
82
81
  }
83
82
 
84
83
  const { files: fls } = publish;
@@ -121,10 +120,5 @@ export const handler = async (argv) => {
121
120
  ` ${chalk.bold("No files were published to remote server")}\n\n`,
122
121
  );
123
122
  }
124
- } catch (err) {
125
- spinner.warn(err.message);
126
- spinner.text = "";
127
- spinner.stopAndPersist();
128
- process.exit(1);
129
- }
130
- };
123
+ },
124
+ );
@@ -1,44 +1,40 @@
1
1
  import { execSync } from "child_process";
2
2
  import { join } from "path";
3
- import ora from "ora";
4
3
  import VersionPackage from "../classes/version.js";
5
- import { logger, getDefaults } from "../utils/index.js";
6
4
  import json from "../utils/json/index.js";
5
+ import { EikCliError, errors } from "../utils/error.js";
6
+ import { commandHandler } from "../utils/command-handler.js";
7
7
 
8
8
  export const command = "version [level]";
9
9
 
10
- export const describe = `Compares local files with files on server and increments "version" field in eik.json if necessary.`;
10
+ export const describe =
11
+ 'Compare local files with files on server and increment "version" field if different';
11
12
 
13
+ /** @type {import('yargs').CommandBuilder} */
12
14
  export const builder = (yargs) => {
13
- yargs.positional("level", {
14
- describe: "Semver level to increment version by",
15
- default: "patch",
16
- type: "string",
17
- choices: ["major", "minor", "patch"],
18
- });
19
-
20
- yargs.options({
21
- dryRun: {
22
- alias: "d",
23
- describe:
24
- "Terminates the publish early (before upload) and provides information about created bundles for inspection.",
25
- default: false,
26
- type: "boolean",
27
- },
28
- });
29
-
30
- yargs.example(`eik version`);
31
- yargs.example(`eik version minor`);
15
+ return yargs
16
+ .positional("level", {
17
+ describe: "Semver level to increment version by",
18
+ default: "patch",
19
+ type: "string",
20
+ choices: ["major", "minor", "patch"],
21
+ })
22
+ .options({
23
+ dryRun: {
24
+ alias: "d",
25
+ describe: "Log details about the operation and skip upload",
26
+ type: "boolean",
27
+ },
28
+ })
29
+ .example("eik version")
30
+ .example("eik version minor")
31
+ .example("eik version --dry-run");
32
32
  };
33
33
 
34
- export const handler = async (argv) => {
35
- const spinner = ora({ stream: process.stdout }).start("working...");
36
- const { level, debug, dryRun, cwd, config } = argv;
37
- // @ts-expect-error
38
- const { name, version, server, map, out, files } = getDefaults(config || cwd);
39
-
40
- try {
41
- const log = logger(spinner, debug);
34
+ export const handler = commandHandler(
35
+ { command, options: ["server"] },
36
+ async (argv, log) => {
37
+ const { level, dryRun, cwd, name, version, server, map, out, files } = argv;
42
38
 
43
39
  const options = {
44
40
  logger: log,
@@ -68,8 +64,10 @@ export const handler = async (argv) => {
68
64
  execSync(`git add ${join(cwd, "eik.json")}`);
69
65
  log.debug(` ==> stage: ${join(cwd, "eik.json")}`);
70
66
  } catch (err) {
71
- throw new Error(
67
+ throw new EikCliError(
68
+ errors.ERR_NOT_GIT,
72
69
  'Failed to stage file "eik.json". Is this directory (or any parent directories) a git repository?',
70
+ err,
73
71
  );
74
72
  }
75
73
 
@@ -90,16 +88,12 @@ export const handler = async (argv) => {
90
88
 
91
89
  log.info(`New version ${newVersion} written back to eik.json`);
92
90
  } catch (err) {
93
- throw new Error('Failed to commit changes to file "eik.json".');
91
+ throw new EikCliError(
92
+ errors.ERR_GIT_COMMIT,
93
+ 'Failed to commit changes to file "eik.json".',
94
+ err,
95
+ );
94
96
  }
95
97
  }
96
-
97
- spinner.text = "";
98
- spinner.stopAndPersist();
99
- process.exit();
100
- } catch (err) {
101
- spinner.warn(err.message);
102
- spinner.text = "";
103
- spinner.stopAndPersist();
104
- }
105
- };
98
+ },
99
+ );
package/index.js CHANGED
@@ -1,84 +1,88 @@
1
1
  #!/usr/bin/env node
2
- import chalk from "chalk";
3
2
  import yargs from "yargs";
4
3
  import { hideBin } from "yargs/helpers";
5
- import boxen from "boxen";
6
4
  import { join } from "path";
7
5
  import { readFileSync } from "fs";
8
6
  import { fileURLToPath } from "url";
9
7
  import { dirname } from "path";
10
- import { commands } from "./commands/index.js";
8
+
9
+ import * as alias from "./commands/alias.js";
10
+ import * as init from "./commands/init.js";
11
+ import * as integrity from "./commands/integrity.js";
12
+ import * as login from "./commands/login.js";
13
+ import * as mapAlias from "./commands/map-alias.js";
14
+ import * as map from "./commands/map.js";
15
+ import * as meta from "./commands/meta.js";
16
+ import * as npmAlias from "./commands/npm-alias.js";
17
+ import * as packageAlias from "./commands/package-alias.js";
18
+ import * as ping from "./commands/ping.js";
19
+ import * as publish from "./commands/publish.js";
20
+ import * as version from "./commands/version.js";
11
21
 
12
22
  const __filename = fileURLToPath(import.meta.url);
13
23
  const __dirname = dirname(__filename);
14
24
 
15
- const { version } = JSON.parse(
25
+ const { version: cliVersion } = JSON.parse(
16
26
  readFileSync(join(__dirname, "./package.json"), { encoding: "utf-8" }),
17
27
  );
18
28
 
19
- // short circuit and provide a -v and --version flag
29
+ // Short circuit and provide a -v and --version flag.
30
+ // It's a known limitation in yargs that you can't have both a command
31
+ // and an option named version https://github.com/yargs/yargs/issues/2064
32
+ // We use the version name as a command in yargs, so handle the version
33
+ // option before using yargs.
20
34
  if (
21
35
  process.argv.includes("-v") ||
22
36
  // last position only to avoid conflict with publish command
23
37
  process.argv[process.argv.length - 1].includes("--version")
24
38
  ) {
25
- console.log(version);
39
+ console.log(cliVersion);
26
40
  process.exit(0);
27
41
  }
28
42
 
29
- const greeting = chalk.white.bold(`Eik CLI (v${version})`);
30
-
31
- const boxenOptions = {
32
- padding: 1,
33
- margin: 1,
34
- borderStyle: "round",
35
- borderColor: "green",
36
- backgroundColor: "#555555",
37
- };
38
- // @ts-expect-error
39
- const msgBox = boxen(greeting, boxenOptions);
40
-
41
- console.log(msgBox);
42
-
43
43
  yargs(hideBin(process.argv))
44
+ .scriptName("eik")
45
+ // inspired by git
46
+ .usage(
47
+ `usage: $0 [-v | --version] [-h | --help] [-c <path> | --config <path>]
48
+ [--cwd <path>] [--debug] <command> [<args>]`,
49
+ )
50
+ .epilogue(
51
+ `Run $0 <command> --help to read more about a specific subcommand.
52
+
53
+ For a more detailed description of commands and options, see the reference documentation:
54
+ https://eik.dev/cli`,
55
+ )
44
56
  .options({
45
57
  config: {
46
58
  alias: "c",
47
- describe:
48
- "Provide an exact path to an eik.json or package.json file to use as config. Default is eik.json in the current working directory.",
59
+ describe: "Path to Eik configuration file (eik.json or package.json)",
49
60
  },
50
61
  cwd: {
51
- describe: "Alter the current working directory.",
52
- default: process.cwd(),
62
+ describe: "Path to a different working directory than the current",
53
63
  },
54
64
  debug: {
55
- describe: "Logs additional messages",
56
- default: false,
65
+ describe: "Show additional logs",
57
66
  type: "boolean",
58
67
  },
59
68
  })
60
- // @ts-expect-error
61
- .example("eik init")
62
- // @ts-expect-error
63
- .example("eik login --server https://assets.myserver.com --key ######")
64
- // @ts-expect-error
65
- .example("eik publish")
66
- // @ts-expect-error
67
- .example("eik meta my-app --server https://assets.myserver.com")
68
- .example(
69
- // @ts-expect-error
70
- "eik npm-alias lit-html 1.0.0 1 --server https://assets.myserver.com --token ######",
71
- )
72
- .example(
73
- // @ts-expect-error
74
- "eik map my-map 1.0.0 ./import-map.json --server https://assets.myserver.com --token ######",
75
- )
76
- // @ts-expect-error
77
- .example("eik map-alias my-map 1.0.0 1")
78
- // @ts-expect-error
79
- .command(commands)
69
+ .command([
70
+ alias,
71
+ init,
72
+ integrity,
73
+ login,
74
+ map,
75
+ mapAlias,
76
+ meta,
77
+ npmAlias,
78
+ packageAlias,
79
+ ping,
80
+ publish,
81
+ version,
82
+ ])
80
83
  .demandCommand()
81
- .wrap(150)
82
- .version(false)
84
+ .wrap(null)
85
+ .version(false) // Turn off the built-in version option to not conflict with the version command
83
86
  .help()
87
+ .alias("h", "help")
84
88
  .parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eik/cli",
3
- "version": "3.1.4",
3
+ "version": "3.1.5",
4
4
  "description": "CLI tool for publishing assets to an Eik server",
5
5
  "main": "./classes/index.js",
6
6
  "types": "./types/classes/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "clean": "rimraf .tap node_modules types",
24
- "test": "cross-env HTTP_PORT=0 LOG_LEVEL=fatal tap --timeout 0 --disable-coverage test/**/*.test.mjs",
24
+ "test": "cross-env HTTP_PORT=0 LOG_LEVEL=fatal tap --timeout 0 --disable-coverage",
25
25
  "test:integration": "cross-env HTTP_PORT=0 LOG_LEVEL=fatal tap --timeout 0 --disable-coverage test/integration/**/*.test.mjs",
26
26
  "test:unit": "cross-env HTTP_PORT=0 LOG_LEVEL=fatal tap --timeout 0 --disable-coverage test/*.test.mjs",
27
27
  "test:tasks": "cross-env HTTP_PORT=0 LOG_LEVEL=fatal tap --timeout 0 --disable-coverage test/tasks/*.test.mjs",
@@ -40,7 +40,6 @@
40
40
  "dependencies": {
41
41
  "@eik/common": "4.1.1",
42
42
  "abslog": "2.4.4",
43
- "boxen": "8.0.1",
44
43
  "bytes": "3.1.2",
45
44
  "chalk": "5.3.0",
46
45
  "date-fns": "3.6.0",
@@ -62,6 +61,7 @@
62
61
  "@eik/service": "2.3.1",
63
62
  "@eik/sink-memory": "1.1.2",
64
63
  "@eik/typescript-config": "1.0.0",
64
+ "@types/yargs": "17.0.33",
65
65
  "cross-env": "7.0.3",
66
66
  "eslint": "9.8.0",
67
67
  "fastify": "4.28.1",
package/readme.md CHANGED
@@ -18,4 +18,4 @@ eik --version
18
18
 
19
19
  ## Usage
20
20
 
21
- See [the CLI reference documentation](https://eik.dev/docs/reference/at-eik-cli) to learn about the available commands.
21
+ See [the CLI reference documentation](https://eik.dev/cli) to learn about the available commands.
@@ -2,7 +2,7 @@
2
2
  * @typedef {object} AliasOptions
3
3
  * @property {import('abslog').AbstractLoggerOptions} [logger]
4
4
  * @property {string} server
5
- * @property {"package" | "npm" | "map"} [type="package"]
5
+ * @property {"package" | "npm" | "image" | "map"} [type="package"]
6
6
  * @property {string} name
7
7
  * @property {string} version
8
8
  * @property {string} alias
@@ -40,7 +40,7 @@ export default class Alias {
40
40
  export type AliasOptions = {
41
41
  logger?: import("abslog").AbstractLoggerOptions;
42
42
  server: string;
43
- type?: "package" | "npm" | "map";
43
+ type?: "package" | "npm" | "image" | "map";
44
44
  name: string;
45
45
  version: string;
46
46
  alias: string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Get defaults for things like server, name and version from Eik config.
3
+ * If a specific argument is given for it, that takes precedence.
4
+ * @template [T=Record<string, unknown>]
5
+ * @param {any} argv
6
+ * @param {{ command: string; options?: string[] }} opts
7
+ * @returns {import('@eik/common').EikConfig & typeof defaults & T}
8
+ */
9
+ export function getArgsOrDefaults<T = Record<string, unknown>>(argv: any, opts: {
10
+ command: string;
11
+ options?: string[];
12
+ }): import("@eik/common").EikConfig & typeof defaults & T;
13
+ declare const defaults: {
14
+ name: string;
15
+ type: string;
16
+ version: string;
17
+ server: string;
18
+ out: string;
19
+ files: string;
20
+ "import-map": any[];
21
+ };
22
+ export {};
@@ -0,0 +1,19 @@
1
+ export namespace errors {
2
+ let ERR_MISSING_CONFIG: string;
3
+ let ERR_WRONG_TYPE: string;
4
+ let ERR_VERSION_EXISTS: string;
5
+ let ERR_NOT_GIT: string;
6
+ let ERR_GIT_COMMIT: string;
7
+ }
8
+ export class EikCliError extends Error {
9
+ /**
10
+ * @param {string} errorCode
11
+ * @param {string} message
12
+ * @param {Error} [cause]
13
+ */
14
+ constructor(errorCode: string, message: string, cause?: Error);
15
+ cause: Error;
16
+ get errorCode(): string;
17
+ get exitCode(): number;
18
+ #private;
19
+ }
@@ -1,6 +1,6 @@
1
1
  import logger from "./logger.js";
2
- export const getDefaults: typeof helpers.getDefaults;
2
+ import { getArgsOrDefaults } from "./defaults.js";
3
3
  export const typeSlug: typeof helpers.typeSlug;
4
4
  import typeTitle from "./type-title.js";
5
5
  import { helpers } from "@eik/common";
6
- export { logger, typeTitle };
6
+ export { logger, getArgsOrDefaults, typeTitle };
@@ -0,0 +1,72 @@
1
+ import ora from "ora";
2
+ import logger from "./logger.js";
3
+ import { EikCliError } from "./error.js";
4
+ import { getArgsOrDefaults } from "./defaults.js";
5
+
6
+ /**
7
+ * @typedef {ReturnType<import('./defaults.js').getArgsOrDefaults>} Argv
8
+ * @typedef {ReturnType<import('./logger.js').default>} Logger
9
+ * @typedef {import('ora').Ora} Spinner
10
+ */
11
+
12
+ /**
13
+ * @template [T=Record<string, unknown>]
14
+ * @callback HandlerFunction
15
+ * @param {Argv & T} argv
16
+ * @param {Logger} log
17
+ * @param {Spinner} spinner Can we remove this?
18
+ * @returns {Promise<void>}
19
+ */
20
+
21
+ /**
22
+ * @param {{ command: string; options?: string[] }} opts
23
+ * @param {HandlerFunction} handlerFunction
24
+ * @returns {import('yargs').CommandModule["handler"]}
25
+ */
26
+ export function commandHandler(opts, handlerFunction) {
27
+ return async (argv) => {
28
+ const spinner = ora({ stream: process.stdout }).start();
29
+ const log = logger(spinner, argv.debug);
30
+
31
+ try {
32
+ const args = getArgsOrDefaults(argv, opts);
33
+
34
+ if (argv.debug) {
35
+ log.debug(`command inputs:
36
+ ${JSON.stringify(args, null, 2)}`);
37
+ }
38
+
39
+ await handlerFunction(args, log, spinner);
40
+
41
+ spinner.text = "";
42
+ spinner.stopAndPersist();
43
+ } catch (e) {
44
+ if (e instanceof EikCliError) {
45
+ log.error(e.message);
46
+
47
+ if (argv.debug) {
48
+ log.debug(e.stack);
49
+ if (e.cause) {
50
+ log.debug(`Caused by ${e.cause.message}`);
51
+ log.debug(e.cause.stack);
52
+ }
53
+ }
54
+
55
+ spinner.text = "";
56
+ spinner.stopAndPersist();
57
+ return process.exit(e.exitCode);
58
+ }
59
+
60
+ const error = /** @type {Error} */ (e);
61
+ log.error(`${error.name}: ${error.message}`);
62
+
63
+ if (argv.debug) {
64
+ log.debug(error.stack);
65
+ }
66
+
67
+ spinner.text = "";
68
+ spinner.stopAndPersist();
69
+ process.exit(1);
70
+ }
71
+ };
72
+ }
@@ -0,0 +1,79 @@
1
+ import fs from "fs";
2
+ import { join, isAbsolute } from "path";
3
+ import { helpers } from "@eik/common";
4
+ import { EikCliError, errors } from "./error.js";
5
+
6
+ const defaults = {
7
+ name: "",
8
+ type: "package",
9
+ version: "1.0.0",
10
+ server: "",
11
+ out: "./.eik",
12
+ files: "./public",
13
+ "import-map": [],
14
+ };
15
+
16
+ /**
17
+ * Get defaults for things like server, name and version from Eik config.
18
+ * If a specific argument is given for it, that takes precedence.
19
+ * @template [T=Record<string, unknown>]
20
+ * @param {any} argv
21
+ * @param {{ command: string; options?: string[] }} opts
22
+ * @returns {import('@eik/common').EikConfig & typeof defaults & T}
23
+ */
24
+ export function getArgsOrDefaults(argv, opts) {
25
+ let { cwd, config: configPath } = argv;
26
+ if (!cwd) {
27
+ cwd = process.cwd();
28
+ }
29
+
30
+ let config = {};
31
+ if (!opts.command.startsWith("init")) {
32
+ let path = cwd;
33
+ if (configPath) {
34
+ path = isAbsolute(configPath) ? configPath : join(cwd, configPath);
35
+ }
36
+ try {
37
+ const stats = fs.statSync(path);
38
+ if (stats.isDirectory()) {
39
+ config = helpers.configStore.findInDirectory(path).toJSON();
40
+ } else {
41
+ config = helpers.configStore.loadFromPath(path).toJSON();
42
+ }
43
+ } catch (error) {
44
+ const e = /** @type {Error} */ (error);
45
+ if (e.constructor.name === "MissingConfigError") {
46
+ if (!hasOptionsOnArgv(argv, opts.options)) {
47
+ throw new EikCliError(
48
+ errors.ERR_MISSING_CONFIG,
49
+ `No eik.json or package.json with eik configuration in ${cwd}, and did not get required fields as options`,
50
+ error,
51
+ );
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ const result = {
58
+ ...defaults,
59
+ ...config,
60
+ ...argv,
61
+ cwd,
62
+ };
63
+
64
+ return result;
65
+ }
66
+
67
+ /**
68
+ * @param {any} argv
69
+ * @param {string[]} options
70
+ * @returns {boolean}
71
+ */
72
+ function hasOptionsOnArgv(argv, options = []) {
73
+ for (const arg of options) {
74
+ if (typeof argv[arg] === "undefined") {
75
+ return false;
76
+ }
77
+ }
78
+ return true;
79
+ }