create-contractor-site 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,96 +1,96 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- /**
5
- * Normalize a path for stable prefix comparisons on Windows and POSIX.
6
- *
7
- * @param {string} value
8
- * @returns {string}
9
- */
10
- export function normalizePathForCompare(value) {
11
- const resolved = path.resolve(value);
12
- // path.resolve already drops trailing separators except root; lower-case on win32.
13
- const normalized =
14
- process.platform === 'win32' ? resolved.toLowerCase() : resolved;
15
- // Ensure directory prefix checks use a trailing separator boundary.
16
- return normalized;
17
- }
18
-
19
- /**
20
- * True when `candidate` is the same path as `root` or a path inside `root`.
21
- *
22
- * @param {string} candidate
23
- * @param {string} root
24
- * @returns {boolean}
25
- */
26
- export function isSameOrInside(candidate, root) {
27
- const child = normalizePathForCompare(candidate);
28
- const parent = normalizePathForCompare(root);
29
-
30
- if (child === parent) return true;
31
-
32
- const prefix = parent.endsWith(path.sep) ? parent : parent + path.sep;
33
- return child.startsWith(prefix);
34
- }
35
-
36
- /**
37
- * Resolve and validate the scaffold target directory.
38
- * Refuses existing non-empty directories.
39
- * When `templateRoot` is provided, refuses targets equal to or inside the template.
40
- *
41
- * @param {string} rawTarget
42
- * @param {{ templateRoot?: string }} [opts]
43
- * @returns {string} absolute target path
44
- */
45
- export function validateTarget(rawTarget, opts = {}) {
46
- if (!rawTarget || !String(rawTarget).trim()) {
47
- throw new TargetValidationError(
48
- 'Missing target directory.\n\nUsage: create-contractor-site <target-dir>',
49
- );
50
- }
51
-
52
- const targetDir = path.resolve(process.cwd(), String(rawTarget).trim());
53
- const { templateRoot } = opts;
54
-
55
- if (templateRoot) {
56
- const root = path.resolve(templateRoot);
57
- if (isSameOrInside(targetDir, root)) {
58
- throw new TargetValidationError(
59
- `Target directory must not be the template root or inside it.\n` +
60
- ` Template: ${root}\n` +
61
- ` Target: ${targetDir}\n` +
62
- `Choose a directory outside the template repository.`,
63
- );
64
- }
65
- }
66
-
67
- if (!fs.existsSync(targetDir)) {
68
- return targetDir;
69
- }
70
-
71
- const stat = fs.statSync(targetDir);
72
- if (!stat.isDirectory()) {
73
- throw new TargetValidationError(
74
- `Target path exists and is not a directory: ${targetDir}`,
75
- );
76
- }
77
-
78
- const entries = fs.readdirSync(targetDir);
79
- if (entries.length > 0) {
80
- throw new TargetValidationError(
81
- `Target directory is not empty: ${targetDir}\nChoose a different directory or remove existing files first.`,
82
- );
83
- }
84
-
85
- return targetDir;
86
- }
87
-
88
- export class TargetValidationError extends Error {
89
- /**
90
- * @param {string} message
91
- */
92
- constructor(message) {
93
- super(message);
94
- this.name = 'TargetValidationError';
95
- }
96
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * Normalize a path for stable prefix comparisons on Windows and POSIX.
6
+ *
7
+ * @param {string} value
8
+ * @returns {string}
9
+ */
10
+ export function normalizePathForCompare(value) {
11
+ const resolved = path.resolve(value);
12
+ // path.resolve already drops trailing separators except root; lower-case on win32.
13
+ const normalized =
14
+ process.platform === 'win32' ? resolved.toLowerCase() : resolved;
15
+ // Ensure directory prefix checks use a trailing separator boundary.
16
+ return normalized;
17
+ }
18
+
19
+ /**
20
+ * True when `candidate` is the same path as `root` or a path inside `root`.
21
+ *
22
+ * @param {string} candidate
23
+ * @param {string} root
24
+ * @returns {boolean}
25
+ */
26
+ export function isSameOrInside(candidate, root) {
27
+ const child = normalizePathForCompare(candidate);
28
+ const parent = normalizePathForCompare(root);
29
+
30
+ if (child === parent) return true;
31
+
32
+ const prefix = parent.endsWith(path.sep) ? parent : parent + path.sep;
33
+ return child.startsWith(prefix);
34
+ }
35
+
36
+ /**
37
+ * Resolve and validate the scaffold target directory.
38
+ * Refuses existing non-empty directories.
39
+ * When `templateRoot` is provided, refuses targets equal to or inside the template.
40
+ *
41
+ * @param {string} rawTarget
42
+ * @param {{ templateRoot?: string }} [opts]
43
+ * @returns {string} absolute target path
44
+ */
45
+ export function validateTarget(rawTarget, opts = {}) {
46
+ if (!rawTarget || !String(rawTarget).trim()) {
47
+ throw new TargetValidationError(
48
+ 'Missing target directory.\n\nUsage: create-contractor-site <target-dir>',
49
+ );
50
+ }
51
+
52
+ const targetDir = path.resolve(process.cwd(), String(rawTarget).trim());
53
+ const { templateRoot } = opts;
54
+
55
+ if (templateRoot) {
56
+ const root = path.resolve(templateRoot);
57
+ if (isSameOrInside(targetDir, root)) {
58
+ throw new TargetValidationError(
59
+ `Target directory must not be the template root or inside it.\n` +
60
+ ` Template: ${root}\n` +
61
+ ` Target: ${targetDir}\n` +
62
+ `Choose a directory outside the template repository.`,
63
+ );
64
+ }
65
+ }
66
+
67
+ if (!fs.existsSync(targetDir)) {
68
+ return targetDir;
69
+ }
70
+
71
+ const stat = fs.statSync(targetDir);
72
+ if (!stat.isDirectory()) {
73
+ throw new TargetValidationError(
74
+ `Target path exists and is not a directory: ${targetDir}`,
75
+ );
76
+ }
77
+
78
+ const entries = fs.readdirSync(targetDir);
79
+ if (entries.length > 0) {
80
+ throw new TargetValidationError(
81
+ `Target directory is not empty: ${targetDir}\nChoose a different directory or remove existing files first.`,
82
+ );
83
+ }
84
+
85
+ return targetDir;
86
+ }
87
+
88
+ export class TargetValidationError extends Error {
89
+ /**
90
+ * @param {string} message
91
+ */
92
+ constructor(message) {
93
+ super(message);
94
+ this.name = 'TargetValidationError';
95
+ }
96
+ }