.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / @mrmlnc / readdir-enhanced / lib / normalize-options.js
1 'use strict';
2
3 const path = require('path');
4 const globToRegExp = require('glob-to-regexp');
5
6 module.exports = normalizeOptions;
7
8 let isWindows = /^win/.test(process.platform);
9
10 /**
11  * @typedef {Object} FSFacade
12  * @property {fs.readdir} readdir
13  * @property {fs.stat} stat
14  * @property {fs.lstat} lstat
15  */
16
17 /**
18  * Validates and normalizes the options argument
19  *
20  * @param {object} [options] - User-specified options, if any
21  * @param {object} internalOptions - Internal options that aren't part of the public API
22  *
23  * @param {number|boolean|function} [options.deep]
24  * The number of directories to recursively traverse. Any falsy value or negative number will
25  * default to zero, so only the top-level contents will be returned. Set to `true` or `Infinity`
26  * to traverse all subdirectories.  Or provide a function that accepts a {@link fs.Stats} object
27  * and returns a truthy value if the directory's contents should be crawled.
28  *
29  * @param {function|string|RegExp} [options.filter]
30  * A function that accepts a {@link fs.Stats} object and returns a truthy value if the data should
31  * be returned.  Or a RegExp or glob string pattern, to filter by file name.
32  *
33  * @param {string} [options.sep]
34  * The path separator to use. By default, the OS-specific separator will be used, but this can be
35  * set to a specific value to ensure consistency across platforms.
36  *
37  * @param {string} [options.basePath]
38  * The base path to prepend to each result. If empty, then all results will be relative to `dir`.
39  *
40  * @param {FSFacade} [options.fs]
41  * Synchronous or asynchronous facades for Node.js File System module
42  *
43  * @param {object} [internalOptions.facade]
44  * Synchronous or asynchronous facades for various methods, including for the Node.js File System module
45  *
46  * @param {boolean} [internalOptions.emit]
47  * Indicates whether the reader should emit "file", "directory", and "symlink" events
48  *
49  * @param {boolean} [internalOptions.stats]
50  * Indicates whether the reader should emit {@link fs.Stats} objects instead of path strings
51  *
52  * @returns {object}
53  */
54 function normalizeOptions (options, internalOptions) {
55   if (options === null || options === undefined) {
56     options = {};
57   }
58   else if (typeof options !== 'object') {
59     throw new TypeError('options must be an object');
60   }
61
62   let recurseDepth, recurseFn, recurseRegExp, recurseGlob, deep = options.deep;
63   if (deep === null || deep === undefined) {
64     recurseDepth = 0;
65   }
66   else if (typeof deep === 'boolean') {
67     recurseDepth = deep ? Infinity : 0;
68   }
69   else if (typeof deep === 'number') {
70     if (deep < 0 || isNaN(deep)) {
71       throw new Error('options.deep must be a positive number');
72     }
73     else if (Math.floor(deep) !== deep) {
74       throw new Error('options.deep must be an integer');
75     }
76     else {
77       recurseDepth = deep;
78     }
79   }
80   else if (typeof deep === 'function') {
81     recurseDepth = Infinity;
82     recurseFn = deep;
83   }
84   else if (deep instanceof RegExp) {
85     recurseDepth = Infinity;
86     recurseRegExp = deep;
87   }
88   else if (typeof deep === 'string' && deep.length > 0) {
89     recurseDepth = Infinity;
90     recurseGlob = globToRegExp(deep, { extended: true, globstar: true });
91   }
92   else {
93     throw new TypeError('options.deep must be a boolean, number, function, regular expression, or glob pattern');
94   }
95
96   let filterFn, filterRegExp, filterGlob, filter = options.filter;
97   if (filter !== null && filter !== undefined) {
98     if (typeof filter === 'function') {
99       filterFn = filter;
100     }
101     else if (filter instanceof RegExp) {
102       filterRegExp = filter;
103     }
104     else if (typeof filter === 'string' && filter.length > 0) {
105       filterGlob = globToRegExp(filter, { extended: true, globstar: true });
106     }
107     else {
108       throw new TypeError('options.filter must be a function, regular expression, or glob pattern');
109     }
110   }
111
112   let sep = options.sep;
113   if (sep === null || sep === undefined) {
114     sep = path.sep;
115   }
116   else if (typeof sep !== 'string') {
117     throw new TypeError('options.sep must be a string');
118   }
119
120   let basePath = options.basePath;
121   if (basePath === null || basePath === undefined) {
122     basePath = '';
123   }
124   else if (typeof basePath === 'string') {
125     // Append a path separator to the basePath, if necessary
126     if (basePath && basePath.substr(-1) !== sep) {
127       basePath += sep;
128     }
129   }
130   else {
131     throw new TypeError('options.basePath must be a string');
132   }
133
134   // Convert the basePath to POSIX (forward slashes)
135   // so that glob pattern matching works consistently, even on Windows
136   let posixBasePath = basePath;
137   if (posixBasePath && sep !== '/') {
138     posixBasePath = posixBasePath.replace(new RegExp('\\' + sep, 'g'), '/');
139
140     /* istanbul ignore if */
141     if (isWindows) {
142       // Convert Windows root paths (C:\) and UNCs (\\) to POSIX root paths
143       posixBasePath = posixBasePath.replace(/^([a-zA-Z]\:\/|\/\/)/, '/');
144     }
145   }
146
147   // Determine which facade methods to use
148   let facade;
149   if (options.fs === null || options.fs === undefined) {
150     // The user didn't provide their own facades, so use our internal ones
151     facade = internalOptions.facade;
152   }
153   else if (typeof options.fs === 'object') {
154     // Merge the internal facade methods with the user-provided `fs` facades
155     facade = Object.assign({}, internalOptions.facade);
156     facade.fs = Object.assign({}, internalOptions.facade.fs, options.fs);
157   }
158   else {
159     throw new TypeError('options.fs must be an object');
160   }
161
162   return {
163     recurseDepth,
164     recurseFn,
165     recurseRegExp,
166     recurseGlob,
167     filterFn,
168     filterRegExp,
169     filterGlob,
170     sep,
171     basePath,
172     posixBasePath,
173     facade,
174     emit: !!internalOptions.emit,
175     stats: !!internalOptions.stats,
176   };
177 }