Skip to content

Commit

Permalink
Nofify user if dart-sass is installed
Browse files Browse the repository at this point in the history
  • Loading branch information
KinyaElGrande committed Oct 27, 2023
1 parent 72de58e commit f6c244d
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 130 deletions.
51 changes: 35 additions & 16 deletions client/web/admin/src/components/Settings/UI/CUIBrandingEditor.vue
Expand Up @@ -10,6 +10,14 @@
</h3>
</template>

<div
v-if="!sassInstalled"
class="bg-warning rounded p-2 mb-2"
>
{{ $t('sassNotInstalled') }}
[<a :href="installSassDocs">{{ $t('installSassDocs') }}</a>]
</div>

<b-row>
<b-col
v-for="key in Object.keys(brandingVariables)"
Expand Down Expand Up @@ -88,19 +96,19 @@ export default {
data () {
return {
brandingVariables: {
white: '#FFFFFF',
black: '#162425',
primary: '#0B344E',
secondary: '#758D9B',
success: '#43AA8B',
warning: '#E2A046',
danger: '#E54122',
light: '#E4E9EF',
extraLight: '#F3F5F7',
dark: '#162425',
tertiary: '#5E727E',
gray200: '#F9FAFB',
bodyBg: '#F9FAFB',
'white': '#FFFFFF',
'black': '#162425',
'primary': '#0B344E',
'secondary': '#758D9B',
'success': '#43AA8B',
'warning': '#E2A046',
'danger': '#E54122',
'light': '#E4E9EF',
'extra-light': '#F3F5F7',
'dark': '#162425',
'tertiary': '#5E727E',
'gray-200': '#F9FAFB',
'body-bg': '#F9FAFB',
},
colorTranslations: {
modalTitle: this.$t('colorPicker'),
Expand All @@ -109,20 +117,31 @@ export default {
}
},
computed: {
sassInstalled () {
return this.settings['ui.studio.sass-installed']
},
installSassDocs () {
// eslint-disable-next-line no-undef
const [year, month] = VERSION.split('.')
return `https://docs.cortezaproject.org/corteza-docs/${year}.${month}`
},
},
watch: {
settings: {
immediate: true,
handler (settings) {
if (settings['ui.branding-sass']) {
this.brandingVariables = JSON.parse(settings['ui.branding-sass'])
if (settings['ui.studio.branding-sass']) {
this.brandingVariables = JSON.parse(settings['ui.studio.branding-sass'])
}
},
},
},
methods: {
onSubmit () {
this.$emit('submit', { 'ui.branding-sass': JSON.stringify(this.brandingVariables) })
this.$emit('submit', { 'ui.studio.branding-sass': JSON.stringify(this.brandingVariables) })
},
},
}
Expand Down
4 changes: 2 additions & 2 deletions client/web/admin/src/components/Settings/UI/CUICustomCSS.vue
Expand Up @@ -108,14 +108,14 @@ export default {
settings: {
immediate: true,
handler (settings) {
this.customCSS = settings['ui.custom-css'] || ''
this.customCSS = settings['ui.studio.custom-css'] || ''
},
},
},
methods: {
onSubmit () {
this.$emit('submit', { 'ui.custom-css': this.customCSS })
this.$emit('submit', { 'ui.studio.custom-css': this.customCSS })
},
openEditorModal () {
Expand Down
8 changes: 5 additions & 3 deletions locale/en/corteza-webapp-admin/ui.settings.yaml
Expand Up @@ -25,6 +25,8 @@ editor:
corteza-studio:
title: Branding
colorPicker: Choose a color
sassNotInstalled: No dart-sass binaries were detected. For guidance on how to set it up, refer to
installSassDocs: docs here
brandVariables:
white: White
black: Black
Expand All @@ -34,11 +36,11 @@ editor:
warning: Warning
danger: Danger
light: Light
extraLight: Extra light
extra-light: Extra light
dark: Dark
tertiary: Tertiary
gray200: Gray 200
bodyBg: Body background
gray-200: Gray 200
body-bg: Body background

custom-css:
title: Custom CSS
Expand Down
172 changes: 70 additions & 102 deletions server/pkg/sass/processor.go
Expand Up @@ -20,103 +20,38 @@ var (
sassVariablesPattern = regexp.MustCompile(`(\$[a-zA-Z_-]+):\s*([^;]+);`)
)

// GenerateCSS takes care of creating CSS for webapps by reading SASS content from embedded assets,
// combining it with brandingSass and customCSS, and then transpiling it using the dart-sass compiler.
//
// If dart sass isn't installed on the host machine, it will default to css content from the minified-custom.css which is
// generated from [Boostrap, bootstrap-vue and custom variables sass content].
// If dart isn't installed on the host machine, customCustom css will continue to function, but without sass support.
//
// In case of an error, it will return default css and log out the error
func GenerateCSS(brandingSass, customCSS, sassDirPath string) (string, error) {
var transpiler = dartSass()

//check if cache has already compiled css value
if !StylesheetCache.empty() {
return StylesheetCache.Get("css"), nil
}

// if dart sass is not installed, or when the sass transpiler creation and startup process fails.
// return contents from default css
if transpiler == nil {
return defaultCSS(customCSS), nil
}

// transpile sass to css
err := sassTranspiler(brandingSass, customCSS, sassDirPath, transpiler)

if err != nil {
return defaultCSS(customCSS), err
}

return StylesheetCache.Get("css"), nil
}

func dartSass() *godartsass.Transpiler {
transpiler, err := godartsass.Start(godartsass.Options{
DartSassEmbeddedFilename: "sass",
})
// DefaultCSS contains css contents from minified-custom.css and custom-css editor content
func DefaultCSS(customCSS string) string {
var strBuilder strings.Builder

//read css contents from minified-custom.css
minifiedCSSFile, err := assets.Files(logger.Default(), "").Open(path.Join("css", "minified-custom.css"))
if err != nil {
logger.Default().Warn("dart sass is not installed in your system", zap.Error(err))
return nil
logger.Default().Error("failed to open minified-custom.css file", zap.Error(err))
}

return transpiler
}

// readSassFiles reads SASS contents from provided embedded directory and subdirectories then converts them to a string
func readSassFiles(dirPath string) (string, error) {
var stringsBuilder strings.Builder

filenames, subDirs, err := assets.DirEntries(dirPath)
reader := bufio.NewReader(minifiedCSSFile)
_, err = io.Copy(&strBuilder, reader)
if err != nil {
logger.Default().Error(fmt.Sprintf("failed to read assets/src/%s entries", dirPath), zap.Error(err))
return "", err
}

if len(filenames) > 0 {
err := readSassContents(dirPath, filenames, &stringsBuilder)
if err != nil {
return "", err
}
logger.Default().Error("failed to read css content from minified-custom.css", zap.Error(err))
}

if len(subDirs) > 0 {
for _, subDir := range subDirs {
sassContents, err := readSassFiles(path.Join(dirPath, subDir))
if err != nil {
return "", err
}
stringsBuilder.WriteString(sassContents)
}
if customCSS != "" {
strBuilder.WriteString(customCSS)
}

return stringsBuilder.String(), nil
}

func readSassContents(dirPath string, filenames []string, stringsBuilder *strings.Builder) error {
assetFiles := assets.Files(logger.Default(), "")

for _, fileName := range filenames {
open, err := assetFiles.Open(path.Join(dirPath, fileName))
if err != nil {
logger.Default().Error(fmt.Sprintf("failed to open asset %s file", fileName), zap.Error(err))
return err
}
processedCSS := strBuilder.String()

reader := bufio.NewReader(open)
_, err = io.Copy(stringsBuilder, reader)
if err != nil {
logger.Default().Error(fmt.Sprintf("failed to copy sass content from %s", fileName), zap.Error(err))
return err
}
}
StylesheetCache.Set(
map[string]string{
"css": processedCSS,
},
)

return nil
return processedCSS
}

func sassTranspiler(brandingSass, customCSS, sassDirPath string, transpiler *godartsass.Transpiler) error {
func Transpiler(brandingSass, customCSS, sassDirPath string, transpiler *godartsass.Transpiler) error {
var stringsBuilder strings.Builder

if brandingSass != "" {
Expand Down Expand Up @@ -178,35 +113,68 @@ func sassTranspiler(brandingSass, customCSS, sassDirPath string, transpiler *god
return nil
}

// defaultCSS contains css contents from minified-custom.css and custom-css editor content
func defaultCSS(customCSS string) string {
var strBuilder strings.Builder
func DartSass() *godartsass.Transpiler {
transpiler, err := godartsass.Start(godartsass.Options{
DartSassEmbeddedFilename: "sass",
})

//read css contents from minified-custom.css
minifiedCSSFile, err := assets.Files(logger.Default(), "").Open(path.Join("css", "minified-custom.css"))
if err != nil {
logger.Default().Error("failed to open minified-custom.css file", zap.Error(err))
logger.Default().Warn("dart sass is not installed in your system", zap.Error(err))
return nil
}

reader := bufio.NewReader(minifiedCSSFile)
_, err = io.Copy(&strBuilder, reader)
return transpiler
}

// readSassFiles reads SASS contents from provided embedded directory and subdirectories then converts them to a string
func readSassFiles(dirPath string) (string, error) {
var stringsBuilder strings.Builder

filenames, subDirs, err := assets.DirEntries(dirPath)
if err != nil {
logger.Default().Error("failed to read css content from minified-custom.css", zap.Error(err))
logger.Default().Error(fmt.Sprintf("failed to read assets/src/%s entries", dirPath), zap.Error(err))
return "", err
}

if customCSS != "" {
strBuilder.WriteString(customCSS)
if len(filenames) > 0 {
err := readSassContents(dirPath, filenames, &stringsBuilder)
if err != nil {
return "", err
}
}

processedCSS := strBuilder.String()
if len(subDirs) > 0 {
for _, subDir := range subDirs {
sassContents, err := readSassFiles(path.Join(dirPath, subDir))
if err != nil {
return "", err
}
stringsBuilder.WriteString(sassContents)
}
}

StylesheetCache.Set(
map[string]string{
"css": processedCSS,
},
)
return stringsBuilder.String(), nil
}

return processedCSS
func readSassContents(dirPath string, filenames []string, stringsBuilder *strings.Builder) error {
assetFiles := assets.Files(logger.Default(), "")

for _, fileName := range filenames {
open, err := assetFiles.Open(path.Join(dirPath, fileName))
if err != nil {
logger.Default().Error(fmt.Sprintf("failed to open asset %s file", fileName), zap.Error(err))
return err
}

reader := bufio.NewReader(open)
_, err = io.Copy(stringsBuilder, reader)
if err != nil {
logger.Default().Error(fmt.Sprintf("failed to copy sass content from %s", fileName), zap.Error(err))
return err
}
}

return nil
}

// jsonToSass converts JSON string to SASS variable assignment string
Expand Down
2 changes: 1 addition & 1 deletion server/pkg/sass/sass_store.go
Expand Up @@ -30,7 +30,7 @@ func (c *stylesheetCache) Get(key string) string {
return value
}

func (c *stylesheetCache) empty() bool {
func (c *stylesheetCache) Empty() bool {
c.mu.RLock()
defer c.mu.RUnlock()

Expand Down
6 changes: 4 additions & 2 deletions server/pkg/webapp/serve.go
Expand Up @@ -3,6 +3,7 @@ package webapp
import (
"bytes"
"fmt"
"github.com/cortezaproject/corteza/server/pkg/auth"
"io"
"net/http"
"os"
Expand All @@ -12,7 +13,6 @@ import (

"github.com/cortezaproject/corteza/server/pkg/logger"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/pkg/sass"
"github.com/cortezaproject/corteza/server/system/service"
"github.com/cortezaproject/corteza/server/system/types"
"github.com/go-chi/chi/v5"
Expand Down Expand Up @@ -152,7 +152,9 @@ func serveConfig(r chi.Router, config webappConfig) {
r.Get(options.CleanBase(config.appUrl, "custom.css"), func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/css")

stylesheet, err := sass.GenerateCSS(config.settings.UI.BrandingSASS, config.settings.UI.CustomCSS, config.scssDirPath)
ctx := auth.SetIdentityToContext(r.Context(), auth.ServiceUser())

stylesheet, err := service.GenerateCSS(ctx, config.settings.UI.Studio.BrandingSASS, config.settings.UI.Studio.CustomCSS, config.scssDirPath)
if err != nil {
logger.Default().Error("failed to generate CSS", zap.Error(err))
}
Expand Down
2 changes: 1 addition & 1 deletion server/system/service/settings.go
Expand Up @@ -246,7 +246,7 @@ func (svc *settings) BulkSet(ctx context.Context, vv types.SettingValueSet) (err

for _, v := range vv {
// if branding-sass or custom-css is updated, empty stylesheet cache
if v.Name == "ui.branding-sass" || v.Name == "ui.custom-css" {
if v.Name == "ui.studio.branding-sass" || v.Name == "ui.studio.custom-css" {
sass.StylesheetCache.Set(map[string]string{})
}

Expand Down

0 comments on commit f6c244d

Please sign in to comment.