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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
package libsass
import (
"errors"
"io"
"sync"
"time"
"github.com/wellington/go-libsass/libs"
)
var (
ErrImportNotFound = errors.New("Import unreachable or not found")
)
// Import contains Rel and Abs path and a string of the contents
// representing an import.
type Import struct {
Body io.ReadCloser
bytes []byte
mod time.Time
Prev string
Path string
}
// ModTime returns modification time
func (i Import) ModTime() time.Time {
return i.mod
}
// Imports is a map with key of "path/to/file"
type Imports struct {
wg sync.WaitGroup
closing chan struct{}
sync.RWMutex
m map[string]Import
resolver libs.ImportResolver
idx int
}
func NewImports() *Imports {
return &Imports{
closing: make(chan struct{}),
}
}
func NewImportsWithResolver(resolver libs.ImportResolver) *Imports {
return &Imports{
closing: make(chan struct{}),
resolver: resolver,
}
}
func (i *Imports) Close() {
close(i.closing)
i.wg.Wait()
}
// Init sets up a new Imports map
func (p *Imports) Init() {
p.m = make(map[string]Import)
}
// Add registers an import in the context.Imports
func (p *Imports) Add(prev string, path string, bs []byte) error {
p.Lock()
defer p.Unlock()
// TODO: align these with libsass name "stdin"
if len(prev) == 0 || prev == "string" {
prev = "stdin"
}
im := Import{
bytes: bs,
mod: time.Now(),
Prev: prev,
Path: path,
}
p.m[prev+":"+path] = im
return nil
}
// Del removes the import from the context.Imports
func (p *Imports) Del(path string) {
p.Lock()
defer p.Unlock()
delete(p.m, path)
}
// Get retrieves import bytes by path
func (p *Imports) Get(prev, path string) ([]byte, error) {
p.RLock()
defer p.RUnlock()
for _, imp := range p.m {
if imp.Prev == prev && imp.Path == path {
return imp.bytes, nil
}
}
return nil, ErrImportNotFound
}
// Update attempts to create a fresh Body from the given path
// Files last modified stamps are compared against import timestamp
func (p *Imports) Update(name string) {
p.Lock()
defer p.Unlock()
}
// Len counts the number of entries in context.Imports
func (p *Imports) Len() int {
return len(p.m)
}
// Bind accepts a SassOptions and adds the registered
// importers in the context.
func (p *Imports) Bind(opts libs.SassOptions) {
entries := make([]libs.ImportEntry, p.Len())
i := 0
p.RLock()
for _, ent := range p.m {
bs := ent.bytes
entries[i] = libs.ImportEntry{
Parent: ent.Prev,
Path: ent.Path,
Source: string(bs),
}
i++
}
p.RUnlock()
resolver := func(url string, prev string) (newURL string, body string, resolved bool) {
if p.resolver != nil {
newURL, body, resolved = p.resolver(url, prev)
if resolved {
return
}
}
entry, err := libs.GetEntry(entries, prev, url)
if err == nil {
return url, entry, true
}
return "", "", false
}
// set entries somewhere so GC doesn't collect it
p.idx = libs.BindImporter(opts, resolver)
}
|