-
Notifications
You must be signed in to change notification settings - Fork 175
Expand file tree
/
Copy pathutils.ts
More file actions
122 lines (114 loc) · 4.04 KB
/
utils.ts
File metadata and controls
122 lines (114 loc) · 4.04 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
import fs from 'node:fs';
import path from 'node:path';
import validateNpmPackageName from 'validate-npm-package-name';
import { editJsonFile } from '../utils/json.js';
// Helper functions for file operations
export function copy(src: string, dest: string) {
const stat = fs.statSync(src);
if (stat.isDirectory()) {
copyDir(src, dest);
} else {
fs.copyFileSync(src, dest);
}
}
export function copyDir(srcDir: string, destDir: string) {
fs.mkdirSync(destDir, { recursive: true });
for (const file of fs.readdirSync(srcDir)) {
const srcFile = path.resolve(srcDir, file);
const destFile = path.resolve(destDir, file);
copy(srcFile, destFile);
}
}
/**
* Format the target directory into a valid directory name and package name
*
* Examples:
* ```
* # invalid target directories
* ./ -> { directory: '', packageName: '', error: 'Invalid target directory' }
* /foo/bar -> { directory: '', packageName: '', error: 'Absolute path is not allowed' }
* @scope/ -> { directory: '', packageName: '', error: 'Invalid target directory' }
* ../../foo/bar -> { directory: '', packageName: '', error: 'Invalid target directory' }
*
* # valid target directories
* ./my-package -> { directory: './my-package', packageName: 'my-package' }
* ./foo/bar-package -> { directory: './foo/bar-package', packageName: 'bar-package' }
* ./foo/bar-package/ -> { directory: './foo/bar-package', packageName: 'bar-package' }
* my-package -> { directory: 'my-package', packageName: 'my-package' }
* @my-scope/my-package -> { directory: 'my-package', packageName: '@my-scope/my-package' }
* foo/@my-scope/my-package -> { directory: 'foo/my-package', packageName: '@scope/my-package' }
* ./foo/@my-scope/my-package -> { directory: './foo/my-package', packageName: '@scope/my-package' }
* ./foo/bar/@scope/my-package -> { directory: './foo/bar/my-package', packageName: '@scope/my-package' }
* ```
*/
export function formatTargetDir(input: string): {
directory: string;
packageName: string;
error?: string;
} {
let targetDir = path.normalize(input.trim());
const parsed = path.parse(targetDir);
if (parsed.root || path.isAbsolute(targetDir)) {
return {
directory: '',
packageName: '',
error: 'Absolute path is not allowed',
};
}
if (targetDir.includes('..')) {
return {
directory: '',
packageName: '',
error: 'Relative path contains ".." which is not allowed',
};
}
let packageName = parsed.base;
const parentName = path.basename(parsed.dir);
if (parentName.startsWith('@')) {
// skip scope directory
// ./@my-scope/my-package -> ./my-package
targetDir = path.join(path.dirname(parsed.dir), packageName);
packageName = `${parentName}/${packageName}`;
}
const result = validateNpmPackageName(packageName);
if (!result.validForNewPackages) {
// invalid package name
const message = result.errors?.[0] ?? result.warnings?.[0] ?? 'Invalid package name';
return {
directory: '',
packageName: '',
error: `Parsed package name "${packageName}" is invalid: ${message}`,
};
}
return { directory: targetDir.split(path.sep).join('/'), packageName };
}
// Get the project directory from the project name
// If the project name is a scoped package name, return the second part
// Otherwise, return the project name
export function getProjectDirFromPackageName(packageName: string) {
if (packageName.startsWith('@')) {
return packageName.split('/')[1];
}
return packageName;
}
export function setPackageName(projectDir: string, packageName: string) {
editJsonFile<{ name?: string }>(path.join(projectDir, 'package.json'), (pkg) => {
pkg.name = packageName;
return pkg;
});
}
export function formatDisplayTargetDir(targetDir: string) {
const normalized = targetDir.split(path.sep).join('/');
if (normalized === '' || normalized === '.') {
return './';
}
if (
normalized.startsWith('./') ||
normalized.startsWith('../') ||
normalized.startsWith('/') ||
normalized.startsWith('~')
) {
return normalized;
}
return `./${normalized}`;
}