Repository navigation
Expand file tree
/
Copy pathindex.ts
More file actions
248 lines (206 loc) · 7.36 KB
/
Copy pathindex.ts
File metadata and controls
248 lines (206 loc) · 7.36 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
import * as Glob from 'fast-glob'
import * as NodeFS from 'fs'
import * as FS from 'fs-jetpack'
import * as OS from 'os'
import * as Path from 'path'
import { log } from '../nexus-logger'
/**
* Write a file after forcefully removing it, so that VSC will observe the
* change. It is the case that a plain write, when the file exists, in node,
* does an update-in-place tactic, probably for optimization, but this isn't
* enough for proper file change watch detection or something, not sure.
*/
export function removeWrite(filePath: string, fileContent: string): void {
FS.remove(filePath)
FS.write(filePath, fileContent)
}
export async function removeWriteAsync(filePath: string, fileContent: string): Promise<void> {
await FS.removeAsync(filePath)
await FS.writeAsync(filePath, fileContent)
}
/**
* Search for a file in cwd or in parent directory recursively up to the root
* directory.
*/
export async function findFileRecurisvelyUpward(
fileName: string,
opts: { projectRoot: string }
): Promise<null | string> {
let found: null | string = null
let currentDir = opts.projectRoot
while (true) {
const checkFilePath = Path.join(currentDir, fileName)
if (await FS.existsAsync(checkFilePath)) {
found = checkFilePath
break
}
if (currentDir === '/') {
break
}
currentDir = Path.dirname(currentDir)
}
return found
}
/**
* Search for a file in cwd or in parent directory recursively up to the root
* directory.
*/
export function findFileRecurisvelyUpwardSync(
fileName: string,
opts: { cwd: string }
): { path: string; dir: string } | null {
let found: { path: string; dir: string } | null = null
let currentDir = opts.cwd
const localFS = FS.cwd(currentDir)
while (true) {
const filePath = Path.join(currentDir, fileName)
if (localFS.exists(filePath)) {
found = { dir: currentDir, path: filePath }
break
}
if (isRootPath(currentDir)) {
break
}
currentDir = Path.join(currentDir, '..')
}
return found
}
/**
* Write file contents but first delete the file off disk if present. This is a
* useful function when the effect of file delete is needed to trigger some file
* watch/refresh mechanism, such as is the case with VSCode TS declaration files
* inside `@types/` packages.
*
* For more details that motivated this utility refer to the originating issue
* https://github.com/graphql-nexus/nexus-plugin-prisma/issues/453.
*/
export function hardWriteFileSync(filePath: string, data: string) {
FS.remove(Path.dirname(filePath))
FS.write(filePath, data)
}
export async function hardWriteFile(filePath: string, data: string) {
await FS.removeAsync(Path.dirname(filePath))
await FS.writeAsync(filePath, data)
}
/**
* Return the path to a temporary directory on the machine. This works around a
* limitation in Node wherein a symlink is returned on macOS for `os.tmpdir`.
*/
export function getTmpDir(prefix: string = '', baseTmpDir?: string) {
const tmpDirPath = NodeFS.realpathSync(baseTmpDir ?? OS.tmpdir())
const id = Math.random().toString().slice(2)
const dirName = [prefix, id].filter((x) => x).join('-')
// https://github.com/nodejs/node/issues/11422
const tmpDir = Path.join(tmpDirPath, dirName)
return tmpDir
}
export const writeCachedFile = async (filePath: string, fileContent: string): Promise<void> => {
const alreadyExistingFallbackFileContents = FS.read(filePath)
if (alreadyExistingFallbackFileContents === undefined) {
log.trace('writing file', { filePath })
await FS.writeAsync(filePath, fileContent)
} else if (alreadyExistingFallbackFileContents !== fileContent) {
log.trace(
'there is a file already present on disk but its content does not match, replacing old with new %s',
{
filePath,
}
)
log.trace(alreadyExistingFallbackFileContents)
log.trace(fileContent)
await FS.writeAsync(filePath, fileContent)
} else {
log.trace('there is a file already present on disk and its content matches, therefore doing nothing')
}
}
// build/index.js => index.ts
export function getTranspiledPath(projectDir: string, filePath: string, outDir: string) {
const pathFromRootToFile = Path.relative(projectDir, filePath)
const jsFileName = Path.basename(pathFromRootToFile, '.ts') + '.js'
const pathToJsFile = Path.join(Path.dirname(pathFromRootToFile), jsFileName)
return Path.join(outDir, pathToJsFile)
}
// build/index.js => /Users/me/project/src/index.ts
export function sourceFilePathFromTranspiledPath({
transpiledPath,
outDir,
rootDir,
packageJsonPath,
}: {
transpiledPath: string
outDir: string
rootDir: string
packageJsonPath: string
}) {
const normalizedTranspiledPath = transpiledPath.startsWith('/')
? transpiledPath
: Path.join(packageJsonPath, transpiledPath)
const pathFromOutDirToFile = Path.relative(outDir, normalizedTranspiledPath)
const tsFileName = Path.basename(pathFromOutDirToFile, '.js') + '.ts'
const maybeAppFolders = Path.dirname(pathFromOutDirToFile)
return Path.join(rootDir, maybeAppFolders, tsFileName)
}
/**
* Find the given file within the directory tree under the given root path (cwd).
*
* Dot-folders, dot-files, node_modules are all always ignored
*/
export function findFile(pattern: string, config: { ignore?: string[]; cwd: string }): null | string {
const cwd = config.cwd
const ignore = ['node_modules/**', ...(config.ignore ?? [])]
const foundFiles = Glob.sync(pattern, { cwd, ignore, absolute: true, dot: false })
// TODO: What if several files were found?
return foundFiles[0] ?? null
}
export async function findFiles(
fileNames: string | string[],
config?: { ignore?: string[]; cwd?: string }
): Promise<string[]> {
const cwd = config?.cwd ?? process.cwd()
const paths = Array.isArray(fileNames) ? fileNames : [fileNames]
const localFS = FS.cwd(cwd)
const files = await localFS.findAsync({
matching: [...paths, ...baseIgnores, ...(config?.ignore?.map((i) => `!${i}`) ?? [])],
})
return files.map((f) => (Path.isAbsolute(f) ? f : localFS.path(f)))
}
export const baseIgnores = ['!node_modules/**/*', '!.*/**/*']
export function trimNodeModulesIfInPath(path: string) {
if (path.includes('node_modules')) {
return path.substring(path.indexOf('node_modules') + 'node_modules'.length + 1)
}
return path
}
/**
* Strip the extension of a file path.
*
* This can be handy for example when going from a file to a module path
* suitable for import like a user would do, not supplying the ext.
*/
export function stripExt(filePath: string): string {
return filePath.replace(/\.[^.]*$/, '')
}
/**
* Check if the CWD is empty of any files or folders.
* TODO we should make nice exceptions for known meaningless files, like .DS_Store
*/
export async function isEmptyDir(dirPath: string): Promise<boolean> {
const contents = await FS.listAsync(dirPath)
return contents === undefined || contents.length === 0
}
// https://github.com/iojs/io.js/blob/5883a59b21a97e8b7339f435c977155a2c29ba8d/lib/path.js#L43
const windowsPathRegex = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)?[\\/]$/
/**
* Ask whether the given path is a root path or not.
*
* @remarks
*
* Take from https://github.com/sindresorhus/is-root-path/blob/master/index.js
*/
export function isRootPath(path: string) {
path = path.trim()
if (path === '') {
return false
}
return process.platform === 'win32' ? windowsPathRegex.test(path) : path === '/'
}