UNPKG

4.88 kBJavaScriptView Raw
1'use strict';
2
3const { regexpEscape } = require('./string');
4const fs = require('fs');
5const md5 = require('md5');
6const path = require('path');
7
8const RE_UNIQUE_HASH_TOKEN = /%hash%/;
9const RE_UNIQUE_TOKEN = /%(?:hash|date)%/;
10
11const existsCache = new Set();
12
13module.exports = {
14 exists,
15 isAbsoluteFilepath,
16 isRelativeFilepath,
17
18 /**
19 * Determine if 'str' is a filepath or package reference
20 * @param {String} str
21 * @returns {Boolean}
22 */
23 isFilepath(str) {
24 return str && (isAbsoluteFilepath(str) || isRelativeFilepath(str));
25 },
26
27 /**
28 * Retrieve path name (dirname/filename) of 'p'
29 * @param {String} p
30 * @returns {String}
31 */
32 filepathName(p) {
33 p = path.resolve(p);
34
35 let dir = path.resolve(p, '..');
36
37 if (dir == process.cwd()) dir = '.';
38
39 return `${path.basename(dir)}${path.sep}${path.basename(p)}`;
40 },
41
42 /**
43 * Determine type of 'filepath'
44 * @param {String} filepath
45 * @param {Object} fileExtensions
46 * @returns {String}
47 */
48 filepathType(filepath, fileExtensions) {
49 const ext = path.extname(filepath).slice(1);
50
51 // Match input extension to type
52 for (const t in fileExtensions) {
53 const exts = fileExtensions[t];
54
55 for (let i = 0, n = exts.length; i < n; i++) {
56 if (ext == exts[i]) return t;
57 }
58 }
59
60 return '';
61 },
62
63 /**
64 * Check the location of 'filepath'
65 * @param {String} filepath
66 * @param {String} type
67 * @param {Array} fileExtensions
68 * @returns {String}
69 */
70 findFilepath(filepath, type, fileExtensions) {
71 if ('string' == typeof filepath && type && fileExtensions) {
72 let stat;
73
74 try {
75 stat = fs.statSync(filepath);
76 } catch (err) {
77 /* no file */
78 }
79
80 // Already have full filepath
81 if (stat && stat.isFile()) return filepath;
82
83 let ext, fp;
84
85 // Loop through fileExtensions and locate file
86 for (let i = 0, n = fileExtensions[type].length; i < n; i++) {
87 ext = fileExtensions[type][i];
88 // Add extension
89 fp = filepath + '.' + ext;
90 if (exists(fp)) return fp;
91 // Try 'index' + extension
92 fp = path.resolve(filepath, `index.${ext}`);
93 if (exists(fp)) return fp;
94 }
95
96 return '';
97 }
98
99 return filepath;
100 },
101
102 /**
103 * Find file matching unique 'pattern'
104 * @param {String} pattern
105 * @returns {String}
106 */
107 findUniqueFilepath(pattern) {
108 pattern = path.resolve(pattern);
109
110 // Limit scope to containing directory
111 const dir = path.dirname(pattern);
112 let files, reToken;
113
114 // Matches {hash} or {date}
115 if ((reToken = RE_UNIQUE_TOKEN.exec(pattern))) {
116 try {
117 files = fs.readdirSync(dir);
118 } catch (err) {
119 // Directory doesn't exist
120 return '';
121 }
122
123 // Generate regexp with pattern as wildcard
124 const re = new RegExp(
125 regexpEscape(pattern).replace(reToken[0], RE_UNIQUE_HASH_TOKEN.test(pattern) ? '[a-f0-9]{32}' : '[0-9]{13,}')
126 );
127
128 for (let i = 0, n = files.length; i < n; i++) {
129 const filepath = path.resolve(dir, files[i]);
130
131 if (re.test(filepath)) return filepath;
132 }
133 }
134
135 return '';
136 },
137
138 /**
139 * Generate unique filepath from 'pattern'
140 * @param {String} pattern
141 * @param {String|Boolean} content
142 * @returns {String}
143 */
144 generateUniqueFilepath(pattern, content) {
145 pattern = path.resolve(pattern);
146
147 let reToken, wildcard;
148
149 if ((reToken = RE_UNIQUE_TOKEN.exec(pattern))) {
150 wildcard = reToken[0];
151 if (wildcard == '%hash%') {
152 // Remove if content == false
153 // Hash content if not already a hash
154 pattern = pattern.replace(wildcard, content ? content.length == 32 ? content : md5(content) : '');
155 } else if (wildcard == '%date%') {
156 pattern = pattern.replace(wildcard, content ? Date.now() : '');
157 }
158 }
159
160 return pattern;
161 },
162
163 /**
164 * Determine whether 'pattern' is supported
165 * @param {String} pattern
166 * @returns {Boolean}
167 */
168 isUniqueFilepath(pattern) {
169 return RE_UNIQUE_TOKEN.test(pattern);
170 }
171};
172
173/**
174 * Determine if 'filepath' exists
175 * @param {String} filepath
176 * @returns {Boolean}
177 */
178function exists(filepath) {
179 const cached = existsCache.has(filepath);
180
181 // Only return positive to allow for generated files
182 if (cached) return true;
183
184 const filepathExists = fs.existsSync(filepath);
185
186 if (filepathExists) existsCache.add(filepath);
187
188 return filepathExists;
189}
190
191/**
192 * Determine if 'filepath' is relative
193 * @param {String} filepath
194 * @returns {Boolean}
195 */
196function isRelativeFilepath(filepath) {
197 return 'string' == typeof filepath && filepath.charAt(0) == '.';
198}
199
200/**
201 * Determine if 'filepath' is absolute
202 * @param {String} filepath
203 * @returns {Boolean}
204 */
205function isAbsoluteFilepath(filepath) {
206 return 'string' == typeof filepath && path.resolve(filepath) == filepath;
207}