@stonyx/utils 0.1.0 → 0.1.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.
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - dev
7
+ - main
8
+
9
+ concurrency:
10
+ group: ci-${{ github.head_ref || github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Checkout code
19
+ uses: actions/checkout@v3
20
+
21
+ - name: Set up Node.js
22
+ uses: actions/setup-node@v3
23
+ with:
24
+ node-version: 22.18.0
25
+ cache: 'npm'
26
+
27
+ - name: Install dependencies
28
+ run: npm ci
29
+
30
+ - name: Run tests
31
+ run: npm test
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # stonyx-utils
2
2
 
3
+ # TODO: Generate documentation for all utils
4
+
3
5
  ## Running the test suite
4
6
  ```
5
7
  npm test
@@ -27,3 +29,6 @@ The File utils wrap the `path` and `fs` library to allow consuming classes to ma
27
29
  | `rawName` | **Boolean** | *false* | When set to true, `forEachFileImport` will not convert the file name to be camelCase and leave it raw instead |
28
30
  | `ignoreAccessFailure` | **Boolean** | *false* | When set to true, failure to load directory will be ignored |
29
31
 
32
+ ## License
33
+
34
+ Apache — do what you want, just keep attribution.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "keywords": [
4
4
  "stonyx-module"
5
5
  ],
6
- "version": "0.1.0",
6
+ "version": "0.1.1",
7
7
  "description": "Utils module for Stonyx Framework",
8
8
  "type": "module",
9
9
  "exports": {
@@ -24,5 +24,6 @@
24
24
  "devDependencies": {
25
25
  "fs": "^0.0.1-security",
26
26
  "qunit": "^2.24.1"
27
- }
27
+ },
28
+ "dependencies": {}
28
29
  }
package/src/file.js CHANGED
@@ -4,20 +4,23 @@ import { objToJson } from '@stonyx/utils/object';
4
4
  import { promises as fsp } from 'fs';
5
5
  import path from 'path';
6
6
 
7
- export async function createFile(filePath, data, { json }) {
7
+ export async function createFile(filePath, data, options={}) {
8
8
  try {
9
- await fsp.writeFile(filePath, json ? objToJson(data) : data, 'utf8');
9
+ filePath = path.resolve(filePath);
10
+
11
+ await createDirectory(path.dirname(filePath));
12
+ await fsp.writeFile(filePath, options.json ? objToJson(data) : data, 'utf8');
10
13
  } catch (error) {
11
14
  throw new Error(error);
12
15
  }
13
16
  }
14
17
 
15
- export async function updateFile(filePath, data, { json }) {
18
+ export async function updateFile(filePath, data, options={}) {
16
19
  try {
17
20
  await fsp.access(filePath);
18
21
 
19
22
  const swapFile = `${filePath}.temp-${getTimestamp()}`;
20
- await fsp.writeFile(swapFile, json ? objToJson(data) : data);
23
+ await fsp.writeFile(swapFile, options.json ? objToJson(data) : data);
21
24
  await fsp.rename(swapFile, filePath);
22
25
  } catch (error) {
23
26
 
@@ -25,15 +28,40 @@ export async function updateFile(filePath, data, { json }) {
25
28
  }
26
29
  }
27
30
 
28
- export async function readFile(filePath, { json, missingFileCallback }) {
31
+ export async function copyFile(sourcePath, targetPath, options={}) {
32
+ try {
33
+ sourcePath = path.resolve(sourcePath);
34
+ targetPath = path.resolve(targetPath);
35
+ await fsp.access(sourcePath);
36
+ } catch (error) {
37
+ throw new Error(error);
38
+ }
39
+
40
+ try {
41
+ await fsp.access(targetPath);
42
+ if (!options.overwrite) return false;
43
+ } catch {}
44
+
45
+ try {
46
+ await fsp.copyFile(sourcePath, targetPath);
47
+ } catch (error) {
48
+ throw new Error(error);
49
+ }
50
+
51
+ return true;
52
+ }
53
+
54
+ export async function readFile(filePath, options={}) {
29
55
  try {
30
56
  filePath = path.resolve(filePath);
31
57
 
32
58
  await fsp.access(filePath);
33
59
  const fileData = await fsp.readFile(filePath, 'utf8');
34
60
 
35
- return json ? JSON.parse(fileData) : fileData;
61
+ return options.json ? JSON.parse(fileData) : fileData;
36
62
  } catch (error) {
63
+ const { missingFileCallback } = options;
64
+
37
65
  if (error.code === 'ENOENT' && missingFileCallback) {
38
66
  return missingFileCallback(filePath);
39
67
  }
@@ -90,3 +118,13 @@ export async function forEachFileImport(dir, callback, options={}) {
90
118
  callback(output, { name, stats, path: filePath });
91
119
  }
92
120
  }
121
+
122
+ export async function fileExists(filePath) {
123
+ try {
124
+ filePath = path.resolve(filePath);
125
+ await fsp.access(filePath);
126
+ return true;
127
+ } catch (error) {
128
+ return false;
129
+ }
130
+ }
package/src/object.js CHANGED
@@ -6,42 +6,34 @@ export function objToJson(obj) {
6
6
  return JSON.stringify(obj);
7
7
  }
8
8
 
9
- export function mergeObject(obj1, obj2) {
9
+ export function makeArray(obj) {
10
+ return Array.isArray(obj) ? obj : [obj];
11
+ }
12
+
13
+ export function mergeObject(obj1, obj2, options={}) {
10
14
  if (Array.isArray(obj1) || Array.isArray(obj2)) throw new Error('Cannot merge arrays.');
11
- if (typeof obj1 !== 'object' || obj1 === null) return structuredClone(obj2);
12
- if (typeof obj2 !== 'object' || obj2 === null) return structuredClone(obj1);
15
+
16
+ if (obj1 === null || typeof obj1 !== 'object') return cloneShallow(obj2);
17
+ if (obj2 === null || typeof obj2 !== 'object') return cloneShallow(obj1);
13
18
 
14
19
  const result = {};
15
- const keys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
16
20
 
17
- for (const key of keys) {
21
+ for (const key of Object.keys(obj1)) result[key] = cloneShallow(obj1[key]);
22
+ for (const key of Object.keys(obj2)) {
23
+ if (options.ignoreNewKeys && !(key in obj1)) continue;
24
+
18
25
  const val1 = obj1[key];
19
26
  const val2 = obj2[key];
20
-
21
- if (val2 === undefined) {
22
- result[key] = structuredClone(val1);
23
- } else if (val1 === undefined) {
24
- result[key] = structuredClone(val2);
25
- } else if (
26
- typeof val1 === 'object' &&
27
- typeof val2 === 'object' &&
28
- val1 !== null &&
29
- val2 !== null &&
30
- !Array.isArray(val1) &&
31
- !Array.isArray(val2)
32
- ) {
33
- result[key] = mergeObject(val1, val2);
34
- } else {
35
- result[key] = structuredClone(val2);
36
- }
27
+ const shouldMerge = val1 && val2 && typeof val1 === 'object' && typeof val2 === 'object' && !Array.isArray(val1) && !Array.isArray(val2);
28
+ result[key] = shouldMerge ? mergeObject(val1, val2, options) : cloneShallow(val2);
29
+
37
30
  }
38
31
 
39
32
  return result;
40
33
  }
41
34
 
42
- export function makeArray(value) {
43
- if (Array.isArray(value)) return value;
44
- if (value === null || value === undefined) return [];
45
-
46
- return [value];
35
+ function cloneShallow(value) {
36
+ if (Array.isArray(value)) return value.slice();
37
+ if (value && typeof value === 'object') return { ...value };
38
+ return value;
47
39
  }
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- v22.18.0
@@ -1,32 +0,0 @@
1
- import Qunit from 'qunit';
2
- import { mergeObject } from '@stonyx/utils/object';
3
-
4
- const { module, test } = Qunit;
5
-
6
- module('[Unit] Object', function() {
7
- test('mergeObject works as expected', async function(assert) {
8
- assert.deepEqual(
9
- mergeObject({ a: 1, b: 2 }, { c: 3 }),
10
- { a: 1, b: 2, c: 3 },
11
- 'New properties are added'
12
- );
13
-
14
- assert.deepEqual(
15
- mergeObject({ a: 1, b: 2, c: 3 }, { c: 4 }),
16
- { a: 1, b: 2, c: 4 },
17
- 'Old properties are overwritten'
18
- );
19
-
20
- assert.deepEqual(
21
- mergeObject({ a: 1, b: 2, c: 3 }, { a: 7, c: 23, d: { a: 1, b: 2, c: 3 } }),
22
- { a: 7, b: 2, c: 23, d: { a: 1, b: 2, c: 3 } },
23
- 'Objects are merged recursively'
24
- );
25
-
26
- try {
27
- mergeObject([], {});
28
- } catch {
29
- assert.ok(true, 'Array inputs throws error');
30
- }
31
- });
32
- });