@voxframeworks/linetools 1.0.2 → 1.0.5

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.
@@ -0,0 +1,3 @@
1
+ {
2
+ "liveServer.settings.port": 5501
3
+ }
package/main.js CHANGED
@@ -4,11 +4,13 @@ const touch = require('./tools/touch.js');
4
4
  const mv = require('./tools/mv.js');
5
5
  const tar = require('./tools/tar.js');
6
6
  const rmdir = require('./tools/rmdir.js');
7
+ const chmod = require('./tools/chmod.js');
7
8
  module.exports = {
8
9
  mkdir : mkdir,
9
10
  cp : cp,
10
11
  touch : touch,
11
12
  mv : mv,
12
13
  rmdir : rmdir,
13
- tar : tar
14
+ tar : tar,
15
+ chmod : chmod
14
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxframeworks/linetools",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "command line tools for js",
5
5
  "main": "main.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -29,6 +29,11 @@ TestOut = Directory for the zip to be put in
29
29
  rezip = zip
30
30
  unzip = unzip
31
31
  ------------------
32
+ Chmod tips:
33
+ 7 = read, write, execute
34
+ 5 = read, execute
35
+ 0 = none
36
+ ------------------
32
37
  We will be adding more soon!
33
38
 
34
39
  By VoxFrameworks
package/test.js CHANGED
@@ -1,2 +1,3 @@
1
1
  const linetools = require('linetools');
2
2
  linetools.tar('testOUT', 'testDir', 'rezip')
3
+ linetools.chmod('testDir', '755')
package/tools/chmod.js ADDED
@@ -0,0 +1,122 @@
1
+ // lib/chmod.js
2
+ /**
3
+ * Change the permissions of a file or directory.
4
+ *
5
+ * @param {string} targetPath – Path to file or directory.
6
+ * @param {string|number} mode – Permission bits.
7
+ * * If a string: interpreted as octal (e.g. '755')
8
+ * * If a number: used directly (e.g. 0o755)
9
+ * @param {object} [options] – Optional flags.
10
+ * @param {boolean} [options.recursive] – If true and target is a directory,
11
+ * change permissions of every descendant.
12
+ *
13
+ * @returns {Promise<void>}
14
+ *
15
+ * @example
16
+ * // change a single file
17
+ * await chmod('tmp/foo.txt', '644');
18
+ *
19
+ * // change a folder recursively
20
+ * await chmod('tmp/myDir', 0o755, { recursive: true });
21
+ */
22
+ const { promises: fs } = require('fs');
23
+ const path = require('path');
24
+
25
+ /**
26
+ * Convert a user‑provided mode to a proper numeric value.
27
+ * Accepts:
28
+ * • string like "755", "0644", "0o777"
29
+ * • number (already a mode)
30
+ */
31
+ function normalizeMode(mode) {
32
+ if (typeof mode === 'number') return mode; // already numeric
33
+ if (typeof mode !== 'string')
34
+ throw new TypeError('Mode must be a string or a number');
35
+
36
+ // Strip common prefixes (0o, 0, etc.) and parse as octal.
37
+ const clean = mode.replace(/^0o?/, '');
38
+ const parsed = parseInt(clean, 8);
39
+ if (Number.isNaN(parsed))
40
+ throw new Error(`Invalid permission mode: "${mode}"`);
41
+ return parsed;
42
+ }
43
+
44
+ /**
45
+ * Recursively walk a directory tree and collect *all* entries (files + dirs).
46
+ * Returns an array of absolute paths.
47
+ */
48
+ async function walkRecursively(dir) {
49
+ const result = [];
50
+
51
+ async function walk(current) {
52
+ const entries = await fs.readdir(current, { withFileTypes: true });
53
+ for (const entry of entries) {
54
+ const full = path.join(current, entry.name);
55
+ result.push(full);
56
+ if (entry.isDirectory()) await walk(full);
57
+ }
58
+ }
59
+
60
+ await walk(dir);
61
+ return result;
62
+ }
63
+
64
+ /**
65
+ * The public API – async function.
66
+ */
67
+ async function chmod(targetPath, mode, options = {}) {
68
+ const normalizedMode = normalizeMode(mode);
69
+ const { recursive = false } = options;
70
+
71
+ // Verify the target exists first – otherwise `fs.chmod` would throw a less‑friendly ENOENT.
72
+ let stats;
73
+ try {
74
+ stats = await fs.lstat(targetPath);
75
+ } catch (err) {
76
+ throw new Error(`Path does not exist: ${targetPath}`);
77
+ }
78
+
79
+ // --------------------------------------------------------------
80
+ // 1️⃣ Non‑recursive case (file OR directory)
81
+ // --------------------------------------------------------------
82
+ if (!recursive) {
83
+ await fs.chmod(targetPath, normalizedMode);
84
+ return;
85
+ }
86
+
87
+ // --------------------------------------------------------------
88
+ // 2️⃣ Recursive case – target must be a directory
89
+ // --------------------------------------------------------------
90
+ if (!stats.isDirectory())
91
+ throw new Error(
92
+ `Recursive chmod can only be used on directories. "${targetPath}" is not a directory.`
93
+ );
94
+
95
+ // Change the *directory* itself first (so it matches the children afterwards)
96
+ await fs.chmod(targetPath, normalizedMode);
97
+
98
+ // Walk the tree and chmod everything we find.
99
+ const allPaths = await walkRecursively(targetPath);
100
+
101
+ // Use `Promise.all` but limit concurrency a bit to avoid overwhelming the FS on huge trees.
102
+ const MAX_CONCURRENT = 100;
103
+ const queue = [...allPaths];
104
+ const workers = Array.from({ length: MAX_CONCURRENT }, async () => {
105
+ while (queue.length) {
106
+ const p = queue.pop();
107
+ try {
108
+ await fs.chmod(p, normalizedMode);
109
+ } catch (e) {
110
+ // We *continue* on errors so the rest of the tree still gets processed.
111
+ console.warn(`⚠️ Could not chmod "${p}": ${e.message}`);
112
+ }
113
+ }
114
+ });
115
+
116
+ await Promise.all(workers);
117
+ }
118
+
119
+ /* ------------------------------------------------------------------ */
120
+ /* Export for the public API */
121
+ /* ------------------------------------------------------------------ */
122
+ module.exports = chmod;