-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
393 lines (364 loc) · 16 KB
/
index.js
File metadata and controls
393 lines (364 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/**
* Main entry of `temppath` module.
* @module temppath
* @author Ryuu Mitsuki
*/
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const { randomUUID } = require('node:crypto');
// Should be avoid due to deprecated
// const { isNullOrUndefined } = require('node:util');
/**
* Callback function to handle the result or error from calling the
* {@link module:temppath~createTempPath|createTempPath} function.
*
* @callback CreateTempPathCallback
* @param {?Error} error - An error object if an error occurred, or `null` if no error.
* @param {string} resultPath - The path of the created temporary directory or file.
* @global
* @since 0.2.0
*/
/**
* An object representating options to configure the temporary file or directory
* creation within this module.
*
* @typedef {Object} TempPathOptions
* @property {boolean} [asFile=false] - Whether to create a temporary file instead directory.
* @property {string} [ext='.tmp'] - An extension name to use for tempoarary file creation.
* Ignored if the `asFile` is set to `false`.
* @property {number} [maxLen=32] - The maximum characters' length of the generated temporary
* directory or file name.
* @global
* @since 0.3.0
*/
// Alternative for `node:util.isNullOrUndefined`
function isNullOrUndefined(o) {
return (o === null || typeof o === 'undefined');
}
/**
* Retrieves the temporary directory path based on the current environment.
*
* This function first checks common environment variables for temporary directories:
* - `TMPDIR` on UNIX-like and macOS systems
* - `TMP` or `TEMP` on Windows systems
*
* If none of these environment variables are set or return null, it falls back to the
* `os.tmpdir()` function. If this also returns null, it uses the current working directory
* and creates a new directory called `tmp`.
*
* @private
* @function
* @returns {string} The temporary directory path.
* @since 0.1.0
*/
function __getTempDir() {
return process.env.TMPDIR // Unix-like & MacOS systems
|| (process.env.TMP || process.env.TEMP) // Windows system
|| require('node:os').tmpdir() // Fallback
|| path.resolve(process.cwd(), 'tmp'); // Otherwise, use current directory
}
/**
* Generates a temporary path based on the provided or system temporary directory.
*
* This function utilizes a random UUID for the directory name, ensuring that each time
* it is called, the path will be different from previous calls. The returned path can be
* used for either a temporary file or directory, according to user preferences.
*
* To limit the characters' length of generated temporary directory or file name,
* set the `maxLen` to the desired value and it must be a positive number.
* Thus, the function will trim the name to the desired maximum length.
*
* @public
* @function
* @param {string} [tmpdir] - The temporary directory path. If not provided or empty,
* it defaults to the system's temporary directory.
* @param {number} [maxLen=32] - The maximum characters' length of the generated temporary path.
* Must be a positive number and greater than zero.
*
* @returns {string} The generated temporary path.
*
* @throws {TypeError} Throws a `TypeError` if the provided `tmpdir` is not a string.
* @throws {RangeError} If the given `maxLen` is less than or equal to zero.
*
* @since 0.1.0
*/
function getTempPath(tmpdir, maxLen) {
if (tmpdir && typeof tmpdir !== 'string') {
throw new TypeError(`Expected type is string. Received ${typeof tmpdir}`);
}
if (maxLen <= 0) {
throw new RangeError('Maximum characters must be greater than zero');
}
return path.join(
(isNullOrUndefined(tmpdir) || tmpdir.length === 0) ? __getTempDir() : tmpdir,
randomUUID().replace(/-/g, '').substr(0, maxLen)
);
}
/**
* Asynchronously creates a temporary path, either as a directory or file,
* based on the provided or system temporary directory.
*
* This function utilizes the {@link module:temppath~getTempPath|getTempPath} function.
* To limit the characters' length of generated temporary directory or file name,
* set the {@link TempPathOptions.maxLen `options.maxLen`} to the desired value and
* it must be a positive number. Thus, the function will trim the name to the desired
* maximum length.
*
* @public
* @async
* @function
* @param {string | TempPathOptions | CreateTempPathCallback} [tmpdir] - The temporary directory path.
* If an object is provided, it is treated as the `options` parameter.
* If a function is provided, it is treated as the `callback` parameter,
* and `tmpdir` will fallback to system-based temporary directory.
* @param {TempPathOptions | CreateTempPathCallback} [options] - Options for creating the temporary path.
* If a function is provided, it is treated as the `callback` parameter,
* and `options` is set to `{}` (an empty object).
* @param {boolean} [options.asFile=false] - If `true`, create a temporary file. Otherwise, create a directory.
* @param {string} [options.ext='.tmp'] - The extension for the temporary file. If `asFile` option is `false`,
* this option will be ignored. Default is '.tmp'.
* @param {number} [options.maxLen=32] - The maximum characters' length of the generated directory or file name.
* Defaults to 32 characters.
* @param {CreateTempPathCallback} callback - A callback function to handle the result path or error.
* This is crucial and required, even when you wanted to omit all arguments.
*
* @throws {TypeError} If the given arguments or the extension name specified with incorrect type.
*
* @example <caption>Only specify a callback function</caption>
* // Without any argument but a callback function
* // This will create a temporary directory in system's temporary path
* createTempPath(function (err, createdPath) {
* if (err) console.error(err);
* else console.log(createdPath);
* // Unix: "$TMPDIR/<TEMPPATH_DIR>"
* // Termux Android: "$TMPDIR/<TEMPPATH_DIR>" or "$PREFIX/tmp/<TEMPPATH_DIR>"
* // Windows: "%TMP%\<TEMPPATH_DIR>" or "%TEMP%\<TEMPPATH_DIR>"
* });
*
* @example <caption>Create a temporary file in system's temporary path</caption>
* // This will create a temporary file in system's temporary path
* // with extension name of '.foo_temp'
* createTempPath({ asFile: true, ext: 'foo_temp' }, function (err, createdPath) {
* if (err) console.error(err);
* else console.log(createdPath);
* // Unix: "$TMPDIR/<TEMPPATH_FILE>.foo_temp"
* // Termux Android: "$TMPDIR/<TEMPPATH_FILE>.foo_temp" or "$PREFIX/tmp/<TEMPPATH_FILE>.foo_temp"
* // Windows: "%TMP%\<TEMPPATH_FILE>.foo_temp" or "%TEMP%\<TEMPPATH_FILE>.foo_temp"
* });
*
* @example <caption>Create a temporary file in current directory</caption>
* const path = require('node:path');
* // For ESM: import path from 'node:path';
*
* // Use `process.cwd()` to get current directory path
* createTempPath(path.join(process.cwd(), 'tmp'), { asFile: true }, function (err, createdPath) {
* if (err) console.error(err);
* else console.log(createdPath);
* // Unix: "$PWD/tmp/<TEMPPATH_FILE>.tmp"
* // Termux Android: "$PWD/tmp/<TEMPPATH_FILE>.tmp"
* // Windows: "%CD%\tmp\<TEMPPATH_FILE>.tmp"
* });
*
* @since 0.2.0
*/
function createTempPath(tmpdir, options, callback) {
if ((typeof tmpdir === 'object' && !Array.isArray(tmpdir))
&& typeof options === 'function' && isNullOrUndefined(callback)) {
callback = options; // Swap the `options` to `callback`
options = tmpdir; // Swap the `tmpdir` to `options`
tmpdir = null; // Keep this empty
} else if (!isNullOrUndefined(tmpdir) && typeof options === 'function'
&& isNullOrUndefined(callback)) {
callback = options;
options = {};
} else if (typeof tmpdir === 'function' && isNullOrUndefined(options)
&& isNullOrUndefined(callback)) {
callback = tmpdir;
options = {};
tmpdir = null;
} else if (isNullOrUndefined(options)) {
options = {};
}
if (!callback || typeof callback !== 'function') {
throw new TypeError(
`The "callback" argument must be a function. Received ${typeof callback}`);
}
if (options && typeof options !== 'object') {
throw new TypeError(
`The "options" argument must be an object. Received ${typeof options}`);
}
if (options.ext && typeof options.ext !== 'string') {
throw new TypeError(`Expected a string extension, got ${typeof options.ext}`);
}
if (options.maxLen && typeof options.maxLen !== 'number') {
throw new TypeError(`Expected a number for maxLen, got ${typeof options.maxLen}`);
}
// Resolve the temporary path
tmpdir = getTempPath(tmpdir, options.maxLen);
const extension = (options.ext)
? options.ext.startsWith('.')
? options.ext
: `.${options.ext}`
// Use default extension, if the extension name is not specified
// * feat: Add support for temporary file creation with no extension
: ((typeof options.ext === 'string' && options.ext.length === 0)
? options.ext
: '.tmp'
);
// Create the parent directory of generated temporary path
fs.promises.mkdir(path.dirname(tmpdir), { recursive: true })
.then(function () {
if (options.asFile) {
const filename = tmpdir + extension;
// Create an empty file in the temporary directory
fs.promises.writeFile(filename, '')
.then(() => callback(null, filename)) // Return the created the temporary file path
.catch(err => callback(err));
} else {
// Create an empty directory in the temporary directory
fs.promises.mkdir(tmpdir)
.then(() => callback(null, tmpdir)) // Return the created temporary directory path
.catch(err => callback(err));
}
})
.catch(err => callback(err));
}
/**
* Synchronously creates a temporary path, either as a directory or file, based on the provided
* or system temporary directory and then returns a path that refers to the generated temporary directory or file.
*
* This function utilizes the {@link module:temppath~getTempPath|getTempPath} function.
* To limit the characters' length of generated temporary directory or file name,
* set the {@link TempPathOptions.maxLen `options.maxLen`} to the desired value and
* it must be a positive number. Thus, the function will trim the name to the desired
* maximum length.
*
* @public
* @function
* @param {string | TempPathOptions} [tmpdir] - The temporary directory path. If an object is provided,
* it is treated as the `options` parameter,
* and `tmpdir` will fallback to system-based temporary directory.
* @param {Object} [options] - Options for creating the temporary path.
* @param {boolean} [options.asFile=false] - If `true`, create a temporary file. Otherwise, create a directory.
* @param {string} [options.ext='.tmp'] - The extension for the temporary file. If `asFile` option is `false`,
* this option will be ignored. Default is `'.tmp'`.
* @param {number} [options.maxLen=32] - The maximum characters' length of the generated directory or file name.
* Defaults to 32 characters.
*
* @returns {string} The path of the created temporary directory or file.
*
* @throws {TypeError} If the given arguments or the extension name specified with incorrect type.
* @throws {Error} Throws an `Error` if there is an issue creating the temporary directory or file.
*
* @example <caption>Call the function without any argument</caption>
* // If no argument specified, it will creates a temporary directory
* // in system's temporary path
* const tmpDirPath = createTempPathSync();
* console.log(tmpDirPath);
* // Unix: "$TMPDIR/<TEMPPATH_DIR>"
* // Termux Android: "$TMPDIR/<TEMPPATH_DIR>" or "$PREFIX/tmp/<TEMPPATH_DIR>"
* // Windows: "%TMP%\<TEMPPATH_DIR>" or "%TEMP%\<TEMPPATH_DIR>"
*
* @example <caption>Create a temporary file with custom extension</caption>
* const tmpFilePath = createTempPathSync({ asFile: true, ext: 'txt' });
* console.log(tmpFilePath);
* // Unix: "$TMPDIR/<TEMPPATH_FILE>.txt"
* // Termux Android: "$TMPDIR/<TEMPPATH_FILE>.txt" or "$PREFIX/tmp/<TEMPPATH_FILE>.txt"
* // Windows: "%TMP%\<TEMPPATH_FILE>.txt" or "%TEMP%\<TEMPPATH_FILE>.txt"
*
* @example <caption>Create a temporary file in current directory</caption>
* const path = require('node:path');
* // For ESM: import path from 'node:path';
*
* // Use `process.cwd()` to get current directory path
* const customTmpFilePath = createTempPathSync(
* path.join(process.cwd(), 'tmp'), { asFile: true });
* console.log(customTmpFilePath);
* // Unix: "$PWD/tmp/<TEMPPATH_FILE>.tmp"
* // Termux Android: "$PWD/tmp/<TEMPPATH_FILE>.tmp"
* // Windows: "%CD%\tmp\<TEMPPATH_FILE>.tmp"
*
* @since 0.2.0
*/
function createTempPathSync(tmpdir, options) {
// Swap the 'tmpdir' argument to 'options', if the provided is an object
// and the 'options' argument is undefined or empty
if (typeof tmpdir === 'object' && !Array.isArray(tmpdir)
&& isNullOrUndefined(options)) {
options = tmpdir; // Swap
tmpdir = null; // Make this empty
} else if (isNullOrUndefined(options)) {
// By using this approach, all paramaters will be optional.
// Users can simply call this function without any argument and with no error.
options = {};
}
if (typeof options !== 'object') {
throw new TypeError(
`The "options" argument must be an object. Received ${typeof options}`);
}
if (options.ext && typeof options.ext !== 'string') {
throw new TypeError(`Expected a string extension, got ${typeof options.ext}`);
}
if (options.maxLen && typeof options.maxLen !== 'number') {
throw new TypeError(`Expected a number for maxLen, got ${typeof options.maxLen}`);
}
// Resolve the temporary path
tmpdir = getTempPath(tmpdir, options.maxLen);
const extension = (options.ext)
? options.ext.startsWith('.')
? options.ext
: `.${options.ext}`
// Use default extension, if the extension name is not specified
// * feat: Add support for temporary file creation with no extension
: ((typeof options.ext === 'string' && options.ext.length === 0)
? options.ext
: '.tmp'
);
try {
// Create the parent directory of generated temporary path
fs.mkdirSync(path.dirname(tmpdir), { recursive: true });
if (options.asFile) {
const filename = tmpdir + extension;
fs.writeFileSync(filename, ''); // Create an empty temporary file
return filename; // Return the created temporary file path
}
// Create a temporary directory with synchronous operation
fs.mkdirSync(tmpdir);
return tmpdir; // Return the created temporary directory path
} catch (err) {
// Throw a new error with source error as causative error
throw new Error(
`temppath: Failed to create temporary ${
(options.asFile) ? 'file' : 'directory'
}.`, { cause: err }
);
}
}
/*----------
* EXPORTS
----------*/
// Create a frozen object used for exports
const temppath = Object.freeze({
getTempPath,
createTempPath,
createTempPathSync
});
// For CommonJS
if (!isNullOrUndefined(module)) {
Object.defineProperty(module, 'exports', {
value: Object.isFrozen(temppath) ? temppath : Object.freeze(temppath),
writable: false
});
}
// For ESModule
if (!isNullOrUndefined(exports) && typeof exports === 'object') {
// Export the 'temppath' as default export
Object.defineProperty(exports, 'default', {
value: Object.isFrozen(temppath) ? temppath : Object.freeze(temppath),
writable: false
});
exports.getTempPath = getTempPath;
exports.createTempPath = createTempPath;
exports.createTempPathSync = createTempPathSync;
}