@tiveor/scg 0.1.6 → 0.2.0
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/.prettierrc +0 -3
- package/README.md +151 -44
- package/package.json +17 -3
- package/src/command_helper.js +14 -15
- package/src/file_helper.js +25 -27
- package/src/string_helper.js +8 -2
- package/src/template_builder.js +1 -1
- package/src/template_handlers.js +1 -1
- package/test/test.js +390 -7
- package/.vscode/settings.json +0 -5
package/.prettierrc
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"singleQuote": true,
|
|
3
3
|
"printWidth": 80,
|
|
4
|
-
"editor.formatOnSave": true,
|
|
5
4
|
"proseWrap": "always",
|
|
6
5
|
"tabWidth": 2,
|
|
7
|
-
"requireConfig": false,
|
|
8
6
|
"useTabs": false,
|
|
9
7
|
"trailingComma": "none",
|
|
10
8
|
"bracketSpacing": true,
|
|
11
|
-
"jsxBracketSameLine": false,
|
|
12
9
|
"semi": true
|
|
13
10
|
}
|
package/README.md
CHANGED
|
@@ -1,64 +1,171 @@
|
|
|
1
|
-
SCG
|
|
2
|
-
|
|
1
|
+
# SCG - Simple Code Generator
|
|
2
|
+
|
|
3
3
|
[](https://badge.fury.io/js/%40tiveor%2Fscg)
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
A utility library for code generation and template processing in Node.js. Provides helpers for template rendering (EJS, Handlebars, Pug), string manipulation, file operations, command execution, and CLI parameter parsing.
|
|
5
6
|
|
|
6
7
|
## Installation
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install @tiveor/scg
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
12
15
|
```javascript
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
const {
|
|
17
|
+
StringHelper,
|
|
18
|
+
FileHelper,
|
|
19
|
+
CommandHelper,
|
|
20
|
+
ParamHelper,
|
|
21
|
+
TemplateBuilder,
|
|
22
|
+
TEMPLATE_HANDLERS
|
|
23
|
+
} = require('@tiveor/scg');
|
|
16
24
|
```
|
|
17
25
|
|
|
18
|
-
##
|
|
26
|
+
## API Reference
|
|
27
|
+
|
|
28
|
+
### TemplateBuilder
|
|
29
|
+
|
|
30
|
+
Unified interface to render templates with EJS, Handlebars, or Pug.
|
|
31
|
+
|
|
19
32
|
```javascript
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
.then((replaced) => {
|
|
28
|
-
// replaced = "This is a joke"
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const pugBuilder = new TemplateBuilder(TEMPLATE_HANDLERS.PUG);
|
|
32
|
-
pugBuilder
|
|
33
|
-
.render('This is a #{test}', {
|
|
34
|
-
test: 'joke'
|
|
35
|
-
})
|
|
36
|
-
.then((replaced) => {
|
|
37
|
-
// replaced = "This is a joke"
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const handlebarsBuilder = new TemplateBuilder(TEMPLATE_HANDLERS.HANDLEBARS);
|
|
41
|
-
handlebarsBuilder
|
|
42
|
-
.render('This is a {{test}}', {
|
|
43
|
-
test: 'joke'
|
|
44
|
-
})
|
|
45
|
-
.then((replaced) => {
|
|
46
|
-
// replaced = "This is a joke"
|
|
47
|
-
});
|
|
33
|
+
const builder = new TemplateBuilder(TEMPLATE_HANDLERS.EJS);
|
|
34
|
+
|
|
35
|
+
// Render from string
|
|
36
|
+
const html = await builder.render('Hello <%= name %>', { name: 'World' });
|
|
37
|
+
|
|
38
|
+
// Render from file
|
|
39
|
+
const page = await builder.renderFile('template.ejs', { title: 'Home' });
|
|
48
40
|
```
|
|
49
41
|
|
|
50
|
-
|
|
42
|
+
Available handlers: `TEMPLATE_HANDLERS.EJS`, `TEMPLATE_HANDLERS.HANDLEBARS`, `TEMPLATE_HANDLERS.PUG`
|
|
43
|
+
|
|
44
|
+
#### Examples with each engine
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// EJS
|
|
48
|
+
const ejs = new TemplateBuilder(TEMPLATE_HANDLERS.EJS);
|
|
49
|
+
await ejs.render('Hello <%= name %>', { name: 'World' });
|
|
50
|
+
|
|
51
|
+
// Handlebars
|
|
52
|
+
const hbs = new TemplateBuilder(TEMPLATE_HANDLERS.HANDLEBARS);
|
|
53
|
+
await hbs.render('Hello {{name}}', { name: 'World' });
|
|
54
|
+
|
|
55
|
+
// Pug
|
|
56
|
+
const pug = new TemplateBuilder(TEMPLATE_HANDLERS.PUG);
|
|
57
|
+
await pug.render('p #{name}', { name: 'World' });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### StringHelper
|
|
61
|
+
|
|
62
|
+
Static methods for string manipulation.
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// Replace all occurrences of a token (regex-safe)
|
|
66
|
+
StringHelper.replace('Hello {{name}}!', '{{name}}', 'World');
|
|
67
|
+
// => "Hello World!"
|
|
68
|
+
|
|
69
|
+
// Works safely with regex special characters
|
|
70
|
+
StringHelper.replace('Price: $10.00', '$10.00', '$20.00');
|
|
71
|
+
// => "Price: $20.00"
|
|
72
|
+
|
|
73
|
+
// Capitalize first character
|
|
74
|
+
StringHelper.capitalize('hello');
|
|
75
|
+
// => "Hello"
|
|
76
|
+
|
|
77
|
+
// Escape regex special characters
|
|
78
|
+
StringHelper.escapeRegex('$100.00 (test)');
|
|
79
|
+
// => "\\$100\\.00 \\(test\\)"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### FileHelper
|
|
83
|
+
|
|
84
|
+
Static methods for file system operations.
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Read file to string
|
|
88
|
+
const content = FileHelper.readFileToString('config.txt');
|
|
89
|
+
|
|
90
|
+
// Parse JSON file to object
|
|
91
|
+
const config = FileHelper.convertJsonFileToObject('config.json');
|
|
92
|
+
|
|
93
|
+
// Create/remove folders
|
|
94
|
+
FileHelper.createFolder('output/components');
|
|
95
|
+
FileHelper.removeFolder('output/temp');
|
|
96
|
+
|
|
97
|
+
// Remove file
|
|
98
|
+
FileHelper.removeFile('output/old.txt');
|
|
99
|
+
|
|
100
|
+
// Generate file from template with variable replacement
|
|
101
|
+
await FileHelper.createFileFromFile({
|
|
102
|
+
template: 'templates/component.txt',
|
|
103
|
+
newFile: 'output/Button.tsx',
|
|
104
|
+
variables: [
|
|
105
|
+
{ token: '{{name}}', value: 'Button' },
|
|
106
|
+
{ token: '{{style}}', value: 'primary' }
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Generate string from template
|
|
111
|
+
const result = await FileHelper.createStringFromFile({
|
|
112
|
+
template: 'templates/component.txt',
|
|
113
|
+
variables: [
|
|
114
|
+
{ token: '{{name}}', value: 'Button' }
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### CommandHelper
|
|
120
|
+
|
|
121
|
+
Execute shell commands as promises.
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// Run a command
|
|
125
|
+
const output = await CommandHelper.run('.', 'ls -la');
|
|
126
|
+
|
|
127
|
+
// Chain multiple commands
|
|
128
|
+
const result = await CommandHelper.run('.', 'git add .', 'git status');
|
|
129
|
+
|
|
130
|
+
// Run and clean output (strips newlines)
|
|
131
|
+
const version = await CommandHelper.runClean('.', 'node --version');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### ParamHelper
|
|
135
|
+
|
|
136
|
+
Parse CLI parameters from `process.argv`.
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
// Add custom parameters
|
|
140
|
+
ParamHelper.addCustomParam('--env=production');
|
|
141
|
+
|
|
142
|
+
// Get parameter by index
|
|
143
|
+
const script = ParamHelper.getCommandByIndex(1);
|
|
144
|
+
|
|
145
|
+
// Parse all --key=value parameters
|
|
146
|
+
// Example: node script.js --name=Alice --port=3000
|
|
147
|
+
const params = ParamHelper.getParams();
|
|
148
|
+
// => { name: "Alice", port: "3000" }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Running Examples
|
|
152
|
+
|
|
51
153
|
```bash
|
|
52
154
|
node example/index.js
|
|
53
155
|
```
|
|
54
156
|
|
|
55
|
-
|
|
157
|
+
## Running Tests
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm test
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Template Engine Documentation
|
|
56
164
|
|
|
57
|
-
|
|
58
|
-
https://
|
|
165
|
+
- [EJS](https://ejs.co/#docs)
|
|
166
|
+
- [Pug](https://pugjs.org/api/getting-started.html)
|
|
167
|
+
- [Handlebars](https://handlebarsjs.com/guide/)
|
|
59
168
|
|
|
60
|
-
|
|
61
|
-
https://pugjs.org/api/getting-started.html
|
|
169
|
+
## License
|
|
62
170
|
|
|
63
|
-
|
|
64
|
-
https://handlebarsjs.com/guide/
|
|
171
|
+
MIT - Alvaro Orellana - AlvaroTech.dev
|
package/package.json
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiveor/scg",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Simple Code Generator - A utility library for code generation and template processing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "mocha ./test"
|
|
7
|
+
"test": "mocha ./test",
|
|
8
|
+
"example": "node example/index.js"
|
|
8
9
|
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=14.0.0"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"code-generator",
|
|
15
|
+
"template",
|
|
16
|
+
"scaffolding",
|
|
17
|
+
"ejs",
|
|
18
|
+
"handlebars",
|
|
19
|
+
"pug",
|
|
20
|
+
"file-helper",
|
|
21
|
+
"string-helper"
|
|
22
|
+
],
|
|
9
23
|
"repository": {
|
|
10
24
|
"type": "git",
|
|
11
25
|
"url": "git+https://github.com/tiveor/scg.git"
|
package/src/command_helper.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
const { exec } = require(
|
|
1
|
+
const { exec } = require('child_process');
|
|
2
2
|
|
|
3
3
|
class CommandHelper {
|
|
4
4
|
static run(directory, ...command) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
if (!directory) {
|
|
6
|
+
return Promise.reject(new Error('directory is required'));
|
|
7
|
+
}
|
|
8
|
+
if (command.length === 0) {
|
|
9
|
+
return Promise.reject(new Error('at least one command is required'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const cmd = command.join(' && ');
|
|
8
14
|
exec(
|
|
9
15
|
cmd,
|
|
10
16
|
{ cwd: directory, maxBuffer: 1024 * 1024 * 100 },
|
|
@@ -15,26 +21,19 @@ class CommandHelper {
|
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
if (stderr) {
|
|
18
|
-
|
|
24
|
+
resolve(stderr);
|
|
19
25
|
return;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
resolve(stdout);
|
|
23
29
|
}
|
|
24
30
|
);
|
|
25
|
-
}).catch((error) => {
|
|
26
|
-
console.log(error);
|
|
27
31
|
});
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
static runClean(folder, ...command) {
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
.then((cmdRes) => {
|
|
34
|
-
const config = cmdRes && cmdRes.replace(/\r?\n|\r/g, "");
|
|
35
|
-
ok(config);
|
|
36
|
-
})
|
|
37
|
-
.catch(reject);
|
|
35
|
+
return CommandHelper.run(folder, ...command).then((result) => {
|
|
36
|
+
return result && result.replace(/\r?\n|\r/g, '');
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
}
|
package/src/file_helper.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const { StringHelper } = require('./string_helper');
|
|
5
5
|
|
|
6
6
|
class FileHelper {
|
|
7
7
|
static readFileToString(fileName) {
|
|
@@ -18,7 +18,7 @@ class FileHelper {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
static async dynamicReplace(replacement) {
|
|
21
|
-
let res =
|
|
21
|
+
let res = '';
|
|
22
22
|
for (let v in replacement.variables) {
|
|
23
23
|
const properties = replacement.variables[v];
|
|
24
24
|
await FileHelper.readLineByLine(replacement.template, (line) => {
|
|
@@ -43,7 +43,7 @@ class FileHelper {
|
|
|
43
43
|
await FileHelper.readLineByLine(template, async (line) => {
|
|
44
44
|
let newLine = StringHelper.replace(
|
|
45
45
|
line,
|
|
46
|
-
|
|
46
|
+
'@date',
|
|
47
47
|
new Date().toUTCString()
|
|
48
48
|
);
|
|
49
49
|
|
|
@@ -52,7 +52,6 @@ class FileHelper {
|
|
|
52
52
|
|
|
53
53
|
if (newLine.indexOf(replacement.token) > 0) {
|
|
54
54
|
if (replacement.template) {
|
|
55
|
-
//dynamic since there is no value
|
|
56
55
|
newLine = await FileHelper.dynamicReplace(replacement);
|
|
57
56
|
break;
|
|
58
57
|
} else if (replacement.value) {
|
|
@@ -67,12 +66,12 @@ class FileHelper {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
static async createStringFromFile({ template, variables }) {
|
|
70
|
-
let res =
|
|
69
|
+
let res = '';
|
|
71
70
|
|
|
72
71
|
await FileHelper.readLineByLine(template, async (line) => {
|
|
73
72
|
let newLine = StringHelper.replace(
|
|
74
73
|
line,
|
|
75
|
-
|
|
74
|
+
'@date',
|
|
76
75
|
new Date().toUTCString()
|
|
77
76
|
);
|
|
78
77
|
|
|
@@ -81,7 +80,6 @@ class FileHelper {
|
|
|
81
80
|
|
|
82
81
|
if (newLine.indexOf(replacement.token) > 0) {
|
|
83
82
|
if (replacement.template) {
|
|
84
|
-
//dynamic since there is no value
|
|
85
83
|
newLine = await FileHelper.dynamicReplace(replacement);
|
|
86
84
|
break;
|
|
87
85
|
} else if (replacement.value) {
|
|
@@ -89,31 +87,27 @@ class FileHelper {
|
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
|
-
res += newLine +
|
|
90
|
+
res += newLine + '\n';
|
|
93
91
|
});
|
|
94
92
|
return res;
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
static async readLineByLine(fileName,
|
|
95
|
+
static async readLineByLine(fileName, callback) {
|
|
98
96
|
const fileStream = fs.createReadStream(fileName);
|
|
99
97
|
|
|
100
98
|
const rl = readline.createInterface({
|
|
101
99
|
input: fileStream,
|
|
102
|
-
crlfDelay: Infinity
|
|
100
|
+
crlfDelay: Infinity
|
|
103
101
|
});
|
|
104
102
|
|
|
105
|
-
let index = 0;
|
|
106
|
-
|
|
107
103
|
for await (const line of rl) {
|
|
108
|
-
|
|
109
|
-
//console.log(index);
|
|
110
|
-
await newLine(line);
|
|
104
|
+
await callback(line);
|
|
111
105
|
}
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
static writer(filename) {
|
|
115
109
|
return fs.createWriteStream(filename, {
|
|
116
|
-
flags:
|
|
110
|
+
flags: 'a'
|
|
117
111
|
});
|
|
118
112
|
}
|
|
119
113
|
|
|
@@ -122,20 +116,24 @@ class FileHelper {
|
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
static createFolder(folderName) {
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
const resolved = path.resolve(folderName);
|
|
120
|
+
if (!fs.existsSync(resolved)) {
|
|
121
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
122
|
+
}
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
static removeFolder(folderName) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
const resolved = path.resolve(folderName);
|
|
127
|
+
if (fs.existsSync(resolved)) {
|
|
128
|
+
fs.rmSync(resolved, { recursive: true, force: true });
|
|
129
|
+
}
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
static removeFile(filename) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
const resolved = path.resolve(filename);
|
|
134
|
+
if (fs.existsSync(resolved)) {
|
|
135
|
+
fs.rmSync(resolved, { force: true });
|
|
136
|
+
}
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
|
package/src/string_helper.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
class StringHelper {
|
|
2
|
+
static escapeRegex(string) {
|
|
3
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4
|
+
}
|
|
5
|
+
|
|
2
6
|
static replace(line, token, value) {
|
|
3
|
-
|
|
7
|
+
if (typeof line !== 'string') return '';
|
|
8
|
+
if (typeof token !== 'string') return line;
|
|
9
|
+
return line.replace(new RegExp(StringHelper.escapeRegex(token), 'g'), value);
|
|
4
10
|
}
|
|
5
11
|
|
|
6
12
|
static capitalize(s) {
|
|
7
|
-
if (typeof s !==
|
|
13
|
+
if (typeof s !== 'string') return '';
|
|
8
14
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
9
15
|
}
|
|
10
16
|
}
|
package/src/template_builder.js
CHANGED
|
@@ -15,7 +15,7 @@ class TemplateBuilder {
|
|
|
15
15
|
case TEMPLATE_HANDLERS.EJS:
|
|
16
16
|
return EjsHelper.render(source, data, options);
|
|
17
17
|
case TEMPLATE_HANDLERS.PUG:
|
|
18
|
-
return PugHelper.render(
|
|
18
|
+
return PugHelper.render(source, data, options);
|
|
19
19
|
default:
|
|
20
20
|
return HandlebarsHelper.render(source, data, options);
|
|
21
21
|
}
|
package/src/template_handlers.js
CHANGED
package/test/test.js
CHANGED
|
@@ -1,11 +1,394 @@
|
|
|
1
1
|
var assert = require('assert');
|
|
2
|
-
|
|
2
|
+
var path = require('path');
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var os = require('os');
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
var { StringHelper } = require('../src/string_helper');
|
|
7
|
+
var { FileHelper } = require('../src/file_helper');
|
|
8
|
+
var { CommandHelper } = require('../src/command_helper');
|
|
9
|
+
var { ParamHelper } = require('../src/param_helper');
|
|
10
|
+
var { TemplateBuilder } = require('../src/template_builder');
|
|
11
|
+
var TEMPLATE_HANDLERS = require('../src/template_handlers');
|
|
12
|
+
|
|
13
|
+
// Helper to get absolute paths for example templates
|
|
14
|
+
var exampleDir = path.join(__dirname, '..', 'example');
|
|
15
|
+
|
|
16
|
+
describe('StringHelper', function () {
|
|
17
|
+
describe('replace', function () {
|
|
6
18
|
it('should search and replace the test param', function () {
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
var replaced = StringHelper.replace(
|
|
20
|
+
'This is a {{test}}',
|
|
21
|
+
'{{test}}',
|
|
22
|
+
'joke'
|
|
23
|
+
);
|
|
24
|
+
assert.strictEqual(replaced, 'This is a joke');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should replace all occurrences', function () {
|
|
28
|
+
var replaced = StringHelper.replace(
|
|
29
|
+
'{{name}} likes {{name}}',
|
|
30
|
+
'{{name}}',
|
|
31
|
+
'Alice'
|
|
32
|
+
);
|
|
33
|
+
assert.strictEqual(replaced, 'Alice likes Alice');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle tokens with regex special characters', function () {
|
|
37
|
+
var replaced = StringHelper.replace(
|
|
38
|
+
'Price is $100.00 total',
|
|
39
|
+
'$100.00',
|
|
40
|
+
'$200.00'
|
|
41
|
+
);
|
|
42
|
+
assert.strictEqual(replaced, 'Price is $200.00 total');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return empty string for non-string line', function () {
|
|
46
|
+
assert.strictEqual(StringHelper.replace(null, 'a', 'b'), '');
|
|
47
|
+
assert.strictEqual(StringHelper.replace(undefined, 'a', 'b'), '');
|
|
48
|
+
assert.strictEqual(StringHelper.replace(123, 'a', 'b'), '');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should return line unchanged for non-string token', function () {
|
|
52
|
+
assert.strictEqual(StringHelper.replace('hello', null, 'b'), 'hello');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('escapeRegex', function () {
|
|
57
|
+
it('should escape regex special characters', function () {
|
|
58
|
+
var escaped = StringHelper.escapeRegex('$100.00 (test)');
|
|
59
|
+
assert.strictEqual(escaped, '\\$100\\.00 \\(test\\)');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('capitalize', function () {
|
|
64
|
+
it('should capitalize first character', function () {
|
|
65
|
+
assert.strictEqual(StringHelper.capitalize('hello'), 'Hello');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return empty string for non-string', function () {
|
|
69
|
+
assert.strictEqual(StringHelper.capitalize(123), '');
|
|
70
|
+
assert.strictEqual(StringHelper.capitalize(null), '');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle empty string', function () {
|
|
74
|
+
assert.strictEqual(StringHelper.capitalize(''), '');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle single character', function () {
|
|
78
|
+
assert.strictEqual(StringHelper.capitalize('a'), 'A');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('FileHelper', function () {
|
|
84
|
+
var tmpDir;
|
|
85
|
+
|
|
86
|
+
beforeEach(function () {
|
|
87
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scg-test-'));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(function () {
|
|
91
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('readFileToString', function () {
|
|
95
|
+
it('should read a file and return its content as string', function () {
|
|
96
|
+
var filePath = path.join(tmpDir, 'test.txt');
|
|
97
|
+
fs.writeFileSync(filePath, 'hello world');
|
|
98
|
+
var content = FileHelper.readFileToString(filePath);
|
|
99
|
+
assert.strictEqual(content, 'hello world');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('convertJsonFileToObject', function () {
|
|
104
|
+
it('should parse a JSON file to an object', function () {
|
|
105
|
+
var filePath = path.join(tmpDir, 'test.json');
|
|
106
|
+
fs.writeFileSync(filePath, '{"name": "scg", "version": 1}');
|
|
107
|
+
var obj = FileHelper.convertJsonFileToObject(filePath);
|
|
108
|
+
assert.strictEqual(obj.name, 'scg');
|
|
109
|
+
assert.strictEqual(obj.version, 1);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('createFolder', function () {
|
|
114
|
+
it('should create a folder', function () {
|
|
115
|
+
var folderPath = path.join(tmpDir, 'new-folder');
|
|
116
|
+
FileHelper.createFolder(folderPath);
|
|
117
|
+
assert.ok(fs.existsSync(folderPath));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should create nested folders', function () {
|
|
121
|
+
var folderPath = path.join(tmpDir, 'a', 'b', 'c');
|
|
122
|
+
FileHelper.createFolder(folderPath);
|
|
123
|
+
assert.ok(fs.existsSync(folderPath));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should not throw if folder already exists', function () {
|
|
127
|
+
var folderPath = path.join(tmpDir, 'existing');
|
|
128
|
+
fs.mkdirSync(folderPath);
|
|
129
|
+
assert.doesNotThrow(function () {
|
|
130
|
+
FileHelper.createFolder(folderPath);
|
|
131
|
+
});
|
|
9
132
|
});
|
|
10
|
-
})
|
|
11
|
-
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('removeFolder', function () {
|
|
136
|
+
it('should remove a folder', function () {
|
|
137
|
+
var folderPath = path.join(tmpDir, 'to-remove');
|
|
138
|
+
fs.mkdirSync(folderPath);
|
|
139
|
+
FileHelper.removeFolder(folderPath);
|
|
140
|
+
assert.ok(!fs.existsSync(folderPath));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should not throw if folder does not exist', function () {
|
|
144
|
+
assert.doesNotThrow(function () {
|
|
145
|
+
FileHelper.removeFolder(path.join(tmpDir, 'nonexistent'));
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('removeFile', function () {
|
|
151
|
+
it('should remove a file', function () {
|
|
152
|
+
var filePath = path.join(tmpDir, 'to-remove.txt');
|
|
153
|
+
fs.writeFileSync(filePath, 'delete me');
|
|
154
|
+
FileHelper.removeFile(filePath);
|
|
155
|
+
assert.ok(!fs.existsSync(filePath));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should not throw if file does not exist', function () {
|
|
159
|
+
assert.doesNotThrow(function () {
|
|
160
|
+
FileHelper.removeFile(path.join(tmpDir, 'nonexistent.txt'));
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('simpleReplace', function () {
|
|
166
|
+
it('should replace token with value', function () {
|
|
167
|
+
var result = FileHelper.simpleReplace('Hello {{name}}', {
|
|
168
|
+
token: '{{name}}',
|
|
169
|
+
value: 'World'
|
|
170
|
+
});
|
|
171
|
+
assert.strictEqual(result, 'Hello World');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('CommandHelper', function () {
|
|
177
|
+
describe('run', function () {
|
|
178
|
+
it('should execute a command and return stdout', function () {
|
|
179
|
+
return CommandHelper.run('.', 'echo hello').then(function (result) {
|
|
180
|
+
assert.strictEqual(result.trim(), 'hello');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should reject when directory is missing', function () {
|
|
185
|
+
return CommandHelper.run(null, 'echo hello').then(
|
|
186
|
+
function () {
|
|
187
|
+
assert.fail('should have rejected');
|
|
188
|
+
},
|
|
189
|
+
function (err) {
|
|
190
|
+
assert.ok(err.message.includes('directory is required'));
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should reject when no command is given', function () {
|
|
196
|
+
return CommandHelper.run('.').then(
|
|
197
|
+
function () {
|
|
198
|
+
assert.fail('should have rejected');
|
|
199
|
+
},
|
|
200
|
+
function (err) {
|
|
201
|
+
assert.ok(err.message.includes('at least one command is required'));
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should reject on invalid command', function () {
|
|
207
|
+
return CommandHelper.run('.', 'nonexistent_command_xyz').then(
|
|
208
|
+
function () {
|
|
209
|
+
assert.fail('should have rejected');
|
|
210
|
+
},
|
|
211
|
+
function (err) {
|
|
212
|
+
assert.ok(err);
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should chain multiple commands', function () {
|
|
218
|
+
return CommandHelper.run('.', 'echo hello', 'echo world').then(
|
|
219
|
+
function (result) {
|
|
220
|
+
assert.ok(result.includes('hello'));
|
|
221
|
+
assert.ok(result.includes('world'));
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('runClean', function () {
|
|
228
|
+
it('should return output without newlines', function () {
|
|
229
|
+
return CommandHelper.runClean('.', 'echo hello').then(function (result) {
|
|
230
|
+
assert.strictEqual(result, 'hello');
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('ParamHelper', function () {
|
|
237
|
+
var originalArgv;
|
|
238
|
+
|
|
239
|
+
beforeEach(function () {
|
|
240
|
+
originalArgv = process.argv.slice();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
afterEach(function () {
|
|
244
|
+
process.argv = originalArgv;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('addCustomParam', function () {
|
|
248
|
+
it('should add a parameter to process.argv', function () {
|
|
249
|
+
var before = process.argv.length;
|
|
250
|
+
ParamHelper.addCustomParam('--test=123');
|
|
251
|
+
assert.strictEqual(process.argv.length, before + 1);
|
|
252
|
+
assert.strictEqual(process.argv[process.argv.length - 1], '--test=123');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('getCommandByIndex', function () {
|
|
257
|
+
it('should return argv value at index', function () {
|
|
258
|
+
var result = ParamHelper.getCommandByIndex(0);
|
|
259
|
+
assert.strictEqual(result, process.argv[0]);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should return empty string for out-of-bounds index', function () {
|
|
263
|
+
var result = ParamHelper.getCommandByIndex(9999);
|
|
264
|
+
assert.strictEqual(result, '');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe('getParams', function () {
|
|
269
|
+
it('should parse --key=value params', function () {
|
|
270
|
+
process.argv = ['node', 'test', '--name=Alice', '--age=30'];
|
|
271
|
+
var params = ParamHelper.getParams();
|
|
272
|
+
assert.strictEqual(params.name, 'Alice');
|
|
273
|
+
assert.strictEqual(params.age, '30');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('should strip quotes from values', function () {
|
|
277
|
+
process.argv = ['node', 'test', '--name="Alice"', "--city='Paris'"];
|
|
278
|
+
var params = ParamHelper.getParams();
|
|
279
|
+
assert.strictEqual(params.name, 'Alice');
|
|
280
|
+
assert.strictEqual(params.city, 'Paris');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should return empty object when no params', function () {
|
|
284
|
+
process.argv = ['node', 'test'];
|
|
285
|
+
var params = ParamHelper.getParams();
|
|
286
|
+
assert.deepStrictEqual(params, {});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe('TemplateBuilder', function () {
|
|
292
|
+
describe('render (from string)', function () {
|
|
293
|
+
it('should render EJS template from string', function () {
|
|
294
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.EJS);
|
|
295
|
+
return builder
|
|
296
|
+
.render('Hello <%= name %>', { name: 'World' })
|
|
297
|
+
.then(function (result) {
|
|
298
|
+
assert.strictEqual(result, 'Hello World');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should render Handlebars template from string', function () {
|
|
303
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.HANDLEBARS);
|
|
304
|
+
return builder
|
|
305
|
+
.render('Hello {{name}}', { name: 'World' })
|
|
306
|
+
.then(function (result) {
|
|
307
|
+
assert.strictEqual(result, 'Hello World');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should render Pug template from string', function () {
|
|
312
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.PUG);
|
|
313
|
+
return builder
|
|
314
|
+
.render('p #{name}', { name: 'World' })
|
|
315
|
+
.then(function (result) {
|
|
316
|
+
assert.strictEqual(result, '<p>World</p>');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should default to Handlebars for unknown handler', function () {
|
|
321
|
+
var builder = new TemplateBuilder('UNKNOWN');
|
|
322
|
+
return builder
|
|
323
|
+
.render('Hello {{name}}', { name: 'World' })
|
|
324
|
+
.then(function (result) {
|
|
325
|
+
assert.strictEqual(result, 'Hello World');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('renderFile', function () {
|
|
331
|
+
it('should render EJS template from file', function () {
|
|
332
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.EJS);
|
|
333
|
+
return builder
|
|
334
|
+
.renderFile(
|
|
335
|
+
path.join(exampleDir, 'ejs', 'hello.ejs'),
|
|
336
|
+
{ title: 'Test', body: 'Body' },
|
|
337
|
+
{}
|
|
338
|
+
)
|
|
339
|
+
.then(function (result) {
|
|
340
|
+
assert.ok(result.includes('Test'));
|
|
341
|
+
assert.ok(result.includes('Body'));
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should render Handlebars template from file', function () {
|
|
346
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.HANDLEBARS);
|
|
347
|
+
return builder
|
|
348
|
+
.renderFile(
|
|
349
|
+
path.join(exampleDir, 'handlebars', 'hello.handlebars'),
|
|
350
|
+
{ title: 'Test', body: 'Body' },
|
|
351
|
+
{}
|
|
352
|
+
)
|
|
353
|
+
.then(function (result) {
|
|
354
|
+
assert.ok(result.includes('Test'));
|
|
355
|
+
assert.ok(result.includes('Body'));
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should render Pug template from file', function () {
|
|
360
|
+
var builder = new TemplateBuilder(TEMPLATE_HANDLERS.PUG);
|
|
361
|
+
return builder
|
|
362
|
+
.renderFile(
|
|
363
|
+
path.join(exampleDir, 'pug', 'hello.pug'),
|
|
364
|
+
{ title: 'Test', body: 'Body' },
|
|
365
|
+
{}
|
|
366
|
+
)
|
|
367
|
+
.then(function (result) {
|
|
368
|
+
assert.ok(result.includes('Test'));
|
|
369
|
+
assert.ok(result.includes('Body'));
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
describe('TEMPLATE_HANDLERS', function () {
|
|
376
|
+
it('should export HANDLEBARS, EJS, and PUG', function () {
|
|
377
|
+
assert.strictEqual(TEMPLATE_HANDLERS.HANDLEBARS, 'HANDLEBARS');
|
|
378
|
+
assert.strictEqual(TEMPLATE_HANDLERS.EJS, 'EJS');
|
|
379
|
+
assert.strictEqual(TEMPLATE_HANDLERS.PUG, 'PUG');
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('index (main exports)', function () {
|
|
384
|
+
var scg = require('../index');
|
|
385
|
+
|
|
386
|
+
it('should export all modules', function () {
|
|
387
|
+
assert.ok(scg.StringHelper);
|
|
388
|
+
assert.ok(scg.FileHelper);
|
|
389
|
+
assert.ok(scg.CommandHelper);
|
|
390
|
+
assert.ok(scg.ParamHelper);
|
|
391
|
+
assert.ok(scg.TemplateBuilder);
|
|
392
|
+
assert.ok(scg.TEMPLATE_HANDLERS);
|
|
393
|
+
});
|
|
394
|
+
});
|