@zokugun/artifact 0.6.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,6 +68,26 @@ Artifacts published to npm must be prefixed with `artifact-`.
68
68
  Command Reference
69
69
  -----------------
70
70
 
71
+ ### Usage
72
+
73
+ ```
74
+ Usage: artifact [options] [command]
75
+
76
+ Boilerplate your project & keep your configurations up to date
77
+
78
+ Options:
79
+ -v, --version output the version number
80
+ -h, --help display help for command
81
+
82
+ Commands:
83
+ add [options] <artifacts...> add an artifact to the current project
84
+ list|ls list the installed artifacts in the project
85
+ outdated|od check for outdated artifacts
86
+ remove|rm [options] [artifacts...] remove an artifact from the current project
87
+ update|up [options] update the current project using the installed artifacts
88
+ help [command] display help for command
89
+ ```
90
+
71
91
  ### `artifact add`
72
92
 
73
93
  Registers and applies one or more artifacts in the current project.
@@ -78,12 +98,12 @@ Registers and applies one or more artifacts in the current project.
78
98
 
79
99
  `<artifact>` can be `name` or `name:variant`.
80
100
 
81
- ### `artifact list` / `artifact l`
101
+ ### `artifact list` / `artifact ls`
82
102
 
83
103
  Shows the artifacts currently tracked in `.artifactrc*`, including versions and, when available, requested variants.
84
104
 
85
105
  - Syntax: `artifact list`
86
- - Alias: `artifact l`
106
+ - Alias: `artifact ls`
87
107
  - Output: prints each artifact as `name@version:variant`.
88
108
 
89
109
  ### `artifact update` / `artifact up`
package/lib/cli.js CHANGED
@@ -8,33 +8,48 @@ const package_json_1 = __importDefault(require("../package.json"));
8
8
  const index_js_1 = require("./commands/index.js");
9
9
  const program = new commander_1.Command();
10
10
  program
11
- .version(package_json_1.default.version, '-V, --version')
11
+ .version(package_json_1.default.version, '-v, --version')
12
12
  .description(package_json_1.default.description);
13
13
  program
14
14
  .command('add')
15
15
  .description('add an artifact to the current project')
16
16
  .option('-d, --dry-run', 'fake install')
17
- .option('-v, --verbose', 'output more details')
17
+ .option('--verbose', 'output more details')
18
+ .option('--var <name=value>', 'pass a variable (format: name=value), repeatable', (pair, previous = []) => {
19
+ const index = pair.indexOf('=');
20
+ if (index === -1) {
21
+ throw new Error('--var expects format name=value');
22
+ }
23
+ const name = pair.slice(0, index);
24
+ const value = pair.slice(index + 1);
25
+ previous.push({ name, value });
26
+ return previous;
27
+ }, [])
18
28
  .argument('<artifacts...>')
19
29
  .action(index_js_1.add);
20
30
  program
21
- .command('update')
22
- .description('update the current project using the installed artifacts')
23
- .option('-d, --dry-run', 'fake update')
24
- .option('-v, --verbose', 'output more details')
25
- .alias('up')
26
- .action(index_js_1.update);
31
+ .command('list')
32
+ .description('list the installed artifacts in the project')
33
+ .alias('ls')
34
+ .action(index_js_1.list);
35
+ program
36
+ .command('outdated')
37
+ .description('check for outdated artifacts')
38
+ .alias('od')
39
+ .action(index_js_1.outdated);
27
40
  program
28
41
  .command('remove')
29
42
  .description('remove an artifact from the current project')
30
43
  .option('-d, --dry-run', 'fake uninstall')
31
- .option('-v, --verbose', 'output more details')
32
- .argument('<artifacts...>')
44
+ .option('--verbose', 'output more details')
45
+ .argument('[artifacts...]')
33
46
  .alias('rm')
34
47
  .action(index_js_1.remove);
35
48
  program
36
- .command('list')
37
- .description('list the installed artifacts in the project')
38
- .alias('l')
39
- .action(index_js_1.list);
49
+ .command('update')
50
+ .description('update the current project using the installed artifacts')
51
+ .option('-d, --dry-run', 'fake update')
52
+ .option('--verbose', 'output more details')
53
+ .alias('up')
54
+ .action(index_js_1.update);
40
55
  program.parse();
@@ -1,6 +1,12 @@
1
- export declare function add(specs: string[], inputOptions?: {
1
+ type CLIOptions = {
2
+ dryRun?: boolean;
2
3
  force?: boolean;
3
4
  skip?: boolean;
5
+ var?: Array<{
6
+ name: string;
7
+ value: string;
8
+ }>;
4
9
  verbose?: boolean;
5
- dryRun?: boolean;
6
- }): Promise<void>;
10
+ };
11
+ export declare function add(specs: string[], inputOptions?: CLIOptions): Promise<void>;
12
+ export {};
@@ -4,11 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.add = add;
7
- const process_1 = __importDefault(require("process"));
7
+ const node_process_1 = __importDefault(require("node:process"));
8
8
  const cli_utils_1 = require("@zokugun/cli-utils");
9
+ const async_1 = require("@zokugun/xtry/async");
9
10
  const pacote_1 = __importDefault(require("pacote"));
10
11
  const tempy_1 = __importDefault(require("tempy"));
11
12
  const index_js_1 = require("../configs/index.js");
13
+ const read_listing_config_js_1 = require("../configs/package/read-listing-config.js");
12
14
  const index_js_2 = require("../steps/index.js");
13
15
  const resolve_request_js_1 = require("../utils/resolve-request.js");
14
16
  const { mainFlow } = (0, index_js_2.composeSteps)([
@@ -35,18 +37,65 @@ const { mainFlow } = (0, index_js_2.composeSteps)([
35
37
  ]);
36
38
  async function add(specs, inputOptions) {
37
39
  cli_utils_1.logger.beginTimer();
38
- const targetPath = process_1.default.cwd();
40
+ const targetPath = node_process_1.default.cwd();
39
41
  const options = {
42
+ dryRun: inputOptions?.dryRun ?? false,
40
43
  force: inputOptions?.force ?? false,
41
44
  skip: inputOptions?.skip ?? false,
45
+ variables: {},
42
46
  verbose: inputOptions?.verbose ?? false,
43
- dryRun: inputOptions?.dryRun ?? false,
44
47
  };
48
+ if (inputOptions?.var) {
49
+ for (const { name, value } of inputOptions.var) {
50
+ options.variables[name] = value;
51
+ }
52
+ }
45
53
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
46
54
  if (configResult.fails) {
47
55
  cli_utils_1.logger.fatal(configResult.error);
48
56
  }
49
57
  const { config, configStats } = configResult.value;
58
+ if (specs.length === 1 && /^@\w+$/.test(specs[0])) {
59
+ const request = `${specs.shift()}/artifact-listing`;
60
+ const spinner = cli_utils_1.logger.createSpinner(`${cli_utils_1.c.cyan.bold(request)}`);
61
+ const dir = tempy_1.default.directory();
62
+ const pkgResult = await (0, async_1.xtry)(pacote_1.default.extract(request, dir));
63
+ if (pkgResult.fails) {
64
+ cli_utils_1.logger.fatal(`The artifact '${request}' couldn't be found.`);
65
+ }
66
+ spinner.succeed();
67
+ cli_utils_1.logger.stopProgress();
68
+ const listing = await (0, read_listing_config_js_1.readListingConfig)(dir);
69
+ if (listing.fails) {
70
+ cli_utils_1.logger.fatal(listing.error);
71
+ }
72
+ const { value } = await (0, async_1.xtry)(cli_utils_1.enquirer.prompt({
73
+ type: 'multiselect',
74
+ name: 'specs',
75
+ message: 'Pick the artifacts to add',
76
+ // @ts-expect-error TS2353
77
+ limit: 7,
78
+ choices: listing.value.map(({ name, description }) => ({ name, message: `${name}${cli_utils_1.c.grey(`: ${description}`)}` })),
79
+ }));
80
+ const marked = value?.specs;
81
+ if (!marked || marked.length === 0) {
82
+ cli_utils_1.logger.warn('No artifacts marked for addition');
83
+ }
84
+ else {
85
+ const { value } = await (0, async_1.xtry)(cli_utils_1.enquirer.prompt([
86
+ (0, cli_utils_1.confirm)({
87
+ name: 'addition',
88
+ message: `Adds the following artifacts: ${marked.map((name) => cli_utils_1.c.green(name)).join(',')}`,
89
+ }),
90
+ ]));
91
+ if (value?.addition) {
92
+ specs.push(...marked);
93
+ }
94
+ else {
95
+ cli_utils_1.logger.warn('Artifacts addition has been rejected');
96
+ }
97
+ }
98
+ }
50
99
  for (const spec of specs) {
51
100
  const requestResult = (0, resolve_request_js_1.resolveRequest)(spec);
52
101
  if (requestResult.fails) {
@@ -1,4 +1,5 @@
1
1
  export { add } from './add.js';
2
2
  export { list } from './list.js';
3
+ export { outdated } from './outdated.js';
3
4
  export { remove } from './remove.js';
4
5
  export { update } from './update.js';
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.update = exports.remove = exports.list = exports.add = void 0;
3
+ exports.update = exports.remove = exports.outdated = exports.list = exports.add = void 0;
4
4
  var add_js_1 = require("./add.js");
5
5
  Object.defineProperty(exports, "add", { enumerable: true, get: function () { return add_js_1.add; } });
6
6
  var list_js_1 = require("./list.js");
7
7
  Object.defineProperty(exports, "list", { enumerable: true, get: function () { return list_js_1.list; } });
8
+ var outdated_js_1 = require("./outdated.js");
9
+ Object.defineProperty(exports, "outdated", { enumerable: true, get: function () { return outdated_js_1.outdated; } });
8
10
  var remove_js_1 = require("./remove.js");
9
11
  Object.defineProperty(exports, "remove", { enumerable: true, get: function () { return remove_js_1.remove; } });
10
12
  var update_js_1 = require("./update.js");
@@ -4,12 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.list = list;
7
+ const node_path_1 = __importDefault(require("node:path"));
7
8
  const process_1 = __importDefault(require("process"));
8
9
  const cli_utils_1 = require("@zokugun/cli-utils");
9
- const lodash_es_1 = require("lodash-es");
10
+ const async_1 = __importDefault(require("@zokugun/fs-extra-plus/async"));
11
+ const xtry_1 = require("@zokugun/xtry");
10
12
  const index_js_1 = require("../configs/index.js");
13
+ const format_table_js_1 = require("../utils/format-table.js");
11
14
  function formatVariant(artifact) {
12
- const variant = Array.isArray(artifact.requires) ? (0, lodash_es_1.last)(artifact.requires) ?? '' : '';
15
+ const variant = Array.isArray(artifact.requires) ? artifact.requires.at(-1) ?? '' : '';
13
16
  if (variant.length > 0) {
14
17
  return `:${variant}`;
15
18
  }
@@ -18,22 +21,37 @@ function formatVariant(artifact) {
18
21
  }
19
22
  }
20
23
  async function list() {
21
- const targetPath = process_1.default.env.INIT_CWD;
24
+ const targetPath = process_1.default.cwd();
22
25
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
23
26
  if (configResult.fails) {
24
27
  cli_utils_1.logger.fatal(configResult.error);
25
28
  }
29
+ const packageResult = await async_1.default.readJSON(node_path_1.default.resolve(targetPath, './package.json'));
30
+ if (packageResult.fails) {
31
+ cli_utils_1.logger.fatal((0, xtry_1.stringifyError)(packageResult.error));
32
+ }
33
+ const { name } = packageResult.value;
26
34
  const { config, configStats } = configResult.value;
27
35
  const artifacts = Object.entries(config.artifacts);
36
+ cli_utils_1.logger.newLine();
28
37
  if (artifacts.length === 0) {
29
38
  cli_utils_1.logger.info('No artifacts have been installed.');
30
39
  }
31
40
  else {
32
- cli_utils_1.logger.info(`List of installed artifacts (${configStats.name}):\n`);
41
+ cli_utils_1.logger.print(`${name} ${cli_utils_1.c.grey(configStats.name)}`);
42
+ cli_utils_1.logger.newLine();
43
+ const table = [];
33
44
  for (const [name, artifact] of artifacts) {
34
- const version = artifact.version ? `@${artifact.version}` : '';
35
- cli_utils_1.logger.info(`- ${name}${version}${formatVariant(artifact)}`);
45
+ const line = [
46
+ ' ',
47
+ name,
48
+ ' ',
49
+ artifact.version ? `${cli_utils_1.c.green(artifact.version)}${cli_utils_1.c.gray(formatVariant(artifact))}` : '',
50
+ ];
51
+ table.push(line);
36
52
  }
53
+ const lines = (0, format_table_js_1.formatTable)(table, 'LLLL');
54
+ cli_utils_1.logger.print(lines.join('\n'));
37
55
  }
38
56
  cli_utils_1.logger.newLine();
39
57
  }
@@ -0,0 +1 @@
1
+ export declare function outdated(): Promise<void>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.outdated = outdated;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const process_1 = __importDefault(require("process"));
9
+ const cli_utils_1 = require("@zokugun/cli-utils");
10
+ const async_1 = __importDefault(require("@zokugun/fs-extra-plus/async"));
11
+ const xtry_1 = require("@zokugun/xtry");
12
+ const cli_progress_1 = require("cli-progress");
13
+ const pacote_1 = __importDefault(require("pacote"));
14
+ const semver_1 = require("semver");
15
+ const index_js_1 = require("../configs/index.js");
16
+ const format_table_js_1 = require("../utils/format-table.js");
17
+ const time_difference_js_1 = require("../utils/time-difference.js");
18
+ async function outdated() {
19
+ const targetPath = process_1.default.cwd();
20
+ const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
21
+ if (configResult.fails) {
22
+ cli_utils_1.logger.fatal(configResult.error);
23
+ }
24
+ const packageResult = await async_1.default.readJSON(node_path_1.default.resolve(targetPath, './package.json'));
25
+ if (packageResult.fails) {
26
+ cli_utils_1.logger.fatal((0, xtry_1.stringifyError)(packageResult.error));
27
+ }
28
+ const { name } = packageResult.value;
29
+ const { config, configStats } = configResult.value;
30
+ const artifacts = Object.entries(config.artifacts);
31
+ cli_utils_1.logger.newLine();
32
+ if (artifacts.length === 0) {
33
+ cli_utils_1.logger.info('No artifacts have been installed.');
34
+ }
35
+ else {
36
+ cli_utils_1.logger.print(`${name} ${cli_utils_1.c.grey(configStats.name)}`);
37
+ const bar = new cli_progress_1.SingleBar({
38
+ clearOnComplete: true,
39
+ hideCursor: true,
40
+ format: `{bar} {value}/{total} ${cli_utils_1.c.gray('{name}')}`,
41
+ linewrap: false,
42
+ barsize: 40,
43
+ }, cli_progress_1.Presets.shades_classic);
44
+ bar.start(artifacts.length, 0);
45
+ const table = [];
46
+ for (const [index, [name, artifact]] of artifacts.entries()) {
47
+ const current = await pacote_1.default.manifest(`${name}@${artifact.version}`, {
48
+ fullMetadata: true,
49
+ });
50
+ const latest = await pacote_1.default.manifest(name, {
51
+ fullMetadata: true,
52
+ });
53
+ const newer = (0, semver_1.gt)(latest.version, artifact.version);
54
+ const line = [
55
+ ' ',
56
+ newer ? name : cli_utils_1.c.grey(name),
57
+ ' ',
58
+ (0, time_difference_js_1.timeDifference)(current._time),
59
+ artifact.version ? cli_utils_1.c.grey(artifact.version) : '',
60
+ cli_utils_1.c.dim.grey('→'),
61
+ newer ? latest.version : cli_utils_1.c.grey.strikethrough(latest.version),
62
+ newer ? (0, time_difference_js_1.timeDifference)(latest._time) : '',
63
+ ];
64
+ table.push(line);
65
+ bar.update(index, { name });
66
+ }
67
+ bar.stop();
68
+ cli_utils_1.logger.newLine();
69
+ const lines = (0, format_table_js_1.formatTable)(table, 'LLLLRRRL');
70
+ cli_utils_1.logger.print(lines.join('\n'));
71
+ }
72
+ cli_utils_1.logger.newLine();
73
+ }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.remove = remove;
7
7
  const process_1 = __importDefault(require("process"));
8
8
  const cli_utils_1 = require("@zokugun/cli-utils");
9
+ const async_1 = require("@zokugun/xtry/async");
9
10
  const pacote_1 = __importDefault(require("pacote"));
10
11
  const tempy_1 = __importDefault(require("tempy"));
11
12
  const index_js_1 = require("../configs/index.js");
@@ -32,16 +33,43 @@ async function remove(specs, inputOptions) {
32
33
  cli_utils_1.logger.beginTimer();
33
34
  const targetPath = process_1.default.cwd();
34
35
  const options = {
36
+ dryRun: inputOptions?.dryRun ?? false,
35
37
  force: inputOptions?.force ?? false,
36
38
  skip: inputOptions?.skip ?? false,
39
+ variables: {},
37
40
  verbose: inputOptions?.verbose ?? false,
38
- dryRun: inputOptions?.dryRun ?? false,
39
41
  };
40
42
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
41
43
  if (configResult.fails) {
42
44
  cli_utils_1.logger.fatal(configResult.error);
43
45
  }
44
46
  const { config, configStats } = configResult.value;
47
+ if (specs.length === 0) {
48
+ const { value } = await (0, async_1.xtry)(cli_utils_1.enquirer.prompt({
49
+ type: 'multiselect',
50
+ name: 'specs',
51
+ message: 'Pick the artifacts to remove',
52
+ choices: Object.keys(config.artifacts).map((name) => ({ name })),
53
+ }));
54
+ const marked = value?.specs;
55
+ if (!marked || marked.length === 0) {
56
+ cli_utils_1.logger.warn('No artifacts marked for removal');
57
+ }
58
+ else {
59
+ const { value } = await (0, async_1.xtry)(cli_utils_1.enquirer.prompt([
60
+ (0, cli_utils_1.confirm)({
61
+ name: 'remove',
62
+ message: `Remove the following artifacts: ${marked.map((name) => cli_utils_1.c.green(name)).join(',')}`,
63
+ }),
64
+ ]));
65
+ if (value?.remove) {
66
+ specs.push(...marked);
67
+ }
68
+ else {
69
+ cli_utils_1.logger.warn('Artifacts removal has been rejected');
70
+ }
71
+ }
72
+ }
45
73
  for (const spec of specs) {
46
74
  const requestResult = (0, resolve_request_js_1.resolveRequest)(spec);
47
75
  if (requestResult.fails) {
@@ -37,10 +37,11 @@ async function update(inputOptions) {
37
37
  cli_utils_1.logger.beginTimer();
38
38
  const targetPath = process_1.default.cwd();
39
39
  const options = {
40
+ dryRun: inputOptions?.dryRun ?? false,
40
41
  force: inputOptions?.force ?? false,
41
42
  skip: false,
43
+ variables: {},
42
44
  verbose: inputOptions?.verbose ?? false,
43
- dryRun: inputOptions?.dryRun ?? false,
44
45
  };
45
46
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
46
47
  if (configResult.fails) {
@@ -0,0 +1,7 @@
1
+ import { type AsyncDResult } from '@zokugun/xtry';
2
+ export type Listing = ListingEntry[];
3
+ export type ListingEntry = {
4
+ name: string;
5
+ description: string;
6
+ };
7
+ export declare function readListingConfig(targetPath: string): AsyncDResult<Listing>;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readListingConfig = readListingConfig;
7
+ const path_1 = __importDefault(require("path"));
8
+ const async_1 = __importDefault(require("@zokugun/fs-extra-plus/async"));
9
+ const is_it_type_1 = require("@zokugun/is-it-type");
10
+ const xtry_1 = require("@zokugun/xtry");
11
+ const yaml_1 = __importDefault(require("yaml"));
12
+ const CONFIG_LOCATIONS = [
13
+ {
14
+ name: 'artifact-listing.yml',
15
+ type: 'yaml',
16
+ },
17
+ {
18
+ name: 'artifact-listing.yaml',
19
+ type: 'yaml',
20
+ },
21
+ {
22
+ name: 'artifact-listing.json',
23
+ type: 'json',
24
+ },
25
+ ];
26
+ async function readListingConfig(targetPath) {
27
+ let content;
28
+ let name;
29
+ let type;
30
+ for (const place of CONFIG_LOCATIONS) {
31
+ const result = await async_1.default.readFile(path_1.default.join(targetPath, place.name), 'utf8');
32
+ if (!result.fails) {
33
+ content = result.value;
34
+ ({ name, type } = place);
35
+ }
36
+ }
37
+ if (!content) {
38
+ return normalizeConfig(content, name);
39
+ }
40
+ if (type === 'json') {
41
+ return normalizeConfig(JSON.parse(content), name);
42
+ }
43
+ else if (type === 'yaml') {
44
+ return normalizeConfig(yaml_1.default.parse(content), name);
45
+ }
46
+ else {
47
+ try {
48
+ return normalizeConfig(JSON.parse(content), name);
49
+ }
50
+ catch {
51
+ return normalizeConfig(yaml_1.default.parse(content), name);
52
+ }
53
+ }
54
+ }
55
+ function normalizeConfig(data, source) {
56
+ if (!(0, is_it_type_1.isArray)(data)) {
57
+ return (0, xtry_1.err)(`Listing file ${source} must export an array.`);
58
+ }
59
+ for (const entry of data) {
60
+ if (!(0, is_it_type_1.isRecord)(entry)) {
61
+ return (0, xtry_1.err)('Listing entry must be an object.');
62
+ }
63
+ if (!(0, is_it_type_1.isString)(entry.name)) {
64
+ return (0, xtry_1.err)('Listing entry/name must be a string.');
65
+ }
66
+ if (!(0, is_it_type_1.isString)(entry.description)) {
67
+ return (0, xtry_1.err)('Listing entry/description must be a string.');
68
+ }
69
+ }
70
+ return (0, xtry_1.ok)(data);
71
+ } // }}}
@@ -1,3 +1,3 @@
1
1
  import { type AsyncDResult } from '@zokugun/xtry';
2
2
  import { type Context } from '../types/context.js';
3
- export declare function replaceTemplates({ textFiles, binaryFiles, targetPath, config, incomingConfig }: Context): AsyncDResult;
3
+ export declare function replaceTemplates({ textFiles, binaryFiles, targetPath, config, incomingConfig, options }: Context): AsyncDResult;
@@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.replaceTemplates = replaceTemplates;
4
4
  const xtry_1 = require("@zokugun/xtry");
5
5
  const template_js_1 = require("../utils/template.js");
6
- async function replaceTemplates({ textFiles, binaryFiles, targetPath, config, incomingConfig }) {
6
+ async function replaceTemplates({ textFiles, binaryFiles, targetPath, config, incomingConfig, options }) {
7
7
  const variables = {
8
8
  ...incomingConfig?.variables,
9
9
  ...config?.variables,
10
+ ...options.variables,
10
11
  };
11
12
  const engine = new template_js_1.TemplateEngine(targetPath, variables);
12
13
  for (const file of textFiles) {
@@ -12,12 +12,12 @@ async function validateNotPresentPackage({ incomingPackage, config, options }) {
12
12
  if (artifact) {
13
13
  if (options.skip) {
14
14
  if (options.verbose) {
15
- cli_utils_1.logger.debug('The incoming artifact is already present, skipping...');
15
+ cli_utils_1.logger.debug(`The "${name}" artifact is already present, skipping...`);
16
16
  }
17
17
  return xtry_1.OK_TRUE;
18
18
  }
19
19
  else {
20
- return (0, xtry_1.err)('The incoming artifact has already been added.');
20
+ return (0, xtry_1.err)(`The "${name}" artifact has already been added.`);
21
21
  }
22
22
  }
23
23
  return xtry_1.OK;
@@ -1,3 +1,4 @@
1
+ import { type Primitive } from '@zokugun/is-it-type';
1
2
  import { type AsyncDResult } from '@zokugun/xtry';
2
3
  import { type BinaryFile } from './binary-file.js';
3
4
  import { type Request, type InstallConfig, type PackageConfig, type ArtifactResult, type PackageManifest, type FileTransform } from './config.js';
@@ -45,6 +46,7 @@ export type Options = {
45
46
  skip: boolean;
46
47
  verbose: boolean;
47
48
  dryRun: boolean;
49
+ variables: Record<string, Primitive>;
48
50
  };
49
51
  export type Block = {
50
52
  name: string;
@@ -1,6 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- // export type OutputTextFile = {
4
- // data: string;
5
- // name: string;
6
- // };
@@ -0,0 +1 @@
1
+ export declare function formatTable(rows: string[][], align: string, spaces?: string): string[];
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatTable = formatTable;
4
+ const node_util_1 = require("node:util");
5
+ function formatTable(rows, align, spaces = ' ') {
6
+ const maxLength = [];
7
+ for (const row of rows) {
8
+ for (const [index, cell] of row.entries()) {
9
+ const length = getVisualLength(cell);
10
+ if (!maxLength[index] || maxLength[index] < length) {
11
+ maxLength[index] = length;
12
+ }
13
+ }
14
+ }
15
+ const lines = [];
16
+ for (const row of rows) {
17
+ const cells = [];
18
+ for (const [index, cell] of row.entries()) {
19
+ const pad = align[index] === 'R' ? visualPadStart : visualPadEnd;
20
+ cells.push(pad(cell, maxLength[index]));
21
+ }
22
+ lines.push(cells.join(spaces));
23
+ }
24
+ return lines;
25
+ }
26
+ function getVisualLength(cell) {
27
+ if (cell === '') {
28
+ return 0;
29
+ }
30
+ cell = (0, node_util_1.stripVTControlCharacters)(cell);
31
+ let width = 0;
32
+ for (let i = 0; i < cell.length; i++) {
33
+ const code = cell.codePointAt(i);
34
+ if (!code) {
35
+ continue;
36
+ }
37
+ // Ignore control characters
38
+ if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) {
39
+ continue;
40
+ }
41
+ // Ignore combining characters
42
+ if (code >= 0x3_00 && code <= 0x3_6F) {
43
+ continue;
44
+ }
45
+ // Surrogates
46
+ if (code > 0xFF_FF) {
47
+ i++;
48
+ }
49
+ width += 1;
50
+ }
51
+ return width;
52
+ }
53
+ function visualPadStart(text, pad, char = ' ') {
54
+ return text.padStart(pad - getVisualLength(text) + text.length, char);
55
+ }
56
+ function visualPadEnd(text, pad, char = ' ') {
57
+ return text.padEnd(pad - getVisualLength(text) + text.length, char);
58
+ }
@@ -8,6 +8,7 @@ export declare class TemplateEngine {
8
8
  constructor(basePath: string, variables?: Record<string, Primitive>);
9
9
  render(template: string): DResult<string>;
10
10
  private getValueByPath;
11
+ private evaluateExpression;
11
12
  private parseFile;
12
13
  private readConfigFile;
13
14
  private resolveExpression;
@@ -15,4 +16,3 @@ export declare class TemplateEngine {
15
16
  private splitPlaceholder;
16
17
  private toDate;
17
18
  }
18
- export declare function unescapeCode(code: string): string;
@@ -37,19 +37,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.TemplateEngine = void 0;
40
- exports.unescapeCode = unescapeCode;
41
40
  const path_1 = __importDefault(require("path"));
42
41
  const sync_1 = __importDefault(require("@zokugun/fs-extra-plus/sync"));
43
42
  const is_it_type_1 = require("@zokugun/is-it-type");
44
43
  const xtry_1 = require("@zokugun/xtry");
45
44
  const dayjs_1 = __importDefault(require("dayjs"));
46
45
  const utc_1 = __importDefault(require("dayjs/plugin/utc"));
46
+ const git_url_parse_1 = __importDefault(require("git-url-parse"));
47
47
  const lodash_es_1 = require("lodash-es");
48
48
  const YAML = __importStar(require("../parsers/yaml.js"));
49
49
  const try_json_js_1 = require("./try-json.js");
50
50
  dayjs_1.default.extend(utc_1.default);
51
51
  const EXPRESSION_REGEX = /#\[\[(.*?)]]/g;
52
- const NEXT_PROPERTY_REGEX = /^(\w+?)((?=\.|$).*)$/;
52
+ const PATH_PROPERTY_REGEX = /^(\w+?)(?:\.|$)(.*)$/;
53
+ const PIPE_SEPARATOR_REGEX = /\s*\|>\s*/;
54
+ // Pattern: |> fn(_).prop
55
+ const PIPE_CALL_REGEX = /^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\(\s*_\s*\)\s*(\.\s*.*?)\s*$/;
56
+ // Pattern: |> fn
57
+ const PIPE_FUNCTION_REGEX = /^([A-Za-z_$][\w$]+(?:\.[A-Za-z_$][\w$]*)*)\s*$/;
58
+ // Pattern: |> _.prop
59
+ const PIPE_PROPERTY_REGEX = /^_\.\s*(.*?)\s*$/;
53
60
  const PLACEHOLDER_REGEX = /^((?:[^/.]+(?:[^/]+\/)*\/)?\.?[^.]+(?:\.(?:json|ya?ml))?)\.(.*)$/;
54
61
  class TemplateEngine {
55
62
  basePath;
@@ -79,7 +86,7 @@ class TemplateEngine {
79
86
  let currentPath = propertyPath;
80
87
  let currentValue = values;
81
88
  let match;
82
- while ((match = NEXT_PROPERTY_REGEX.exec(currentPath))) {
89
+ while ((match = PATH_PROPERTY_REGEX.exec(currentPath))) {
83
90
  if (!(0, lodash_es_1.isPlainObject)(currentValue)) {
84
91
  return (0, xtry_1.err)(`Property path not found: ${propertyPath}`);
85
92
  }
@@ -89,12 +96,51 @@ class TemplateEngine {
89
96
  }
90
97
  currentPath = match[2];
91
98
  if (currentPath.length === 0) {
92
- return (0, xtry_1.ok)(currentValue);
99
+ return (0, xtry_1.ok)({ value: currentValue });
93
100
  }
94
101
  }
102
+ return (0, xtry_1.ok)({ value: currentValue, expression: `.${currentPath}` });
103
+ } // }}}
104
+ evaluateExpression(expression, values) {
105
+ const operands = expression.split(PIPE_SEPARATOR_REGEX);
106
+ const root = this.getValueByPath(values, operands.shift());
107
+ if (root.fails) {
108
+ return root;
109
+ }
110
+ const { value, expression: rootExpression } = root.value;
111
+ if (operands.length === 0) {
112
+ if ((0, is_it_type_1.isNonEmptyString)(rootExpression)) {
113
+ // eslint-disable-next-line no-new-func
114
+ const fn = new Function('it', `return it${unescapeCode(rootExpression)};`);
115
+ return (0, xtry_1.ok)(fn(value));
116
+ }
117
+ else {
118
+ return (0, xtry_1.ok)(value);
119
+ }
120
+ }
121
+ const lines = [];
122
+ if ((0, is_it_type_1.isNonEmptyString)(rootExpression)) {
123
+ lines.push(`it = it${unescapeCode(rootExpression)};`);
124
+ }
125
+ let match = null;
126
+ for (const operand of operands) {
127
+ if ((match = PIPE_FUNCTION_REGEX.exec(operand))) {
128
+ lines.push(`it = ${unescapeCode(match[1])}(it);`);
129
+ }
130
+ else if ((match = PIPE_PROPERTY_REGEX.exec(operand))) {
131
+ lines.push(`it = it.${unescapeCode(match[1])};`);
132
+ }
133
+ else if ((match = PIPE_CALL_REGEX.exec(operand))) {
134
+ lines.push(`it = ${unescapeCode(match[1])}(it)${unescapeCode(match[2])};`);
135
+ }
136
+ else {
137
+ return (0, xtry_1.err)(`Cannot evaluate "${expression}"`);
138
+ }
139
+ }
140
+ lines.push('return it;');
95
141
  // eslint-disable-next-line no-new-func
96
- const fn = new Function('it', `return it${unescapeCode(currentPath)};`);
97
- return (0, xtry_1.ok)(fn(currentValue));
142
+ const fn = new Function('it, toGitUrl', lines.join('\n'));
143
+ return (0, xtry_1.ok)(fn(value, git_url_parse_1.default));
98
144
  } // }}}
99
145
  parseFile(filename) {
100
146
  const result = sync_1.default.readFile(filename, 'utf8');
@@ -151,14 +197,15 @@ class TemplateEngine {
151
197
  if (readResult.fails) {
152
198
  return readResult;
153
199
  }
154
- const getValueResult = this.getValueByPath(readResult.value, propertyPath);
155
- if (getValueResult.fails) {
156
- return getValueResult;
200
+ // const getValueResult = this.getValueByPath(readResult.value, propertyPath);
201
+ const evalResult = this.evaluateExpression(propertyPath, readResult.value);
202
+ if (evalResult.fails) {
203
+ return evalResult;
157
204
  }
158
- if ((0, lodash_es_1.isNil)(getValueResult.value)) {
205
+ if ((0, lodash_es_1.isNil)(evalResult.value)) {
159
206
  return (0, xtry_1.err)(expression);
160
207
  }
161
- return (0, xtry_1.ok)(String(getValueResult.value));
208
+ return (0, xtry_1.ok)(String(evalResult.value));
162
209
  }
163
210
  } // }}}
164
211
  resolveVariable(name) {
@@ -0,0 +1 @@
1
+ export declare function timeDifference(from: number | string, to?: number): string;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.timeDifference = timeDifference;
4
+ const cli_utils_1 = require("@zokugun/cli-utils");
5
+ const is_it_type_1 = require("@zokugun/is-it-type");
6
+ const msPerMinute = 60 * 1000;
7
+ const msPerHour = msPerMinute * 60;
8
+ const msPerDay = msPerHour * 24;
9
+ const msPerMonth = msPerDay * 30;
10
+ const msPerYear = msPerDay * 365;
11
+ function timeDifference(from, to = Date.now()) {
12
+ if ((0, is_it_type_1.isString)(from)) {
13
+ from = (new Date(from)).getTime();
14
+ }
15
+ const elapsed = to - from;
16
+ if (elapsed < msPerDay) {
17
+ return cli_utils_1.c.gray('⩽1d');
18
+ }
19
+ else if (elapsed < msPerMonth) {
20
+ return cli_utils_1.c.green(`~${Math.round(elapsed / msPerDay)}d`);
21
+ }
22
+ else if (elapsed < msPerYear) {
23
+ return cli_utils_1.c.yellow(`~${Math.round(elapsed / msPerMonth)}mo`);
24
+ }
25
+ else {
26
+ return cli_utils_1.c.red(`~${(elapsed / msPerYear).toFixed(1)}y`);
27
+ }
28
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zokugun/artifact",
3
3
  "description": "Boilerplate your project & keep your configurations up to date",
4
- "version": "0.6.3",
4
+ "version": "0.8.0",
5
5
  "author": {
6
6
  "name": "Baptiste Augrain",
7
7
  "email": "daiyam@zokugun.org"
@@ -46,13 +46,16 @@
46
46
  "watch:test": "tsc-watch -p test"
47
47
  },
48
48
  "dependencies": {
49
+ "@types/cli-progress": "^3.11.6",
49
50
  "@zokugun/cli-utils": "0.3.1",
50
51
  "@zokugun/configdotts-merge": "0.2.1",
51
52
  "@zokugun/fs-extra-plus": "0.3.7",
52
53
  "@zokugun/is-it-type": "0.8.1",
53
54
  "@zokugun/xtry": "0.11.4",
55
+ "cli-progress": "^3.12.0",
54
56
  "dayjs": "1.11.20",
55
57
  "editorconfig": "0.15.3",
58
+ "git-url-parse": "16.1.0",
56
59
  "globby": "11.1.0",
57
60
  "istextorbinary": "6.0.0",
58
61
  "jq-wasm": "1.1.0-jq-1.8.1",