@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 +22 -2
- package/lib/cli.js +29 -14
- package/lib/commands/add.d.ts +9 -3
- package/lib/commands/add.js +52 -3
- package/lib/commands/index.d.ts +1 -0
- package/lib/commands/index.js +3 -1
- package/lib/commands/list.js +24 -6
- package/lib/commands/outdated.d.ts +1 -0
- package/lib/commands/outdated.js +73 -0
- package/lib/commands/remove.js +29 -1
- package/lib/commands/update.js +2 -1
- package/lib/configs/package/read-listing-config.d.ts +7 -0
- package/lib/configs/package/read-listing-config.js +71 -0
- package/lib/steps/replace-templates.d.ts +1 -1
- package/lib/steps/replace-templates.js +2 -1
- package/lib/steps/validate-not-present-package.js +2 -2
- package/lib/types/context.d.ts +2 -0
- package/lib/types/context.js +0 -4
- package/lib/utils/format-table.d.ts +1 -0
- package/lib/utils/format-table.js +58 -0
- package/lib/utils/template.d.ts +1 -1
- package/lib/utils/template.js +58 -11
- package/lib/utils/time-difference.d.ts +1 -0
- package/lib/utils/time-difference.js +28 -0
- package/package.json +4 -1
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
|
|
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
|
|
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, '-
|
|
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('
|
|
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('
|
|
22
|
-
.description('
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
.
|
|
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('
|
|
32
|
-
.argument('
|
|
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('
|
|
37
|
-
.description('
|
|
38
|
-
.
|
|
39
|
-
.
|
|
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();
|
package/lib/commands/add.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
6
|
-
|
|
10
|
+
};
|
|
11
|
+
export declare function add(specs: string[], inputOptions?: CLIOptions): Promise<void>;
|
|
12
|
+
export {};
|
package/lib/commands/add.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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) {
|
package/lib/commands/index.d.ts
CHANGED
package/lib/commands/index.js
CHANGED
|
@@ -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");
|
package/lib/commands/list.js
CHANGED
|
@@ -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
|
|
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) ?
|
|
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.
|
|
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.
|
|
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
|
|
35
|
-
|
|
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
|
+
}
|
package/lib/commands/remove.js
CHANGED
|
@@ -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) {
|
package/lib/commands/update.js
CHANGED
|
@@ -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,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(
|
|
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)(
|
|
20
|
+
return (0, xtry_1.err)(`The "${name}" artifact has already been added.`);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
return xtry_1.OK;
|
package/lib/types/context.d.ts
CHANGED
|
@@ -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;
|
package/lib/types/context.js
CHANGED
|
@@ -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
|
+
}
|
package/lib/utils/template.d.ts
CHANGED
|
@@ -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;
|
package/lib/utils/template.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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',
|
|
97
|
-
return (0, xtry_1.ok)(fn(
|
|
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
|
-
|
|
156
|
-
|
|
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)(
|
|
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(
|
|
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.
|
|
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",
|