環境
- node.js 0.10.33
- express 4.0.0-rc4
- CoffeeScript 1.9.2
経緯
昼に書いた奴の続きです。なぬっ。今北産業とな? おぬし、お里が知れるぞ。
だばだー
だーば
だばだー
いや、失敬。
画面遷移を
変な構文で短く書いて
CoffeeScript生成
もうだいぶ壊れているので、詳しい話は前記事と前々記事を見てくださいね。
で、結局どうなったか
震えながら、-w オプションを試してみたら、何事もなく機能したので、coffee -> jsまでやらせることにしました。
なので結局、使い方は、後述のgoldblend.coffeeを適当な場所に置き、
以上です。mytest.goldが更新される度に、mytest.coffeeが出力され、mytest.jsが生成されます。常駐させないで都度やる場合は、今まで同様、-wを付けなければOKです。
あとは、設定行に「viewengine:」という設定項目ができました。これと、views/__template.jadeとか(拡張子は諸々読み替えてね)を作っておくと、views/classname.jadeなんかを自動的に作ってくれます。1行目に「updateとかなんとか」というコメントが挿入されるので、その行を削除してから編集して下さい。
多くの画面はとりあえずほっといて後で作る場合が多い&最初に作りこんだ画面で「ああ、こういうふうにしとかなきゃいかんな」と思いついたことを「__template.jade」に反映させると思いますので、自動コピーしたまま手を触れていない(=1行目を消していない)ファイルは、プリプロセス時に上書きされます。
では、だばだを聴こう!
「例の曲」の時間ですよ! だばだー、だーーば、だばだー。敢えて説明はしませんが、設定行を処理したときに挿入されるコメントのデリミタがカンマになったり、ちょっと苦しいやり方で、画面定義クラスの下のスコープ中のコメントアウトと、画面定義の次のセクションの始めのコメントを見分けるようにしました。ほんと苦しい。というわけで、画面定義にぶら下がるスコープ中に、インデントされていない1行コメントは入れないようにして下さい。
この問題があるので、CoffeeScript -> JavaScriptのコンパイルでは、行コメントを消してしまうんだな、というのがよく分かりました。なるほどな。細かい使い方は再掲しません。プリプロセッサ「goldblend.coffee」の最新版をご覧下さい。
なんだかんだこいつを育てている間に、app.goldの雛形と、viewsの一連の雛形と、public/jsのjquery等のローカルコピーとかができました。(なお、余談ですが、views/layout.jadeとかあると思いますが、アレはviews/__layout.jadeとかに名前を変えた方が良いですよ。error.jadeも同じです。名前の衝突って、存外心が荒むものです)で、ここからもう一手間かけるなら、express-goldblend-generator.coffeeでも作れば充分過ぎだと思いますが、それにしても、 どっぱどっぱ焦茶色の粉が出てくる感じ のファイル名ですな。
fs = require('fs')
# ------------------------------------
# -- コマンドライン引数の解釈
for val,index in process.argv
if index>1
if /^-/.test(val)
if /w/.test(val)
watch = true
else
if /\.gold$/i.test(val) and fs.existsSync val
filesrc = val
filedst = val.replace /gold$/i, 'coffee'
if not(filesrc?)
console.log 'mi a ta ra naaai!'
process.exit 1
# ------------------------------------
# -- 成果物のjsへのコンパイル
coffee = require('coffee-script').compile
compile = (filedst) ->
read = fs.readFileSync filedst
jssrc = coffee read.toString(),{bare:true}
fs.writeFileSync filedst.replace(/coffee$/,'js'),jssrc,'utf8'
# ------------------------------------
# -- テンプレートファイルのコピー
filecopy = (classname,cvparams,viewengine) ->
docopy = false
filename = 'views/'+classname+'.'+viewengine
template = 'views/__template.'+viewengine
cv = cvparams.replace /^\s*,/, ''
switch viewengine
when 'ect'
regex = /^<!--update/
text = "<!--update:この行を消さないと、毎回上書きされます-->\n"
text += "<!--このテンプレートの自明な引数 ⇒ #{cv} -->\n"
when 'jade'
regex = /^\/\/update/
text = "//update:この行を消さないと、毎回上書きされます\n"
text += "//このテンプレートの自明な引数 ⇒ #{cv}\n"
when 'ejs'
regex = /^<% \/\* update/
text = "<% /* update:この行を消さないと、毎回上書きされます */ %>\n"
text += "<% /* このテンプレートの自明な引数 ⇒ #{cv} */ %>\n"
when 'coffee'
regex = /^#update/
text = "#update:この行を消さないと、毎回上書きされます\n"
text += "#このテンプレートの自明な引数 ⇒ #{cv}\n"
when 'haml'
regex = /^-#update/
text = "-#update:この行を消さないと、毎回上書きされます\n"
text += "-#このテンプレートの自明な引数 ⇒ #{cv}\n"
else
console.log 'view engine ['+viewengine+'] is mi ta i o u!'
reutrn
if not fs.existsSync template
console.log 'template file is not found!'
return
if fs.existsSync filename
if regex.test fs.readFileSync(filename).toString()
#コピー先が上書き許可なので、コピーする
docopy = true
fs.unlinkSync filename
else
#コピー先が有効なので、コピーしない
docopy = false
else
#コピー先が存在しないので、コピーする
docopy = true
if docopy
fs.writeFileSync(filename, text+fs.readFileSync(template, 'utf8'))
console.log 'created: '+filename
# ------------------------------------
# -- ファイル書き出し
writetext = (dst, text) ->
fs.appendFileSync dst,text,'utf8', (err) -> console.log err
# ------------------------------------
# -- 画面定義の出力(末尾)
generateTail = (dst, indentsp, classname, routername) ->
text = indentsp+classname+'.install '+routername+"\n\n"
writetext dst,text
# ------------------------------------
# -- 画面定義の出力(前半)
generateMain = (dst,indentsp,classname,temp,routername,mw,nocache,viewengine) ->
args = []
for str,index in temp
str = str.replace(/^\s+/, '').replace(/\s+$/, '')
switch index
when 0
argreq = str
when 1
argres = str
else
args.push str
urlprm = ''
cvparams = ''
buildparams4sp = ''
reqparams4sp = ''
yamlparams6sp = ''
for str,index in args
urlprm += '/:'+str
cvparams += ','+str
if index>0
buildparams4sp += "\n"
reqparams4sp += "\n"
yamlparams6sp += "\n"
buildparams4sp += "#{indentsp} params += '/'+encodeURIComponent(#{str})"
reqparams4sp += "#{indentsp} #{str} = #{argreq}.params.#{str} ? ''"
yamlparams6sp += "#{indentsp} #{str}: #{str}"
emptyobject = if args.length == 0 then ' {}' else ''
randomstring = 'Math.random().toString(36).slice(-8)'
appendnocache = if nocache then "+'?'+#{randomstring}+#{randomstring}" else ''
path = classname.replace /__/,'/'
text = """
# {indentsp}class #{classname}
# {indentsp} @install: (obj) => obj.get '/#{path}#{urlprm}',#{mw}@get
# {indentsp} @redirect: (#{argreq},#{argres}#{cvparams}) =>
# {indentsp} params = ''
# {buildparams4sp}
# {indentsp} #{argres}.redirect '/#{path}'+params#{appendnocache}
# {indentsp} @get: (#{argreq},#{argres}) =>
# {reqparams4sp}
# {indentsp} @direct #{argreq},#{argres}#{cvparams}
# {indentsp} @direct: (#{argreq},#{argres}#{cvparams}) =>
# {indentsp} params =#{emptyobject}
# {yamlparams6sp}
"""
writetext dst,text
filecopy classname,cvparams,viewengine
return argres
# ------------------------------------
# -- トークンの発見と処理
doconv = (dst, src) ->
console.log 'processing... '+src
content = fs.readFileSync(src).toString()
content = content.replace(/\r\n/, "\n").replace(/\r/, "\n").split("\n")
if fs.existsSync(dst)
fs.writeFile dst, '', 'utf8', (err) -> console.log err
comments = false
generate = false
indentsp = ''
defaultmode = true
routername = 'router'
mw = ''
nocache = true
viewengine = 'ect'
for line,index in content
if index>0
writetext dst,"\n"
if comments
if generate
writetext dst,' '+line
else
writetext dst,line
if /^\s*###/.test(line)
comments = false
else
normaloutput = false
addindent = false
result = line.match /^(\s*)#(\s*)/
if result == null
comsp = 0
linecom = false
else
comsp = result[1].length+result[2].length
linecom = true
if /^\s*###/.test(line) #コメントのみ行を影響させない
comments = true
normaloutput = true
addindent = generate
else if linecom and ((not generate) or (comsp > indentsp.length))
#コメント行は、コメントアウトと見なされる限りは無視
normaloutput = true
addindent = generate
else if /^\s*$/.test(line) #空行を影響させない
normaloutput = true
addindent = generate
else
if /^\s*<<</.test(line)
if generate
#generate末尾出力
generateTail dst,indentsp,classname,routername
result = line.match /^(\s*)<<<\s*{([^{}]*)}/
if result == null
#newgenerate
if defaultmode
defaultmode = false
confcomment = '#'+routername+', '
confcomment += mw.replace(/,$/,'')+', '
confcomment += nocache.toString()+', '
confcomment += viewengine
writetext dst,confcomment
#indentspの取得
#argreq,argres,argsの取得
#classnameの取得
result = line.match /^(\s*)<<<\s*(\S+)\s*\(([^()]+)\)/
if result == null
console.log 'syntax error ['+index+']'
process.exit 1
#generate要素読み取り
indentsp = result[1]
classname = result[2]
temp = result[3].split(',')
argres = generateMain dst,indentsp,classname,temp,routername,mw,nocache,viewengine
generate = true
normaloutput = false
else
#routernameの取得
#mwの取得
#nocache
defaultmode = false
scopeindentsp = result[1]
tempkv = {}
keyvalues = result[2].split(',')
for keyvalue in keyvalues
keyvalue = keyvalue.replace(/^\s+/, '').replace(/\s+$/, '')
keyvalue = keyvalue.split(':')
key = keyvalue[0].replace(/^\s+/, '').replace(/\s+$/, '')
value = keyvalue[1].replace(/^\s+/, '').replace(/\s+$/, '')
tempkv[key] = value
if tempkv.routername?
routername = tempkv.routername
if routername.length == 0
routername = 'router'
if tempkv.middleware?
mw = tempkv.middleware
if mw.length > 0
mw = mw + ','
if tempkv.viewengine
viewengine = tempkv.viewengine
if viewengine = ''
viewengine = 'ect'
if tempkv.nocache?
nocache = (/(true|ok|yes)/i.test(tempkv.nocache))
confcomment = '#'+routername+', '
confcomment += mw.replace(/,$/,'')+', '
confcomment += nocache.toString()+', '
confcomment += viewengine
writetext dst,confcomment
normaloutput = false
else if generate
spaces = line.match /^\s*/
if spaces == null
console.log 'a ri e nai error!'
process.exit()
if spaces[0].length > indentsp.length
#generateスコープ内出力
line = line.replace /@render\b/, argres+'.render'
line = line.replace /@@/, "'"+classname+"'"
writetext dst,' '+line
normaloutput = false
else
#generate末尾出力
generateTail dst,indentsp,classname,routername
generate = false
normaloutput = true
else
normaloutput = true
if normaloutput
if addindent
fs.appendFileSync dst,' '+line,'utf8', (err) -> console.log err
else
fs.appendFileSync dst,line,'utf8', (err) -> console.log err
console.log 'completed!'
# ------------------------------------
# -- メインルーチン
doconv filedst,filesrc
compile filedst
if watch?
fs.watchFile filesrc, (curr, prev) ->
doconv filedst,filesrc
compile filedst
else
process.exit 0