UNPKG

8.84 kBMarkdownView Raw
1# Conartist
2
3Hate managing all your different config files across all your different
4repositories? Great. Step into my office.
5
6> Conartist is a tool that gives you a way to manage all of your config files
7> from a single source of truth. Not only will it scaffold them out, it can also
8> keep them in sync even if you modify them manually.
9
10Major use cases:
11
12- Keeping separate repos in sync.
13- Keeping monorepo packages in sync.
14- Scaffolding out new projects.
15
16## Install
17
18```sh
19npm i -D conartist
20```
21
22Conartist can be configured by any one of the following:
23
24- `conartist` field in the `package.json`
25- `.conartistrc`
26- `.conartistrc.json`
27- `.conartistrc.yaml`
28- `.conartistrc.yml`
29- `.conartistrc.js`
30- `.conartist.config.js`
31
32If you use a `.js` file, you will be able to have finer-grained control if you
33require it. See the secion on [custom file handlers](#custom-file-handlers) for
34more information.
35
36## Usage
37
38If you put the following in a `package.json`.
39
40```json
41{
42 "conartist": {
43 ".gitignore": ["*.log", "node_modules"],
44 ".nvmrc": "10.9.0",
45 ".travis.yml": "language: node_js"
46 }
47}
48```
49
50Running `$ conartist` will create the specified files relative to the `cwd`.
51This is great for scaffolding out a project or keeping it in sync with what the
52configuration has in it.
53
54## Built-in file handlers
55
56The following are the built-in - and exported - file handlers.
57
58- [`handeArray`](#async-handlearrayfile-data)
59- [`handleJs`](#async-handlejsfile-data)
60- [`handleJson`](#async-handlejsonfile-data)
61- [`handleString`](#async-handlestringfile-data)
62
63These handlers will handle the following file patterns:
64
65```js
66const mapGlob = {
67 ".nvmrc": handleString,
68 ".*rc": handleJson,
69 ".*ignore": handleArray,
70 "*.js": handleJs,
71 "*.json": handleJson
72};
73```
74
75And attempt to handle the following value types:
76
77```js
78const mapType = {
79 object: handleJson
80};
81```
82
83If a handler cannot be found, it defaults to using `handleString`.
84
85## Custom file handlers
86
87Custom file handlers require a `.js` file in order to be applied. There are two
88types of handlers you can use:
89
90- Global
91- File-specific
92
93### Global file handlers
94
95Global file handlers handle all files in your configuration file. When you set a
96global handler, it overwrites the existing handler, so you must call
97[`getHandler`](#gethandler) and callback to it if you want to retain its
98functionality around your new one.
99
100You set a new handler by calling [`setHandler`](#sethandler-handler). You are
101responsible for returing the string that will be output to the file and for
102handling all types of files that you might set in your configuration.
103
104```js
105// conartist.config.js
106
107const { getHandler, setHandler } = require("conartist");
108
109const previousHandler = getHandler();
110
111setHandler(function myCustomHandler(file, data) {
112 if (file === ".gitignore") {
113 return data.join("\n");
114 }
115 return previousHandler(file, data);
116});
117
118module.exports = {
119 ".gitignore": ["*.log", "node_modules"]
120};
121```
122
123### File-specific handlers
124
125File-specific handlers are applied as a function to the corresponding file
126instead of other data types. The global file handler will then call this and
127write its return value to the file as a priority over any other handler.
128
129_If you overwrite the default global file handler, file-specific handlers will
130not work because their behaviour is provided by the default global file handler.
131If you need to override the global file handler and still retain this behaviour,
132it's up to you to implement it or call back to the default global file handler._
133
134```js
135// conartist.config.js
136
137module.exports = {
138 ".gitignore": (file, data) => data.join("\n")
139};
140```
141
142## Use cases
143
144File-specific handlers are a good way to compose in your own behaviour.
145
146### Preventing merging
147
148You may _not_ want to merge JSON data from the config with what's already there,
149by default. To override this, all you need to do is provide a function that
150returns JSON:
151
152```js
153// conartist.config.js
154
155module.exports = {
156 "somefile.json": () => ({
157 my: "data"
158 })
159};
160```
161
162In the above example, the config file would always overwrite any existing
163`somefile.json`. If you would rather the existing file overwrite the config, you
164can do something like:
165
166```js
167const { loadJson } = require("conartist");
168
169module.exports = {
170 "somefile.json": file =>
171 loadJson(file) || {
172 my: "data"
173 }
174};
175```
176
177### Custom merging
178
179If you would prefer to implement custom merging, you might do something like:
180
181```js
182const { loadJson } = require("conartist");
183
184module.exports = {
185 "somefile.json": file => ({
186 // Putting your data here means that it can
187 // be overridden by existing data.
188 my: "data",
189
190 // loadJson returns null if no file is found.
191 ...(loadJson(file) || {})
192 })
193};
194```
195
196Another use case is composing files together into another file. This can be
197useful if you have custom files that you want to compose together into a file.
198
199The following example will take data read from a `package.json` and generate a
200`README.md` from it:
201
202```js
203const { loadJson } = require("conartist");
204const outdent = require("outdent");
205
206module.exports = {
207 "README.md": async file => {
208 const pkg = await loadJson("package.json");
209 return outdent`
210 # ${pkg.name}
211
212 > ${pkg.description}
213 `;
214 }
215};
216```
217
218_As shown above, you can also use `async` functions for file handlers!_
219
220## API
221
222All exported API points are documented below.
223
224### Global file handling
225
226APIs for handling all files.
227
228#### `getHandler()`
229
230Returns the current file handler.
231
232```js
233const { getHandler } = require("conartist");
234
235// function defaultHandler() { ... }
236getHandler();
237```
238
239#### `setHandler(handler)`
240
241Sets a new file handler, overwriting any current handler. If you require
242existing handler functionality, make sure you call `getHandler()` and callback
243to it.
244
245```js
246const { getHandler, setHandler } = require("conartist");
247
248function customHandler() {}
249setHandler(customHandler);
250
251// true
252getHandler() === customHandler;
253```
254
255### File-specific handling
256
257APIs for handling specific files.
258
259#### `async handleArray(file, data)`
260
261Handles an array.
262
263```js
264const { handleArray } = require("conartist");
265
266// my\narray
267await handleArray("somefile", ["my", "array"]);
268```
269
270#### `async handleJs(file, data)`
271
272Handles JS code depending on the value type and applies it as `module.exports`.
273
274- `typeof` `string` - it is formatted and exported.
275- `typeof` `object` - it is stringified, formatted and exported.
276
277```js
278const { handleJs } = require("conartist");
279
280// module.exports = { some: "data" };
281await handleJs("somefile", { some: "data" });
282```
283
284#### `async handleJson(file, data)`
285
286Handles JSON. It can be a `string` or anyting that `JSON.parse()` handles.
287
288```js
289const { handleJson } = require("conartist");
290
291// { some: "data" };
292await handleJson("somefile", { some: "data" });
293```
294
295#### `async handleString(file, data)`
296
297Handles a `string` by ensuring that whatever is passed in is converted to a
298`string`.
299
300```js
301const { handleString } = require("conartist");
302
303// [object Object]
304await handleString("somefile", { some: "data" });
305```
306
307### Formatting
308
309APIs for formatting data types.
310
311### `formatCode`
312
313Formats JavaScript code using Prettier and the `babel` parser.
314
315```js
316const { formatCode } = require("conartist");
317
318// { some: "data" }
319formatCode('{"some":"data"}');
320```
321
322### `formatJson`
323
324Formats JSON using `JSON.stringify(json, null, 2)`.
325
326```js
327const { formatJson } = require("conartist");
328
329// {
330// "some": "data"
331// }
332formatJson('{"some":"data"}');
333```
334
335### Processing
336
337#### `async process(config)`
338
339Syncs the configuration with what's on the file system using the file handlers.
340
341```js
342const { process } = require("conartist");
343
344await process({
345 "package.json": { name: "my-project" }
346});
347```
348
349### Utils
350
351General utils for loading configs and reading files.
352
353#### `async getConfig`
354
355Returns the current configuration from the configuration file.
356
357```js
358const { getConfig } = require("conartist");
359
360// { ... }
361await getConfig();
362```
363
364#### `filePath(file)`
365
366Returns the full path to the file relative to the `cwd`.
367
368```js
369const { filePath } = require("conartist");
370
371// "/path/to/cwd/package.json"
372filePath("package.json");
373```
374
375#### `async loadFile(file)`
376
377Loads a file using `require` relative to the `cwd`. If it does not exist, it
378returns `null`.
379
380```js
381const { loadFile } = require("conartist");
382
383// { ... }
384loadFile("package.json");
385```
386
387#### `async readFile(file)`
388
389Reads a file into a `string` relative to the `cwd`. If it does not exist, it
390returns `null`.
391
392```js
393const { readFile } = require("conartist");
394
395// { ... }
396readFile("package.json");
397```
398
399#### `async readJson(file)`
400
401Reads a file into JSON relative to the `cwd`. If it does not exist, it returns
402`null`.
403
404```js
405const { readJson } = require("conartist");
406
407// { ... }
408readJson("package.json");
409```