elrh-cosca 0.3.6 → 0.4.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 +237 -99
- package/dist/chunks/magicast-B0ixXlDw.mjs +14033 -0
- package/dist/chunks/rolldown-runtime-Dqa2HsxW.mjs +20 -0
- package/dist/chunks/yaml-BwPmYYDT.mjs +4297 -0
- package/dist/elrh-cosca.mjs +328 -14157
- package/dist/types/src/_private/fetch-file.d.ts +1 -1
- package/dist/types/src/checks/get-package-manager.d.ts +2 -2
- package/dist/types/src/checks/has-json-key.d.ts +5 -3
- package/dist/types/src/checks/has-text.d.ts +6 -4
- package/dist/types/src/checks/has-yaml-key.d.ts +11 -0
- package/dist/types/src/checks/path-exists.d.ts +6 -4
- package/dist/types/src/functions/create-file-from-template.d.ts +10 -8
- package/dist/types/src/functions/create-file-from-web-template.d.ts +10 -8
- package/dist/types/src/functions/delete-path.d.ts +9 -7
- package/dist/types/src/functions/remove-from-json-file.d.ts +8 -6
- package/dist/types/src/functions/remove-from-text-file.d.ts +8 -6
- package/dist/types/src/functions/remove-from-yaml-file.d.ts +13 -0
- package/dist/types/src/functions/update-config-file.d.ts +13 -7
- package/dist/types/src/functions/update-json-file.d.ts +12 -10
- package/dist/types/src/functions/update-text-file.d.ts +11 -7
- package/dist/types/src/functions/update-yaml-file.d.ts +15 -0
- package/dist/types/src/main.d.ts +5 -1
- package/dist/types/src/terminal/prompt-user.d.ts +8 -8
- package/dist/types/src/terminal/show-error.d.ts +5 -3
- package/dist/types/src/terminal/show-message.d.ts +5 -3
- package/dist/types/src/types/data.d.ts +6 -0
- package/dist/types/src/types/functions.d.ts +99 -0
- package/dist/types/src/utils/get-env-value.d.ts +6 -4
- package/dist/types/src/utils/parse-qualified-path.d.ts +4 -2
- package/dist/types/src/utils/resolve-package-path.d.ts +7 -5
- package/dist/types/test/checks-has-yaml-key.test.d.ts +1 -0
- package/dist/types/test/functions-remove-from-yaml-file.test.d.ts +1 -0
- package/dist/types/test/functions-update-yaml-file.test.d.ts +1 -0
- package/dist/types/test/private-deep-merge-object.test.d.ts +1 -0
- package/dist/types/test/private-fetch-file.test.d.ts +1 -0
- package/dist/types/test/snapshots/created-config-file.d.ts +7 -0
- package/package.json +15 -11
- package/test/cosca-test.js +17 -17
- package/dist/types/src/types/json.d.ts +0 -6
package/README.md
CHANGED
|
@@ -1,74 +1,113 @@
|
|
|
1
1
|
# COSCA
|
|
2
2
|
|
|
3
|
-
Library of
|
|
3
|
+
Library of I/O functions helping with building CLI scripts for making changes in target projects - like adding default configuration files or new sections in `package.json`.
|
|
4
4
|
|
|
5
5
|
The first experimental "customers" are my [Nuxt Spec](https://github.com/AloisSeckar/nuxt-spec) and [Nuxt Ignis](https://github.com/AloisSeckar/nuxt-ignis) projects.
|
|
6
6
|
|
|
7
|
-
The **"COSCA"** abbreviation stands for **CO**de **SCA**ffolding which points out the library's purpose of providing methods for altering existing and adding new
|
|
7
|
+
The **"COSCA"** abbreviation stands for **CO**de **SCA**ffolding, which points out the library's purpose of providing methods for altering existing files and adding new ones from scratch using Node-based filesystem APIs.
|
|
8
8
|
|
|
9
9
|
## How to use
|
|
10
10
|
|
|
11
|
-
**NOTE:** The library is **ESM only** and it is advised to use with at least **Node
|
|
11
|
+
**NOTE:** The library is **ESM only** and it is advised to use it with at least **Node 22**.
|
|
12
12
|
|
|
13
|
-
`npm install elrh-cosca` to include
|
|
13
|
+
Run `npm install elrh-cosca` to include it in your project.
|
|
14
|
+
|
|
15
|
+
### Options objects
|
|
16
|
+
|
|
17
|
+
All functions with arguments take a single required `opts` object with one or more properties. Each options type is exported from `elrh-cosca` and defined in [src/types/functions.ts](src/types/functions.ts).
|
|
18
|
+
|
|
19
|
+
### Bundle size and lazy loading
|
|
20
|
+
|
|
21
|
+
The library has no runtime dependencies. Third-party parsers are bundled into the published package, but split into separate chunks that are only loaded on demand:
|
|
22
|
+
|
|
23
|
+
| Chunk | Approx. size | Loaded by |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `dist/elrh-cosca.mjs` (main entry) | ~19 kB | always |
|
|
26
|
+
| `dist/chunks/magicast-*.mjs` ([magicast](https://github.com/unjs/magicast) incl. `@babel/parser`) | ~590 kB | [`updateConfigFile`](#updateconfigfile) |
|
|
27
|
+
| `dist/chunks/yaml-*.mjs` ([yaml](https://github.com/eemeli/yaml)) | ~140 kB | [`updateYamlFile`](#updateyamlfile), [`removeFromYamlFile`](#removefromyamlfile), [`hasYamlKey`](#hasyamlkey) |
|
|
28
|
+
|
|
29
|
+
What this means in practice:
|
|
30
|
+
|
|
31
|
+
- **Installed package size** is (sadly) not reduced - all chunks are always downloaded with the package.
|
|
32
|
+
- **Nothing extra to install** - all chunks are part of the `elrh-cosca` package, so the parsers are always available and never clash with other versions in your dependency tree.
|
|
33
|
+
- **Running with Node** (e.g. a CLI script) - only the main entry is loaded on startup. A parser chunk is loaded via dynamic `import()` the first time a function needing it is called, and it is cached by Node afterwards.
|
|
34
|
+
- **Bundling into your own code** - the package is marked as `"sideEffects": false`, so unused functions can be tree-shaken. If you don't use any of the functions listed above, bundlers like Vite drop the related chunks entirely. Other bundlers may still emit them as separate files, but they are never loaded.
|
|
14
35
|
|
|
15
36
|
### List of file-manipulation functions
|
|
16
37
|
|
|
38
|
+
All file-manipulation options extend the following shared shape. `force` defaults to `false`. An omitted or empty `prompt` uses the function's built-in initial confirmation question.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
interface FileOperationOptions {
|
|
42
|
+
force?: boolean
|
|
43
|
+
prompt?: string
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
17
47
|
#### `createFileFromTemplate`
|
|
18
48
|
|
|
19
49
|
```ts
|
|
20
|
-
|
|
21
|
-
templateFile: string
|
|
22
|
-
|
|
50
|
+
interface CreateFileFromTemplateOptions extends FileOperationOptions {
|
|
51
|
+
templateFile: string
|
|
52
|
+
targetFile: string
|
|
53
|
+
}
|
|
54
|
+
async function createFileFromTemplate(opts: CreateFileFromTemplateOptions): Promise<void>
|
|
23
55
|
```
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
Takes the file given by `templateFile` from an installed package and creates a fresh copy of it in the target project.
|
|
26
58
|
|
|
27
|
-
Path to `templateFile` must be prefixed with the package name to allow proper resolution, e.g. `your-package:path/to/template
|
|
59
|
+
Path to `templateFile` must be prefixed with the package name to allow proper resolution, e.g. `your-package:path/to/template` (the path is relative to the package root). The package name can be scoped (e.g. `@scope/package`). The package is resolved using [`resolvePackagePath`](#resolvepackagepath).
|
|
28
60
|
|
|
29
|
-
Path to `targetFile` is relative to `process.cwd()
|
|
61
|
+
Path to `targetFile` is relative to `process.cwd()`, which allows consumers to run `npx your-script` in their project roots during development. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. If the target directory does not exist, it will be created automatically.
|
|
30
62
|
|
|
31
|
-
By default the function asks for confirmation before attempting to create the file and if
|
|
63
|
+
By default the function asks for confirmation before attempting to create the file and again if a file with the same name as `targetFile` already exists. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
32
64
|
|
|
33
65
|
#### `createFileFromWebTemplate`
|
|
34
66
|
|
|
35
67
|
```ts
|
|
36
|
-
|
|
37
|
-
url: string
|
|
38
|
-
|
|
68
|
+
interface CreateFileFromWebTemplateOptions extends FileOperationOptions {
|
|
69
|
+
url: string
|
|
70
|
+
targetFile: string
|
|
71
|
+
}
|
|
72
|
+
async function createFileFromWebTemplate(opts: CreateFileFromWebTemplateOptions): Promise<void>
|
|
39
73
|
```
|
|
40
74
|
|
|
41
|
-
|
|
75
|
+
Downloads the file given by `url` and creates a fresh copy of it in the target project.
|
|
42
76
|
|
|
43
|
-
Contents of `url` must be accessible via `node:https.get` function and will be fetched as raw text data.
|
|
77
|
+
Contents of `url` must be accessible via the `node:https.get` function and will be fetched as raw text data. Redirects (5) are followed before the fetch fails.
|
|
44
78
|
|
|
45
|
-
Path to `targetFile` is relative to `process.cwd()
|
|
79
|
+
Path to `targetFile` is relative to `process.cwd()`, which allows consumers to run `npx your-script` in their project roots during development. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. If the target directory does not exist, it will be created automatically.
|
|
46
80
|
|
|
47
|
-
By default the function asks for confirmation before attempting to create the file and if
|
|
81
|
+
By default the function asks for confirmation before attempting to create the file and again if a file with the same name as `targetFile` already exists. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
48
82
|
|
|
49
83
|
#### `updateConfigFile`
|
|
50
84
|
|
|
51
85
|
```ts
|
|
52
|
-
|
|
53
|
-
targetFile: string
|
|
54
|
-
|
|
55
|
-
|
|
86
|
+
interface UpdateConfigFileOptions extends FileOperationOptions {
|
|
87
|
+
targetFile: string
|
|
88
|
+
newConfig: Record<string | number | symbol, any>
|
|
89
|
+
createMissing?: boolean
|
|
90
|
+
}
|
|
91
|
+
async function updateConfigFile(opts: UpdateConfigFileOptions): Promise<void>
|
|
56
92
|
```
|
|
57
93
|
|
|
58
94
|
Takes a path to a configuration file and updates it with the provided `newConfig` object.
|
|
59
95
|
|
|
60
|
-
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths being passed in. Path traversal outside of CWD
|
|
96
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file currently must use ESM format with either a `default` export or **exactly one** named export. The exported value must be a configuration object or a function call with a configuration object as its first argument (e.g. `defineConfig({...})`). CommonJS `module.exports` is not supported.
|
|
97
|
+
|
|
98
|
+
If `targetFile` does not exist, the function throws an error by default. Setting `opts.createMissing` to `true` will instead create the file (including missing directories) as `export default {}` with `newConfig` merged in. The user is asked to confirm the creation unless `opts.force` is `true`.
|
|
61
99
|
|
|
62
|
-
The
|
|
100
|
+
The merge is performed using [unjs/magicast](https://github.com/unjs/magicast). It should:
|
|
63
101
|
|
|
64
102
|
- preserve comments
|
|
65
103
|
- work recursively to allow deep-merge
|
|
66
|
-
- extend existing object with new keys from `newConfig`
|
|
67
|
-
- overwrite keys with same name with values from `newConfig`
|
|
68
|
-
- create a unique
|
|
104
|
+
- extend the existing object with new keys from `newConfig`
|
|
105
|
+
- overwrite keys with the same name with values from `newConfig`
|
|
106
|
+
- create a unique union in case of arrays
|
|
107
|
+
|
|
69
108
|
Please [report](https://github.com/AloisSeckar/elrh-cosca/issues) any logical flaws and issues of the process.
|
|
70
109
|
|
|
71
|
-
**Warning**: The function will fail
|
|
110
|
+
**Warning**: The function will fail if the extracted object is proxied (e.g. when created using `defu`). In such case, the error would be:
|
|
72
111
|
|
|
73
112
|
```text
|
|
74
113
|
TypeError: 'set' on proxy: trap returned falsish for property '<YOUR_PROPERTY>'
|
|
@@ -76,127 +115,207 @@ TypeError: 'set' on proxy: trap returned falsish for property '<YOUR_PROPERTY>'
|
|
|
76
115
|
|
|
77
116
|
If possible, you need to alter your logic, e.g. by creating a new object via the spread operator.
|
|
78
117
|
|
|
79
|
-
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting
|
|
118
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
80
119
|
|
|
81
120
|
#### `updateJsonFile`
|
|
82
121
|
|
|
83
122
|
```ts
|
|
84
|
-
|
|
85
|
-
targetFile: string
|
|
86
|
-
|
|
87
|
-
|
|
123
|
+
interface UpdateJsonFileOptions extends FileOperationOptions {
|
|
124
|
+
targetFile: string
|
|
125
|
+
jsonKey: string
|
|
126
|
+
patch: DataValue
|
|
127
|
+
createMissing?: boolean
|
|
128
|
+
}
|
|
129
|
+
async function updateJsonFile(opts: UpdateJsonFileOptions): Promise<void>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Takes a path to a JSON file and injects `patch` under the `jsonKey` key. The `patch` is of type `DataValue` - a custom type defined as follows:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
type DataPrimitive = string | number | boolean | null
|
|
136
|
+
type DataObject = { [key: string]: DataValue }
|
|
137
|
+
type DataArray = DataValue[]
|
|
138
|
+
type DataValue = DataPrimitive | DataObject | DataArray
|
|
88
139
|
```
|
|
89
140
|
|
|
90
|
-
|
|
141
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must be a valid JSON file. It is parsed using plain `JSON.parse`.
|
|
142
|
+
|
|
143
|
+
The given `jsonKey` might point to a nested key using dot notation, e.g. `a.b.c`. Missing intermediate levels are created as empty objects, and existing intermediate levels that are not objects (primitives, arrays and `null`) are replaced with objects. Empty key segments (e.g. `a..b`) and the segments `__proto__`, `constructor` and `prototype` are rejected with an error. If `jsonKey` does not exist, a new key is added. If `patch` is an object, its keys are shallow-merged into the existing value. If the existing value is not an object, it is replaced. Other values (primitives, arrays and `null`) replace the existing value. The function tracks if any real change was made and notifies the user if not.
|
|
144
|
+
|
|
145
|
+
If `targetFile` does not exist, the function throws an error by default. Setting `opts.createMissing` to `true` will instead create the file (including missing directories) as an empty JSON object with `patch` applied. The user is asked to confirm the creation unless `opts.force` is `true`.
|
|
146
|
+
|
|
147
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
148
|
+
|
|
149
|
+
#### `updateYamlFile`
|
|
91
150
|
|
|
92
151
|
```ts
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
152
|
+
interface UpdateYamlFileOptions extends FileOperationOptions {
|
|
153
|
+
targetFile: string
|
|
154
|
+
yamlKey: string
|
|
155
|
+
patch: DataValue
|
|
156
|
+
createMissing?: boolean
|
|
157
|
+
}
|
|
158
|
+
async function updateYamlFile(opts: UpdateYamlFileOptions): Promise<void>
|
|
97
159
|
```
|
|
98
160
|
|
|
99
|
-
|
|
161
|
+
Takes a path to a YAML file and injects `patch` under the `yamlKey` key. The `patch` is of type `DataValue` (see [`updateJsonFile`](#updatejsonfile)).
|
|
162
|
+
|
|
163
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must be a valid single-document YAML file with a map at its root (an empty file is accepted). It is parsed using [eemeli/yaml](https://github.com/eemeli/yaml) package.
|
|
164
|
+
|
|
165
|
+
The given `yamlKey` might point to a nested key using dot notation, e.g. `a.b.c`. Missing intermediate levels are created as empty maps, and existing intermediate levels that are not maps (scalars and sequences) are replaced with maps. Empty key segments (e.g. `a..b`) and the segments `__proto__`, `constructor` and `prototype` are rejected with an error. If `yamlKey` does not exist, a new key is added. If `patch` is an object, its keys are shallow-merged into the existing value. If the existing value is not a map, it is replaced. Other values (primitives, arrays and `null`) replace the existing value. The function tracks if any real change was made (values are compared deeply) and notifies the user if not.
|
|
100
166
|
|
|
101
|
-
|
|
167
|
+
If `targetFile` does not exist, the function throws an error by default. Setting `opts.createMissing` to `true` will instead create the file (including missing directories) as an empty YAML map with `patch` applied. The user is asked to confirm the creation unless `opts.force` is `true`.
|
|
102
168
|
|
|
103
|
-
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting
|
|
169
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
104
170
|
|
|
105
171
|
#### `updateTextFile`
|
|
106
172
|
|
|
107
173
|
```ts
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
174
|
+
interface UpdateTextFileOptions extends FileOperationOptions {
|
|
175
|
+
targetFile: string
|
|
176
|
+
rowsToAdd: string[]
|
|
177
|
+
allowDuplicates?: boolean
|
|
178
|
+
createMissing?: boolean
|
|
179
|
+
}
|
|
180
|
+
async function updateTextFile(opts: UpdateTextFileOptions): Promise<void>
|
|
111
181
|
```
|
|
112
182
|
|
|
113
|
-
Takes a path to a plain text file and
|
|
183
|
+
Takes a path to a plain text file and appends `rowsToAdd` at the end of the file, **provided they are not already present in the file** (as an exact line match). Setting `opts.allowDuplicates` to `true` disables this check and all `rowsToAdd` are appended regardless of the current file contents. The function tracks if any real change was made and notifies the user if not.
|
|
114
184
|
|
|
115
|
-
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths being passed in. Path traversal outside of CWD
|
|
185
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed.
|
|
116
186
|
|
|
117
|
-
|
|
187
|
+
If `targetFile` does not exist, the function throws an error by default. Setting `opts.createMissing` to `true` will instead create the file (including missing directories) containing `rowsToAdd`. The user is asked to confirm the creation unless `opts.force` is `true`.
|
|
188
|
+
|
|
189
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
118
190
|
|
|
119
191
|
#### `removeFromJsonFile`
|
|
120
192
|
|
|
121
193
|
```ts
|
|
122
|
-
|
|
123
|
-
targetFile: string
|
|
124
|
-
|
|
194
|
+
interface RemoveFromJsonFileOptions extends FileOperationOptions {
|
|
195
|
+
targetFile: string
|
|
196
|
+
jsonKey: string
|
|
197
|
+
}
|
|
198
|
+
async function removeFromJsonFile(opts: RemoveFromJsonFileOptions): Promise<void>
|
|
125
199
|
```
|
|
126
200
|
|
|
127
201
|
Takes a path to a JSON file and removes the specified `jsonKey`.
|
|
128
202
|
|
|
129
|
-
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths being passed in. Path traversal outside of CWD
|
|
203
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist and be a valid JSON file. It is parsed using plain `JSON.parse`.
|
|
130
204
|
|
|
131
|
-
|
|
205
|
+
The given `jsonKey` might point to a nested key using dot notation, e.g. `a.b.c`. If the key is not present, the file is left untouched and the user is notified.
|
|
132
206
|
|
|
133
|
-
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting
|
|
207
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
134
208
|
|
|
135
209
|
#### `removeFromTextFile`
|
|
136
210
|
|
|
137
211
|
```ts
|
|
138
|
-
|
|
139
|
-
targetFile: string
|
|
140
|
-
|
|
212
|
+
interface RemoveFromTextFileOptions extends FileOperationOptions {
|
|
213
|
+
targetFile: string
|
|
214
|
+
searchText: string
|
|
215
|
+
}
|
|
216
|
+
async function removeFromTextFile(opts: RemoveFromTextFileOptions): Promise<void>
|
|
141
217
|
```
|
|
142
218
|
|
|
143
219
|
Takes a path to a plain text file and removes all lines that include the given `searchText`. The matching is done using `String.includes()`, so partial matches within a line will cause that line to be removed. The function tracks if any real change was made and notifies the user if not.
|
|
144
220
|
|
|
145
|
-
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths being passed in. Path traversal outside of CWD
|
|
221
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist.
|
|
222
|
+
|
|
223
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
224
|
+
|
|
225
|
+
#### `removeFromYamlFile`
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
interface RemoveFromYamlFileOptions extends FileOperationOptions {
|
|
229
|
+
targetFile: string
|
|
230
|
+
yamlKey: string
|
|
231
|
+
}
|
|
232
|
+
async function removeFromYamlFile(opts: RemoveFromYamlFileOptions): Promise<void>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Takes a path to a YAML file and removes the specified `yamlKey`.
|
|
146
236
|
|
|
147
|
-
|
|
237
|
+
Path to `targetFile` is relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist and be a valid single-document YAML file. It is parsed using [eemeli/yaml](https://github.com/eemeli/yaml) package, which preserves comments and formatting of the untouched parts of the file.
|
|
238
|
+
|
|
239
|
+
The given `yamlKey` might point to a nested key using dot notation, e.g. `a.b.c`. If the key is not present (including when the file is empty or its root is not a map), the file is left untouched and the user is notified.
|
|
240
|
+
|
|
241
|
+
Comments attached to the removed key are removed as well, with two exceptions. A comment directly above the first root-level key is treated as a file header and kept at the top of the file. A comment left above a nested map that became empty is dropped.
|
|
242
|
+
|
|
243
|
+
By default the function asks for confirmation before attempting to alter the `targetFile`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
148
244
|
|
|
149
245
|
#### `deletePath`
|
|
150
246
|
|
|
151
247
|
```ts
|
|
152
|
-
|
|
153
|
-
targetPath: string
|
|
154
|
-
|
|
248
|
+
interface DeletePathOptions extends FileOperationOptions {
|
|
249
|
+
targetPath: string
|
|
250
|
+
}
|
|
251
|
+
async function deletePath(opts: DeletePathOptions): Promise<void>
|
|
155
252
|
```
|
|
156
253
|
|
|
157
|
-
Deletes given `targetPath` from FS. Path is resolved
|
|
254
|
+
Deletes the given `targetPath` (a file or a directory, recursively) from FS. Path is resolved relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed.
|
|
158
255
|
|
|
159
|
-
If the `targetPath` does not exist, the
|
|
256
|
+
If the `targetPath` does not exist, nothing is deleted and the user is notified.
|
|
160
257
|
|
|
161
|
-
By default the function asks for confirmation before attempting to delete the `targetPath`. Setting
|
|
258
|
+
By default the function asks for confirmation before attempting to delete the `targetPath`. Setting `opts.force` to `true` will suppress manual confirmation prompts. Passing `opts.prompt` allows tailoring your own initial question to the user.
|
|
162
259
|
|
|
163
260
|
### List of content checkers
|
|
164
261
|
|
|
165
262
|
#### `pathExists`
|
|
166
263
|
|
|
167
264
|
```ts
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
265
|
+
interface PathExistsOptions {
|
|
266
|
+
targetPath: string
|
|
267
|
+
}
|
|
268
|
+
function pathExists(opts: PathExistsOptions): boolean
|
|
171
269
|
```
|
|
172
270
|
|
|
173
|
-
Checks if the specified `targetPath` exists on FS. Path is resolved
|
|
271
|
+
Checks if the specified `targetPath` (a file or a directory) exists on FS. Path is resolved relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed.
|
|
174
272
|
|
|
175
|
-
If the path exists
|
|
273
|
+
If the path exists, the function returns `true`, `false` otherwise.
|
|
176
274
|
|
|
177
275
|
#### `hasJsonKey`
|
|
178
276
|
|
|
179
277
|
```ts
|
|
180
|
-
|
|
181
|
-
targetFile: string
|
|
182
|
-
|
|
278
|
+
interface HasJsonKeyOptions {
|
|
279
|
+
targetFile: string
|
|
280
|
+
jsonKey: string
|
|
281
|
+
}
|
|
282
|
+
function hasJsonKey(opts: HasJsonKeyOptions): boolean
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Checks whether the given `jsonKey` exists in the JSON file located at `targetFile`. Path is resolved relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist and be a valid JSON file.
|
|
286
|
+
|
|
287
|
+
The given `jsonKey` might point to a nested key using dot notation, e.g. `a.b.c`. If the key is present, the function returns `true`, `false` otherwise.
|
|
288
|
+
|
|
289
|
+
#### `hasYamlKey`
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
interface HasYamlKeyOptions {
|
|
293
|
+
targetFile: string
|
|
294
|
+
yamlKey: string
|
|
295
|
+
}
|
|
296
|
+
async function hasYamlKey(opts: HasYamlKeyOptions): Promise<boolean>
|
|
183
297
|
```
|
|
184
298
|
|
|
185
|
-
Checks whether given `
|
|
299
|
+
Checks whether the given `yamlKey` exists in the YAML file located at `targetFile`. Path is resolved relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist and be a valid single-document YAML file. It is parsed using [eemeli/yaml](https://github.com/eemeli/yaml) package.
|
|
300
|
+
|
|
301
|
+
The given `yamlKey` might point to a nested key using dot notation, e.g. `a.b.c`. If the key is present, the function resolves to `true`, `false` otherwise (including when the file is empty or its root is not a map).
|
|
186
302
|
|
|
187
|
-
|
|
303
|
+
Unlike other checks, this function runs asynchronously, because the YAML parser is loaded lazily on demand.
|
|
188
304
|
|
|
189
305
|
#### `hasText`
|
|
190
306
|
|
|
191
307
|
```ts
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
308
|
+
interface HasTextOptions {
|
|
309
|
+
targetFile: string
|
|
310
|
+
pattern: string | RegExp
|
|
311
|
+
exact?: boolean
|
|
312
|
+
}
|
|
313
|
+
function hasText(opts: HasTextOptions): boolean
|
|
195
314
|
```
|
|
196
315
|
|
|
197
|
-
Checks whether given `pattern` exists in text file located at `targetFile`. Path is resolved
|
|
316
|
+
Checks whether the given `pattern` exists in the text file located at `targetFile`. Path is resolved relative to `process.cwd()`. Several checks are in place to prevent accidental and malicious paths from being passed in. Path traversal outside of CWD and absolute paths are disallowed. The file must exist.
|
|
198
317
|
|
|
199
|
-
The `pattern` might be a plain string or a regular expression. If
|
|
318
|
+
The `pattern` might be a plain string or a regular expression. The file is checked line by line. If the pattern is found, the function returns `true`, `false` otherwise. By default partial matches are allowed for string patterns (`exact` defaults to `false`). If you set the optional `exact` property to `true`, the string must completely match at least one line in the file. Surrounding whitespace is trimmed from the lines and from string patterns in both cases. The `exact` property is ignored for regular expressions.
|
|
200
319
|
|
|
201
320
|
#### `getPackageManager`
|
|
202
321
|
|
|
@@ -204,72 +323,91 @@ The `pattern` might be a plain string or a regular expression. If it is present,
|
|
|
204
323
|
function getPackageManager(): 'npm' | 'yarn' | 'pnpm' | 'deno' | 'bun'
|
|
205
324
|
```
|
|
206
325
|
|
|
207
|
-
Tries to detect the package manager used in the current environment by checking for
|
|
326
|
+
Tries to detect the package manager (or runtime) used in the current environment by checking for `Deno` and `Bun` global variables and the `npm_config_user_agent` environment variable. Falls back to `npm` if the checks fail to detect anything else.
|
|
208
327
|
|
|
209
328
|
### List of terminal helpers
|
|
210
329
|
|
|
211
330
|
#### `promptUser`
|
|
212
331
|
|
|
213
332
|
```ts
|
|
214
|
-
|
|
215
|
-
question: string
|
|
216
|
-
|
|
217
|
-
|
|
333
|
+
interface PromptUserOptions {
|
|
334
|
+
question: string
|
|
335
|
+
input?: NodeJS.ReadableStream
|
|
336
|
+
output?: NodeJS.WritableStream
|
|
337
|
+
}
|
|
338
|
+
async function promptUser(opts: PromptUserOptions): Promise<boolean>
|
|
218
339
|
```
|
|
219
340
|
|
|
220
|
-
Prints out a `question` to the console and waits for the input. Returns `true` when `y`
|
|
341
|
+
Prints out a `question` (with ` (y/N): ` appended) to the console and waits for the input. Returns `true` when the user answers `y` or `yes` (case-insensitive) and `false` otherwise.
|
|
221
342
|
|
|
222
|
-
By default it uses `process.stdin` and `process.stdout` streams. To use custom NodeJS streams,
|
|
343
|
+
By default it uses `process.stdin` and `process.stdout` streams. To use custom NodeJS streams, pass `input` and `output` directly in `opts`, e.g. `promptUser({ question: 'Continue?', input, output })`.
|
|
223
344
|
|
|
224
345
|
#### `showMessage`
|
|
225
346
|
|
|
226
347
|
```ts
|
|
227
|
-
|
|
348
|
+
interface ShowMessageOptions {
|
|
349
|
+
message: string
|
|
350
|
+
linesAfter?: number
|
|
351
|
+
}
|
|
352
|
+
function showMessage(opts: ShowMessageOptions): void
|
|
228
353
|
```
|
|
229
354
|
|
|
230
|
-
Prints out a `message` to `process.stdout` and adds
|
|
355
|
+
Prints out a `message` to `process.stdout` and adds `linesAfter` newlines after it (default is 1). Set `linesAfter: 0` to omit newlines. This function is synchronous.
|
|
231
356
|
|
|
232
357
|
#### `showError`
|
|
233
358
|
|
|
234
359
|
```ts
|
|
235
|
-
|
|
360
|
+
interface ShowErrorOptions {
|
|
361
|
+
message: string
|
|
362
|
+
linesAfter?: number
|
|
363
|
+
}
|
|
364
|
+
function showError(opts: ShowErrorOptions): void
|
|
236
365
|
```
|
|
237
366
|
|
|
238
|
-
Prints out a `message` to `process.stderr` and adds
|
|
367
|
+
Prints out a `message` to `process.stderr` and adds `linesAfter` newlines after it (default is 1). Set `linesAfter: 0` to omit newlines. This function is synchronous.
|
|
239
368
|
|
|
240
369
|
### List of other utils
|
|
241
370
|
|
|
242
371
|
#### `getEnvValue`
|
|
243
372
|
|
|
244
373
|
```ts
|
|
245
|
-
|
|
246
|
-
key: string
|
|
247
|
-
|
|
374
|
+
interface GetEnvValueOptions {
|
|
375
|
+
key: string
|
|
376
|
+
envFilePath?: string
|
|
377
|
+
}
|
|
378
|
+
function getEnvValue(opts: GetEnvValueOptions): string | undefined
|
|
248
379
|
```
|
|
249
380
|
|
|
250
|
-
Reads a `.env` file and returns the value of the specified key or `undefined` if key not found. By default it reads from `.env` in the current working directory (usually the root of the project). You can specify a custom path to `.env` file
|
|
381
|
+
Reads a `.env` file and returns the value of the specified `key` (without surrounding quotes) or `undefined` if the file or the key is not found. By default it reads from `.env` in the current working directory at call time (usually the root of the project). You can specify a custom path to the `.env` file with the `envFilePath` property.
|
|
251
382
|
|
|
252
383
|
#### `parseQualifiedPath`
|
|
253
384
|
|
|
254
385
|
```ts
|
|
255
|
-
|
|
386
|
+
interface ParseQualifiedPathOptions {
|
|
387
|
+
path: string
|
|
388
|
+
}
|
|
389
|
+
function parseQualifiedPath(opts: ParseQualifiedPathOptions): { pkg: string; file: string }
|
|
256
390
|
```
|
|
257
391
|
|
|
258
|
-
Expects path to file in `"package:relative/path/to/file"` format and splits it into `{ pkg, file }`. The package name can be scoped (e.g. `@scope/package`).
|
|
392
|
+
Expects a path to a file in `"package:relative/path/to/file"` format and splits it into `{ pkg, file }`. The package name can be scoped (e.g. `@scope/package`). Throws an error if the input format is invalid.
|
|
259
393
|
|
|
260
394
|
#### `resolvePackagePath`
|
|
261
395
|
|
|
262
396
|
```ts
|
|
263
|
-
|
|
397
|
+
interface ResolvePackagePathOptions {
|
|
398
|
+
packageName: string
|
|
399
|
+
}
|
|
400
|
+
function resolvePackagePath(opts: ResolvePackagePathOptions): string
|
|
264
401
|
```
|
|
265
402
|
|
|
266
|
-
|
|
403
|
+
Resolves a package's root directory *from the target app* (CWD). Returns CWD itself if its `package.json` has the same name (i.e. the package is being developed), otherwise looks for the package inside *node_modules* in CWD. The package name can be scoped (e.g. `@scope/package`). Throws an error if the package cannot be found.
|
|
267
404
|
|
|
268
405
|
## Tech stack
|
|
269
406
|
|
|
270
407
|
- Developed with [TypeScript](https://www.typescriptlang.org/) in mind
|
|
271
408
|
- Using [magicast](https://github.com/unjs/magicast) for parsing files
|
|
272
|
-
-
|
|
409
|
+
- Using [yaml](https://github.com/eemeli/yaml) for parsing YAML files
|
|
410
|
+
- Built with [Vite](https://vitejs.dev/)
|
|
273
411
|
- Tested with [Vitest](https://vitest.dev/)
|
|
274
412
|
|
|
275
413
|
See [Changelog](https://github.com/AloisSeckar/elrh-cosca/blob/main/CHANGELOG.md) for project history and development.
|