platformdirs 4.3.6 → 4.3.8-rc1
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 +80 -6
- package/package.json +15 -17
- package/src/android.js +203 -0
- package/src/api.js +444 -0
- package/src/configparser.js +73 -0
- package/src/index.js +656 -0
- package/src/macos.js +199 -0
- package/src/main.js +50 -0
- package/src/unix.js +350 -0
- package/src/version.js +16 -0
- package/src/windows.js +279 -0
package/src/api.js
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base API.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Abstract base class for platform directories.
|
|
11
|
+
* @abstract
|
|
12
|
+
*/
|
|
13
|
+
export class PlatformDirsABC {
|
|
14
|
+
/**
|
|
15
|
+
* The name of the application.
|
|
16
|
+
* @type {string|undefined}
|
|
17
|
+
*/
|
|
18
|
+
appname;
|
|
19
|
+
/**
|
|
20
|
+
* The name of the app author or distributing body for this application.
|
|
21
|
+
*
|
|
22
|
+
* Typically it is the owning company name. Defaults to `appname`. You may pass `false` to disable it.
|
|
23
|
+
* @type {string|false|undefined}
|
|
24
|
+
*/
|
|
25
|
+
appauthor;
|
|
26
|
+
/**
|
|
27
|
+
* An optional version path element to append to the path.
|
|
28
|
+
*
|
|
29
|
+
* You might want to use this if you want multiple versions of your app to be able to run independently. If used, this would typically be `<major>.<minor>`.
|
|
30
|
+
* @type {string|undefined}
|
|
31
|
+
*/
|
|
32
|
+
version;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to use the roaming appdata directory on Windows.
|
|
35
|
+
*
|
|
36
|
+
* That means that for users on a Windows network setup for roaming profiles, this user data will be synced on login (see [here](https://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx)).
|
|
37
|
+
* @type {boolean}
|
|
38
|
+
*/
|
|
39
|
+
roaming;
|
|
40
|
+
/**
|
|
41
|
+
* An optional parameter which indicates that the entire list of data dirs should be returned.
|
|
42
|
+
*
|
|
43
|
+
* By default, the first item would only be returned.
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
*/
|
|
46
|
+
multipath;
|
|
47
|
+
/**
|
|
48
|
+
* A flag to indicating to use opinionated values.
|
|
49
|
+
* @type {boolean}
|
|
50
|
+
*/
|
|
51
|
+
opinion;
|
|
52
|
+
/**
|
|
53
|
+
* Optionally create the directory (and any missing parents) upon access if
|
|
54
|
+
* it does not exist.
|
|
55
|
+
*
|
|
56
|
+
* By default, no directories are created.
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ Since the getters (`dirs.userDataDir`, etc.) are synchronous, the
|
|
59
|
+
* directory creation will use `fs.mkdirSync()` which **is a blocking
|
|
60
|
+
* synchronous operation** to ensure that the returned path does indeed
|
|
61
|
+
* exist before returning the path to the caller. This is for convenience.
|
|
62
|
+
* If you require non-blocking async operation you should set this to
|
|
63
|
+
* `false` and use `fs.mkdir()` or `fsPromises.mkdir()` yourself.
|
|
64
|
+
* @type {boolean}
|
|
65
|
+
*/
|
|
66
|
+
ensureExists;
|
|
67
|
+
/**
|
|
68
|
+
* Create a new platform directory.
|
|
69
|
+
* @param {string} [appname] - See `appname`
|
|
70
|
+
* @param {string|false} [appauthor] - See `appauthor`
|
|
71
|
+
* @param {string} [version] - See `version`
|
|
72
|
+
* @param {boolean} [roaming=false] - See `roaming`
|
|
73
|
+
* @param {boolean} [multipath=false] - See `multipath`
|
|
74
|
+
* @param {boolean} [opinion=true] - See `opinion`
|
|
75
|
+
* @param {boolean} [ensureExists=false] - See `ensureExists`
|
|
76
|
+
*/
|
|
77
|
+
constructor(
|
|
78
|
+
appname,
|
|
79
|
+
appauthor,
|
|
80
|
+
version,
|
|
81
|
+
roaming = false,
|
|
82
|
+
multipath = false,
|
|
83
|
+
opinion = true,
|
|
84
|
+
ensureExists = false,
|
|
85
|
+
) {
|
|
86
|
+
this.appname = appname;
|
|
87
|
+
this.appauthor = appauthor;
|
|
88
|
+
this.version = version;
|
|
89
|
+
this.roaming = roaming;
|
|
90
|
+
this.multipath = multipath;
|
|
91
|
+
this.opinion = opinion;
|
|
92
|
+
this.ensureExists = ensureExists;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string[]} base
|
|
97
|
+
* @return {string}
|
|
98
|
+
* @protected @ignore @internal
|
|
99
|
+
*/
|
|
100
|
+
_appendAppNameAndVersion(...base) {
|
|
101
|
+
const params = base.slice(1);
|
|
102
|
+
if (this.appname) {
|
|
103
|
+
params.push(this.appname);
|
|
104
|
+
if (this.version) {
|
|
105
|
+
params.push(this.version);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const path2 = path.join(base[0], ...params);
|
|
109
|
+
this._optionallyCreateDirectory(path2);
|
|
110
|
+
return path2;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} dir
|
|
115
|
+
* @protected @ignore @internal
|
|
116
|
+
*/
|
|
117
|
+
_optionallyCreateDirectory(dir) {
|
|
118
|
+
if (this.ensureExists) {
|
|
119
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {string} directory
|
|
125
|
+
* @return {string}
|
|
126
|
+
* @protected @ignore @internal
|
|
127
|
+
*/
|
|
128
|
+
_firstItemAsPathIfMultipath(directory) {
|
|
129
|
+
if (this.multipath) {
|
|
130
|
+
return directory.split(path.delimiter)[0];
|
|
131
|
+
}
|
|
132
|
+
return directory;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @return {string} data directory tied to the user
|
|
137
|
+
* @abstract
|
|
138
|
+
*/
|
|
139
|
+
get userDataDir() {
|
|
140
|
+
throw new Error("abstract");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @return {string} data directory shared by users
|
|
145
|
+
* @abstract
|
|
146
|
+
*/
|
|
147
|
+
get siteDataDir() {
|
|
148
|
+
throw new Error("abstract");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @return {string} config directory tied to the user
|
|
153
|
+
* @abstract
|
|
154
|
+
*/
|
|
155
|
+
get userConfigDir() {
|
|
156
|
+
throw new Error("abstract");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @return {string} config directory shared by users
|
|
161
|
+
* @abstract
|
|
162
|
+
*/
|
|
163
|
+
get siteConfigDir() {
|
|
164
|
+
throw new Error("abstract");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @return {string} cache directory tied to the user
|
|
169
|
+
* @abstract
|
|
170
|
+
*/
|
|
171
|
+
get userCacheDir() {
|
|
172
|
+
throw new Error("abstract");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @return {string} cache directory shared by users
|
|
177
|
+
* @abstract
|
|
178
|
+
*/
|
|
179
|
+
get siteCacheDir() {
|
|
180
|
+
throw new Error("abstract");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @return {string} state directory tied to the user
|
|
185
|
+
* @abstract
|
|
186
|
+
*/
|
|
187
|
+
get userStateDir() {
|
|
188
|
+
throw new Error("abstract");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @return {string} log directory tied to the user
|
|
193
|
+
* @abstract
|
|
194
|
+
*/
|
|
195
|
+
get userLogDir() {
|
|
196
|
+
throw new Error("abstract");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @return {string} documents directory tied to the user
|
|
201
|
+
* @abstract
|
|
202
|
+
*/
|
|
203
|
+
get userDocumentsDir() {
|
|
204
|
+
throw new Error("abstract");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @return {string} downloads directory tied to the user
|
|
209
|
+
* @abstract
|
|
210
|
+
*/
|
|
211
|
+
get userDownloadsDir() {
|
|
212
|
+
throw new Error("abstract");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @return {string} pictures directory tied to the user
|
|
217
|
+
* @abstract
|
|
218
|
+
*/
|
|
219
|
+
get userPicturesDir() {
|
|
220
|
+
throw new Error("abstract");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @return {string} videos directory tied to the user
|
|
225
|
+
* @abstract
|
|
226
|
+
*/
|
|
227
|
+
get userVideosDir() {
|
|
228
|
+
throw new Error("abstract");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @return {string} music directory tied to the user
|
|
233
|
+
* @abstract
|
|
234
|
+
*/
|
|
235
|
+
get userMusicDir() {
|
|
236
|
+
throw new Error("abstract");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @return {string} desktop directory tied to the user
|
|
241
|
+
* @abstract
|
|
242
|
+
*/
|
|
243
|
+
get userDesktopDir() {
|
|
244
|
+
throw new Error("abstract");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @return {string} runtime directory tied to the user
|
|
249
|
+
* @abstract
|
|
250
|
+
*/
|
|
251
|
+
get userRuntimeDir() {
|
|
252
|
+
throw new Error("abstract");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @return {string} runtime directory shared by users
|
|
257
|
+
* @abstract
|
|
258
|
+
*/
|
|
259
|
+
get siteRuntimeDir() {
|
|
260
|
+
throw new Error("abstract");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @return {string} data path tied to the user
|
|
265
|
+
*/
|
|
266
|
+
get userDataPath() {
|
|
267
|
+
return this.userDataDir;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @return {string} data path shared by users
|
|
272
|
+
*/
|
|
273
|
+
get siteDataPath() {
|
|
274
|
+
return this.siteDataDir;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @return {string} config path tied to the user
|
|
279
|
+
*/
|
|
280
|
+
get userConfigPath() {
|
|
281
|
+
return this.userConfigDir;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @return {string} config path shared by users
|
|
286
|
+
*/
|
|
287
|
+
get siteConfigPath() {
|
|
288
|
+
return this.siteConfigDir;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @return {string} cache path tied to the user
|
|
293
|
+
*/
|
|
294
|
+
get userCachePath() {
|
|
295
|
+
return this.userCacheDir;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* @return {string} cache path shared by users
|
|
300
|
+
*/
|
|
301
|
+
get siteCachePath() {
|
|
302
|
+
return this.siteCacheDir;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @return {string} state path tied to the user
|
|
307
|
+
*/
|
|
308
|
+
get userStatePath() {
|
|
309
|
+
return this.userStateDir;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @return {string} log path tied to the user
|
|
314
|
+
*/
|
|
315
|
+
get userLogPath() {
|
|
316
|
+
return this.userLogDir;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @return {string} documents path tied to the user
|
|
321
|
+
*/
|
|
322
|
+
get userDocumentsPath() {
|
|
323
|
+
return this.userDocumentsDir;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @return {string} downloads path tied to the user
|
|
328
|
+
*/
|
|
329
|
+
get userDownloadsPath() {
|
|
330
|
+
return this.userDownloadsDir;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @return {string} pictures path tied to the user
|
|
335
|
+
*/
|
|
336
|
+
get userPicturesPath() {
|
|
337
|
+
return this.userPicturesDir;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @return {string} videos path tied to the user
|
|
342
|
+
*/
|
|
343
|
+
get userVideosPath() {
|
|
344
|
+
return this.userVideosDir;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @return {string} music path tied to the user
|
|
349
|
+
*/
|
|
350
|
+
get userMusicPath() {
|
|
351
|
+
return this.userMusicDir;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @return {string} desktop path tied to the user
|
|
356
|
+
*/
|
|
357
|
+
get userDesktopPath() {
|
|
358
|
+
return this.userDesktopDir;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @return {string} runtime path tied to the user
|
|
363
|
+
*/
|
|
364
|
+
get userRuntimePath() {
|
|
365
|
+
return this.userRuntimeDir;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @return {string} runtime path shared by users
|
|
370
|
+
*/
|
|
371
|
+
get siteRuntimePath() {
|
|
372
|
+
return this.siteRuntimeDir;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// https://github.com/microsoft/TypeScript/issues/23857
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @yields all user and site configuration directories
|
|
379
|
+
* @returns {Generator<string>}
|
|
380
|
+
*/
|
|
381
|
+
*iterConfigDirs() {
|
|
382
|
+
yield this.userConfigDir;
|
|
383
|
+
yield this.siteConfigDir;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @yields all user and site data directories
|
|
388
|
+
* @returns {Generator<string>}
|
|
389
|
+
*/
|
|
390
|
+
*iterDataDirs() {
|
|
391
|
+
yield this.userDataDir;
|
|
392
|
+
yield this.siteDataDir;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @yields all user and site cache directories
|
|
397
|
+
* @returns {Generator<string>}
|
|
398
|
+
*/
|
|
399
|
+
*iterCacheDirs() {
|
|
400
|
+
yield this.userCacheDir;
|
|
401
|
+
yield this.siteCacheDir;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @yields all user and site runtime directories
|
|
406
|
+
* @returns {Generator<string>}
|
|
407
|
+
*/
|
|
408
|
+
*iterRuntimeDirs() {
|
|
409
|
+
yield this.userRuntimeDir;
|
|
410
|
+
yield this.siteRuntimeDir;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @yields all user and site state directories
|
|
415
|
+
* @returns {Generator<string>}
|
|
416
|
+
*/
|
|
417
|
+
*iterConfigPaths() {
|
|
418
|
+
yield* this.iterConfigDirs();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* @yields all user and site data directories
|
|
423
|
+
* @returns {Generator<string>}
|
|
424
|
+
*/
|
|
425
|
+
*iterDataPaths() {
|
|
426
|
+
yield* this.iterDataDirs();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* @yields all user and site cache directories
|
|
431
|
+
* @returns {Generator<string>}
|
|
432
|
+
*/
|
|
433
|
+
*iterCachePaths() {
|
|
434
|
+
yield* this.iterCacheDirs();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* @yields all user and site runtime directories
|
|
439
|
+
* @returns {Generator<string>}
|
|
440
|
+
*/
|
|
441
|
+
*iterRuntimePaths() {
|
|
442
|
+
yield* this.iterRuntimeDirs();
|
|
443
|
+
}
|
|
444
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Stub of to https://docs.python.org/3/library/configparser.html.
|
|
2
|
+
// Must parse files like https://github.com/search?q=path%3Auser-dirs.dirs&type=code.
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
# comment
|
|
6
|
+
[section]
|
|
7
|
+
key = value
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
# skeleton ~/.config/user-dirs.dirs
|
|
12
|
+
XDG_DESKTOP_DIR="$HOME/.local/share/Desktop"
|
|
13
|
+
XDG_DOCUMENTS_DIR="$HOME/Dropbox"
|
|
14
|
+
XDG_DOWNLOAD_DIR="$HOME/Downloads"
|
|
15
|
+
XDG_MUSIC_DIR="$HOME/Music"
|
|
16
|
+
XDG_PICTURES_DIR="$HOME/Pictures"
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export class ConfigParser {
|
|
20
|
+
/**
|
|
21
|
+
* @type {Record<string, Record<string, string>> | null}
|
|
22
|
+
*/
|
|
23
|
+
data = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} input
|
|
27
|
+
*/
|
|
28
|
+
readString(input) {
|
|
29
|
+
/** @type {Record<string, Record<string, string>>} */
|
|
30
|
+
const data = {};
|
|
31
|
+
let sectionName = "";
|
|
32
|
+
for (const line of input.split(/\r?\n/g)) {
|
|
33
|
+
if (line.startsWith("#")) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (line.trim() === "") {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const match = line.match(/^\[(.*)\]$/);
|
|
42
|
+
if (match != null) {
|
|
43
|
+
sectionName = match[1];
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
data[sectionName] ??= {};
|
|
48
|
+
const match2 = line.match(/^([^=]+)=(.*)$/);
|
|
49
|
+
if (match2 == null) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const key = match2[1].trim();
|
|
54
|
+
const value = match2[2].trim();
|
|
55
|
+
data[sectionName][key] = value;
|
|
56
|
+
}
|
|
57
|
+
this.data = data;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} key
|
|
62
|
+
* @return {Record<string, string> | undefined}
|
|
63
|
+
*/
|
|
64
|
+
get(key) {
|
|
65
|
+
if (this.data === null) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
if (Object.hasOwn(this.data, key)) {
|
|
69
|
+
return this.data[key];
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|