-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.rkt
118 lines (100 loc) · 4.2 KB
/
main.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#lang racket/base
(require racket/contract
racket/port
racket/file
racket/tcp
racket/path
net/sendurl
web-server/web-server
"private/misc.rkt"
"private/create-javascript-package.rkt"
"private/zip-temp-dir.rkt"
"private/log-port.rkt"
"private/suck-directory.rkt")
(provide/contract [run-in-browser (path-string? . -> . any)]
[create-zip-package (path-string? path-string? . -> . any)])
;; run-in-browser: path-string -> void
(define (run-in-browser a-filename)
(log-info "Starting web server.\n")
(let ([a-filename (normalize-path a-filename)]
[sema (make-semaphore 0)])
(let ([dispatcher (make-web-serving-dispatcher a-filename)])
(let* ([port (find-open-port)]
[url (format "http://localhost:~a/index.html" port)])
;; Runs the server under the user custodian
;; so it properly gets cleaned up.
(serve #:dispatch dispatcher
#:port port)
(send-url url)
(log-info (format
"Your web application is running at ~a. Click 'Stop' at any time to terminate the Web Server.\n"
url))
(semaphore-wait/enable-break sema)))))
;; create-zip-package: path-string path-string -> void
;; Write out a package zip.
(define (create-zip-package a-filename output-file)
(let ([a-filename (normalize-path a-filename)]
[output-file (normalize-path output-file)])
(with-handlers
([exn:fail?
(lambda (exn)
(log-warning (format
"An internal error occurred during compilation: ~a\n"
(exn-message exn)))
(raise exn))])
(let-values ([(ip dont-care)
(call-with-temporary-directory->zip
(make-package-subdirectory-name output-file)
(lambda (output-path)
(log-info "Compiling Javascript...\n")
(create-javascript-package a-filename
output-path)))])
(call-with-output-file output-file
(lambda (op)
(log-info (format "Writing package to file ~a...\n" output-file))
(copy-port ip op))
#:exists 'replace)
(log-info "Done!\n")))))
;; make-reasonable-package-name: path -> string
;; Tries to pick a reasonable default for the zip file name.
(define (make-reasonable-package-name a-path)
(let-values ([(base name dir?)
(split-path a-path)])
(string-append (remove-filename-extension name)
".zip")))
;; make-package-subdirectory-name: path -> path
(define (make-package-subdirectory-name a-path)
(let-values ([(base name dir?)
(split-path a-path)])
(remove-filename-extension name)))
;; make-web-serving-dispatcher: path -> dispatcher/c
(define (make-web-serving-dispatcher a-filename)
(let* ([tmpdir
(make-temporary-file "mztmp~a"
'directory
#f)])
(dynamic-wind
(lambda () (void))
(lambda ()
(create-javascript-package a-filename tmpdir)
(make-web-dispatcher tmpdir))
(lambda () (delete-directory/files tmpdir)))))
;; find-open-port: -> number
;; Tries to find an open port.
(define (find-open-port)
(let* ([T 84]
[portno
(let loop (;; Numerology at work (P = 80, L = 76, T=84).
[portno 8076]
[attempts 0])
(with-handlers ((exn:fail:network? (lambda (exn)
(cond [(< attempts T)
(loop (add1 portno)
(add1 attempts))]
[else
(raise exn)]))))
;; There's still a race condition here... Not sure how to do this right.
(let ([port (tcp-listen portno 4 #t #f)])
(tcp-close port)
portno)))])
portno))