1 | util = require("./util.coffee")
|
2 | _ = require("lodash")
|
3 | wrench = require("wrench")
|
4 | cheerio = require("cheerio")
|
5 | pathModule = require("path")
|
6 | fs = require("fs")
|
7 | express = require("express")
|
8 | CoffeeScript = require("coffee-script")
|
9 | Less = require("less")
|
10 | commander = (require "commander")
|
11 | program = commander.usage("[options] <target widgetSource.coffee>")
|
12 | .option("-p,--add-path <path>","add pathes other than CWD to search root")
|
13 | .option("-r,--server-root <path>","change test server root other than CWD")
|
14 | .option("-s,--test-style-sheet <path>","add an stylesheet to test page")
|
15 | .option("-e,--test-entry <path>","path to your own test logic")
|
16 | .option("-j,--jquery <path>","specify the jquery path")
|
17 | .option("-l,--leafjs <path>","specify the leafjs path")
|
18 | .option("-g,--test-page <path>","path to your own index.html of the test page")
|
19 | .option("-b,--build-package","build the file into a single js package rather than run it in browser")
|
20 | .option("-t,--temp-path <path>","path to temp file")
|
21 | .option("--use-bare","don't add anything to the index.html of this test")
|
22 | .option("--port <port>","port of the test server")
|
23 | .option("--host <host>","host of the test server")
|
24 | .parse(process.argv)
|
25 | server = express()
|
26 | defaultServerRoot = "./"
|
27 | defaultServerHost = "0.0.0.0"
|
28 | defaultServerPort = 8000
|
29 | defaultTempPath = "/tmp/"
|
30 | pathes = (program.addPath or "").split(",").map (item)->item.trim()
|
31 | if "./" not in pathes and "." not in pathes
|
32 | pathes.unshift "./"
|
33 |
|
34 | serverRoot = program.serverRoot or defaultServerRoot
|
35 | serverPort = parseInt(program.port) or defaultServerPort
|
36 | serverHost = program.host or defaultServerHost
|
37 | tempPath = program.tempPath or defaultTempPath
|
38 | tempPath = pathModule.join tempPath,"leaf-tester"
|
39 | indexHtml = null
|
40 | server.get "/",(req,res,next)->
|
41 | build()
|
42 | res.end(indexHtml)
|
43 | server.get "*",(req,res,next)->
|
44 | filePath = pathModule.join tempPath,req.url
|
45 | console.log "try get",filePath
|
46 | if fs.existsSync filePath
|
47 | fs.createReadStream(filePath).pipe(res)
|
48 | else
|
49 | next()
|
50 |
|
51 |
|
52 | server.use "/",express.static(serverRoot)
|
53 | server.listen serverPort,serverHost
|
54 |
|
55 |
|
56 | testTargetPath = program.args[0]
|
57 | if not testTargetPath
|
58 | commander.help()
|
59 |
|
60 | testIndexTemplate = fs.readFileSync(pathModule.join(__dirname,"asset/index.html"),"utf8");
|
61 |
|
62 | jqueryPath = program.jquery || pathModule.join(__dirname,"asset/jquery.js")
|
63 | leafJsPath = program.leafjs || pathModule.join(__dirname,"asset/leaf.js")
|
64 | testLogicPath = program.testEntry or pathModule.join(__dirname,"asset/testEntry.coffee")
|
65 |
|
66 | if program.buildPackage
|
67 | dependencies = [
|
68 | ]
|
69 | else
|
70 | dependencies = [
|
71 | jqueryPath
|
72 | leafJsPath
|
73 | testLogicPath
|
74 | ]
|
75 | dependencies = dependencies.map (item)->
|
76 | return {path:item}
|
77 | htmlDependencies = null
|
78 | build = ()->
|
79 | if fs.existsSync tempPath
|
80 | wrench.rmdirSyncRecursive tempPath
|
81 | if not fs.existsSync tempPath
|
82 | wrench.mkdirSyncRecursive tempPath
|
83 | testDeps = resolve {path:testTargetPath,expand:true},[{path:testTargetPath,expand:true}]
|
84 | htmlDependencies = dependencies.concat testDeps
|
85 | if not program.buildPackage
|
86 | console.log "solve dependencies",htmlDependencies
|
87 | content = prepareFiles(htmlDependencies)
|
88 | if program.buildPackage
|
89 | console.log content
|
90 | process.exit(0)
|
91 | else
|
92 | indexHtml = content
|
93 |
|
94 |
|
95 | resolve = (fileInfo,deps)->
|
96 | file = fileInfo.path
|
97 | if pathModule.extname(file) isnt ".coffee"
|
98 | return deps
|
99 | if not fileInfo.expand
|
100 | return deps
|
101 | content = util.getFileInSearchPath file,pathes
|
102 | results = []
|
103 | lines = content.split("\n").filter (line)->line.trim()
|
104 | lines = lines.filter (line)->line.indexOf("## require ") is 0
|
105 | lines.reverse()
|
106 | for line in lines
|
107 | asVar = false
|
108 | if line[0] is "#" and line[1] isnt "#"
|
109 | continue
|
110 | if line.indexOf("## require ") isnt 0
|
111 | break
|
112 | params = line.substring(2).trim().split(" ").filter (item)->item
|
113 | depPath = params[1]
|
114 | if params[2] is "as" and params[3]
|
115 | asVar = true
|
116 | if pathModule.basename(depPath) in (deps.map (info)-> pathModule.basename(info.path))
|
117 | continue
|
118 | if not depPath
|
119 | throw new Error "invalid require clause '#{line.trim()}', missing target file."
|
120 | deps.unshift {path:depPath,asVar:asVar,varName:params[3]}
|
121 | if pathModule.extname(depPath) is ".coffee"
|
122 | resolve depPath,deps
|
123 | return deps
|
124 |
|
125 | prepareFiles = (files)->
|
126 | if program.buildPackage
|
127 | processor = packager
|
128 | else
|
129 | processor = preparer
|
130 | $ = cheerio.load testIndexTemplate
|
131 | content = ""
|
132 | for fileInfo in files
|
133 | file = fileInfo.path
|
134 | if fileInfo.asVar
|
135 | if program.buildPackage
|
136 | content = processor["#"](fileInfo,content)
|
137 | else
|
138 | processor["#"](fileInfo,$)
|
139 | continue
|
140 | ext = pathModule.extname(file)
|
141 | if processor[ext]
|
142 | if program.buildPackage
|
143 | content = processor[ext](fileInfo,content)
|
144 | else
|
145 | processor[ext](fileInfo,$)
|
146 | else
|
147 | throw new Error "unkown ext type #{ext} of file #{file}"
|
148 | if content
|
149 | return content
|
150 | html = $.html()
|
151 | fs.writeFileSync pathModule.join(tempPath,"index.html"),html
|
152 | return html
|
153 | preparer = {
|
154 | ".html":(fileInfo,$)->
|
155 | path = fileInfo.path
|
156 | content = util.getFileInSearchPath path,pathes
|
157 | $("body").append(content)
|
158 |
|
159 | ".js":(fileInfo,$)->
|
160 | path = fileInfo.path
|
161 | src = path
|
162 | target = pathModule.join tempPath,pathModule.basename path
|
163 | util.copySync src,target
|
164 | $("head").append("<script src='./#{pathModule.basename(path)}'></script>\n")
|
165 | ".coffee":(fileInfo,$)->
|
166 | path = fileInfo.path
|
167 | content = CoffeeScript.compile util.getFileInSearchPath path,pathes
|
168 | target = pathModule.join tempPath,pathModule.basename(path,".coffee")+".js"
|
169 | fs.writeFileSync target,content
|
170 | $("head").append("<script src='./#{pathModule.basename(target)}'></script>\n")
|
171 | ".css":(fileInfo,$)->
|
172 | path = fileInfo.path
|
173 | src = path
|
174 | target = pathModule.join tempPath,pathModule.basename path
|
175 | util.copySync src,target
|
176 | $("head").append("<link rel='stylesheet' href='./#{pathModule.basename(target)}' type='text/css' media='screen' />\n")
|
177 |
|
178 | ".less":(fileInfo,$)->
|
179 | path = fileInfo.path
|
180 | content = null
|
181 | Less.render (util.getFileInSearchPath path,pathes),(err,result)->
|
182 | if err
|
183 | throw err
|
184 | content = result
|
185 |
|
186 | target = pathModule.join tempPath,pathModule.basename(path,".less")+".css"
|
187 | fs.writeFileSync target,content
|
188 |
|
189 | $("head").append("<link rel='stylesheet' href='./#{pathModule.basename(target)}' type='text/css' media='screen' />\n")
|
190 | "#":(fileInfo,$)->
|
191 | path = fileInfo.path
|
192 | varName = fileInfo.varName
|
193 | if varName is "var"
|
194 | varName = pathModule.basename path,pathModule.extname(path)
|
195 | content = util.getFileInSearchPath path,pathes
|
196 | jsContent = "window.#{varName} = #{JSON.stringify(content)}"
|
197 | fileName = "import-var-#{varName}.js"
|
198 | target = pathModule.join tempPath,fileName
|
199 | fs.writeFileSync target,jsContent
|
200 | $("head").append("<script src='./#{fileName}'></script>\n");
|
201 |
|
202 | }
|
203 | packager = {
|
204 | ".html":(fileInfo,content)->
|
205 | path = fileInfo.path
|
206 | console.warn "//packge mode don't support include html #{fileInfo.path}"
|
207 | return content
|
208 | ".js":(fileInfo,content)->
|
209 | path = fileInfo.path
|
210 | js = util.getFileInSearchPath path,pathes
|
211 | return content + ";;;\n#{js}"
|
212 | ".coffee":(fileInfo,content)->
|
213 | path = fileInfo.path
|
214 | js = CoffeeScript.compile util.getFileInSearchPath path,pathes
|
215 | return content + ";;;\n#{js}"
|
216 | ".css":(fileInfo,content)->
|
217 | path = fileInfo.path
|
218 | css = util.getFileInSearchPath path,pathes
|
219 | return content + "(function(){var style = document.createElement('style');style.setAttribute('data-debug-path',#{JSON.stringify('path')});style.innerHTML = #{JSON.stringify(css)};document.head.appendChild(style)})()"
|
220 | ".less":(fileInfo,content)->
|
221 | path = fileInfo.path
|
222 | css = null
|
223 | Less.render (util.getFileInSearchPath path,pathes),(err,result)->
|
224 | if err
|
225 | throw err
|
226 | css = result
|
227 | return content + "(function(){var style = document.createElement('style');style.setAttribute('data-debug-path',#{JSON.stringify('path')});style.innerHTML = #{JSON.stringify(css)};document.head.appendChild(style)})()"
|
228 | "#":(fileInfo,content)->
|
229 | path = fileInfo.path
|
230 | varName = fileInfo.varName
|
231 | if varName is "var"
|
232 | varName = pathModule.basename path,pathModule.extname(path)
|
233 | content = util.getFileInSearchPath path,pathes
|
234 | jsContent = "window.#{varName} = #{JSON.stringify(content)}"
|
235 | return content + jsContent
|
236 |
|
237 | }
|
238 | build()
|