Skip to content

Commit

Permalink
Move the ImportResolver to Args
Browse files Browse the repository at this point in the history
Closes #2
  • Loading branch information
bep committed Dec 26, 2020
1 parent bb079a6 commit c4d0ff2
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 77 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
**Note** that this repository will probably be archived if I cannot find a solution to [this blocking issue](https://github.com/bep/godartsass/issues/2).

[![Tests on Linux, MacOS and Windows](https://github.com/bep/godartsass/workflows/Test/badge.svg)](https://github.com/bep/godartsass/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/bep/godartsass)](https://goreportcard.com/report/github.com/bep/godartsass)
[![codecov](https://codecov.io/gh/bep/godartsass/branch/main/graph/badge.svg?token=OWZ9RCAYWO)](https://codecov.io/gh/bep/godartsass)
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,5 @@ go 1.15
require (
github.com/cli/safeexec v1.0.0
github.com/frankban/quicktest v1.11.2
github.com/google/go-cmp v0.5.2
github.com/sanity-io/litter v1.3.0
google.golang.org/protobuf v1.25.0
)
8 changes: 0 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b h1:XxMZvQZtTXpWMNWK82vdjCLCe7uGMFXdTsJH0v3Hkvw=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.11.2 h1:mjwHjStlXWibxOohM7HYieIViKyh56mmt3+6viyhDDI=
Expand Down Expand Up @@ -33,13 +31,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 h1:GD+A8+e+wFkqje55/2fOVnZPkoDIu1VooBWfNrnY8Uo=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/sanity-io/litter v1.3.0 h1:5ZO+weUsqdSWMUng5JnpkW/Oz8iTXiIdeumhQr1sSjs=
github.com/sanity-io/litter v1.3.0/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312 h1:UsFdQ3ZmlzS0BqZYGxvYaXvFGUbCmPGy8DM7qWJJiIQ=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
13 changes: 7 additions & 6 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ type Options struct {
// There may be several ways to install this, one would be to
// download it from here: https://github.com/sass/dart-sass-embedded/releases
DartSassEmbeddedFilename string

// Custom resolver to use to resolve imports.
ImportResolver ImportResolver
}

func (opts *Options) init() error {
Expand Down Expand Up @@ -68,6 +65,10 @@ type Args struct {
// If enabled, a sourcemap will be generated and returned in Result.
EnableSourceMap bool

// Custom resolver to use to resolve imports.
// If set, this will be the first in the resolver chain.
ImportResolver ImportResolver

// Additional file paths to uses to resolve imports.
IncludePaths []string

Expand All @@ -78,7 +79,7 @@ type Args struct {
sassImporters []*embeddedsass.InboundMessage_CompileRequest_Importer
}

func (args *Args) init(opts Options) error {
func (args *Args) init(seq uint32, opts Options) error {
if args.OutputStyle == "" {
args.OutputStyle = OutputStyleNested
}
Expand All @@ -99,11 +100,11 @@ func (args *Args) init(opts Options) error {

args.sassSourceSyntax = embeddedsass.InboundMessage_Syntax(v)

if opts.ImportResolver != nil {
if args.ImportResolver != nil {
args.sassImporters = []*embeddedsass.InboundMessage_CompileRequest_Importer{
{
Importer: &embeddedsass.InboundMessage_CompileRequest_Importer_ImporterId{
ImporterId: importerID,
ImporterId: seq,
},
},
}
Expand Down
119 changes: 63 additions & 56 deletions transpiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ import (

var defaultDartSassEmbeddedFilename = "dart-sass-embedded"

const (

// There is only one, and this number is picked out of a hat.
importerID = 5679
)

// Start creates an starts a new SCSS transpiler that communicates with the
// Dass Sass Embedded protocol via Stdin and Stdout.
//
Expand Down Expand Up @@ -75,8 +69,8 @@ type Transpiler struct {
// stdin/stdout of the Dart Sass protocol
conn io.ReadWriteCloser

// Protects the sending of the compile request.
reqMu sync.Mutex
// Protects the sending of messages to Dart Sass.
sendMu sync.Mutex

mu sync.Mutex // Protects all below.
seq uint32
Expand Down Expand Up @@ -122,35 +116,42 @@ func (t *Transpiler) Close() error {
func (t *Transpiler) Execute(args Args) (Result, error) {
var result Result

if err := args.init(t.opts); err != nil {
return result, err
}
createInboundMessage := func(seq uint32) (*embeddedsass.InboundMessage, error) {
if err := args.init(seq, t.opts); err != nil {
return nil, err
}

message := &embeddedsass.InboundMessage_CompileRequest_{
CompileRequest: &embeddedsass.InboundMessage_CompileRequest{
Importers: args.sassImporters,
Style: args.sassOutputStyle,
Input: &embeddedsass.InboundMessage_CompileRequest_String_{
String_: &embeddedsass.InboundMessage_CompileRequest_StringInput{
Syntax: args.sassSourceSyntax,
Source: args.Source,
Url: args.URL,
message := &embeddedsass.InboundMessage_CompileRequest_{
CompileRequest: &embeddedsass.InboundMessage_CompileRequest{
Importers: args.sassImporters,
Style: args.sassOutputStyle,
Input: &embeddedsass.InboundMessage_CompileRequest_String_{
String_: &embeddedsass.InboundMessage_CompileRequest_StringInput{
Syntax: args.sassSourceSyntax,
Source: args.Source,
Url: args.URL,
},
},
SourceMap: args.EnableSourceMap,
},
SourceMap: args.EnableSourceMap,
},
}
}

resp, err := t.invoke(
&embeddedsass.InboundMessage{
return &embeddedsass.InboundMessage{
Message: message,
},
)
}, nil
}

call, err := t.newCall(createInboundMessage, args)
if err != nil {
return result, err
}
call = <-call.Done
if call.Error != nil {
return result, call.Error
}

csp := resp.Message.(*embeddedsass.OutboundMessage_CompileResponse_)
response := call.Response
csp := response.Message.(*embeddedsass.OutboundMessage_CompileResponse_)

switch resp := csp.CompileResponse.Result.(type) {
case *embeddedsass.OutboundMessage_CompileResponse_Success:
Expand All @@ -174,6 +175,16 @@ func (t *Transpiler) Execute(args Args) (Result, error) {
return result, nil
}

func (t *Transpiler) getImportResolver(id uint32) ImportResolver {
t.mu.Lock()
defer t.mu.Unlock()
call, found := t.pending[id]
if !found {
panic(fmt.Sprintf("call with ID %d not found", id))
}
return call.importResolver
}

func (t *Transpiler) input() {
var err error

Expand Down Expand Up @@ -214,13 +225,13 @@ func (t *Transpiler) input() {
call.Response = &msg
call.done()
case *embeddedsass.OutboundMessage_CanonicalizeRequest_:
resolver := t.getImportResolver(c.CanonicalizeRequest.CompilationId)
var url *embeddedsass.InboundMessage_CanonicalizeResponse_Url
if resolved := t.opts.ImportResolver.CanonicalizeURL(c.CanonicalizeRequest.GetUrl()); resolved != "" {
if resolved := resolver.CanonicalizeURL(c.CanonicalizeRequest.GetUrl()); resolved != "" {
url = &embeddedsass.InboundMessage_CanonicalizeResponse_Url{
Url: resolved,
}
}

response := &embeddedsass.InboundMessage_CanonicalizeResponse_{
CanonicalizeResponse: &embeddedsass.InboundMessage_CanonicalizeResponse{
Id: c.CanonicalizeRequest.GetId(),
Expand All @@ -235,6 +246,7 @@ func (t *Transpiler) input() {
)

case *embeddedsass.OutboundMessage_ImportRequest_:
resolver := t.getImportResolver(c.ImportRequest.CompilationId)
url := c.ImportRequest.GetUrl()
var sourceMapURL string
// Dart Sass expect a browser-accessible URL or an empty string.
Expand All @@ -251,7 +263,7 @@ func (t *Transpiler) input() {
Id: c.ImportRequest.GetId(),
Result: &embeddedsass.InboundMessage_ImportResponse_Success{
Success: &embeddedsass.InboundMessage_ImportResponse_ImportSuccess{
Contents: t.opts.ImportResolver.Load(url),
Contents: resolver.Load(url),
SourceMapUrl: sourceMapURL,
},
},
Expand Down Expand Up @@ -279,28 +291,22 @@ func (t *Transpiler) input() {
}
}

func (t *Transpiler) invoke(message *embeddedsass.InboundMessage) (*embeddedsass.OutboundMessage, error) {
request := &call{
Request: message,
Done: make(chan *call, 1),
func (t *Transpiler) newCall(createInbound func(seq uint32) (*embeddedsass.InboundMessage, error), args Args) (*call, error) {
t.mu.Lock()
// TODO1 handle shutdown.
id := t.seq
req, err := createInbound(id)
if err != nil {
t.mu.Unlock()
return nil, err
}

response := <-t.sendCall(request).Done

if response.Error != nil {
return nil, response.Error
call := &call{
Request: req,
Done: make(chan *call, 1),
importResolver: args.ImportResolver,
}

return response.Response, nil
}

func (t *Transpiler) sendCall(call *call) *call {
t.reqMu.Lock()
defer t.reqMu.Unlock()

t.mu.Lock()
// TODO1 handle shutdown.
id := t.seq
t.pending[id] = call
t.seq++
t.mu.Unlock()
Expand All @@ -309,16 +315,16 @@ func (t *Transpiler) sendCall(call *call) *call {
case *embeddedsass.InboundMessage_CompileRequest_:
c.CompileRequest.Id = id
default:
call.Error = fmt.Errorf("unsupported request message type. %T", call.Request.Message)
return call
return nil, fmt.Errorf("unsupported request message type. %T", call.Request.Message)
}

call.Error = t.sendInboundMessage(call.Request)

return call
return call, t.sendInboundMessage(call.Request)
}

func (t *Transpiler) sendInboundMessage(message *embeddedsass.InboundMessage) error {
t.sendMu.Lock()
defer t.sendMu.Unlock()

out, err := proto.Marshal(message)
if err != nil {
return fmt.Errorf("failed to marshal request: %s", err)
Expand All @@ -340,8 +346,9 @@ func (t *Transpiler) sendInboundMessage(message *embeddedsass.InboundMessage) er
}

type call struct {
Request *embeddedsass.InboundMessage
Response *embeddedsass.OutboundMessage
Request *embeddedsass.InboundMessage
Response *embeddedsass.OutboundMessage
importResolver ImportResolver

Error error
Done chan *call
Expand Down
54 changes: 51 additions & 3 deletions transpiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (t testImportResolver) CanonicalizeURL(url string) string {
return ""
}

return "file:/myproject/scss/" + url + "_myfile.scss"
return "file:/my" + t.name + "/scss/" + url + "_myfile.scss"
}

func (t testImportResolver) Load(url string) string {
Expand Down Expand Up @@ -79,8 +79,7 @@ body
OutputStyle: OutputStyleCompressed,
SourceSyntax: SourceSyntaxSASS,
}, Result{CSS: "body{font:100% Helvetica,sans-serif;color:#333}"}},
{"Import resolver", Options{ImportResolver: colorsResolver}, Args{Source: "@import \"colors\";\ndiv { p { color: $white; } }"}, Result{CSS: "div p {\n color: #ffff;\n}"}},
{"Import resolver with source map", Options{ImportResolver: colorsResolver}, Args{Source: "@import \"colors\";\ndiv { p { color: $white; } }", EnableSourceMap: true}, Result{CSS: "div p {\n color: #ffff;\n}", SourceMap: "{\"version\":3,\"sourceRoot\":\"\",\"sources\":[\"data:;charset=utf-8,@import%20%22colors%22;%0Adiv%20%7B%20p%20%7B%20color:%20$white;%20%7D%20%7D\",\"file:///myproject/scss/colors_myfile.scss\"],\"names\":[],\"mappings\":\"AACM;EAAI,OCDC\"}"}},
{"Import resolver with source map", Options{}, Args{Source: "@import \"colors\";\ndiv { p { color: $white; } }", EnableSourceMap: true, ImportResolver: colorsResolver}, Result{CSS: "div p {\n color: #ffff;\n}", SourceMap: "{\"version\":3,\"sourceRoot\":\"\",\"sources\":[\"data:;charset=utf-8,@import%20%22colors%22;%0Adiv%20%7B%20p%20%7B%20color:%20$white;%20%7D%20%7D\",\"file:///mycolors/scss/colors_myfile.scss\"],\"names\":[],\"mappings\":\"AACM;EAAI,OCDC\"}"}},

// Error cases
{"Invalid syntax", Options{}, Args{Source: "div { color: $white; }"}, false},
Expand Down Expand Up @@ -179,6 +178,55 @@ div { color: $primary-color; }`, num)
wg.Wait()
}

func TestTranspilerParallelImportResolver(t *testing.T) {
c := qt.New(t)

createImportResolver := func(width int) ImportResolver {

return testImportResolver{
name: "widths",
content: fmt.Sprintf(`$width: %d`, width),
}

}

transpiler, clean := newTestTranspiler(c, Options{})
defer clean()

var wg sync.WaitGroup

for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()

for j := 0; j < 10; j++ {

for k := 0; k < 20; k++ {
args := Args{
OutputStyle: OutputStyleCompressed,
ImportResolver: createImportResolver(j + i),
Source: `
@import "widths";
div { p { width: $width; } }`,
}

result, err := transpiler.Execute(args)
c.Check(err, qt.IsNil)
c.Check(result.CSS, qt.Equals, fmt.Sprintf("div p{width:%d}", j+i))
if c.Failed() {
return
}
}
}
}(i)
}

wg.Wait()

}

func BenchmarkTranspiler(b *testing.B) {
type tester struct {
src string
Expand Down

0 comments on commit c4d0ff2

Please sign in to comment.