diff --git a/.travis.yml b/.travis.yml index b8824c4..472a3a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,8 @@ install: - go get github.com/shaoshing/gotest - go get github.com/shaoshing/train - go get github.com/shaoshing/train/interpreter - - gem in sass + - gem in sass --pre - gem in coffee-script script: + cd $HOME/gopath/src/github.com/shaoshing/train ./test_all.sh diff --git a/cmd/bundle_test.go b/cmd/bundle_test.go index 2362a7a..bd36e40 100644 --- a/cmd/bundle_test.go +++ b/cmd/bundle_test.go @@ -16,7 +16,7 @@ func assertEqual(path, content string) { if err != nil { panic(err) } - assert.Equal(content, string(c)) + assert.Contain(content, string(c)) } func TestCommand(t *testing.T) { diff --git a/cmd/diagnose.go b/cmd/diagnose.go index 7a9417d..4b7a5ed 100644 --- a/cmd/diagnose.go +++ b/cmd/diagnose.go @@ -11,7 +11,7 @@ import ( func diagnose() bool { var err error - fmt.Println("== Diagnosing\n") + fmt.Println("== Diagnosing") var rubyVersion string rubyVersion, err = bash(`ruby -e "puts RUBY_VERSION"`) @@ -34,15 +34,19 @@ func diagnose() bool { _, err = bash("gem which sass") if err != nil { fmt.Println("-- SASS is disabled because the required gem is not found.") - fmt.Println(" (install it if you wish to use SASS: gem install sass)\n") + fmt.Println(" (install it if you wish to use SASS: gem install sass)") allGood = false } else { _, err = interpreter.Compile(assetsPath + "/stylesheets/font.sass") if err != nil { fmt.Println("-- SASS is disabled because error raised while compiling. Error:") fmt.Printf("%s\n", err.Error()) - fmt.Println("(this might related to your Ruby Environment; try re-installing Ruby)\n") + fmt.Println("(this might related to your Ruby Environment; try re-installing Ruby)") allGood = false + } else { + sassVersion, _ := bash(`ruby -e "require 'sass'; puts Sass::VERSION"`) + supportSourceMap, _ := bash(`ruby -e 'require "sass"; e = Sass::Engine.new ""; puts(e.respond_to?(:render_with_sourcemap) ? "yes" : "no")'`) + fmt.Printf("-- SASS [supported] version: %s, sourcemap: %s\n", strings.Trim(sassVersion, "\n"), strings.Trim(supportSourceMap, "\n")) } } @@ -56,8 +60,11 @@ func diagnose() bool { if err != nil { fmt.Println("-- CoffeeScript is disabled because error raised while compiling. Error: ") fmt.Printf("%s\n", err.Error()) - fmt.Println("(this might related to your Ruby Environment; try re-installing Ruby)\n") + fmt.Println("(this might related to your Ruby Environment; try re-installing Ruby)") allGood = false + } else { + coffeeVersion, _ := bash(`ruby -e "require 'coffee-script'; puts CoffeeScript.version"`) + fmt.Printf("-- Coffee [supported] version: %s\n", strings.Trim(coffeeVersion, "\n")) } } diff --git a/handler.go b/handler.go index a51a958..83eb56a 100644 --- a/handler.go +++ b/handler.go @@ -1,6 +1,7 @@ package train import ( + "github.com/shaoshing/train/interpreter" "io" "log" "net/http" @@ -18,6 +19,7 @@ func servePublicAssets(w http.ResponseWriter, r *http.Request) { var contentTypes = map[string]string{ ".js": "application/javascript", ".css": "text/css", + ".map": "text/plain", } func serveAssets(w http.ResponseWriter, r *http.Request) { @@ -40,6 +42,16 @@ func serveAssets(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", contentTypes[ext]) io.Copy(w, strings.NewReader(content)) } + case ".map": + filePath := strings.Replace(url, "/", "", 1) + sourcemap, err := interpreter.Compile(filePath) + if err != nil { + io.Copy(w, strings.NewReader(err.Error())) + log.Printf("Failed to compile sourcemap\nGET %s\n-----------------------\n%s\n", url, err.Error()) + } else { + w.Header().Set("Content-Type", contentTypes[ext]) + io.Copy(w, strings.NewReader(sourcemap)) + } default: (*assetServer).ServeHTTP(w, r) } diff --git a/handler_test.go b/handler_test.go index db09b8a..2417f51 100644 --- a/handler_test.go +++ b/handler_test.go @@ -56,6 +56,8 @@ h2 { color: green; } `, "text/css") + assertAsset("/assets/stylesheets/app.sass.map", `"file": "assets/stylesheets/app.css"`, "text/plain") + assertAsset("/assets/stylesheets/app2.css", `h2 { color: green; } @@ -108,8 +110,8 @@ func get(url string) (body, contentType string, status int) { func assertAsset(url, expectedBody, expectedContentType string) { body, contentType, _ := get(url) - assert.Equal(expectedBody, body) - assert.Equal(true, strings.Index(contentType, expectedContentType) != -1) + assert.Contain(expectedBody, body) + assert.True(strings.Index(contentType, expectedContentType) != -1) } func assert404(url string) { diff --git a/interpreter/bridge.go b/interpreter/bridge.go index 5018d48..8c6e7c0 100644 --- a/interpreter/bridge.go +++ b/interpreter/bridge.go @@ -41,7 +41,17 @@ func Compile(filePath string) (result string, err error) { if e != nil { panic(err) } - result, err = interpreter.Render(strings.Replace(fileExt, ".", "", 1), content) + result, err = interpreter.Render(strings.Replace(fileExt, ".", "", 1), content, filePath) + case ".map": + filePath = strings.Replace(filePath, ".map", "", 1) + fileExt := path.Ext(filePath) + content, e := ioutil.ReadFile(filePath) + if e != nil { + panic(err) + } + result, err = interpreter.Render(strings.Replace(fileExt, ".", "", 1)+"_source_map", content, filePath) + wd, _ := os.Getwd() + result = strings.Replace(result, "../.."+wd, "", -1) default: err = errors.New("Unsupported format (" + filePath + "). Valid formats are: sass.") } @@ -49,7 +59,7 @@ func Compile(filePath string) (result string, err error) { return } -func (this *Interpreter) Render(format string, content []byte) (result string, err error) { +func (this *Interpreter) Render(format string, content []byte, filePath string) (result string, err error) { this.Lock() defer this.Unlock() @@ -62,7 +72,7 @@ func (this *Interpreter) Render(format string, content []byte) (result string, e option := getOption() - conn.Write([]byte(format + "<<" + option + "<<" + string(content))) + conn.Write([]byte(format + "<<" + option + "<<" + string(content) + "<<" + filePath)) var data bytes.Buffer data.ReadFrom(conn) conn.Close() diff --git a/interpreter/bridge_test.go b/interpreter/bridge_test.go index fd7809b..c5d3499 100644 --- a/interpreter/bridge_test.go +++ b/interpreter/bridge_test.go @@ -38,6 +38,9 @@ func TestSass(t *testing.T) { Config.SASS.LineNumbers = true css, e = Compile("assets/stylesheets/app.sass") assert.Contain("line 1", css) + + css, e = Compile("assets/stylesheets/app.sass.map") + assert.Contain(`"file": "assets/stylesheets/app.css"`, css) } func TestCoffee(t *testing.T) { diff --git a/interpreter/interpreter.rb b/interpreter/interpreter.rb index bb687c6..26b8b55 100644 --- a/interpreter/interpreter.rb +++ b/interpreter/interpreter.rb @@ -10,10 +10,10 @@ def self.serve server = listen loop do client = server.accept - format, option, content = read_all(client) + format, option, content, file_path = read_all(client) begin - result = self.send("render_#{format}", content, option) + result = self.send("render_#{format}", content, option, file_path) client.write "success<<#{result}" rescue Exception => e puts e @@ -71,15 +71,19 @@ def self.read_all client data.split("<<") end - def self.render_sass content, option - _render_sass(content, :sass, option) + def self.render_sass content, option, file_path + _render_sass(content, :sass, option, file_path) end - def self.render_scss content, option - _render_sass(content, :scss, option) + def self.render_scss content, option, file_path + _render_sass(content, :scss, option, file_path) end - def self._render_sass content, syntax, option + def self.render_sass_source_map content, option, file_path + _render_sass(content, :scss, "source_map", file_path) + end + + def self._render_sass content, syntax, option, file_path require "sass" options = { @@ -89,12 +93,31 @@ def self._render_sass content, syntax, option options[:debug_info] = true if option == "debug_info" options[:line_numbers] = true if option == "line_numbers" - + options[:filename] = file_path engine = Sass::Engine.new(content, options) - engine.render + + css_uri = file_path.sub(/\.(scss|sass)/, ".css") + @source_map ||= {} + return @source_map[css_uri] if option == "source_map" && @source_map[css_uri] + + if engine.respond_to?(:render_with_sourcemap) + source_map_uri = "/" + file_path + ".map" + results = engine.render_with_sourcemap(source_map_uri) + css = results[0] + source_map = results[1].to_json(:css_uri => css_uri, :sourcemap_path => source_map_uri) + + @source_map[css_uri] = source_map + option == "source_map" ? @source_map[css_uri] : css + else + if option == "source_map" + raise "Please install sass pre-released SASS version (gem in sass --pre) to get support of sourcemap" + end + + engine.render + end end - def self.render_coffee content, option + def self.render_coffee content, option, file_path require "coffee-script" CoffeeScript.compile content end