178 lines
4.6 KiB
Go
178 lines
4.6 KiB
Go
// Copyright © 2020 Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>.
|
|
//
|
|
// Use of this source code is governed by an MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package libsass a SCSS transpiler to CSS using LibSASS.
|
|
package libsass
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/bep/golibsass/internal/libsass"
|
|
)
|
|
|
|
type libsassTranspiler struct {
|
|
options Options
|
|
}
|
|
|
|
// New creates a new libsass transpiler configured with the given options.
|
|
func New(options Options) (Transpiler, error) {
|
|
return libsassTranspiler{options: options}, nil
|
|
}
|
|
|
|
// Execute transpiles the SCSS or SASS from src into dst.
|
|
func (t libsassTranspiler) Execute(src string) (Result, error) {
|
|
var result Result
|
|
|
|
if t.options.SassSyntax {
|
|
// LibSass does not support this directly, so have to handle the main SASS content
|
|
// special.
|
|
src = libsass.SassToScss(src)
|
|
}
|
|
|
|
dataCtx := libsass.SassMakeDataContext(src)
|
|
|
|
opts := libsass.SassDataContextGetOptions(dataCtx)
|
|
{
|
|
// Set options
|
|
|
|
if t.options.ImportResolver != nil {
|
|
idx := libsass.AddImportResolver(opts, t.options.ImportResolver)
|
|
defer libsass.DeleteImportResolver(idx)
|
|
}
|
|
|
|
if t.options.Precision != 0 {
|
|
libsass.SassOptionSetPrecision(opts, t.options.Precision)
|
|
}
|
|
|
|
if t.options.SourceMapOptions.Filename != "" {
|
|
libsass.SassOptionSetSourceMapFile(opts, t.options.SourceMapOptions.Filename)
|
|
}
|
|
|
|
if t.options.SourceMapOptions.Root != "" {
|
|
libsass.SassOptionSetSourceMapRoot(opts, t.options.SourceMapOptions.Root)
|
|
}
|
|
|
|
if t.options.SourceMapOptions.OutputPath != "" {
|
|
libsass.SassOptionSetOutputPath(opts, t.options.SourceMapOptions.OutputPath)
|
|
}
|
|
if t.options.SourceMapOptions.InputPath != "" {
|
|
libsass.SassOptionSetInputPath(opts, t.options.SourceMapOptions.InputPath)
|
|
}
|
|
|
|
libsass.SassOptionSetSourceMapContents(opts, t.options.SourceMapOptions.Contents)
|
|
libsass.SassOptionSetOmitSourceMapURL(opts, t.options.SourceMapOptions.OmitURL)
|
|
libsass.SassOptionSetSourceMapEmbed(opts, t.options.SourceMapOptions.EnableEmbedded)
|
|
libsass.SassOptionSetIncludePath(opts, strings.Join(t.options.IncludePaths, string(os.PathListSeparator)))
|
|
libsass.SassOptionSetOutputStyle(opts, int(t.options.OutputStyle))
|
|
libsass.SassOptionSetSourceComments(opts, false)
|
|
libsass.SassDataContextSetOptions(dataCtx, opts)
|
|
}
|
|
|
|
ctx := libsass.SassDataContextGetContext(dataCtx)
|
|
compiler := libsass.SassMakeDataCompiler(dataCtx)
|
|
defer libsass.SassDeleteCompiler(compiler)
|
|
|
|
libsass.SassCompilerParse(compiler)
|
|
libsass.SassCompilerExecute(compiler)
|
|
|
|
if status := libsass.SassContextGetErrorStatus(ctx); status != 0 {
|
|
return result, jsonToError(libsass.SassContextGetErrorJSON(ctx))
|
|
}
|
|
|
|
result.CSS = libsass.SassContextGetOutputString(ctx)
|
|
result.SourceMapFilename = libsass.SassOptionGetSourceMapFile(opts)
|
|
result.SourceMapContent = libsass.SassContextGetSourceMapString(ctx)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
type Result struct {
|
|
CSS string
|
|
|
|
// If source maps are configured.
|
|
SourceMapFilename string
|
|
SourceMapContent string
|
|
}
|
|
|
|
type Transpiler interface {
|
|
Execute(src string) (Result, error)
|
|
}
|
|
|
|
type (
|
|
OutputStyle int
|
|
)
|
|
|
|
const (
|
|
NestedStyle OutputStyle = iota
|
|
ExpandedStyle
|
|
CompactStyle
|
|
CompressedStyle
|
|
)
|
|
|
|
// ParseOutputStyle will convert s into OutputStyle.
|
|
// Case insensitive, returns NestedStyle for unknown values.
|
|
func ParseOutputStyle(s string) OutputStyle {
|
|
switch strings.ToLower(s) {
|
|
case "nested":
|
|
return NestedStyle
|
|
case "expanded":
|
|
return ExpandedStyle
|
|
case "compact":
|
|
return CompactStyle
|
|
case "compressed":
|
|
return CompressedStyle
|
|
}
|
|
return NestedStyle
|
|
}
|
|
|
|
type Options struct {
|
|
// Default is nested.
|
|
OutputStyle OutputStyle
|
|
|
|
// Precision of floating point math.
|
|
Precision int
|
|
|
|
// File paths to use to resolve imports.
|
|
IncludePaths []string
|
|
|
|
// ImportResolver can be used to supply a custom import resolver, both to redirect
|
|
// to another URL or to return the body.
|
|
ImportResolver func(url string, prev string) (newURL string, body string, resolved bool)
|
|
|
|
// Used to indicate "old style" SASS for the input stream.
|
|
SassSyntax bool
|
|
|
|
SourceMapOptions SourceMapOptions
|
|
}
|
|
|
|
type SourceMapOptions struct {
|
|
Filename string
|
|
Root string
|
|
InputPath string
|
|
OutputPath string
|
|
Contents bool
|
|
OmitURL bool
|
|
EnableEmbedded bool
|
|
}
|
|
|
|
func jsonToError(jsonstr string) (e Error) {
|
|
_ = json.Unmarshal([]byte(jsonstr), &e)
|
|
return
|
|
}
|
|
|
|
type Error struct {
|
|
Status int `json:"status"`
|
|
Column int `json:"column"`
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (e Error) Error() string {
|
|
return fmt.Sprintf("file %q, line %d, col %d: %s ", e.File, e.Line, e.Column, e.Message)
|
|
}
|