@awesomeness-js/utils 1.0.12 β†’ 1.0.14

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.
package/README.md CHANGED
@@ -1,16 +1,149 @@
1
1
  # Nothing Special
2
2
 
3
- Just some utils...
3
+ Just some <u>zero dependency</u> utils ...
4
+
5
+
6
+ ---
7
+
8
+ # πŸš€ Auto-Generate API Exports for Your Node.js Project
9
+
10
+
11
+ ## πŸ“Œ Why "build" Exists
12
+
13
+ When working on a Node.js project, you often need to import multiple functions from a directory and structure them in a clean, accessible way. Webpack is overkill for thisβ€”it’s designed for browser bundling, not for generating structured API exports in a Node.js environment.
14
+
15
+ This script **automates** that process. It dynamically scans your source directory (`./src`), imports functions, and generates an `index.js` file that:
16
+
17
+ βœ… **Consolidates all exports** into a structured API object.
18
+ βœ… **Preserves function names and namespaces** based on folder structure.
19
+ βœ… **Extracts JSDoc comments** for better documentation.
20
+ βœ… **Works in plain Node.js**β€”no need for Webpack or extra dependencies.
21
+
22
+ ---
23
+
24
+ ## πŸš€ Why Use "build" Instead of Webpack?
25
+
26
+ **Webpack is great for frontend bundling, but it’s not ideal for this use case.** Here’s why this script is a better choice:
27
+
28
+ βœ” **Zero dependencies**β€”Runs in plain Node.js, no Webpack or config files required.
29
+ βœ” **Automatic function export generation**β€”No need to manually update an `index.js`.
30
+ βœ” **JSDoc extraction**β€”Includes comments directly in the generated file.
31
+ βœ” **Simple and predictable**β€”You control how exports are structured.
32
+ βœ” **Namespace support**β€”Uses folder structure to organize functions logically.
33
+
34
+ With this script, you can stop wasting time managing exports and focus on writing code.
35
+
36
+ ---
37
+
38
+ ## ⚑ How It Works
39
+
40
+ 1. **Scans** the `./src` directory for `.js` files.
41
+ 2. **Generates** import statements dynamically.
42
+ 3. **Creates** an API object that mirrors your folder structure.
43
+ 4. **Extracts JSDoc comments** from each file and attaches them to the exports.
44
+ 5. **Outputs** a clean, structured `index.js` file, ready to use.
45
+
46
+ ---
47
+
48
+ ## πŸ”§ Usage
49
+
50
+ Simply run:
51
+
52
+ By default, this will:
53
+ - Scan `./src` for JavaScript files
54
+ - Generate an `index.js` file
55
+ - Structure exports based on your folder hierarchy
56
+
57
+ ```javascript
58
+
59
+ import { build } from '@awesomeness-js/utils';
60
+
61
+ build();
62
+
63
+ ```
64
+
65
+ If you need custom paths, modify the `src` and `dest` options in the script:
66
+
67
+ ```javascript
4
68
 
5
- example usage:
6
- ```javascript
7
69
  import { build } from '@awesomeness-js/utils';
8
70
 
9
- const built = build({
10
- src: './src',
11
- dest: ['./', 'index.js']
71
+ build({
72
+ src: './my-functions',
73
+ dest: './api.js'
12
74
  });
75
+ ```
76
+
77
+ ---
78
+
79
+ ## πŸ“œ Example Output
80
+
81
+ If your folder structure looks like this:
82
+
83
+ ```
84
+ src/
85
+ │── utils/
86
+ β”‚ β”œβ”€β”€ formatDate.js
87
+ β”‚ β”œβ”€β”€ generateId.js
88
+ │── services/
89
+ β”‚ β”œβ”€β”€ fetchData.js
90
+ │── calculate.js
91
+ ```
92
+
93
+ Your generated `index.js` will look like this:
94
+
95
+ ```javascript
96
+ /**
97
+ * This file is auto-generated by the build script.
98
+ * It consolidates API functions for use in the application.
99
+ * Do not edit manually.
100
+ */
101
+
102
+ import utils_formatDate from './src/utils/formatDate.js';
103
+ import utils_generateId from './src/utils/generateId.js';
104
+ import services_fetchData from './src/services/fetchData.js';
105
+ import calculate from './src/calculate.js';
106
+
107
+ export { calculate };
108
+ export default {
109
+ utils: {
110
+ formatDate: utils_formatDate,
111
+ generateId: utils_generateId
112
+ },
113
+ services: {
114
+ fetchData: services_fetchData
115
+ },
116
+ calculate
117
+ };
118
+ ```
119
+
120
+ Your api is now neatly organized and ready to use!
121
+ and will look like this
122
+ ```javascript
123
+ import { calculate } from './api.js';
124
+ const result = calculate(5, 10);
125
+ console.log(result);
126
+ ```
127
+
128
+ or
129
+
130
+ ```javascript
131
+ import { utils } from './api.js';
132
+ const id = utils.generateId();
133
+ console.log(id);
134
+ ```
135
+
136
+
137
+
138
+ ---
139
+
140
+ ## πŸš€ Who Should Use This?
141
+
142
+ - **Node.js developers** who want automatic API exports.
143
+ - **Backend teams** managing large function directories.
144
+ - **Anyone tired of manually updating `index.js` files.**
145
+
146
+ If you don’t need Webpack’s **complexity** but want **automatic structured exports**, this script is for you.
13
147
 
14
- console.log({ built });
148
+ πŸ‘‰ **Try it out and let automation handle your exports!** πŸš€
15
149
 
16
- ```
package/build.js CHANGED
@@ -2,5 +2,10 @@ import build from './src/build.js';
2
2
 
3
3
  build({
4
4
  src: './src',
5
- dest: './index.js'
5
+ dest: './index.js',
6
+ ignore: [
7
+ 'ignoreMe.js',
8
+ 'ignoreFolder/*',
9
+ 'namespaceExample/*',
10
+ ],
6
11
  });
package/index.js CHANGED
@@ -28,16 +28,16 @@ export { _toPennies as toPennies };
28
28
  export { _uuid as uuid };
29
29
 
30
30
  export default {
31
+ /**${match[1]}*/
32
+ build: _build,
33
+ combineFiles: _combineFiles,
31
34
  /**
32
- * Generates a output file that consolidates all src functions.
35
+ * Converts a given number of bytes into a more readable string format with appropriate units.
33
36
  *
34
- * @param {Object} [options] - The options for generating the output file.
35
- * @param {string} [options.src='./src'] - The source directory.
36
- * @param {array} [options.dest=['./', 'index.js']] - The destination file.
37
- * @returns {bool} - Returns true if the output file is generated successfully.
37
+ * @param {number} bytes - The number of bytes to convert.
38
+ * @param {number} [precision=2] - The number of decimal places to include in the result.
39
+ * @returns {string} The converted bytes in a string format with appropriate units.
38
40
  */
39
- build: _build,
40
- combineFiles: _combineFiles,
41
41
  convertBytes: _convertBytes,
42
42
  each: _each,
43
43
  eachAsync: _eachAsync,
@@ -46,5 +46,5 @@ export default {
46
46
  md5: _md5,
47
47
  setLocalEnvs: _setLocalEnvs,
48
48
  toPennies: _toPennies,
49
- uuid: _uuid
49
+ uuid: _uuid,
50
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomeness-js/utils",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Awesomeness - Utils",
5
5
  "repository": {
6
6
  "type": "git",
package/src/build.js CHANGED
@@ -1,115 +1,161 @@
1
1
  import { readdirSync, statSync, writeFileSync, readFileSync } from 'fs';
2
2
  import { join, sep } from 'path';
3
3
 
4
- /**
5
- * Generates a output file that consolidates all src functions.
6
- *
7
- * @param {Object} [options] - The options for generating the output file.
8
- * @param {string} [options.src='./src'] - The source directory.
9
- * @param {array} [options.dest=['./', 'index.js']] - The destination file.
10
- * @returns {bool} - Returns true if the output file is generated successfully.
11
- */
12
-
4
+ function shouldIgnore(filePath, ignorePatterns) {
5
+ const ignore = ignorePatterns.some(pattern => {
6
+ const normalizedPath = filePath.replace(/\\/g, '/');
7
+ const normalizedPattern = pattern.replace(/\\/g, '/');
8
+ if (normalizedPath === normalizedPattern) return true;
9
+ if (normalizedPattern.endsWith('/*')) {
10
+ const baseDir = normalizedPattern.slice(0, -2);
11
+ return normalizedPath.startsWith(baseDir + '/');
12
+ }
13
+ if (normalizedPattern.endsWith('/')) {
14
+ return normalizedPath === normalizedPattern.slice(0, -1) ||
15
+ normalizedPath.startsWith(normalizedPattern);
16
+ }
17
+ return false;
18
+ });
19
+ //console.log('shouldIgnore', filePath, ignore);
20
+ return ignore;
21
+ }
13
22
 
14
- function getAllFiles(base, dir, files = []) {
23
+ function getAllFiles(base, dir, files = [], ignore = []) {
15
24
  const directory = join(base, dir);
16
- let sortedFiles = readdirSync(directory).sort();
25
+ const normalizedDir = dir.replace(/\\/g, '/');
26
+ if (ignore.some(pattern => normalizedDir.startsWith(pattern.replace(/\/\*$/, '')))) {
27
+ //.log('Ignoring folder:', normalizedDir);
28
+ return files;
29
+ }
30
+ let sortedFiles = readdirSync(directory).sort();
17
31
  sortedFiles.forEach(file => {
18
32
  const fullPath = join(directory, file);
19
-
20
- if (
21
- statSync(fullPath).isDirectory() &&
22
- file !== '_template'
23
- ) {
24
- files = getAllFiles(base, join(dir, file), files);
25
- } else if (file.endsWith('.js') && !file.match(/\..*\./)) { // Exclude files with two or more dots
26
- files.push(join(dir, file));
33
+ const relativePath = join(dir, file).replace(/\\/g, '/');
34
+ if (shouldIgnore(relativePath, ignore)) {
35
+ //console.log('Ignoring file:', relativePath);
36
+ return;
27
37
  }
28
- });
29
- return files;
30
- }
31
-
32
-
33
- function objectToString(obj, allComments, indent = 4, level = 1) {
34
- const spaces = ' '.repeat(indent * level);
35
- const entries = Object.entries(obj).map(([key, value]) => {
36
-
37
- if (typeof value === 'object') {
38
- return `${spaces}${key}: ${objectToString(value, allComments, indent, level + 1)}`;
38
+ if (statSync(fullPath).isDirectory() && file !== '_template') {
39
+ getAllFiles(base, join(dir, file), files, ignore);
40
+ } else if (file.endsWith('.js') && !file.match(/\..*\./)) {
41
+ files.push(relativePath);
39
42
  }
40
-
41
- if(key.startsWith('___')){
42
- let realKey = key.replace('___', '');
43
-
44
- // split comment based on lines and insert spaces
45
- let comment = allComments[value];
46
- comment = comment.split('\n').map((line) => `${spaces.replace(' ', '')} ${line}`).join('\n');
47
-
48
- return `${comment}\n${spaces}${realKey}: ${value}`;
49
- } else {
50
- return `${spaces}${key}: ${value}`;
51
- }
52
-
53
43
  });
54
-
55
- return `{\n${entries.join(',\n')}\n${' '.repeat(indent * (level - 1))}}`;
56
-
44
+ return files;
57
45
  }
58
46
 
59
-
60
47
  function extractJSDocComment(filePath) {
61
48
  const fileContent = readFileSync(filePath, 'utf8');
62
49
  const match = fileContent.match(/\/\*\*([\s\S]*?)\*\//);
63
50
  return match ? `/**${match[1]}*/` : '';
64
51
  }
65
52
 
53
+ /**
54
+ * Generates the export file contents.
55
+ *
56
+ * For each file, if includeComments is true, its JSDoc comment is placed
57
+ * immediately above its key in the default export object.
58
+ */
59
+ function generateExports(src, exportRoots, ignore, includeComments = false) {
60
+ const allFiles = getAllFiles(src, '.', [], ignore);
61
+ let importStatements = '';
62
+ let flatExports = [];
63
+ let nestedExports = {}; // Build a tree for nested exports
64
+
65
+ // Create file info objects
66
+ const fileDataList = allFiles.map(file => {
67
+ const normalizedFile = file.replace(/\\/g, '/');
68
+ const parts = normalizedFile.split('/');
69
+ const fileName = parts.pop();
70
+ const functionName = fileName.replace(/\.js$/, '');
71
+ const namespaceParts = parts;
72
+ const importVarName = namespaceParts.length > 0
73
+ ? '_' + [...namespaceParts, functionName].join('_')
74
+ : '_' + functionName;
75
+ const importPath = src + '/' + normalizedFile.replace(/\.js$/, '');
76
+ const jsDocComment = includeComments ? extractJSDocComment(join(src, normalizedFile)) : '';
77
+ return { normalizedFile, parts: namespaceParts, functionName, importVarName, importPath, jsDocComment };
78
+ });
66
79
 
67
- function generateExports(src, exportRoots) {
68
- const allFiles = [];
69
- const fnFiles = getAllFiles(src, '.');
70
- allFiles.push(...fnFiles);
71
-
72
- let imports = '';
73
- let allExports = '';
74
- let apiObject = {};
75
-
76
- let allComments = {};
77
-
80
+ // Generate import statements (without comments)
81
+ fileDataList.forEach(({ importVarName, importPath }) => {
82
+ importStatements += `import ${importVarName} from '${importPath}.js';\n`;
83
+ });
78
84
 
79
- for (const file of allFiles) {
80
- const parts = file.split(sep).filter(p => p !== '.');
81
- const functionName = parts.pop().replace('.js', '');
82
- const namespace = parts.join('_');
83
- const importPath = src + '/' + file.replace(/\.js$/, '').replace(/\\/g, '/');
84
- const filePath = join(src, file);
85
+ // Build flat exports and nested export tree.
86
+ fileDataList.forEach(({ parts, functionName, importVarName, jsDocComment }) => {
87
+ if (parts.length === 0) {
88
+ flatExports.push({ functionName, importVarName, jsDocComment });
89
+ } else {
90
+ let current = nestedExports;
91
+ parts.forEach(part => {
92
+ if (!current[part]) {
93
+ current[part] = {};
94
+ }
95
+ current = current[part];
96
+ });
97
+ // Store the leaf export along with its JSDoc comment.
98
+ current[functionName] = { importVarName, jsDocComment };
99
+ }
100
+ });
85
101
 
86
- // Extract JSDoc comment, if present
87
- let hasComment = '';
88
- const jsDocComment = extractJSDocComment(filePath);
89
- if(jsDocComment){
90
- allComments[`${namespace}_${functionName}`] = jsDocComment
91
- hasComment = '___'
92
- }
102
+ // Generate flat export statements for default export object.
103
+ let flatExportLines = '';
104
+ flatExports.forEach(({ functionName, importVarName, jsDocComment }) => {
105
+ if (exportRoots) {
106
+ if (includeComments && jsDocComment) {
107
+ // Indent each comment line by 4 spaces.
108
+ const indentedComment = jsDocComment.split('\n').map(line => ' ' + line).join('\n');
109
+ flatExportLines += indentedComment + '\n';
110
+ }
111
+ flatExportLines += ` ${functionName}: ${importVarName},\n`;
112
+ }
113
+ });
93
114
 
115
+ // Recursively generate code for nested namespaces,
116
+ // placing JSDoc comments above each leaf key.
117
+ function generateNamespaceCode(nsObj, indentLevel) {
118
+ const indent = ' '.repeat(indentLevel);
119
+ let lines = ['{'];
120
+ for (const key in nsObj) {
121
+ const value = nsObj[key];
122
+ if (typeof value === 'object' && value.hasOwnProperty('importVarName')) {
123
+ // Leaf node.
124
+ if (includeComments && value.jsDocComment) {
125
+ const indentedComment = value.jsDocComment.split('\n').map(line => indent + ' ' + line).join('\n');
126
+ lines.push(indentedComment);
127
+ }
128
+ lines.push(`${indent} ${key}: ${value.importVarName},`);
129
+ } else {
130
+ // Nested namespace.
131
+ const nestedCode = generateNamespaceCode(value, indentLevel + 1);
132
+ lines.push(`${indent} ${key}: ${nestedCode},`);
133
+ }
134
+ }
135
+ lines.push(indent + '}');
136
+ return lines.join('\n');
137
+ }
94
138
 
95
- // Generate import statement with JSDoc comment if available
96
- imports += `import ${namespace}_${functionName} from '${importPath}.js';\n`;
139
+ // Generate the default export object.
140
+ let namespaceExportLines = '';
141
+ for (const ns in nestedExports) {
142
+ const nsCode = generateNamespaceCode(nestedExports[ns], 1);
143
+ namespaceExportLines += ` ${ns}: ${nsCode},\n`;
144
+ }
97
145
 
98
- // generate exports
99
- if(exportRoots && !namespace){ allExports += `export { _${functionName} as ${functionName} };\n`; }
146
+ const defaultExportCode = 'export default {\n' +
147
+ flatExportLines +
148
+ namespaceExportLines +
149
+ '};';
100
150
 
101
- // Populate the API object structure
102
- let current = apiObject;
103
- for (const part of parts) {
104
- current[part] = current[part] || {};
105
- current = current[part];
151
+ // Generate individual flat export statements.
152
+ let flatExportStatements = '';
153
+ flatExports.forEach(({ functionName, importVarName }) => {
154
+ if (exportRoots) {
155
+ flatExportStatements += `export { ${importVarName} as ${functionName} };\n`;
106
156
  }
107
- current[`${hasComment}${functionName}`] = `${namespace}_${functionName}`;
108
- }
109
-
110
- const apiContent = 'export default ' + objectToString(apiObject, allComments) + ';';
157
+ });
111
158
 
112
- // Add a header comment
113
159
  const headerComment = `/**
114
160
  * This file is auto-generated by the build script.
115
161
  * It consolidates API functions for use in the application.
@@ -117,16 +163,21 @@ function generateExports(src, exportRoots) {
117
163
  */
118
164
  `;
119
165
 
120
- return headerComment + imports + '\n' + allExports + '\n' + apiContent;
166
+ return headerComment +
167
+ importStatements + '\n' +
168
+ flatExportStatements +
169
+ '\n' +
170
+ defaultExportCode;
121
171
  }
122
172
 
123
-
124
173
  async function build({
125
174
  src = './src',
126
175
  dest = './index.js',
127
- exportRoots = true
176
+ exportRoots = true,
177
+ ignore = [],
178
+ includeComments = true
128
179
  } = {}) {
129
- const indexContent = generateExports(src, exportRoots);
180
+ const indexContent = generateExports(src, exportRoots, ignore, includeComments);
130
181
  writeFileSync(dest, indexContent);
131
182
  return true;
132
183
  }
@@ -1,3 +1,11 @@
1
+
2
+ /**
3
+ * Converts a given number of bytes into a more readable string format with appropriate units.
4
+ *
5
+ * @param {number} bytes - The number of bytes to convert.
6
+ * @param {number} [precision=2] - The number of decimal places to include in the result.
7
+ * @returns {string} The converted bytes in a string format with appropriate units.
8
+ */
1
9
  export default function convertBytes(bytes, precision = 2) {
2
10
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
3
11
  bytes = Math.max(bytes, 0);
@@ -0,0 +1,3 @@
1
+ export default function ignoreMeTest() {
2
+ console.log('I should not be in the build!');
3
+ };
@@ -0,0 +1,3 @@
1
+ export default function ignoreMeTest() {
2
+ console.log('I should not be in the build!');
3
+ };
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * A function that returns the input value.
4
+ *
5
+ * @param {*} something - The input value to be returned.
6
+ * @returns {*} The same value that was passed as input.
7
+ */
8
+ export default function deep(something) {
9
+ return something;
10
+ }
@@ -0,0 +1,3 @@
1
+ export default function example() {
2
+ return true
3
+ }
package/test.js CHANGED
@@ -9,6 +9,9 @@ console.log({md5Test});
9
9
  let uuidTest = utils.uuid();
10
10
  console.log({uuidTest});
11
11
 
12
+ let convertBytesTest = utils.convertBytes(1024);
13
+ console.log({convertBytesTest});
14
+
12
15
  let isUUIDTest = utils.isUUID(uuidTest);
13
16
  console.log({isUUIDTest});
14
17