froggy-docs 1.1.1 → 1.1.4
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/.claude/settings.local.json +21 -0
- package/.github/workflows/froggy-docs.yml +59 -0
- package/ABOUT.md +99 -0
- package/MILESTONE.md +29 -17
- package/bin/froggy_docs.dart +3 -45
- package/frontend/lib/app.dart +488 -280
- package/frontend/lib/components/file_field.dart +75 -0
- package/frontend/web/app.js +224 -67
- package/frontend/web/froggy_docs.json +193 -4
- package/lib/froggy_docs.dart +1 -0
- package/lib/src/cli_runner.dart +123 -0
- package/lib/src/parser_engine.dart +369 -23
- package/lib/src/watcher_engine.dart +46 -14
- package/lib/src/web_server.dart +40 -17
- package/package.js +0 -0
- package/package.json +4 -4
- package/bin/froggy-docs +0 -0
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import 'dart:io';
|
|
2
|
+
import 'dart:async';
|
|
2
3
|
import 'package:watcher/watcher.dart';
|
|
3
4
|
import 'package:path/path.dart' as p;
|
|
4
5
|
import 'parser_engine.dart';
|
|
5
6
|
|
|
6
7
|
class WatcherEngine {
|
|
7
|
-
final ParserEngine _parser
|
|
8
|
+
final ParserEngine _parser;
|
|
9
|
+
Timer? _debounceTimer;
|
|
10
|
+
final Duration _debounceDuration;
|
|
11
|
+
final String? _ignorePattern;
|
|
12
|
+
|
|
13
|
+
WatcherEngine({
|
|
14
|
+
String outputPath = 'frontend/web/froggy_docs.json',
|
|
15
|
+
String ignorePattern = '',
|
|
16
|
+
Duration debounceDuration = const Duration(milliseconds: 300),
|
|
17
|
+
}) : _parser = ParserEngine(),
|
|
18
|
+
_debounceDuration = debounceDuration,
|
|
19
|
+
_ignorePattern = ignorePattern.isEmpty ? null : ignorePattern {
|
|
20
|
+
_parser.setOutputPath(outputPath);
|
|
21
|
+
}
|
|
8
22
|
|
|
9
23
|
void startWatching(String directoryPath) {
|
|
10
24
|
print('🚀 Initializing FroggyDocs scan...');
|
|
@@ -28,31 +42,49 @@ class WatcherEngine {
|
|
|
28
42
|
}
|
|
29
43
|
});
|
|
30
44
|
} catch (e) {
|
|
31
|
-
print('⚠️
|
|
45
|
+
print('⚠️ Warning during initial scan: $e');
|
|
32
46
|
}
|
|
33
47
|
print('✅ Initial scan complete.\n');
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
bool _shouldIgnore(String path) {
|
|
37
51
|
final normalizedPath = path.replaceAll('\\', '/');
|
|
38
|
-
|
|
52
|
+
if (normalizedPath.contains('/.dart_tool/') ||
|
|
39
53
|
normalizedPath.contains('/frontend/') ||
|
|
40
54
|
normalizedPath.contains('/node_modules/') ||
|
|
41
55
|
normalizedPath.contains('/.git/') ||
|
|
42
56
|
normalizedPath.endsWith('.json') ||
|
|
43
|
-
normalizedPath.endsWith('.md')
|
|
57
|
+
normalizedPath.endsWith('.md')) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (_ignorePattern != null) {
|
|
62
|
+
final regexPattern = _ignorePattern
|
|
63
|
+
.replaceAll('.', r'\.')
|
|
64
|
+
.replaceAll('*', '.*')
|
|
65
|
+
.replaceAll('?', '.');
|
|
66
|
+
if (RegExp(regexPattern).hasMatch(normalizedPath)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return false;
|
|
44
72
|
}
|
|
45
73
|
|
|
46
74
|
void _handleFileChange(WatchEvent event) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
_debounceTimer?.cancel();
|
|
76
|
+
|
|
77
|
+
_debounceTimer = Timer(_debounceDuration, () {
|
|
78
|
+
switch (event.type) {
|
|
79
|
+
case ChangeType.ADD:
|
|
80
|
+
case ChangeType.MODIFY:
|
|
81
|
+
_parser.parseFile(event.path);
|
|
82
|
+
break;
|
|
83
|
+
case ChangeType.REMOVE:
|
|
84
|
+
print('🗑️ Removing documentation for: ${p.basename(event.path)}');
|
|
85
|
+
_parser.removeFile(event.path);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
57
89
|
}
|
|
58
90
|
}
|
package/lib/src/web_server.dart
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 'dart:io';
|
|
2
2
|
import 'dart:async';
|
|
3
3
|
import 'dart:convert';
|
|
4
|
+
import 'dart:typed_data';
|
|
4
5
|
import 'package:shelf/shelf.dart';
|
|
5
6
|
import 'package:shelf/shelf_io.dart' as shelf_io;
|
|
6
7
|
import 'package:shelf_router/shelf_router.dart';
|
|
@@ -47,14 +48,14 @@ Router get _router {
|
|
|
47
48
|
|
|
48
49
|
router.get('/froggy_docs.json', (Request request) async {
|
|
49
50
|
var file = File(p.join(userWebDir, 'froggy_docs.json'));
|
|
50
|
-
if (
|
|
51
|
+
if (file.existsSync()) {
|
|
51
52
|
return Response.ok(
|
|
52
53
|
file.openRead(),
|
|
53
54
|
headers: {'Content-Type': 'application/json'},
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
file = File(p.join(webDir, 'froggy_docs.json'));
|
|
57
|
-
if (
|
|
58
|
+
if (file.existsSync()) {
|
|
58
59
|
return Response.ok(
|
|
59
60
|
file.openRead(),
|
|
60
61
|
headers: {'Content-Type': 'application/json'},
|
|
@@ -65,7 +66,7 @@ Router get _router {
|
|
|
65
66
|
|
|
66
67
|
router.get('/', (Request request) async {
|
|
67
68
|
final indexFile = File(p.join(webDir, 'index.html'));
|
|
68
|
-
if (
|
|
69
|
+
if (indexFile.existsSync()) {
|
|
69
70
|
return Response.ok(
|
|
70
71
|
indexFile.openRead(),
|
|
71
72
|
headers: {'Content-Type': 'text/html'},
|
|
@@ -74,11 +75,6 @@ Router get _router {
|
|
|
74
75
|
return Response.notFound('index.html not found');
|
|
75
76
|
});
|
|
76
77
|
|
|
77
|
-
// ═════════════════════════════════════════════════════════════
|
|
78
|
-
// Demo/Mock API Endpoints
|
|
79
|
-
// These are included for testing "Try It Out" functionality
|
|
80
|
-
// Remove these routes in production - your real API will handle requests
|
|
81
|
-
// ═════════════════════════════════════════════════════════════
|
|
82
78
|
router.post('/api/simple', (Request request) async {
|
|
83
79
|
final body = await request.readAsString();
|
|
84
80
|
return Response.ok(
|
|
@@ -110,6 +106,27 @@ Router get _router {
|
|
|
110
106
|
);
|
|
111
107
|
});
|
|
112
108
|
|
|
109
|
+
router.post('/api/upload', (Request request) async {
|
|
110
|
+
final contentType = request.headers['content-type'] ?? '';
|
|
111
|
+
if (contentType.contains('multipart/form-data')) {
|
|
112
|
+
return Response.ok(
|
|
113
|
+
jsonEncode({
|
|
114
|
+
'status': 'success',
|
|
115
|
+
'message': 'File upload received (multipart/form-data)',
|
|
116
|
+
}),
|
|
117
|
+
headers: {'Content-Type': 'application/json'},
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
final body = await request.readAsString();
|
|
121
|
+
return Response.ok(
|
|
122
|
+
jsonEncode({
|
|
123
|
+
'status': 'success',
|
|
124
|
+
'received': body.isNotEmpty ? jsonDecode(body) : {},
|
|
125
|
+
}),
|
|
126
|
+
headers: {'Content-Type': 'application/json'},
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
113
130
|
router.get(
|
|
114
131
|
'/api/simple',
|
|
115
132
|
(Request request) => Response.ok(
|
|
@@ -139,13 +156,6 @@ Router get _router {
|
|
|
139
156
|
),
|
|
140
157
|
);
|
|
141
158
|
|
|
142
|
-
// ═════════════════════════════════════════════════════════════
|
|
143
|
-
// End of Demo/Mock API
|
|
144
|
-
// In production: Remove lines 66-124 or connect to your real API
|
|
145
|
-
// The "Try It Out" button will call your actual API endpoints
|
|
146
|
-
// ═══════════════════════════════════════════════════════════════
|
|
147
|
-
|
|
148
|
-
// Proxy API requests to backend server
|
|
149
159
|
router.all('/api/<path.*>', (Request request, String path) async {
|
|
150
160
|
if (_proxyUrl.isEmpty) {
|
|
151
161
|
return Response.notFound('{"error": "Proxy not configured. Use --proxy http://localhost:3000"');
|
|
@@ -154,12 +164,21 @@ Router get _router {
|
|
|
154
164
|
try {
|
|
155
165
|
final targetUrl = '$_proxyUrl/api/$path';
|
|
156
166
|
final method = request.method;
|
|
167
|
+
final contentType = request.headers['content-type'] ?? '';
|
|
157
168
|
|
|
158
169
|
final client = http.Client();
|
|
159
170
|
http.Request req;
|
|
160
171
|
|
|
161
172
|
if (method == 'GET' || method == 'HEAD') {
|
|
162
173
|
req = http.Request(method, Uri.parse(targetUrl));
|
|
174
|
+
} else if (contentType.contains('multipart/form-data')) {
|
|
175
|
+
// Read as bytes to preserve binary file data.
|
|
176
|
+
final chunks = <int>[];
|
|
177
|
+
await for (final chunk in request.read()) {
|
|
178
|
+
chunks.addAll(chunk);
|
|
179
|
+
}
|
|
180
|
+
req = http.Request(method, Uri.parse(targetUrl));
|
|
181
|
+
req.bodyBytes = Uint8List.fromList(chunks);
|
|
163
182
|
} else {
|
|
164
183
|
final body = await request.readAsString();
|
|
165
184
|
req = http.Request(method, Uri.parse(targetUrl));
|
|
@@ -174,6 +193,10 @@ Router get _router {
|
|
|
174
193
|
}
|
|
175
194
|
});
|
|
176
195
|
|
|
196
|
+
if (contentType.contains('multipart/form-data')) {
|
|
197
|
+
req.headers['content-type'] = contentType;
|
|
198
|
+
}
|
|
199
|
+
|
|
177
200
|
final streamedResponse = await client.send(req);
|
|
178
201
|
final responseBody = await streamedResponse.stream.bytesToString();
|
|
179
202
|
|
|
@@ -195,14 +218,14 @@ Router get _router {
|
|
|
195
218
|
|
|
196
219
|
router.get('/<path|[^/]+>', (Request request, String path) async {
|
|
197
220
|
var file = File(p.join(deployDir, path));
|
|
198
|
-
if (
|
|
221
|
+
if (file.existsSync()) {
|
|
199
222
|
return Response.ok(
|
|
200
223
|
file.openRead(),
|
|
201
224
|
headers: {'Content-Type': _getContentType(path)},
|
|
202
225
|
);
|
|
203
226
|
}
|
|
204
227
|
file = File(p.join(webDir, path));
|
|
205
|
-
if (
|
|
228
|
+
if (file.existsSync()) {
|
|
206
229
|
return Response.ok(
|
|
207
230
|
file.openRead(),
|
|
208
231
|
headers: {'Content-Type': _getContentType(path)},
|
package/package.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "froggy-docs",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Auto-generate API documentation from code annotations. Works with any programming language.",
|
|
5
|
-
"author": "Kaung Mrat Thu <
|
|
5
|
+
"author": "Kaung Mrat Thu <kaungmratthu.dev@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/Kaung-Myat/froggydocs",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/Kaung-Myat/froggydocs.git"
|
|
9
|
+
"url": "git+https://github.com/Kaung-Myat/froggydocs.git"
|
|
10
10
|
},
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"keywords": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"developer-tools"
|
|
20
20
|
],
|
|
21
21
|
"bin": {
|
|
22
|
-
"froggy-docs": "
|
|
22
|
+
"froggy-docs": "package.js"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {},
|
|
25
25
|
"engines": {
|
package/bin/froggy-docs
DELETED
|
Binary file
|