FEATURE: added error messages for bad theme CSS / JS

This commit is contained in:
Sam 2017-04-19 16:46:28 -04:00
parent 3835e16cf7
commit 7eabb90b71
11 changed files with 110 additions and 11 deletions

View File

@ -75,6 +75,11 @@ export default Ember.Controller.extend({
return fieldName && fieldName.indexOf("scss") > -1 ? "scss" : "html";
},
@computed("currentTargetName", "fieldName", "saving")
error(target, fieldName) {
return this.get('model').getError(target, fieldName);
},
@computed("fieldName", "currentTargetName")
editorId(fieldName, currentTarget) {
return fieldName + "|" + currentTarget;
@ -139,7 +144,8 @@ export default Ember.Controller.extend({
actions: {
save() {
this.get('model').saveChanges("theme_fields");
this.set('saving', true);
this.get('model').saveChanges("theme_fields").finally(()=>{this.set('saving', false);});
},
toggleMaximize: function() {

View File

@ -29,6 +29,15 @@ const Theme = RestModel.extend({
}
},
getError(target, name) {
let themeFields = this.get("themeFields");
let key = target + " " + name;
console.log(themeFields);
console.log(key);
let field = themeFields[key];
return field ? field.error : "";
},
getField(target, name) {
let themeFields = this.get("themeFields");
let key = target + " " + name;

View File

@ -2,6 +2,9 @@
<div class='wrapper'>
<h2>{{i18n 'admin.customize.theme.edit_css_html'}} {{#link-to 'adminCustomizeThemes.show' model.id replace=true}}{{model.name}}{{/link-to}}</h2>
{{#if error}}
<pre class='field-error'>{{error}}</pre>
{{/if}}
<div class='edit-main-nav'>
<ul class='nav nav-pills target'>

View File

@ -7,6 +7,13 @@
}
}
.field-error {
margin-top: 10px;
margin-bottom: 10px;
background-color: dark-light-diff($quaternary, $secondary, 70%, -70%);
padding: 5px;
}
.edit-main-nav {
.nav-pills:after, .nav-pills:before {
display: inline;

View File

@ -16,6 +16,8 @@ PLUGIN_API_JS
end
def process_html(html)
errors = nil
doc = Nokogiri::HTML.fragment(html)
doc.css('script[type="text/x-handlebars"]').each do |node|
name = node["name"] || node["data-template-name"] || "broken"
@ -49,11 +51,13 @@ COMPILED
node.replace("<script>#{code}</script>")
rescue MiniRacer::RuntimeError => ex
node.replace("<script type='text/discourse-js-error'>#{ex.message}</script>")
errors ||= []
errors << ex.message
end
end
end
doc.to_s
[doc.to_s, errors&.join("\n")]
end
@ -61,16 +65,38 @@ COMPILED
%w(body_tag head_tag header footer after_header)
end
def self.scss_fields
%w(scss embedded_scss)
end
def ensure_baked!
if ThemeField.html_fields.include?(self.name)
if !self.value_baked || compiler_version != COMPILER_VERSION
self.value_baked = process_html(self.value)
self.value_baked, self.error = process_html(self.value)
self.compiler_version = COMPILER_VERSION
if self.value_baked_changed? || compiler_version.changed?
self.update_columns(value_baked: value_baked, compiler_version: compiler_version)
if self.value_baked_changed? || compiler_version.changed? || self.error_changed?
self.update_columns(value_baked: value_baked,
compiler_version: compiler_version,
error: error)
end
end
end
end
def ensure_scss_compiles!
if ThemeField.scss_fields.include?(self.name)
begin
Stylesheet::Compiler.compile("@import \"theme_variables\"; @import \"theme_field\";",
"theme.scss",
theme_field: self.value.dup)
self.error = nil unless error.nil?
rescue SassC::SyntaxError => e
self.error = e.message
if error_changed?
update_columns(error: self.error)
end
end
end
@ -88,12 +114,13 @@ COMPILED
after_commit do
ensure_baked!
ensure_scss_compiles!
Stylesheet::Manager.clear_theme_cache! if self.name.include?("scss")
# TODO message for mobile vs desktop
MessageBus.publish "/header-change/#{theme.key}", self.value if self.name == "header"
MessageBus.publish "/footer-change/#{theme.key}", self.value if self.name == "footer"
MessageBus.publish "/header-change/#{theme.key}", self.value if theme && self.name == "header"
MessageBus.publish "/footer-change/#{theme.key}", self.value if theme && self.name == "footer"
end
end

View File

@ -1,5 +1,5 @@
class ThemeFieldSerializer < ApplicationSerializer
attributes :name, :target, :value
attributes :name, :target, :value, :error
def target
case object.target
@ -8,6 +8,10 @@ class ThemeFieldSerializer < ApplicationSerializer
when 2 then "mobile"
end
end
def include_error?
object.error.present?
end
end
class ChildThemeSerializer < ApplicationSerializer

View File

@ -0,0 +1,5 @@
class AddErrorToThemeFields < ActiveRecord::Migration
def change
add_column :theme_fields, :error, :string
end
end

View File

@ -31,9 +31,8 @@ module Stylesheet
end
def self.compile(stylesheet, filename, options={})
source_map_file = options[:source_map_file] || "#{filename.sub(".scss","")}.css.map";
engine = SassC::Engine.new(stylesheet,
importer: Importer,
filename: filename,
@ -41,6 +40,7 @@ module Stylesheet
source_map_file: source_map_file,
source_map_contents: true,
theme_id: options[:theme_id],
theme_field: options[:theme_field],
load_paths: [ASSET_ROOT])

View File

@ -13,6 +13,10 @@ module Stylesheet
@special_imports[name] = blk
end
register_import "theme_field" do
Import.new("theme_field.scss", source: @theme_field)
end
register_import "plugins" do
import_files(DiscoursePluginRegistry.stylesheets)
end
@ -67,6 +71,7 @@ module Stylesheet
def initialize(options)
@theme_id = options[:theme_id]
@theme_field = options[:theme_field]
end
def import_files(files)

View File

@ -125,7 +125,12 @@ class Stylesheet::Manager
)
rescue SassC::SyntaxError => e
Rails.logger.error "Failed to compile #{@target} stylesheet: #{e.message}"
[Stylesheet::Compiler.error_as_css(e, "#{@target} stylesheet"), nil]
if %w{embedded_theme mobile_theme desktop_theme}.include?(@target.to_s)
# no special errors for theme, handled in theme editor
["", nil]
else
[Stylesheet::Compiler.error_as_css(e, "#{@target} stylesheet"), nil]
end
end
FileUtils.mkdir_p(cache_fullpath)

View File

@ -0,0 +1,28 @@
# encoding: utf-8
require 'rails_helper'
describe ThemeField do
it "correctly generates errors for transpiled js" do
html = <<HTML
<script type="text/discourse-plugin" version="0.8">
badJavaScript(;
</script>
HTML
field = ThemeField.create!(theme_id: 1, target: 0, name: "header", value: html)
expect(field.error).not_to eq(nil)
field.value = ""
field.save!
expect(field.error).to eq(nil)
end
it "correctly generates errors for transpiled css" do
css = "body {"
field = ThemeField.create!(theme_id: 1, target: 0, name: "scss", value: css)
field.reload
expect(field.error).not_to eq(nil)
field.value = ""
field.save!
expect(field.error).to eq(nil)
end
end