cc4-embedded-system 3.1.1 → 3.1.3

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/dist/gui.js CHANGED
@@ -59,6 +59,7 @@
59
59
  */
60
60
  import express from 'express';
61
61
  import open from 'open';
62
+ import fs from 'node:fs';
62
63
  import path from 'node:path';
63
64
  import { fileURLToPath } from 'node:url';
64
65
  import { runMakeFsData } from './makefsdata.js';
@@ -85,7 +86,11 @@ if (portArgIndex !== -1 && process.argv[portArgIndex + 1]) {
85
86
  let server;
86
87
  let shutdownTimer = null;
87
88
  app.use(express.json());
88
- app.use(express.static(path.join(__dirname, '../public')));
89
+ const publicPath = fs.existsSync(path.join(__dirname, 'public'))
90
+ ? path.join(__dirname, 'public')
91
+ : path.join(__dirname, '../public');
92
+ console.log(`[System] Serving static files from: ${publicPath}`);
93
+ app.use(express.static(publicPath));
89
94
  const cancelShutdown = () => {
90
95
  if (shutdownTimer) {
91
96
  clearTimeout(shutdownTimer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc4-embedded-system",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "A modern typescript version of makefsdata based on html-minifier-next and lwIP v1.3.1",
5
5
  "bin": {
6
6
  "cc4es": "./dist/gui.js"
@@ -8,9 +8,14 @@
8
8
  "type": "module",
9
9
  "scripts": {
10
10
  "dev": "tsx src/gui.ts",
11
- "build": "tsc",
11
+ "build": "tsc && xcopy /E /I /Y public dist\\public",
12
12
  "prepublishOnly": "npm run build"
13
13
  },
14
+ "files": [
15
+ "dist/",
16
+ "package.json",
17
+ "README.md"
18
+ ],
14
19
  "keywords": [
15
20
  "lwip",
16
21
  "makefsdata",
Binary file
package/src/gui.ts DELETED
@@ -1,227 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /*
4
- * ==============================================================================
5
- * CC4EmbeddedSystem V3 (makefsdata)
6
- * Copyright (c) 2026 LunaticGhoulPiano / Emile Su
7
- * Licensed under the MIT License.
8
- * ==============================================================================
9
- *
10
- * This software integrates concepts, logic, and minification processes derived
11
- * from the following open-source projects. We deeply appreciate their work:
12
- *
13
- * ------------------------------------------------------------------------------
14
- * 1. lwIP (Lightweight TCP/IP stack)
15
- * Repository: https://github.com/m-labs/lwip
16
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
17
- * All rights reserved.
18
- * * Redistribution and use in source and binary forms, with or without modification,
19
- * are permitted provided that the following conditions are met:
20
- * * 1. Redistributions of source code must retain the above copyright notice,
21
- * this list of conditions and the following disclaimer.
22
- * 2. Redistributions in binary form must reproduce the above copyright notice,
23
- * this list of conditions and the following disclaimer in the documentation
24
- * and/or other materials provided with the distribution.
25
- * 3. The name of the author may not be used to endorse or promote products
26
- * derived from this software without specific prior written permission.
27
- * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
28
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
30
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
32
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
35
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36
- * OF SUCH DAMAGE.
37
- *
38
- * ------------------------------------------------------------------------------
39
- * 2. html-minifier-next
40
- * Repository: https://github.com/j9t/html-minifier-next
41
- * Copyright (c) Jens Oliver Meiert (html-minifier-next)
42
- * Copyright (c) Daniel Ruf (html-minifier-terser)
43
- * Copyright (c) Juriy "kangax" Zaytsev (html-minifier)
44
- * * Permission is hereby granted, free of charge, to any person obtaining a copy
45
- * of this software and associated documentation files (the "Software"), to deal
46
- * in the Software without restriction, including without limitation the rights
47
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48
- * copies of the Software, and to permit persons to whom the Software is
49
- * furnished to do so, subject to the following conditions:
50
- * * The above copyright notice and this permission notice shall be included in
51
- * all copies or substantial portions of the Software.
52
- * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
55
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
56
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
57
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
58
- * THE SOFTWARE.
59
- * ==============================================================================
60
- */
61
-
62
- import express from 'express';
63
- import open from 'open';
64
- import path from 'node:path';
65
- import { fileURLToPath } from 'node:url';
66
- import { runMakeFsData, MakeFsDataOptions } from './makefsdata.js';
67
- import { getPackageVersion } from './utils.js';
68
- import { execSync } from 'node:child_process';
69
- import os from 'node:os';
70
-
71
- // prevent socket errors
72
- process.on('uncaughtException', (err: any) => {
73
- if (err.code === 'EADDRINUSE') return;
74
- console.error('⚠️ Uncaught Error:', err);
75
- });
76
-
77
- const __filename: string = fileURLToPath(import.meta.url);
78
- const __dirname: string = path.dirname(__filename);
79
- const app = express();
80
-
81
- // port set by command
82
- let PORT: number = parseInt(process.env.PORT || '3000', 10);
83
- const portArgIndex = process.argv.indexOf('--port');
84
- if (portArgIndex !== -1 && process.argv[portArgIndex + 1]) {
85
- const parsedPort = parseInt(process.argv[portArgIndex + 1]!, 10);
86
- if (!isNaN(parsedPort)) PORT = parsedPort;
87
- }
88
-
89
- let server: any;
90
- let shutdownTimer: NodeJS.Timeout | null = null;
91
-
92
- app.use(express.json());
93
- app.use(express.static(path.join(__dirname, '../public')));
94
-
95
- const cancelShutdown = () => {
96
- if (shutdownTimer) {
97
- clearTimeout(shutdownTimer);
98
- shutdownTimer = null;
99
- // console.log('🛡️ Shutdown cancelled (keep alive)');
100
- }
101
- };
102
-
103
- app.get('/api/version', (_req, res) => {
104
- cancelShutdown();
105
- res.json({ version: getPackageVersion() });
106
- });
107
-
108
- app.post('/api/shutdown', (_req, res) => {
109
- res.json({ success: true });
110
-
111
- if (! shutdownTimer) {
112
- shutdownTimer = setTimeout(() => {
113
- console.log('👋 Window closed, shutting down ...');
114
- process.exit(0);
115
- }, 2000);
116
- }
117
- });
118
-
119
- app.post('/api/cancel-shutdown', (_req, res) => {
120
- cancelShutdown();
121
- res.json({ success: true });
122
- });
123
-
124
- app.post('/api/build', async (req: express.Request, res: express.Response): Promise<void> => {
125
- cancelShutdown();
126
- const { inputPath, outputPath, minifyOpts } = req.body;
127
-
128
- console.log(`[Build] Input: ${inputPath}`);
129
- console.log(`[Build] Output: ${outputPath}`);
130
-
131
- const opts: MakeFsDataOptions = {
132
- inputDir: inputPath,
133
- outputFile: outputPath,
134
- processSubs: true,
135
- includeHttpHeader: true,
136
- useHttp11: false,
137
- supportSsi: true,
138
- precalcChksum: false,
139
- minifyOpts: minifyOpts
140
- };
141
-
142
- try {
143
- // get stats
144
- const stats = await runMakeFsData(opts);
145
-
146
- try {
147
- await open(path.dirname(path.resolve(outputPath)));
148
- }
149
- catch (e) {
150
- // ignore open folder error
151
- }
152
-
153
- // return stats
154
- res.json({
155
- success: true,
156
- stats: stats // originalSize, compressedSize, filesCount
157
- });
158
- }
159
- catch (error: any) {
160
- res.json({ success: false, message: error.message });
161
- }
162
- });
163
-
164
- app.post('/api/change-port', (req: express.Request, res: express.Response): void => {
165
- cancelShutdown();
166
- const parsedPort = parseInt(req.body.newPort, 10);
167
-
168
- if (isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) {
169
- res.json({ success: false, message: 'Invalid port number.' });
170
- return;
171
- }
172
-
173
- res.json({ success: true });
174
-
175
- setTimeout(() => {
176
- if (server) {
177
- try {
178
- server.closeAllConnections();
179
- server.close(() => {
180
- PORT = parsedPort;
181
- startServer();
182
- console.log(`🔄 Server successfully restarted on port ${PORT}`);
183
- });
184
- }
185
- catch (e) {
186
- startServer();
187
- }
188
- }
189
- }, 100);
190
- });
191
-
192
- app.get('/api/browse', (req, res) => {
193
- const isDir = req.query.type === 'dir';
194
- const platform = os.platform();
195
- let command = '';
196
-
197
- try {
198
- if (platform === 'win32') { // Windows: Powershell + OpenFileDialog
199
- if (isDir) command = `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.OpenFileDialog; $f.CheckFileExists = $false; $f.CheckPathExists = $true; $f.FileName = 'Select Folder'; $f.ValidateNames = $false; if($f.ShowDialog() -eq 'OK'){ Split-Path $f.FileName }"`;
200
- else command = `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.OpenFileDialog; $f.Filter = 'C Files (*.c)|*.c|All Files (*.*)|*.*'; if($f.ShowDialog() -eq 'OK'){ $f.FileName }"`;
201
- }
202
- else if (platform === 'darwin') { // macOS: darwin
203
- if (isDir) command = `osascript -e 'POSIX path of (choose folder with prompt "Select Input Directory")'`;
204
- else command = `osascript -e 'POSIX path of (choose file with prompt "Select Output File")'`;
205
- }
206
- else { // Linux: Zenity
207
- if (isDir) command = `zenity --file-selection --directory --title="Select Input Directory"`;
208
- else command = `zenity --file-selection --title="Select Output File"`;
209
- }
210
-
211
- const result = execSync(command).toString().trim();
212
- res.json({ success: true, path: result || null });
213
- }
214
- catch (e) {
215
- res.json({ success: true, path: null });
216
- }
217
- });
218
-
219
- const startServer = () => {
220
- server = app.listen(PORT, () => {
221
- console.log(`✨ GUI Server started on http://localhost:${PORT}`);
222
- });
223
- };
224
-
225
- // main run
226
- startServer();
227
- open(`http://localhost:${PORT}`);
package/src/makefsdata.ts DELETED
@@ -1,390 +0,0 @@
1
- /*
2
- * ==============================================================================
3
- * CC4EmbeddedSystem V3 (makefsdata)
4
- * Copyright (c) 2026 LunaticGhoulPiano / Emile Su
5
- * Licensed under the MIT License.
6
- * ==============================================================================
7
- *
8
- * This software integrates concepts, logic, and minification processes derived
9
- * from the following open-source projects. We deeply appreciate their work:
10
- *
11
- * ------------------------------------------------------------------------------
12
- * 1. lwIP (Lightweight TCP/IP stack)
13
- * Repository: https://github.com/m-labs/lwip
14
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
15
- * All rights reserved.
16
- * * Redistribution and use in source and binary forms, with or without modification,
17
- * are permitted provided that the following conditions are met:
18
- * * 1. Redistributions of source code must retain the above copyright notice,
19
- * this list of conditions and the following disclaimer.
20
- * 2. Redistributions in binary form must reproduce the above copyright notice,
21
- * this list of conditions and the following disclaimer in the documentation
22
- * and/or other materials provided with the distribution.
23
- * 3. The name of the author may not be used to endorse or promote products
24
- * derived from this software without specific prior written permission.
25
- * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
26
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
28
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34
- * OF SUCH DAMAGE.
35
- *
36
- * ------------------------------------------------------------------------------
37
- * 2. html-minifier-next
38
- * Repository: https://github.com/j9t/html-minifier-next
39
- * Copyright (c) Jens Oliver Meiert (html-minifier-next)
40
- * Copyright (c) Daniel Ruf (html-minifier-terser)
41
- * Copyright (c) Juriy "kangax" Zaytsev (html-minifier)
42
- * * Permission is hereby granted, free of charge, to any person obtaining a copy
43
- * of this software and associated documentation files (the "Software"), to deal
44
- * in the Software without restriction, including without limitation the rights
45
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46
- * copies of the Software, and to permit persons to whom the Software is
47
- * furnished to do so, subject to the following conditions:
48
- * * The above copyright notice and this permission notice shall be included in
49
- * all copies or substantial portions of the Software.
50
- * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
56
- * THE SOFTWARE.
57
- * ==============================================================================
58
- */
59
-
60
- // modern ESM
61
- import fs from 'node:fs';
62
- import path from 'node:path';
63
- import { minify } from 'html-minifier-next';
64
- import { getPackageVersion } from './utils.js';
65
-
66
- // ----------------------------------------------------------------------
67
- // 1. Configs & Version Recovery
68
- // ----------------------------------------------------------------------
69
- const TCP_MSS: number = 1460;
70
- const LWIP_VERSION: string = "1.3.1"; // this makefsdata.ts is based on lwIP v1.3.1
71
-
72
- // html-minifier-next options
73
- const COMPRESS_OPTS_DEFAULT = {
74
- collapseWhitespace: true,
75
- removeComments: true,
76
- minifyJS: true,
77
- minifyCSS: true,
78
- processConditionalComments: true,
79
- decodeEntities: true
80
- };
81
-
82
- export interface MakeFsDataOptions {
83
- inputDir: string;
84
- outputFile: string;
85
- processSubs: boolean;
86
- includeHttpHeader: boolean;
87
- useHttp11: boolean;
88
- supportSsi: boolean;
89
- precalcChksum: boolean;
90
- minifyOpts?: any; // customize
91
- }
92
-
93
- // ----------------------------------------------------------------------
94
- // 2. Structures
95
- // ----------------------------------------------------------------------
96
- interface ChksumBlock { offset: number; chksum: number; len: number; }
97
- interface HeaderPart { str: string; buf: Buffer; }
98
- interface FileEntry {
99
- varName: string; pathName: string; nameBuffer: Buffer;
100
- headerParts: HeaderPart[]; headerTotalBuffer: Buffer;
101
- contentBuffer: Buffer; chksums: ChksumBlock[];
102
- }
103
-
104
- // ----------------------------------------------------------------------
105
- // 3. lwIP Helpers
106
- // ----------------------------------------------------------------------
107
- function getMimeType(fileName: string): string {
108
- const ext: string = path.extname(fileName).toLowerCase();
109
- switch (ext) {
110
- case '.html':
111
- case '.htm':
112
- case '.shtml':
113
- case '.shtm':
114
- case '.ssi': {
115
- return 'text/html';
116
- }
117
-
118
- case '.css':
119
- return 'text/css';
120
- case '.js':
121
- return 'application/javascript';
122
- case '.png':
123
- return 'image/png';
124
- case '.gif':
125
- return 'image/gif';
126
-
127
- case '.jpg':
128
- case '.jpeg': {
129
- return 'image/jpeg';
130
- }
131
-
132
- case '.ico':
133
- return 'image/x-icon';
134
- case '.xml':
135
- return 'text/xml';
136
- case '.json':
137
- return 'application/json';
138
- default:
139
- return 'text/plain';
140
- }
141
- }
142
-
143
- function inetChksum(buf: Buffer): number {
144
- let sum: number = 0;
145
- for (let i = 0; i < buf.length; i += 2) {
146
- const b1: number | undefined = buf[i];
147
- const b2: number | undefined = (i + 1 < buf.length) ? buf[i + 1] : 0;
148
-
149
- if (b1 === undefined) continue;
150
- sum += (b1 << 8) | (b2 ?? 0);
151
- }
152
-
153
- while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
154
-
155
- return (~sum) & 0xFFFF;
156
- }
157
-
158
- function generateHttpHeaders(fileName: string, dataLength: number, opts: MakeFsDataOptions): { parts: HeaderPart[], totalBuffer: Buffer } {
159
- const parts: HeaderPart[] = [];
160
- const ccVersion = getPackageVersion();
161
- const addPart = (str: string) => { parts.push({ str, buf: Buffer.from(str, 'ascii') }); };
162
-
163
- const baseName: string = path.basename(fileName);
164
- const ext: string = path.extname(fileName).toLowerCase();
165
-
166
- const protocol: string = opts.useHttp11 ? "HTTP/1.1" : "HTTP/1.0";
167
- if (baseName.startsWith("404")) addPart(`${protocol} 404 File not found\r\n`);
168
- else if (baseName.startsWith("400")) addPart(`${protocol} 400 Bad Request\r\n`);
169
- else if (baseName.startsWith("501")) addPart(`${protocol} 501 Not Implemented\r\n`);
170
- else addPart(`${protocol} 200 OK\r\n`);
171
-
172
- const serverInfo = `Server: lwIP & CC4EmbeddedSystem V3\r\n` +
173
- `lwIP (${LWIP_VERSION}): (http://savannah.nongnu.org/projects/lwip)\r\n` +
174
- `CC4EmbeddedSystem V3 (${ccVersion}): https://github.com/LunaticGhoulPiano/CC4EmbeddedSystem\r\n`;
175
- parts.push({ str: serverInfo, buf: Buffer.alloc(0) }); // addPart(serverInfo);
176
-
177
- let isSsi: boolean = false;
178
- if (opts.supportSsi && ['.shtml', '.shtm', '.ssi', '.xml'].includes(ext)) isSsi = true;
179
-
180
- if (opts.useHttp11) {
181
- if (! isSsi) {
182
- addPart(`Content-Length: ${dataLength}\r\n`);
183
- addPart(`Connection: keep-alive\r\n`);
184
- }
185
- else addPart(`Connection: close\r\n`);
186
- }
187
-
188
- addPart(`Content-type: ${getMimeType(fileName)}\r\n\r\n`);
189
-
190
- return { parts, totalBuffer: Buffer.concat(parts.map(p => p.buf)) };
191
- }
192
-
193
- function getFilesRecursive(dir: string, processSubs: boolean): string[] {
194
- const results: string[] = [];
195
- if (! fs.existsSync(dir)) return results;
196
- const list: string[] = fs.readdirSync(dir);
197
-
198
- for (const file of list) {
199
- const fullPath: string = path.join(dir, file);
200
- const stat: fs.Stats = fs.statSync(fullPath);
201
- if (! stat.isDirectory()) results.push(fullPath);
202
- else if (processSubs) results.push(...getFilesRecursive(fullPath, processSubs));
203
- }
204
-
205
- return results;
206
- }
207
-
208
- function bufferToHexCArray(buf: Buffer): string {
209
- let out = '';
210
- for (let i = 0; i < buf.length; i++) {
211
- if (i % 16 === 0) out += '\t';
212
- const b: number | undefined = buf[i];
213
- if (b === undefined) continue;
214
- out += `0x${b.toString(16).padStart(2, '0')},`;
215
- if ((i + 1) % 16 === 0) out += '\n';
216
- }
217
-
218
- if (!out.endsWith('\n')) out += '\n';
219
-
220
- return out;
221
- }
222
-
223
- // ----------------------------------------------------------------------
224
- // 4. export API
225
- // ----------------------------------------------------------------------
226
-
227
- export interface BuildStats {
228
- originalSize: number;
229
- compressedSize: number;
230
- filesCount: number;
231
- }
232
-
233
- export async function runMakeFsData(opts: MakeFsDataOptions): Promise<BuildStats> {
234
- console.log(`🚀 CC4EmbeddedSystem V3: Starting makefsdata compilation...`);
235
-
236
- // checkers
237
- if (! fs.existsSync(opts.inputDir)) {
238
- console.log(`⚠️ Input directory not found. Auto-creating directory: \n${opts.inputDir}`);
239
- fs.mkdirSync(opts.inputDir, { recursive: true });
240
- }
241
-
242
- if (!opts.outputFile.toLowerCase().endsWith('.c')) {
243
- opts.outputFile += '.c';
244
- console.log(`🔧 Auto-appended '.c' to output file -> ${opts.outputFile}`);
245
- }
246
-
247
- const outDir = path.dirname(opts.outputFile);
248
- if (! fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
249
-
250
- const allFiles = getFilesRecursive(opts.inputDir, opts.processSubs);
251
- const fileEntries: FileEntry[] = [];
252
- let totalOriginalSize = 0;
253
- let totalCompressedSize = 0;
254
-
255
- if (allFiles.length === 0) throw new Error(`Input directory is empty! Please put your web files (.html, .css, etc.) into:\n${path.resolve(opts.inputDir)}`);
256
-
257
- // html-minifier-next options
258
- const activeCompressOpts = opts.minifyOpts || COMPRESS_OPTS_DEFAULT;
259
-
260
- for (let i = 0; i < allFiles.length; i++) {
261
- const filePath = allFiles[i]!;
262
- const relativePath = '/' + path.relative(opts.inputDir, filePath).replace(/\\/g, '/');
263
- const ext = path.extname(filePath).toLowerCase();
264
- let content = fs.readFileSync(filePath);
265
-
266
- const originalFileSize = content.length;
267
- totalOriginalSize += originalFileSize;
268
-
269
- if (['.html', '.htm', '.css', '.js'].includes(ext)) {
270
- const minifiedStr = await minify(content.toString('utf8'), activeCompressOpts);
271
-
272
- if (typeof minifiedStr === 'string') {
273
- content = Buffer.from(minifiedStr, 'utf8');
274
- console.log(`📦 Minified: ${relativePath} (${originalFileSize} -> ${content.length} bytes)`);
275
- }
276
- }
277
- else console.log(`📄 Copied: ${relativePath}`);
278
-
279
- totalCompressedSize += content.length;
280
-
281
- // generate header
282
- const headerData = opts.includeHttpHeader ? generateHttpHeaders(filePath, content.length, opts) : { parts: [], totalBuffer: Buffer.alloc(0) };
283
- const nameBuffer = Buffer.from(relativePath + '\0', 'utf8');
284
- const varName = relativePath.replace(/[^A-Za-z0-9]/g, '_');
285
-
286
- const chksums: ChksumBlock[] = [];
287
- if (opts.precalcChksum) {
288
- let offset = 0;
289
-
290
- if (headerData.totalBuffer.length > 0) {
291
- chksums.push({ offset: 0, chksum: inetChksum(headerData.totalBuffer), len: headerData.totalBuffer.length });
292
- offset += headerData.totalBuffer.length;
293
- }
294
-
295
- for (let chunkOffset = 0; chunkOffset < content.length; chunkOffset += TCP_MSS) {
296
- const chunkLen = Math.min(TCP_MSS, content.length - chunkOffset);
297
- chksums.push({ offset: offset, chksum: inetChksum(content.subarray(chunkOffset, chunkOffset + chunkLen)), len: chunkLen });
298
- offset += chunkLen;
299
- }
300
- }
301
-
302
- fileEntries.push({ varName, pathName: relativePath, nameBuffer, headerParts: headerData.parts, headerTotalBuffer: headerData.totalBuffer, contentBuffer: content, chksums });
303
- }
304
-
305
- // ====================================================================
306
- // C Code Generation
307
- // ====================================================================
308
- let cOutput: string = `/* Generated by CC4EmbeddedSystem V3 (makefsdata) */\n\n`;
309
- cOutput += `#include "fs.h"\n#include "lwip/def.h"\n#include "fsdata.h"\n\n\n`;
310
- cOutput += `#define file_NULL (struct fsdata_file *) NULL\n\n\n`;
311
-
312
- if (opts.precalcChksum) {
313
- cOutput += `#if HTTPD_PRECALCULATED_CHECKSUM\n`;
314
- for (const file of fileEntries) {
315
- cOutput += `const struct fsdata_chksum chksums_${file.varName}[] = {\n`;
316
- for (const chk of file.chksums) cOutput += `\t{${chk.offset}, 0x${chk.chksum.toString(16).padStart(4, '0')}, ${chk.len}},\n`;
317
- cOutput += `};\n`;
318
- }
319
-
320
- cOutput += `#endif /* HTTPD_PRECALCULATED_CHECKSUM */\n\n\n`;
321
- }
322
-
323
- // generate data arrays
324
- for (const file of fileEntries) {
325
- cOutput += `static const unsigned int dummy_align_${file.varName} = 0;\n`;
326
- cOutput += `static const unsigned char data_${file.varName}[] = {\n`;
327
- cOutput += `\t/* ${file.pathName} (${file.nameBuffer.length} chars) */\n`;
328
- cOutput += bufferToHexCArray(file.nameBuffer);
329
-
330
- if (file.headerParts.length > 0) {
331
- cOutput += `\n\t/* HTTP header */\n`;
332
- for (const part of file.headerParts) {
333
- let finalStr = part.str.replace(/\n/g, '\n\t');
334
- if (finalStr.endsWith('\t')) finalStr = finalStr.slice(0, -1);
335
- cOutput += `\t/* "${finalStr}\t" (${part.buf.length} bytes) */\n`;
336
- cOutput += bufferToHexCArray(part.buf);
337
- }
338
- }
339
-
340
- cOutput += `\n\t/* raw file data (${file.contentBuffer.length} bytes) */\n`;
341
- cOutput += bufferToHexCArray(file.contentBuffer);
342
- cOutput += `};\n\n\n\n`;
343
- }
344
-
345
- // redirhome.html
346
- cOutput += `/* --- Empty arrays --- */\n`;
347
- cOutput += `static const unsigned char data__redirhome_html[] = {};\n`;
348
- cOutput += `const struct fsdata_file file__redirhome_html[] = { {\n`;
349
- cOutput += `\tfile_NULL,\n`;
350
- cOutput += `\tdata__redirhome_html,\n`;
351
- cOutput += `\tdata__redirhome_html + 16,\n`;
352
- cOutput += `\tsizeof(data__redirhome_html) - 16,\n`;
353
- cOutput += `\t1,\n`;
354
-
355
- // checksum
356
- if (opts.precalcChksum) cOutput += `#if HTTPD_PRECALCULATED_CHECKSUM\n\t0, NULL,\n#endif /* HTTPD_PRECALCULATED_CHECKSUM */\n`;
357
- cOutput += `}};\n\n\n`;
358
-
359
-
360
- // generate linked-list
361
- for (let i = fileEntries.length - 1; i >= 0; i--) {
362
- const current = fileEntries[i]!;
363
-
364
- // last element didn't points to file_NULL but file__redirhome_html
365
- const nextVar = fileEntries[i + 1] ? `file_${fileEntries[i+1]!.varName}` : 'file__redirhome_html';
366
- const nameLen = current.nameBuffer.length;
367
-
368
- // address: e.g. "/index.html\0" = '/' + 'i' + 'n' + 'd' + 'e' + 'x' + '.' + 'h' + 't' + 'm' + 'l' + '\0' = 12
369
- cOutput += `const struct fsdata_file file_${current.varName}[] = { {\n`;
370
- cOutput += `\t${nextVar},\n\tdata_${current.varName},\n\tdata_${current.varName} + ${nameLen},\n\tsizeof(data_${current.varName}) - ${nameLen},\n\t${opts.includeHttpHeader ? 1 : 0},\n`;
371
-
372
- if (opts.precalcChksum) cOutput += `#if HTTPD_PRECALCULATED_CHECKSUM\n\t${current.chksums.length}, chksums_${current.varName},\n#endif /* HTTPD_PRECALCULATED_CHECKSUM */\n`;
373
- cOutput += `}};\n\n`;
374
- }
375
-
376
- const rootNode = fileEntries[0];
377
- if (rootNode) {
378
- cOutput += `#define FS_ROOT file_${rootNode.varName}\n#define FS_NUMFILES ${fileEntries.length + 1}\n`;
379
- fs.writeFileSync(opts.outputFile, cOutput);
380
- console.log(`\n✨ Success! Output written to: ${opts.outputFile}`);
381
-
382
- return {
383
- originalSize: totalOriginalSize,
384
- compressedSize: totalCompressedSize,
385
- filesCount: fileEntries.length
386
- };
387
- }
388
-
389
- throw new Error("No files processed.");
390
- }
package/src/utils.ts DELETED
@@ -1,76 +0,0 @@
1
- /*
2
- * ==============================================================================
3
- * CC4EmbeddedSystem V3 (makefsdata)
4
- * Copyright (c) 2026 LunaticGhoulPiano / Emile Su
5
- * Licensed under the MIT License.
6
- * ==============================================================================
7
- *
8
- * This software integrates concepts, logic, and minification processes derived
9
- * from the following open-source projects. We deeply appreciate their work:
10
- *
11
- * ------------------------------------------------------------------------------
12
- * 1. lwIP (Lightweight TCP/IP stack)
13
- * Repository: https://github.com/m-labs/lwip
14
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
15
- * All rights reserved.
16
- * * Redistribution and use in source and binary forms, with or without modification,
17
- * are permitted provided that the following conditions are met:
18
- * * 1. Redistributions of source code must retain the above copyright notice,
19
- * this list of conditions and the following disclaimer.
20
- * 2. Redistributions in binary form must reproduce the above copyright notice,
21
- * this list of conditions and the following disclaimer in the documentation
22
- * and/or other materials provided with the distribution.
23
- * 3. The name of the author may not be used to endorse or promote products
24
- * derived from this software without specific prior written permission.
25
- * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
26
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
28
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34
- * OF SUCH DAMAGE.
35
- *
36
- * ------------------------------------------------------------------------------
37
- * 2. html-minifier-next
38
- * Repository: https://github.com/j9t/html-minifier-next
39
- * Copyright (c) Jens Oliver Meiert (html-minifier-next)
40
- * Copyright (c) Daniel Ruf (html-minifier-terser)
41
- * Copyright (c) Juriy "kangax" Zaytsev (html-minifier)
42
- * * Permission is hereby granted, free of charge, to any person obtaining a copy
43
- * of this software and associated documentation files (the "Software"), to deal
44
- * in the Software without restriction, including without limitation the rights
45
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46
- * copies of the Software, and to permit persons to whom the Software is
47
- * furnished to do so, subject to the following conditions:
48
- * * The above copyright notice and this permission notice shall be included in
49
- * all copies or substantial portions of the Software.
50
- * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
56
- * THE SOFTWARE.
57
- * ==============================================================================
58
- */
59
-
60
- import fs from 'node:fs';
61
- import path from 'node:path';
62
- import { fileURLToPath } from 'node:url';
63
-
64
- const __filename = fileURLToPath(import.meta.url);
65
- const __dirname = path.dirname(__filename);
66
-
67
- export const getPackageVersion = (): string => {
68
- try {
69
- const pkgPath = path.join(__dirname, '../package.json');
70
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
71
- return pkg.version || "LTS";
72
- }
73
- catch (error) {
74
- return "LTS";
75
- }
76
- };
package/tsconfig.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- /* environment */
4
- "target": "ES2022",
5
- "module": "NodeNext",
6
- "moduleResolution": "NodeNext",
7
- "rootDir": "./src",
8
- "outDir": "./dist",
9
-
10
- /* strict type checking */
11
- "strict": true,
12
- "noImplicitAny": true, /* no "any" */
13
- "strictNullChecks": true,
14
- "noUncheckedIndexedAccess": true,
15
- "exactOptionalPropertyTypes": true,
16
-
17
- /* Coding style */
18
- "noUnusedLocals": true,
19
- "noUnusedParameters": true,
20
- "noImplicitReturns": true,
21
-
22
- /* module and consistency */
23
- "esModuleInterop": true,
24
- "forceConsistentCasingInFileNames": true,
25
- "skipLibCheck": true,
26
- "types": ["node"]
27
- },
28
- "include": ["src/**/*"]
29
- }
File without changes