@m00rl0ck/simple-builder 1.0.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/bundler.js +151 -0
- package/dev-server.js +134 -0
- package/index.js +135 -0
- package/minify-css.js +16 -0
- package/minify.js +135 -0
- package/package.json +30 -0
package/bundler.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
let ID = 0;
|
|
5
|
+
|
|
6
|
+
// ======================
|
|
7
|
+
// 🔍 FIND IMPORTS
|
|
8
|
+
// ======================
|
|
9
|
+
function getImports(code) {
|
|
10
|
+
const regex = /import\s+{([^}]+)}\s+from\s+['"](.+?)['"]/g;
|
|
11
|
+
const imports = [];
|
|
12
|
+
let match;
|
|
13
|
+
|
|
14
|
+
while ((match = regex.exec(code))) {
|
|
15
|
+
imports.push({
|
|
16
|
+
names: match[1].split(',').map(s => s.trim()),
|
|
17
|
+
path: match[2]
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return imports;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ======================
|
|
25
|
+
// 🔄 TRANSFORM CODE
|
|
26
|
+
// ======================
|
|
27
|
+
function transform(code) {
|
|
28
|
+
const exports = [];
|
|
29
|
+
|
|
30
|
+
// 1. ЗНАХОДИМО export-и
|
|
31
|
+
const exportRegex = /export\s+(?:const|function)\s+(\w+)/g;
|
|
32
|
+
let m;
|
|
33
|
+
|
|
34
|
+
while ((m = exportRegex.exec(code))) {
|
|
35
|
+
exports.push(m[1]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. ПРИБИРАЄМО export
|
|
39
|
+
code = code
|
|
40
|
+
.replace(/export const (\w+)\s*=/g, (m, name) => {
|
|
41
|
+
return `const ${name} =`;
|
|
42
|
+
})
|
|
43
|
+
.replace(/export function (\w+)/g, (m, name) => {
|
|
44
|
+
return `function ${name}`;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 3. import → require
|
|
48
|
+
code = code.replace(
|
|
49
|
+
/import\s+{([^}]+)}\s+from\s+['"](.+?)['"]/g,
|
|
50
|
+
(match, names, path) => {
|
|
51
|
+
return `const { ${names} } = require('${path}')`;
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// 4. додаємо exports
|
|
56
|
+
if (exports.length) {
|
|
57
|
+
code += `\nmodule.exports = { ${exports.join(', ')} };`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return code;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ======================
|
|
64
|
+
// 🧱 CREATE MODULE
|
|
65
|
+
// ======================
|
|
66
|
+
function createModule(filePath, modulesCache) {
|
|
67
|
+
const absPath = path.resolve(filePath);
|
|
68
|
+
|
|
69
|
+
if (modulesCache[absPath]) {
|
|
70
|
+
return modulesCache[absPath];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const code = fs.readFileSync(absPath, 'utf-8');
|
|
74
|
+
const imports = getImports(code);
|
|
75
|
+
|
|
76
|
+
const id = ID++;
|
|
77
|
+
|
|
78
|
+
const mapping = {};
|
|
79
|
+
|
|
80
|
+
const dirname = path.dirname(absPath);
|
|
81
|
+
|
|
82
|
+
const module = {
|
|
83
|
+
id,
|
|
84
|
+
filePath: absPath,
|
|
85
|
+
code: '',
|
|
86
|
+
mapping
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
modulesCache[absPath] = module;
|
|
90
|
+
|
|
91
|
+
imports.forEach(imp => {
|
|
92
|
+
const childPath = path.join(dirname, imp.path);
|
|
93
|
+
const child = createModule(childPath, modulesCache);
|
|
94
|
+
mapping[imp.path] = child.id;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
module.code = transform(code, mapping);
|
|
98
|
+
|
|
99
|
+
return module;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ======================
|
|
103
|
+
// 📦 BUNDLE
|
|
104
|
+
// ======================
|
|
105
|
+
export function bundle(entry) {
|
|
106
|
+
ID = 0;
|
|
107
|
+
|
|
108
|
+
const modulesCache = {};
|
|
109
|
+
|
|
110
|
+
const entryModule = createModule(entry, modulesCache);
|
|
111
|
+
|
|
112
|
+
const modules = Object.values(modulesCache);
|
|
113
|
+
|
|
114
|
+
const modulesCode = modules.map(mod => {
|
|
115
|
+
return `
|
|
116
|
+
${mod.id}: [
|
|
117
|
+
function(require, module, exports) {
|
|
118
|
+
${mod.code}
|
|
119
|
+
},
|
|
120
|
+
${JSON.stringify(mod.mapping)}
|
|
121
|
+
]
|
|
122
|
+
`;
|
|
123
|
+
}).join(',');
|
|
124
|
+
|
|
125
|
+
return `
|
|
126
|
+
(function(modules){
|
|
127
|
+
const cache = {};
|
|
128
|
+
|
|
129
|
+
function require(id){
|
|
130
|
+
if (cache[id]) {
|
|
131
|
+
return cache[id].exports;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const [fn, mapping] = modules[id];
|
|
135
|
+
|
|
136
|
+
function localRequire(name){
|
|
137
|
+
return require(mapping[name]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const module = { exports: {} };
|
|
141
|
+
cache[id] = module;
|
|
142
|
+
|
|
143
|
+
fn(localRequire, module, module.exports);
|
|
144
|
+
|
|
145
|
+
return module.exports;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
require(${entryModule.id});
|
|
149
|
+
})({${modulesCode}});
|
|
150
|
+
`;
|
|
151
|
+
}
|
package/dev-server.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { build } from './index.js';
|
|
5
|
+
|
|
6
|
+
const PORT = 3000;
|
|
7
|
+
const ROOT = process.cwd();
|
|
8
|
+
const DIST_DIR = path.join(ROOT, 'dist');
|
|
9
|
+
|
|
10
|
+
let clients = [];
|
|
11
|
+
|
|
12
|
+
// ======================
|
|
13
|
+
// 📡 MIME TYPES
|
|
14
|
+
// ======================
|
|
15
|
+
|
|
16
|
+
const MIME = {
|
|
17
|
+
'.html': 'text/html',
|
|
18
|
+
'.js': 'text/javascript',
|
|
19
|
+
'.css': 'text/css',
|
|
20
|
+
'.json': 'application/json',
|
|
21
|
+
'.png': 'image/png',
|
|
22
|
+
'.jpg': 'image/jpeg',
|
|
23
|
+
'.svg': 'image/svg+xml'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ======================
|
|
27
|
+
// 🔄 LIVE RELOAD SCRIPT
|
|
28
|
+
// ======================
|
|
29
|
+
|
|
30
|
+
function injectReload(html) {
|
|
31
|
+
return html.replace(
|
|
32
|
+
'</body>',
|
|
33
|
+
`
|
|
34
|
+
<script>
|
|
35
|
+
const es = new EventSource('/__reload');
|
|
36
|
+
es.onmessage = () => location.reload();
|
|
37
|
+
</script>
|
|
38
|
+
</body>`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ======================
|
|
43
|
+
// 📂 STATIC SERVER
|
|
44
|
+
// ======================
|
|
45
|
+
|
|
46
|
+
function serveFile(req, res) {
|
|
47
|
+
let filePath = path.join(DIST_DIR, req.url === '/' ? 'index.html' : req.url);
|
|
48
|
+
|
|
49
|
+
const exists = fs.existsSync(filePath);
|
|
50
|
+
const ext = path.extname(filePath);
|
|
51
|
+
|
|
52
|
+
// 👉 якщо файл не існує
|
|
53
|
+
if (!exists) {
|
|
54
|
+
// SPA fallback тільки для HTML запитів
|
|
55
|
+
if (req.headers.accept && req.headers.accept.includes('text/html')) {
|
|
56
|
+
filePath = path.join(DIST_DIR, 'index.html');
|
|
57
|
+
} else {
|
|
58
|
+
res.writeHead(404);
|
|
59
|
+
return res.end('Not found');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const contentType = MIME[ext] || 'text/plain';
|
|
64
|
+
|
|
65
|
+
let content = fs.readFileSync(filePath);
|
|
66
|
+
|
|
67
|
+
if (ext === '.html') {
|
|
68
|
+
content = injectReload(content.toString());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
72
|
+
res.end(content);
|
|
73
|
+
}
|
|
74
|
+
// ======================
|
|
75
|
+
// 🔁 RELOAD CHANNEL (SSE)
|
|
76
|
+
// ======================
|
|
77
|
+
|
|
78
|
+
function handleReload(req, res) {
|
|
79
|
+
res.writeHead(200, {
|
|
80
|
+
'Content-Type': 'text/event-stream',
|
|
81
|
+
'Cache-Control': 'no-cache',
|
|
82
|
+
'Connection': 'keep-alive'
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
clients.push(res);
|
|
86
|
+
|
|
87
|
+
req.on('close', () => {
|
|
88
|
+
clients = clients.filter(c => c !== res);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function triggerReload() {
|
|
93
|
+
clients.forEach(res => {
|
|
94
|
+
res.write('data: reload\n\n');
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ======================
|
|
99
|
+
// 👀 WATCH
|
|
100
|
+
// ======================
|
|
101
|
+
|
|
102
|
+
function watch() {
|
|
103
|
+
const SRC_DIR = path.join(ROOT, 'src');
|
|
104
|
+
|
|
105
|
+
build();
|
|
106
|
+
|
|
107
|
+
fs.watch(SRC_DIR, { recursive: true }, () => {
|
|
108
|
+
console.log('🔄 Rebuilding...');
|
|
109
|
+
build();
|
|
110
|
+
triggerReload();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ======================
|
|
115
|
+
// 🚀 SERVER
|
|
116
|
+
// ======================
|
|
117
|
+
|
|
118
|
+
const server = http.createServer((req, res) => {
|
|
119
|
+
if (req.url === '/__reload') {
|
|
120
|
+
return handleReload(req, res);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
serveFile(req, res);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ======================
|
|
127
|
+
// ▶️ START
|
|
128
|
+
// ======================
|
|
129
|
+
|
|
130
|
+
watch();
|
|
131
|
+
|
|
132
|
+
server.listen(PORT, () => {
|
|
133
|
+
console.log(`\n🚀 Dev server running: http://localhost:${PORT}\n`);
|
|
134
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { IN_POINT, NODE_ENV } from '../../env.js';
|
|
4
|
+
import { minifyJS } from './minify.js';
|
|
5
|
+
import { minifyCSS } from './minify-css.js';
|
|
6
|
+
import { bundle } from './bundler.js';
|
|
7
|
+
|
|
8
|
+
// ======================
|
|
9
|
+
// ⚙️ CONFIG
|
|
10
|
+
// ======================
|
|
11
|
+
|
|
12
|
+
const ROOT = process.cwd();
|
|
13
|
+
const SRC_DIR = path.join(ROOT, 'src');
|
|
14
|
+
const DIST_DIR = path.join(ROOT, 'dist');
|
|
15
|
+
|
|
16
|
+
const ENTRY = path.join(SRC_DIR, `${IN_POINT}.js`);
|
|
17
|
+
const HTML_INPUT = path.join(SRC_DIR, 'index.html');
|
|
18
|
+
const HTML_OUTPUT = path.join(DIST_DIR, 'index.html');
|
|
19
|
+
|
|
20
|
+
const isProd = NODE_ENV === 'production';
|
|
21
|
+
const isWatch = process.argv.includes('--watch');
|
|
22
|
+
|
|
23
|
+
// ======================
|
|
24
|
+
// 🧹 UTILS
|
|
25
|
+
// ======================
|
|
26
|
+
|
|
27
|
+
function ensureDir(dir) {
|
|
28
|
+
if (!fs.existsSync(dir)) {
|
|
29
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cleanDist() {
|
|
34
|
+
if (fs.existsSync(DIST_DIR)) {
|
|
35
|
+
fs.rmSync(DIST_DIR, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ======================
|
|
40
|
+
// 📦 BUILD JS
|
|
41
|
+
// ======================
|
|
42
|
+
function buildJS() {
|
|
43
|
+
const files = fs.readdirSync(SRC_DIR).filter(f => f.endsWith('.js'));
|
|
44
|
+
|
|
45
|
+
const bundle_result = bundle('./src/app.js');
|
|
46
|
+
const result = isProd ? minifyJS(bundle_result) : bundle_result;
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync('./dist/bundle.min.js', result);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildCSS() {
|
|
52
|
+
const files = fs.readdirSync(SRC_DIR).filter(f => f.endsWith('.css'));
|
|
53
|
+
|
|
54
|
+
files.forEach(file => {
|
|
55
|
+
const inputPath = path.join(SRC_DIR, file);
|
|
56
|
+
const code = fs.readFileSync(inputPath, 'utf-8');
|
|
57
|
+
|
|
58
|
+
const outputName = isProd
|
|
59
|
+
? file.replace('.css', '.min.css')
|
|
60
|
+
: file;
|
|
61
|
+
|
|
62
|
+
const outputPath = path.join(DIST_DIR, outputName);
|
|
63
|
+
|
|
64
|
+
const result = isProd ? minifyCSS(code) : code;
|
|
65
|
+
|
|
66
|
+
fs.writeFileSync(outputPath, result);
|
|
67
|
+
|
|
68
|
+
console.log(`🎨 CSS built: ${outputName}`);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ======================
|
|
73
|
+
// 🌐 BUILD HTML
|
|
74
|
+
// ======================
|
|
75
|
+
|
|
76
|
+
function buildHTML() {
|
|
77
|
+
let html = fs.readFileSync(HTML_INPUT, 'utf-8');
|
|
78
|
+
|
|
79
|
+
if (isProd) {
|
|
80
|
+
// JS
|
|
81
|
+
html = html.replace(/src="(.+?)\.js"/g, (match, p1) => {
|
|
82
|
+
if (p1.endsWith('.min')) return match;
|
|
83
|
+
return `src="bundle.min.js"`;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// CSS
|
|
87
|
+
html = html.replace(/href="(.+?)\.css"/g, (match, p1) => {
|
|
88
|
+
if (p1.endsWith('.min')) return match;
|
|
89
|
+
return `href="${p1}.min.css"`;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(HTML_OUTPUT, html);
|
|
94
|
+
|
|
95
|
+
console.log(`✅ HTML built`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ======================
|
|
99
|
+
// 👀 WATCH MODE
|
|
100
|
+
// ======================
|
|
101
|
+
|
|
102
|
+
function watch() {
|
|
103
|
+
console.log('👀 Watch mode ON...\n');
|
|
104
|
+
|
|
105
|
+
fs.watch(SRC_DIR, { recursive: true }, (eventType, filename) => {
|
|
106
|
+
console.log(`🔄 File changed: ${filename}`);
|
|
107
|
+
build();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
fs.watch(HTML_INPUT, () => {
|
|
111
|
+
console.log(`🔄 HTML changed`);
|
|
112
|
+
build();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ======================
|
|
117
|
+
// 🚀 BUILD
|
|
118
|
+
// ======================
|
|
119
|
+
|
|
120
|
+
export function build() {
|
|
121
|
+
console.log(`\n🚀 Build started (${isProd ? 'production' : 'development'})`);
|
|
122
|
+
|
|
123
|
+
cleanDist();
|
|
124
|
+
ensureDir(DIST_DIR);
|
|
125
|
+
|
|
126
|
+
buildJS();
|
|
127
|
+
buildCSS(); // 👈 ДОДАЛИ
|
|
128
|
+
buildHTML();
|
|
129
|
+
|
|
130
|
+
console.log(`🎉 Build finished\n`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (isWatch) {
|
|
134
|
+
watch();
|
|
135
|
+
}
|
package/minify-css.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function minifyCSS(code) {
|
|
2
|
+
return code
|
|
3
|
+
// remove comments
|
|
4
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
5
|
+
|
|
6
|
+
// remove whitespace
|
|
7
|
+
.replace(/\s+/g, ' ')
|
|
8
|
+
|
|
9
|
+
// remove space around symbols
|
|
10
|
+
.replace(/\s*([{}:;,>])\s*/g, '$1')
|
|
11
|
+
|
|
12
|
+
// remove last semicolon
|
|
13
|
+
.replace(/;}/g, '}')
|
|
14
|
+
|
|
15
|
+
.trim();
|
|
16
|
+
}
|
package/minify.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export function minifyJS(code) {
|
|
2
|
+
let out = '';
|
|
3
|
+
let i = 0;
|
|
4
|
+
|
|
5
|
+
let inString = false;
|
|
6
|
+
let stringChar = '';
|
|
7
|
+
let inTemplate = false;
|
|
8
|
+
let inRegex = false;
|
|
9
|
+
let inSingleComment = false;
|
|
10
|
+
let inMultiComment = false;
|
|
11
|
+
|
|
12
|
+
while (i < code.length) {
|
|
13
|
+
const c = code[i];
|
|
14
|
+
const next = code[i + 1];
|
|
15
|
+
|
|
16
|
+
// ======================
|
|
17
|
+
// COMMENTS
|
|
18
|
+
// ======================
|
|
19
|
+
|
|
20
|
+
if (inSingleComment) {
|
|
21
|
+
if (c === '\n') {
|
|
22
|
+
inSingleComment = false;
|
|
23
|
+
}
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (inMultiComment) {
|
|
29
|
+
if (c === '*' && next === '/') {
|
|
30
|
+
inMultiComment = false;
|
|
31
|
+
i += 2;
|
|
32
|
+
} else {
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!inString && !inTemplate && !inRegex) {
|
|
39
|
+
if (c === '/' && next === '/') {
|
|
40
|
+
inSingleComment = true;
|
|
41
|
+
i += 2;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (c === '/' && next === '*') {
|
|
46
|
+
inMultiComment = true;
|
|
47
|
+
i += 2;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ======================
|
|
53
|
+
// STRINGS
|
|
54
|
+
// ======================
|
|
55
|
+
|
|
56
|
+
if (!inTemplate && !inRegex && (c === '"' || c === "'")) {
|
|
57
|
+
if (inString && c === stringChar && code[i - 1] !== '\\') {
|
|
58
|
+
inString = false;
|
|
59
|
+
} else if (!inString) {
|
|
60
|
+
inString = true;
|
|
61
|
+
stringChar = c;
|
|
62
|
+
}
|
|
63
|
+
out += c;
|
|
64
|
+
i++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ======================
|
|
69
|
+
// TEMPLATE
|
|
70
|
+
// ======================
|
|
71
|
+
|
|
72
|
+
if (!inString && !inRegex && c === '`') {
|
|
73
|
+
inTemplate = !inTemplate;
|
|
74
|
+
out += c;
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ======================
|
|
80
|
+
// REGEX (simplified)
|
|
81
|
+
// ======================
|
|
82
|
+
|
|
83
|
+
if (!inString && !inTemplate && c === '/' && !inRegex) {
|
|
84
|
+
// naive check: після ( або = або : або , → regex
|
|
85
|
+
const prev = out.trim().slice(-1);
|
|
86
|
+
if (!prev || '({[=,:;!&|?'.includes(prev)) {
|
|
87
|
+
inRegex = true;
|
|
88
|
+
out += c;
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (inRegex) {
|
|
95
|
+
if (c === '/' && code[i - 1] !== '\\') {
|
|
96
|
+
inRegex = false;
|
|
97
|
+
}
|
|
98
|
+
out += c;
|
|
99
|
+
i++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ======================
|
|
104
|
+
// WHITESPACE
|
|
105
|
+
// ======================
|
|
106
|
+
|
|
107
|
+
if (!inString && !inTemplate && !inRegex) {
|
|
108
|
+
if (/\s/.test(c)) {
|
|
109
|
+
const prev = out[out.length - 1];
|
|
110
|
+
const nextChar = code[i + 1];
|
|
111
|
+
|
|
112
|
+
// не додаємо зайві пробіли
|
|
113
|
+
if (
|
|
114
|
+
prev &&
|
|
115
|
+
/[a-zA-Z0-9_$]/.test(prev) &&
|
|
116
|
+
/[a-zA-Z0-9_$]/.test(nextChar)
|
|
117
|
+
) {
|
|
118
|
+
out += ' ';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ======================
|
|
127
|
+
// DEFAULT
|
|
128
|
+
// ======================
|
|
129
|
+
|
|
130
|
+
out += c;
|
|
131
|
+
i++;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return out.trim();
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@m00rl0ck/simple-builder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-dependency JS bundler with dev server and minifier",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"simple-builder": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "node dev-server.js",
|
|
12
|
+
"build": "node bundler.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"bundler",
|
|
16
|
+
"javascript",
|
|
17
|
+
"build-tool",
|
|
18
|
+
"zero-deps",
|
|
19
|
+
"minifier"
|
|
20
|
+
],
|
|
21
|
+
"author": "m00rlIOck",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"files": [
|
|
24
|
+
"bundler.js",
|
|
25
|
+
"dev-server.js",
|
|
26
|
+
"index.js",
|
|
27
|
+
"minify.js",
|
|
28
|
+
"minify-css.js"
|
|
29
|
+
]
|
|
30
|
+
}
|