@rip-lang/server 1.3.78 → 1.3.80
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/default.rip +139 -28
- package/package.json +1 -1
package/default.rip
CHANGED
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
import { use, start, notFound } from '@rip-lang/server'
|
|
18
18
|
import { serve } from '@rip-lang/server/middleware'
|
|
19
19
|
import { statSync, readdirSync } from 'node:fs'
|
|
20
|
-
import { join, resolve } from 'node:path'
|
|
20
|
+
import { join, resolve, basename } from 'node:path'
|
|
21
21
|
|
|
22
22
|
root = resolve(process.env.APP_BASE_DIR or process.cwd())
|
|
23
23
|
rootSlash = root + '/'
|
|
24
|
+
rootName = basename(root) or root
|
|
24
25
|
|
|
25
26
|
use serve dir: root, bundle: [], watch: true
|
|
26
27
|
|
|
@@ -37,10 +38,17 @@ fmtSize = (n) ->
|
|
|
37
38
|
"#{n.toFixed(1)} TB"
|
|
38
39
|
|
|
39
40
|
fmtDate = (ms) ->
|
|
40
|
-
new Date(ms)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
d = new Date(ms)
|
|
42
|
+
mo = d.toLocaleString('en', { month: 'short' })
|
|
43
|
+
day = String(d.getDate()).padStart(2, '0')
|
|
44
|
+
year = d.getFullYear()
|
|
45
|
+
hour = d.getHours()
|
|
46
|
+
min = String(d.getMinutes()).padStart(2, '0')
|
|
47
|
+
ampm = if hour >= 12 then 'PM' else 'AM'
|
|
48
|
+
hour = hour % 12 or 12
|
|
49
|
+
"#{mo} #{day}, #{year} at #{hour}:#{min} #{ampm}"
|
|
50
|
+
|
|
51
|
+
EXT_ICONS =
|
|
44
52
|
html: '🌐', htm: '🌐', css: '🎨', js: '📜', mjs: '📜', ts: '📜'
|
|
45
53
|
json: '📋', rip: '⚡', md: '📝', txt: '📝', pdf: '📕'
|
|
46
54
|
png: '🖼️', jpg: '🖼️', jpeg: '🖼️', gif: '🖼️', svg: '🖼️', webp: '🖼️', ico: '🖼️'
|
|
@@ -48,11 +56,20 @@ ICONS =
|
|
|
48
56
|
mp3: '🎵', ogg: '🎵', wav: '🎵', mp4: '🎬', webm: '🎬'
|
|
49
57
|
woff: '🔤', woff2: '🔤', ttf: '🔤', otf: '🔤'
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
fileIcon = (name) ->
|
|
60
|
+
EXT_ICONS[name.slice(name.lastIndexOf('.') + 1).toLowerCase()] or '📄'
|
|
61
|
+
|
|
62
|
+
row = (ic, href, label, size = '', date = '') ->
|
|
63
|
+
"<tr><td class=\"ic\">#{ic}</td><td><a href=\"#{esc href}\">#{esc label}</a></td><td class=\"sz\">#{size}</td><td class=\"dt\">#{date}</td></tr>"
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
breadcrumbs = (reqPath) ->
|
|
66
|
+
parts = reqPath.split('/').filter((p) -> p)
|
|
67
|
+
crumbs = "<a href=\"/\">#{esc rootName}</a>"
|
|
68
|
+
href = ''
|
|
69
|
+
for part in parts
|
|
70
|
+
href += "/#{part}"
|
|
71
|
+
crumbs += " <span class=\"sep\">/</span> <a href=\"#{href}/\">#{esc part}</a>"
|
|
72
|
+
crumbs
|
|
56
73
|
|
|
57
74
|
# --- Directory Index ---
|
|
58
75
|
|
|
@@ -67,45 +84,139 @@ renderIndex = (reqPath, fsPath) ->
|
|
|
67
84
|
files.sort()
|
|
68
85
|
|
|
69
86
|
rows = []
|
|
70
|
-
rows.push
|
|
87
|
+
rows.push row('📁', '../', '..') if reqPath isnt '/'
|
|
71
88
|
for d in dirs
|
|
72
|
-
rows.push
|
|
89
|
+
rows.push row('📁', "#{d}/", d)
|
|
73
90
|
for f in files
|
|
74
91
|
try
|
|
75
92
|
s = statSync(join(fsPath, f))
|
|
76
|
-
rows.push
|
|
93
|
+
rows.push row(fileIcon(f), f, f, fmtSize(s.size), fmtDate(s.mtimeMs))
|
|
77
94
|
catch
|
|
78
|
-
rows.push
|
|
95
|
+
rows.push row(fileIcon(f), f, f)
|
|
96
|
+
|
|
97
|
+
numDirs = dirs.length
|
|
98
|
+
numFiles = files.length
|
|
99
|
+
summary = []
|
|
100
|
+
summary.push "#{numDirs} folder#{if numDirs isnt 1 then 's' else ''}" if numDirs > 0
|
|
101
|
+
summary.push "#{numFiles} file#{if numFiles isnt 1 then 's' else ''}" if numFiles > 0
|
|
79
102
|
|
|
80
|
-
title = esc(reqPath)
|
|
81
103
|
"""
|
|
82
104
|
<!DOCTYPE html>
|
|
83
105
|
<html lang="en">
|
|
84
106
|
<head>
|
|
85
107
|
<meta charset="UTF-8">
|
|
86
108
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
87
|
-
<title>Index of #{
|
|
109
|
+
<title>Index of #{esc reqPath}</title>
|
|
88
110
|
<style>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
111
|
+
:root {
|
|
112
|
+
--bg: #f3f6f7;
|
|
113
|
+
--card: #fff;
|
|
114
|
+
--border: #e5e7eb;
|
|
115
|
+
--border-row: #f0f0f0;
|
|
116
|
+
--text: #1f2937;
|
|
117
|
+
--muted: #6b7280;
|
|
118
|
+
--link: #2563eb;
|
|
119
|
+
--link-visited: #7c3aed;
|
|
120
|
+
--hover-bg: #f0f7ff;
|
|
121
|
+
--head-bg: #fafbfc;
|
|
122
|
+
--sep: #cbd5e1;
|
|
123
|
+
}
|
|
124
|
+
@media (prefers-color-scheme: dark) {
|
|
125
|
+
:root {
|
|
126
|
+
--bg: #0d1117;
|
|
127
|
+
--card: #161b22;
|
|
128
|
+
--border: #30363d;
|
|
129
|
+
--border-row: #21262d;
|
|
130
|
+
--text: #c9d1d9;
|
|
131
|
+
--muted: #8b949e;
|
|
132
|
+
--link: #58a6ff;
|
|
133
|
+
--link-visited: #bc8cff;
|
|
134
|
+
--hover-bg: #1c2129;
|
|
135
|
+
--head-bg: #1c2129;
|
|
136
|
+
--sep: #484f58;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
140
|
+
body {
|
|
141
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
142
|
+
font-size: 15px;
|
|
143
|
+
line-height: 1.5;
|
|
144
|
+
color: var(--text);
|
|
145
|
+
background: var(--bg);
|
|
146
|
+
-webkit-font-smoothing: antialiased;
|
|
147
|
+
}
|
|
148
|
+
.wrap { max-width: 960px; margin: 2.5rem auto; padding: 0 1rem; }
|
|
149
|
+
.card {
|
|
150
|
+
background: var(--card);
|
|
151
|
+
border: 1px solid var(--border);
|
|
152
|
+
border-radius: 8px;
|
|
153
|
+
overflow: hidden;
|
|
154
|
+
box-shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
|
155
|
+
}
|
|
156
|
+
.bar {
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: space-between;
|
|
160
|
+
padding: .875rem 1.25rem;
|
|
161
|
+
border-bottom: 1px solid var(--border);
|
|
162
|
+
gap: 1rem;
|
|
163
|
+
}
|
|
164
|
+
.crumbs { font-size: .9375rem; font-weight: 500; }
|
|
165
|
+
.crumbs a { color: var(--link); text-decoration: none; }
|
|
166
|
+
.crumbs a:hover { text-decoration: underline; }
|
|
167
|
+
.sep { color: var(--sep); margin: 0 .3rem; font-weight: 400; }
|
|
168
|
+
.meta { font-size: .8125rem; color: var(--muted); white-space: nowrap; }
|
|
169
|
+
table { width: 100%; border-collapse: collapse; }
|
|
170
|
+
th {
|
|
171
|
+
position: sticky; top: 0;
|
|
172
|
+
background: var(--head-bg);
|
|
173
|
+
text-align: left;
|
|
174
|
+
padding: .625rem 1.25rem;
|
|
175
|
+
font-size: .6875rem;
|
|
176
|
+
font-weight: 600;
|
|
177
|
+
color: var(--muted);
|
|
178
|
+
text-transform: uppercase;
|
|
179
|
+
letter-spacing: .05em;
|
|
180
|
+
border-bottom: 1px solid var(--border);
|
|
181
|
+
}
|
|
182
|
+
td {
|
|
183
|
+
padding: .5rem 1.25rem;
|
|
184
|
+
border-bottom: 1px solid var(--border-row);
|
|
185
|
+
font-size: .875rem;
|
|
186
|
+
vertical-align: middle;
|
|
187
|
+
}
|
|
188
|
+
tr:last-child td { border-bottom: none; }
|
|
189
|
+
tbody tr:hover { background: var(--hover-bg); }
|
|
190
|
+
td a { color: var(--link); text-decoration: none; }
|
|
191
|
+
td a:hover { color: var(--link); text-decoration: underline; }
|
|
192
|
+
td a:visited { color: var(--link-visited); }
|
|
193
|
+
.ic { width: 2.5rem; text-align: center; font-size: 1.125rem; }
|
|
194
|
+
.sz { width: 6rem; text-align: right; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
195
|
+
.dt { color: var(--muted); white-space: nowrap; }
|
|
196
|
+
th.sz { text-align: right; }
|
|
197
|
+
@media (max-width: 640px) {
|
|
198
|
+
.wrap { margin: 1rem auto; }
|
|
199
|
+
td, th { padding: .5rem .75rem; }
|
|
200
|
+
.dt, th.dt { display: none; }
|
|
201
|
+
.bar { padding: .75rem 1rem; }
|
|
202
|
+
}
|
|
99
203
|
</style>
|
|
100
204
|
</head>
|
|
101
205
|
<body>
|
|
102
|
-
<
|
|
206
|
+
<div class="wrap">
|
|
207
|
+
<div class="card">
|
|
208
|
+
<div class="bar">
|
|
209
|
+
<div class="crumbs">#{breadcrumbs reqPath}</div>
|
|
210
|
+
<div class="meta">#{summary.join(' · ')}</div>
|
|
211
|
+
</div>
|
|
103
212
|
<table>
|
|
104
|
-
<thead><tr><th></th><th>Name</th><th class="
|
|
213
|
+
<thead><tr><th></th><th>Name</th><th class="sz">Size</th><th class="dt">Modified</th></tr></thead>
|
|
105
214
|
<tbody>
|
|
106
215
|
#{rows.join('\n')}
|
|
107
216
|
</tbody>
|
|
108
217
|
</table>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
109
220
|
</body>
|
|
110
221
|
</html>
|
|
111
222
|
"""
|
|
@@ -113,7 +224,7 @@ renderIndex = (reqPath, fsPath) ->
|
|
|
113
224
|
# --- Request Handler ---
|
|
114
225
|
|
|
115
226
|
notFound ->
|
|
116
|
-
reqPath = @req.path
|
|
227
|
+
reqPath = try decodeURIComponent(@req.path) catch then @req.path
|
|
117
228
|
path = resolve(root, reqPath.slice(1))
|
|
118
229
|
|
|
119
230
|
unless path is root or path.startsWith(rootSlash)
|