@zokugun/artifact 0.6.3 → 0.7.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/lib/cli.js CHANGED
@@ -8,27 +8,37 @@ 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
31
  .command('update')
22
32
  .description('update the current project using the installed artifacts')
23
33
  .option('-d, --dry-run', 'fake update')
24
- .option('-v, --verbose', 'output more details')
34
+ .option('--verbose', 'output more details')
25
35
  .alias('up')
26
36
  .action(index_js_1.update);
27
37
  program
28
38
  .command('remove')
29
39
  .description('remove an artifact from the current project')
30
40
  .option('-d, --dry-run', 'fake uninstall')
31
- .option('-v, --verbose', 'output more details')
41
+ .option('--verbose', 'output more details')
32
42
  .argument('<artifacts...>')
33
43
  .alias('rm')
34
44
  .action(index_js_1.remove);
@@ -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 {};
@@ -37,11 +37,17 @@ async function add(specs, 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: inputOptions?.skip ?? false,
43
+ variables: {},
42
44
  verbose: inputOptions?.verbose ?? false,
43
- dryRun: inputOptions?.dryRun ?? false,
44
45
  };
46
+ if (inputOptions?.var) {
47
+ for (const { name, value } of inputOptions.var) {
48
+ options.variables[name] = value;
49
+ }
50
+ }
45
51
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
46
52
  if (configResult.fails) {
47
53
  cli_utils_1.logger.fatal(configResult.error);
@@ -32,10 +32,11 @@ async function remove(specs, inputOptions) {
32
32
  cli_utils_1.logger.beginTimer();
33
33
  const targetPath = process_1.default.cwd();
34
34
  const options = {
35
+ dryRun: inputOptions?.dryRun ?? false,
35
36
  force: inputOptions?.force ?? false,
36
37
  skip: inputOptions?.skip ?? false,
38
+ variables: {},
37
39
  verbose: inputOptions?.verbose ?? false,
38
- dryRun: inputOptions?.dryRun ?? false,
39
40
  };
40
41
  const configResult = await (0, index_js_1.readInstallConfig)(targetPath);
41
42
  if (configResult.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) {
@@ -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
- // };
@@ -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) {
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.7.0",
5
5
  "author": {
6
6
  "name": "Baptiste Augrain",
7
7
  "email": "daiyam@zokugun.org"
@@ -37,7 +37,7 @@
37
37
  "release": "release-it",
38
38
  "test": "tsc -p test && mocha",
39
39
  "test:dev": "mocha",
40
- "test:watch": "tsc-watch -p src -p test --onSuccess 'mocha -g=\"\"'",
40
+ "test:watch": "tsc-watch -p src -p test --onSuccess 'mocha -g=\"vars.exp.pipe.gitname.dotprop\"'",
41
41
  "update:artifacts": "artifact update",
42
42
  "update:ci": "PINACT_MIN_AGE=7 pinact run",
43
43
  "update:deps": "taze",
@@ -53,6 +53,7 @@
53
53
  "@zokugun/xtry": "0.11.4",
54
54
  "dayjs": "1.11.20",
55
55
  "editorconfig": "0.15.3",
56
+ "git-url-parse": "16.1.0",
56
57
  "globby": "11.1.0",
57
58
  "istextorbinary": "6.0.0",
58
59
  "jq-wasm": "1.1.0-jq-1.8.1",