zcb 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -11
- package/dist/main/cli.mjs +182 -0
- package/dist/main/cli.mjs.map +1 -0
- package/dist/main/index.mjs +321 -0
- package/dist/main/index.mjs.map +1 -0
- package/dist/main/templates/config.ts.hbs +4 -0
- package/package.json +1 -1
- package/src/__testUtils__/configReader.ts +7 -0
- package/src/cli.ts +3 -3
- package/src/createConfigBuilder.ts +6 -5
- package/src/createConfigParser.ts +16 -0
- package/src/createConfigReader.test.ts +9 -13
- package/src/createConfigReader.ts +4 -7
- package/src/index.ts +1 -0
- package/src/transformers/cloneNonEnumerableValues.ts +2 -2
- package/src/transformers/removeDisabledSlices.ts +7 -2
- package/src/transformers/runExperiments.ts +24 -0
- package/src/transformers/setupExperiments.ts +10 -6
- package/src/types.ts +30 -6
- package/src/utils/{importValidateWriteConfig.ts → importValidateTransformWriteConfig.ts} +7 -6
- package/src/utils/{writeConfig.ts → transformWriteConfig.ts} +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ npm install zcb
|
|
|
23
23
|
* [Create config builder](#create-config-builder)
|
|
24
24
|
* [Transform config builder](#transform-config-builder)
|
|
25
25
|
* [Create config reader](#create-config-reader)
|
|
26
|
+
* [Use config reader](#use-config-reader)
|
|
26
27
|
|
|
27
28
|
### Create schema
|
|
28
29
|
|
|
@@ -31,11 +32,13 @@ Create the schema for your configuration like in the example below.
|
|
|
31
32
|
```ts
|
|
32
33
|
// ./schema.ts
|
|
33
34
|
import { z } from 'zod';
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
import {
|
|
36
|
+
countryCodes,
|
|
37
|
+
countryNames,
|
|
38
|
+
distanceUnits,
|
|
39
|
+
languageCodes,
|
|
40
|
+
timezones,
|
|
41
|
+
} from 'zcb';
|
|
39
42
|
|
|
40
43
|
export const baseSectionSchema = z.object({
|
|
41
44
|
name: z.string(),
|
|
@@ -90,7 +93,7 @@ export type ConfigType = z.infer<typeof configSchema>;
|
|
|
90
93
|
|
|
91
94
|
### Create config builder
|
|
92
95
|
|
|
93
|
-
Then use the schema and its types to create a config builder and build out your configuration like in the example below. The config builder comes with method autocompletion and value type validation. It is important to default export the config builder.
|
|
96
|
+
Then use the schema and its types to create a config builder and build out your configuration like in the example below. The config builder comes with method autocompletion and value type validation. It is important to default export the config builder as this is what the cli build/watch scripts are expecting when they import the config builder.
|
|
94
97
|
|
|
95
98
|
```ts
|
|
96
99
|
// ./configBuilder.ts
|
|
@@ -280,17 +283,63 @@ export default {
|
|
|
280
283
|
} as const;
|
|
281
284
|
```
|
|
282
285
|
|
|
286
|
+
#### cli API
|
|
287
|
+
|
|
288
|
+
* `zcb build <input-file> <output-file>`
|
|
289
|
+
|
|
290
|
+
```sh
|
|
291
|
+
Write config from a config builder
|
|
292
|
+
|
|
293
|
+
Positionals:
|
|
294
|
+
input-file The relative path to the config builder root file
|
|
295
|
+
[string] [required]
|
|
296
|
+
output-file The relative path to the output config file [string] [required]
|
|
297
|
+
|
|
298
|
+
Options:
|
|
299
|
+
--version Show version number [boolean]
|
|
300
|
+
--help Show help [boolean]
|
|
301
|
+
--experiments-callback-file The relative path to the experiment callback file
|
|
302
|
+
[string]
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
* `zcb watch <input-file> <output-file>`
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
Watch a config builder and write config
|
|
309
|
+
|
|
310
|
+
Positionals:
|
|
311
|
+
input-file The relative path to the config builder root file
|
|
312
|
+
[string] [required]
|
|
313
|
+
output-file The relative path to the output config file [string] [required]
|
|
314
|
+
|
|
315
|
+
Options:
|
|
316
|
+
--version Show version number [boolean]
|
|
317
|
+
--help Show help [boolean]
|
|
318
|
+
--experiments-callback-file The relative path to the experiment callback file
|
|
319
|
+
[string]
|
|
320
|
+
```
|
|
321
|
+
|
|
283
322
|
### Create config reader
|
|
284
323
|
|
|
285
|
-
Then use the autogenerated config to create a config reader that you can access config values with. The
|
|
324
|
+
Then use the autogenerated config to create a config reader that you can access config values with. The autogenerated config will always be a default import.
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
// ./configReader.ts
|
|
328
|
+
import { createConfigParser, createConfigReader } from 'zcb';
|
|
329
|
+
import builtConfig from './builtConfig.ts';
|
|
330
|
+
|
|
331
|
+
export default () => createConfigReader(builtConfig);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Use config reader
|
|
335
|
+
|
|
336
|
+
Then import the config reader into the file in which you want access to config values. The config reader comes with config path autocomplete and return value preview.
|
|
286
337
|
|
|
287
338
|
```ts
|
|
288
|
-
import
|
|
289
|
-
import { createConfigReader } from 'zcb';
|
|
339
|
+
import configReader from './configReader.ts';
|
|
290
340
|
|
|
291
|
-
const reader = createConfigReader(builtConfig);
|
|
292
341
|
// scope config path autocompletion and validation
|
|
293
|
-
const scopedReader =
|
|
342
|
+
const scopedReader = configReader.scope('pages.contactDetails')
|
|
294
343
|
.scope('sections.1.sections')
|
|
295
344
|
.scope('0');
|
|
296
345
|
// reader config path autocompletion and validation
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { readFileSync, watchFile } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import shelljs from 'shelljs';
|
|
4
|
+
import yargs from 'yargs';
|
|
5
|
+
import 'core-js/modules/es.array.push.js';
|
|
6
|
+
import { outputFileSync } from 'fs-extra/esm';
|
|
7
|
+
import Handlebars from 'handlebars';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import castArray from 'lodash/castArray.js';
|
|
10
|
+
import isPlainObject from 'lodash/isPlainObject.js';
|
|
11
|
+
|
|
12
|
+
let NonEmumeralProperties = function (NonEmumeralProperties) {
|
|
13
|
+
NonEmumeralProperties["CALLBACKS"] = "__callbacks";
|
|
14
|
+
NonEmumeralProperties["DISABLED"] = "__disabled";
|
|
15
|
+
NonEmumeralProperties["EXPERIMENT"] = "__experiment";
|
|
16
|
+
NonEmumeralProperties["ZCB"] = "__zcb";
|
|
17
|
+
return NonEmumeralProperties;
|
|
18
|
+
}({});
|
|
19
|
+
let TransformConfigHandlerAction = function (TransformConfigHandlerAction) {
|
|
20
|
+
TransformConfigHandlerAction["DELETE_NODE"] = "deleteNode";
|
|
21
|
+
TransformConfigHandlerAction["SKIP_NODE"] = "skipNode";
|
|
22
|
+
return TransformConfigHandlerAction;
|
|
23
|
+
}({});
|
|
24
|
+
|
|
25
|
+
const removeDisabledSlices = (clone, config) => {
|
|
26
|
+
if (NonEmumeralProperties.DISABLED in config) {
|
|
27
|
+
return {
|
|
28
|
+
action: TransformConfigHandlerAction.DELETE_NODE
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
value: clone
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const setupExperiments = callback => async (clone, config) => {
|
|
37
|
+
if (NonEmumeralProperties.EXPERIMENT in config && typeof config.__experiment === 'string') {
|
|
38
|
+
const buckets = await callback(config.__experiment, clone, config);
|
|
39
|
+
clone.__experiment = {
|
|
40
|
+
buckets,
|
|
41
|
+
id: config.__experiment
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
value: clone
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
value: clone
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const transformArray = (list, handler) => Promise.all(list.map(entry => {
|
|
53
|
+
if (isPlainObject(entry)) {
|
|
54
|
+
return transformConfig(entry, handler);
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(entry)) {
|
|
57
|
+
return transformArray(entry, handler);
|
|
58
|
+
}
|
|
59
|
+
return entry;
|
|
60
|
+
}));
|
|
61
|
+
const transformConfig = async (config, handler = []) => {
|
|
62
|
+
let transform = {};
|
|
63
|
+
const handlers = castArray(handler);
|
|
64
|
+
for (const callback of handlers) {
|
|
65
|
+
const result = await Promise.resolve(callback(transform, config));
|
|
66
|
+
if (result.action === TransformConfigHandlerAction.DELETE_NODE) {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
if (result.value) {
|
|
70
|
+
transform = result.value;
|
|
71
|
+
}
|
|
72
|
+
if (result.action === TransformConfigHandlerAction.SKIP_NODE) {
|
|
73
|
+
return transform;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const key in config) {
|
|
77
|
+
const value = config[key];
|
|
78
|
+
if (isPlainObject(value)) {
|
|
79
|
+
transform[key] = await transformConfig(value, handler);
|
|
80
|
+
} else if (Array.isArray(value)) {
|
|
81
|
+
transform[key] = await transformArray(value, handler);
|
|
82
|
+
} else {
|
|
83
|
+
transform[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return transform;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const transformWriteConfig = async (config, {
|
|
90
|
+
experimentsCallback,
|
|
91
|
+
outputFile
|
|
92
|
+
}) => {
|
|
93
|
+
const handlers = [removeDisabledSlices];
|
|
94
|
+
if (experimentsCallback) {
|
|
95
|
+
handlers.push(setupExperiments(experimentsCallback));
|
|
96
|
+
}
|
|
97
|
+
const template = Handlebars.compile(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), './templates/config.ts.hbs'), {
|
|
98
|
+
encoding: 'utf8'
|
|
99
|
+
}));
|
|
100
|
+
const output = template({
|
|
101
|
+
config: JSON.stringify(await transformConfig(config, handlers), undefined, 2)
|
|
102
|
+
}).replaceAll(/"([A-Za-z][\dA-Za-z]+)":/g, '$1:');
|
|
103
|
+
shelljs.echo(`zcd watch => writing to file: ${outputFile}`);
|
|
104
|
+
shelljs.echo(`zcd watch => content to write:\n${output}\n`);
|
|
105
|
+
outputFileSync(resolve(process.cwd(), outputFile), output);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const importValidateTransformWriteConfig = (inputFile, outputFile, command, experimentCallbackFile) => {
|
|
109
|
+
import(resolve(process.cwd(), inputFile)).then(({
|
|
110
|
+
default: configBuilder
|
|
111
|
+
}) => {
|
|
112
|
+
if (!configBuilder.validate()) {
|
|
113
|
+
shelljs.echo(`zcd ${command} => invalid config`);
|
|
114
|
+
shelljs.echo(`zcd ${command} => config values:\n${configBuilder.toJson()}\n`);
|
|
115
|
+
shelljs.echo(`zcd ${command} => errors:\n${JSON.stringify(configBuilder.errors(), undefined, 2)}\n`);
|
|
116
|
+
shelljs.exit(1);
|
|
117
|
+
}
|
|
118
|
+
shelljs.echo('zcd ${command} => valid config');
|
|
119
|
+
shelljs.echo(`zcd ${command} => config values:\n${configBuilder.toJson()}\n`);
|
|
120
|
+
if (experimentCallbackFile) {
|
|
121
|
+
import(resolve(process.cwd(), experimentCallbackFile)).then(({
|
|
122
|
+
default: experimentsCallback
|
|
123
|
+
}) => {
|
|
124
|
+
void transformWriteConfig(configBuilder.values(), {
|
|
125
|
+
experimentsCallback,
|
|
126
|
+
outputFile
|
|
127
|
+
});
|
|
128
|
+
}).catch(error => {
|
|
129
|
+
if (error instanceof Error) {
|
|
130
|
+
shelljs.echo(`zcd ${command} => error message: ${error.message}`);
|
|
131
|
+
if (error.stack) {
|
|
132
|
+
shelljs.echo(`zcd ${command} => error stack:\n${error.stack}\n`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
void transformWriteConfig(configBuilder.values(), {
|
|
139
|
+
outputFile
|
|
140
|
+
});
|
|
141
|
+
}).catch(error => {
|
|
142
|
+
if (error instanceof Error) {
|
|
143
|
+
shelljs.echo(`zcd ${command} => error message: ${error.message}`);
|
|
144
|
+
if (error.stack) {
|
|
145
|
+
shelljs.echo(`zcd ${command} => error stack:\n${error.stack}\n`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let Commands = function (Commands) {
|
|
152
|
+
Commands["BUILD"] = "build";
|
|
153
|
+
Commands["WATCH"] = "watch";
|
|
154
|
+
return Commands;
|
|
155
|
+
}({});
|
|
156
|
+
const generateArguments = cmdYargs => cmdYargs.positional('input-file', {
|
|
157
|
+
demandOption: true,
|
|
158
|
+
desc: 'The relative path to the config builder root file',
|
|
159
|
+
type: 'string'
|
|
160
|
+
}).positional('output-file', {
|
|
161
|
+
demandOption: true,
|
|
162
|
+
desc: 'The relative path to the output config file',
|
|
163
|
+
type: 'string'
|
|
164
|
+
}).option('experiments-callback-file', {
|
|
165
|
+
desc: 'The relative path to the experiment callback file',
|
|
166
|
+
type: 'string'
|
|
167
|
+
});
|
|
168
|
+
const cli = () => {
|
|
169
|
+
yargs.command('watch <input-file> <output-file>', 'Watch a config builder and write config', cmdYargs => generateArguments(cmdYargs), argv => {
|
|
170
|
+
shelljs.echo(`zcd watch => watching file: ${argv['input-file']}`);
|
|
171
|
+
watchFile(resolve(process.cwd(), argv['input-file']), () => {
|
|
172
|
+
shelljs.echo('zcd watch => file change detected');
|
|
173
|
+
importValidateTransformWriteConfig(argv['input-file'], argv['output-file'], Commands.WATCH, argv['experiments-callback-file']);
|
|
174
|
+
});
|
|
175
|
+
}).command('build <input-file> <output-file>', 'Write config from a config builder', cmdYargs => generateArguments(cmdYargs), argv => {
|
|
176
|
+
shelljs.echo(`zcd build => building file: ${argv['input-file']}`);
|
|
177
|
+
importValidateTransformWriteConfig(argv['input-file'], argv['output-file'], Commands.BUILD, argv['experiments-callback-file']);
|
|
178
|
+
}).help().argv;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export { Commands, cli };
|
|
182
|
+
//# sourceMappingURL=cli.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","sources":["../zod-config-builder/src//types.ts","../zod-config-builder/src//transformers/removeDisabledSlices.ts","../zod-config-builder/src//transformers/setupExperiments.ts","../zod-config-builder/src//utils/transformConfig.ts","../zod-config-builder/src//utils/transformWriteConfig.ts","../zod-config-builder/src//utils/importValidateTransformWriteConfig.ts","../zod-config-builder/src//cli.ts"],"sourcesContent":["import type { List } from 'ts-toolbelt';\nimport type { Includes, Join } from 'type-fest';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyRecord = Record<string, any>;\n\nexport type Buckets<Config extends AnyRecord> = Record<string, Experiment<Config>>;\n\nexport interface ConfigParserOptions {\n experimentsCallback?: RunExperimentsCallback;\n}\n\nexport interface Experiment<Config extends AnyRecord> {\n action?: TransformConfigHandlerAction;\n value?: Config;\n}\n\nexport enum NonEmumeralProperties {\n CALLBACKS = '__callbacks',\n DISABLED = '__disabled',\n EXPERIMENT = '__experiment',\n ZCB = '__zcb',\n}\n\nexport type Path<Config extends object> = Join<Leaves<Config>, '.'>;\n\nexport type RunExperimentsCallback = (id: string) => Promise<string>;\n\nexport type Scope<Config extends object> = Join<Paths<Config>, '.'>;\n\nexport type SetupExperimentsCallback = <Config extends AnyRecord>(\n id: string,\n clone: Config,\n config: Config\n) => Promise<Buckets<Config>>;\n\nexport type TransformConfigHandler = <Config extends AnyRecord>(\n clone: Config,\n config: Config\n) => TransformConfigHandlerReturnType<Config> | Promise<TransformConfigHandlerReturnType<Config>>;\n\nexport type TransformConfigHandlerSync = <Config extends AnyRecord>(\n clone: Config,\n config: Config\n) => TransformConfigHandlerReturnType<Config>;\n\nexport enum TransformConfigHandlerAction {\n DELETE_NODE = 'deleteNode',\n SKIP_NODE = 'skipNode',\n}\n\nexport interface TransformConfigHandlerReturnType<Config extends AnyRecord> {\n action?: TransformConfigHandlerAction;\n value?: Config;\n}\n\nexport interface WriteConfigOptions {\n experimentsCallback?: SetupExperimentsCallback;\n outputFile: string;\n}\n\nexport type Leaves<T, Path extends string[] = []> = T extends string\n ? Path\n : {\n [K in keyof T & string]: Leaves<T[K], [...Path, K]>;\n }[keyof T & string];\n\nexport type Paths<T, Path extends string[] = []> = T extends string\n ? Path\n : {\n [K in keyof T & string]: T[K] extends object ? Paths<T[K], [...Path, K]> : Path;\n }[keyof T & string];\n\nexport type LeavesScopeDiffs<T1 extends readonly string[][], T2 extends string[]> = {\n [K1 in keyof T1]: List.Length<List.Intersect<T1[K1], T2, '<-contains'>> extends 1\n ? List.Length<T1[K1]> extends 1\n ? Includes<T2, T1[K1][0]> extends false\n ? T1[K1]\n : never\n : List.Diff<T1[K1], T2>\n : never;\n}[number];\n","import {\n type AnyRecord,\n NonEmumeralProperties,\n TransformConfigHandlerAction,\n type TransformConfigHandlerSync,\n} from '../types.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const removeDisabledSlices: TransformConfigHandlerSync = <Config extends AnyRecord>(\n clone: Config,\n config: Config\n) => {\n if (NonEmumeralProperties.DISABLED in config) {\n return {\n action: TransformConfigHandlerAction.DELETE_NODE,\n };\n }\n\n return {\n value: clone,\n };\n};\n","import {\n type AnyRecord,\n NonEmumeralProperties,\n type SetupExperimentsCallback,\n type TransformConfigHandler,\n} from '../types.ts';\n\nexport const setupExperiments =\n (callback: SetupExperimentsCallback): TransformConfigHandler =>\n async <Config extends AnyRecord>(clone: Config, config: Config) => {\n if (NonEmumeralProperties.EXPERIMENT in config && typeof config.__experiment === 'string') {\n const buckets = await callback(config.__experiment, clone, config);\n\n // @ts-expect-error private property\n clone.__experiment = {\n buckets,\n id: config.__experiment,\n };\n\n return {\n value: clone,\n };\n }\n\n return {\n value: clone,\n };\n };\n","import castArray from 'lodash/castArray.js';\nimport isPlainObject from 'lodash/isPlainObject.js';\nimport {\n type AnyRecord,\n type TransformConfigHandler,\n TransformConfigHandlerAction,\n type TransformConfigHandlerSync,\n} from '../types.ts';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst transformArray = (list: any[], handler: TransformConfigHandler | TransformConfigHandler[]): Promise<any[]> =>\n Promise.all(\n list.map(entry => {\n if (isPlainObject(entry)) {\n return transformConfig(entry, handler);\n }\n\n if (Array.isArray(entry)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformArray(entry, handler);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return entry;\n })\n );\n\nexport const transformConfig = async <Config extends AnyRecord>(\n config: Config,\n handler: TransformConfigHandler | TransformConfigHandler[] = []\n): Promise<Config> => {\n let transform = {} as Config;\n const handlers = castArray(handler);\n\n for (const callback of handlers) {\n const result = await Promise.resolve(callback(transform, config));\n\n if (result.action === TransformConfigHandlerAction.DELETE_NODE) {\n return {} as Config;\n }\n\n if (result.value) {\n transform = result.value;\n }\n\n if (result.action === TransformConfigHandlerAction.SKIP_NODE) {\n return transform;\n }\n }\n\n for (const key in config) {\n const value = config[key];\n\n if (isPlainObject(value)) {\n transform[key] = await transformConfig(value, handler);\n } else if (Array.isArray(value)) {\n transform[key] = (await transformArray(value, handler)) as Config[Extract<keyof Config, string>];\n } else {\n transform[key] = value;\n }\n }\n\n return transform;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst transformArraySync = (list: any[], handler: TransformConfigHandlerSync | TransformConfigHandlerSync[]): any[] =>\n list.map(entry => {\n if (isPlainObject(entry)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformConfigSync(entry, handler);\n }\n\n if (Array.isArray(entry)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformArraySync(entry, handler);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return entry;\n });\n\nexport const transformConfigSync = <Config extends AnyRecord>(\n config: Config,\n handler: TransformConfigHandlerSync | TransformConfigHandlerSync[] = []\n): Config => {\n let transform = {} as Config;\n const handlers = castArray(handler);\n\n for (const callback of handlers) {\n const result = callback(transform, config);\n\n if (result.action === TransformConfigHandlerAction.DELETE_NODE) {\n return {} as Config;\n }\n\n if (result.value) {\n transform = result.value;\n }\n\n if (result.action === TransformConfigHandlerAction.SKIP_NODE) {\n return transform;\n }\n }\n\n for (const key in config) {\n const value = config[key];\n\n if (isPlainObject(value)) {\n transform[key] = transformConfigSync(value, handler);\n } else if (Array.isArray(value)) {\n transform[key] = transformArraySync(value, handler) as Config[Extract<keyof Config, string>];\n } else {\n transform[key] = value;\n }\n }\n\n return transform;\n};\n","import { outputFileSync } from 'fs-extra/esm';\nimport Handlebars from 'handlebars';\nimport { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport shelljs from 'shelljs';\nimport { removeDisabledSlices } from '../transformers/removeDisabledSlices.ts';\nimport { setupExperiments } from '../transformers/setupExperiments.ts';\nimport type { TransformConfigHandler, WriteConfigOptions } from '../types.ts';\nimport { transformConfig } from './transformConfig.ts';\n\nexport const transformWriteConfig = async <Config extends object>(\n config: Config,\n { experimentsCallback, outputFile }: WriteConfigOptions\n) => {\n const handlers: TransformConfigHandler[] = [removeDisabledSlices];\n\n if (experimentsCallback) {\n handlers.push(setupExperiments(experimentsCallback));\n }\n\n const template = Handlebars.compile(\n readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), './templates/config.ts.hbs'), { encoding: 'utf8' })\n );\n\n const output = template({ config: JSON.stringify(await transformConfig(config, handlers), undefined, 2) }).replaceAll(\n /\"([A-Za-z][\\dA-Za-z]+)\":/g,\n '$1:'\n );\n\n shelljs.echo(`zcd watch => writing to file: ${outputFile}`);\n shelljs.echo(`zcd watch => content to write:\\n${output}\\n`);\n outputFileSync(resolve(process.cwd(), outputFile), output);\n};\n","import { resolve } from 'node:path';\nimport shelljs from 'shelljs';\nimport type { Commands } from '../cli.ts';\nimport type { SetupExperimentsCallback } from '../types.ts';\nimport { transformWriteConfig } from './transformWriteConfig.ts';\n\nexport const importValidateTransformWriteConfig = (\n inputFile: string,\n outputFile: string,\n command: Commands,\n experimentCallbackFile?: string\n) => {\n import(resolve(process.cwd(), inputFile))\n .then(\n ({\n default: configBuilder,\n }: {\n default: ReturnType<typeof import('../createConfigBuilder.ts')['createConfigBuilder']>;\n }) => {\n if (!configBuilder.validate()) {\n shelljs.echo(`zcd ${command} => invalid config`);\n shelljs.echo(`zcd ${command} => config values:\\n${configBuilder.toJson()}\\n`);\n shelljs.echo(`zcd ${command} => errors:\\n${JSON.stringify(configBuilder.errors(), undefined, 2)}\\n`);\n shelljs.exit(1);\n }\n\n shelljs.echo('zcd ${command} => valid config');\n shelljs.echo(`zcd ${command} => config values:\\n${configBuilder.toJson()}\\n`);\n\n if (experimentCallbackFile) {\n import(resolve(process.cwd(), experimentCallbackFile))\n .then(({ default: experimentsCallback }: { default: SetupExperimentsCallback }) => {\n void transformWriteConfig(configBuilder.values(), { experimentsCallback, outputFile });\n })\n .catch((error: unknown) => {\n if (error instanceof Error) {\n shelljs.echo(`zcd ${command} => error message: ${error.message}`);\n\n if (error.stack) {\n shelljs.echo(`zcd ${command} => error stack:\\n${error.stack}\\n`);\n }\n }\n });\n\n return;\n }\n\n void transformWriteConfig(configBuilder.values(), { outputFile });\n }\n )\n .catch((error: unknown) => {\n if (error instanceof Error) {\n shelljs.echo(`zcd ${command} => error message: ${error.message}`);\n\n if (error.stack) {\n shelljs.echo(`zcd ${command} => error stack:\\n${error.stack}\\n`);\n }\n }\n });\n};\n","import { watchFile } from 'node:fs';\nimport { resolve } from 'node:path';\nimport shelljs from 'shelljs';\nimport yargs from 'yargs';\nimport { importValidateTransformWriteConfig } from './utils/importValidateTransformWriteConfig.ts';\n\nexport enum Commands {\n BUILD = 'build',\n WATCH = 'watch',\n}\n\nconst generateArguments = (cmdYargs: yargs.Argv) =>\n cmdYargs\n .positional('input-file', {\n demandOption: true,\n desc: 'The relative path to the config builder root file',\n type: 'string',\n })\n .positional('output-file', {\n demandOption: true,\n desc: 'The relative path to the output config file',\n type: 'string',\n })\n .option('experiments-callback-file', {\n desc: 'The relative path to the experiment callback file',\n type: 'string',\n });\n\nexport const cli = () => {\n yargs\n .command(\n 'watch <input-file> <output-file>',\n 'Watch a config builder and write config',\n cmdYargs => generateArguments(cmdYargs),\n argv => {\n shelljs.echo(`zcd watch => watching file: ${argv['input-file']}`);\n\n watchFile(resolve(process.cwd(), argv['input-file']), () => {\n shelljs.echo('zcd watch => file change detected');\n\n importValidateTransformWriteConfig(\n argv['input-file'],\n argv['output-file'],\n Commands.WATCH,\n argv['experiments-callback-file']\n );\n });\n }\n )\n .command(\n 'build <input-file> <output-file>',\n 'Write config from a config builder',\n cmdYargs => generateArguments(cmdYargs),\n argv => {\n shelljs.echo(`zcd build => building file: ${argv['input-file']}`);\n\n importValidateTransformWriteConfig(\n argv['input-file'],\n argv['output-file'],\n Commands.BUILD,\n argv['experiments-callback-file']\n );\n }\n )\n .help().argv;\n};\n"],"names":["NonEmumeralProperties","TransformConfigHandlerAction","removeDisabledSlices","clone","config","DISABLED","action","DELETE_NODE","value","setupExperiments","callback","EXPERIMENT","__experiment","buckets","id","transformArray","list","handler","Promise","all","map","entry","isPlainObject","transformConfig","Array","isArray","transform","handlers","castArray","result","resolve","SKIP_NODE","key","transformWriteConfig","experimentsCallback","outputFile","push","template","Handlebars","compile","readFileSync","dirname","fileURLToPath","import","meta","url","encoding","output","JSON","stringify","undefined","replaceAll","shelljs","echo","outputFileSync","process","cwd","importValidateTransformWriteConfig","inputFile","command","experimentCallbackFile","then","default","configBuilder","validate","toJson","errors","exit","values","catch","error","Error","message","stack","Commands","generateArguments","cmdYargs","positional","demandOption","desc","type","option","cli","yargs","argv","watchFile","WATCH","BUILD","help"],"mappings":"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"}
|