I'm writing a site using nanoc and ruby, and I want to use SCSS. But I'm having a bit of an issue. No matter what I try, I cannot get my SCSS file to convert and output as compiled CSS. It either throws an error or comes through as exactly the same file type. My Ruby rules file and directory structure is below, please help!
#!/usr/bin/env ruby
compile '/**/*.html' do
layout '/default.*'
end
# This is an example rule that matches Markdown (.md) files, and filters them
# using the :kramdown filter. It is commented out by default, because kramdown
# is not bundled with Nanoc or Ruby.
#
#compile '/**/*.md' do
# filter :kramdown
# layout '/default.*'
#end
route '/**/*.{html,md}' do
if item.identifier =~ '/index.*'
'/index.html'
else
item.identifier.without_ext + '/index.html'
end
end
compile '/assets/SCSS/' do
filter :scss => :css
write #item.identifier.without_ext + '.css'
end
compile '/assets/images/*' do
write item.identifier.to_s
end
compile '/**/*' do
write item.identifier.to_s
end
layout '/**/*', :erb
Here is my directory structure:
root
|
|\_content
| |
| \_assets
| |
| \_test.scss
\_public
|
\_assets
|
\_test.scss <-------- This should be compiled CSS
I found a utility called Compass that gives you filter options for SCSS.
Use the SASS filter as outlined in How can I get the nanoc SASS filter to use SCSS syntax?
filter :sass, syntax: :scss
Related
So I have a file not found problem.
I have an engine that works in development mode in the engines test/dummy app, the engine allows the editing of sass variables and stores them in a theme table, the variables are used by a sass partial such as _banner.scss containing variables used in the main stylesheet such as $banner_color which is then imported into the main stylesheet which in turn is precompiled using an initializer in the engine.rb file and inclusion in the app/config/engine_name_manifest.js.
The files are all available in development with the local dummy app but not in the eventual host app due to the assets being compiled.
I have a rake task that takes the data, updates the relevant partial e.g. _banner.scss with the data from the theme table but of course the partials are not not available in a host app as the engine has already compiled them.
I'm looking for a solution that will allow me to edit the raw, uncompiled stylesheets then recompile them. Obviously my Capistrano deploy script will need to reapply the stylesheet changes every deployment but that is just a rake task call.
What approach should I take? Should I find a way to copy the css files to the host app in an engine initializer? Should I use a different approach entirely, I have started looking at propshaft but that is a massive step to replace sass rails and I'm not sure how that would help
The engine
require "deface"
require 'ccs_cms_admin_dashboard'
require 'ccs_cms_custom_page'
require 'ccs_cms_core'
require 'css_menu'
#require 'tinymce-rails'
require 'delayed_job_active_record'
require 'daemons'
require 'sprockets/railtie'
require 'sassc-rails'
module CcsCms
module PublicTheme
class Engine < ::Rails::Engine
isolate_namespace CcsCms::PublicTheme
paths["app/views"] << "app/views/ccs_cms/public_theme"
initializer "ccs_cms.assets.precompile" do |app|
app.config.assets.precompile += %w( public_theme_manifest.js )
end
initializer :append_migrations do |app|
unless app.root.to_s.match?(root.to_s)
config.paths['db/migrate'].expanded.each do |p|
app.config.paths['db/migrate'] << p
end
end
end
initializer :active_job_setup do |app|
app.config.active_job.queue_adapter = :delayed_job
end
config.to_prepare do
Dir.glob(Engine.root.join("app", "decorators", "**", "*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
config.generators do |g|
g.test_framework :rspec,
fixtures: false,
request: false,
view_specs: false,
helper_specs: false,
controller_specs: false,
routing_specs: false
g.fixture_replacement :factory_bot
g.factory_bot dir: 'spec/factories'
end
end
end
end
The Css class that writes the css
class Css
def get_stylesheet_path
Rails.root.join("app/assets/stylesheets/ccs_cms/public_theme")
end
def write_css(theme)
update_css_files_for(theme.banner, '_public_banner.scss', BANNER_ARRAY, BANNER_FIELD_MAP)
update_css_files_for(theme.banner.font, '_public_banner_font.scss', BANNER_FONT_ARRAY, BANNER_FONT_FIELD_MAP)
end
private
def update_css_files_for(model_record_to_use, css_file, array_to_use, field_map)
amended_css = amend_css_for(model_record_to_use, css_file, array_to_use, field_map)
create_css_files_for(css_file, amended_css)
end
def amend_css_for(model_record_to_use, file_name, array_to_use, field_map)
original_css_array = IO.readlines("#{get_stylesheet_path}/#{file_name}")
new_array = []
original_css_array.each do |line|
new_line = line
array_to_use.each do |ma|
if line.start_with?(ma)
field_name = field_map[ma.to_sym]
new_line = ma + ": #{model_record_to_use[field_name.to_sym]};"
#puts("#### original line: #{line}, ma: #{ma}, Field name: #{field_name}, value: #{theme[field_name]}")
break
end
end
new_array << new_line
end
new_array
end
# ---- File and I/O Handling ---- #
def create_css_files_for(file_name, css_array)
File.open("#{get_stylesheet_path}/#{file_name}", "w") do |file|
file.puts css_array
end
end
end
Thanks for clarifying. If I understood correctly here my take on it.
partials are not not available in a host app as the engine has already compiled them
Partials are still there, precompilation just outputs *.{css/js} files into public/assets/ that are declared in app/assets/config/manifest.js.
To get to engines files, instead of Rails.root use:
CcsCms::PublicTheme::Engine.root
In Css class, for example:
def get_stylesheet_path
CcsCms::PublicTheme::Engine.root.join("app/assets/stylesheets/ccs_cms/public_theme")
end
To support changing theme engines. Theme root can be set in an engine initializer to something like Rails.configuration.theme_root and used in the main app.
Because your theme is also configurable, I think it's better to read theme's original sass files but not modify them, copy them into a tmp folder and update with values from theme table, then output a theme.css in the main app with sass.
https://sass-lang.com/documentation/cli/dart-sass
# Compiles all Sass
$ sass tmp/theme/application.scss:app/stylesheets/theme.css
Then let Rails take over the precompilation process.
Another option is to have one sass configuration file and only update this file. That way there is no dependency on the file structure of any particular theme.
import 'configuration' // sass variables with values from theme table
import 'banner' // uses sass variables only
...
Also just use css variables, if that's an option, and avoid all of the above complexity; no precompilation, no redeploys when theme table changes.
Update for css variables.
Just so we're on the same page. I meant these css variables:
https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties. If Internet Explorer is not a priority for you, this is the best solution.
The setup is something like this:
<!-- app/views/layouts/application.erb -->
<!-- NOTE: with turbo this loads only once; full page refresh is needed when #theme changes -->
<head>
<style>
:root { --text-color: <%= #theme.text_color %>; }
</style>
<%= stylesheet_link_tag 'application' %>
</head>
/* app/assets/stylesheets/application.css */
p { color: var(--text-color); }
Possible fix to avoid amending css files. Use erb interpolation inside the sass files. No need to amend every time a theme configuration is changed. In development compiles on the fly. In production it has to be precompiled again when theme configuration is changed; no amending.
// _banner.scss.erb
p { color: <%= Theme.text_color %>; }
You could even use amend_css_for function to insert literal erb code and save some time. For example
new_line = ma + ": <%= Theme.#{model_name}.#{field_name} %>;"
Finally, if you don't want to touch engine files and because these files are not part of the main/host app (as in literally two separate folders in the filesystem). You have to make a copy when amending; read from CcsCms::PublicTheme::Engine.root write to Rails.root.
def get_stylesheet_path
CcsCms::PublicTheme::Engine.root.join("app/assets/stylesheets/ccs_cms/public_theme")
end
# but save to main app
def create_css_files_for(file_name, css_array)
File.open("#{Rails.root.join("app/assets/stylesheets/ccs_cms/public_theme")}/#{file_name}", "w") do |file|
file.puts css_array
end
end
I'm using Google Closure Compiler to minify my JS scripts: https://developers.google.com/closure/compiler/docs/gettingstarted_app?hl=en
The command I'm using is:
java -jar /home/user/compiler/compiler.jar --js $File::Find::name --create_source_map $File::Find::name.map --source_map_format=V3 --compilation_level=WHITESPACE_ONLY --js_output_file $minified --charset=Windows-1251 --output_wrapper '%output%\n//# sourceMappingURL=output.js.map'
Thats fine, apart from one thing - the .js.map file has the FULL path for the file, not the relative one:
"version":3,
"file":"/home/user/public_html/new_design/common37.min.js",
"lineCount":375,
....
I assume I can change this in the invocation of the compiler.jar script? Otherwise, I guess I will have to add some more code into my script (not something I want to do, if its possible "out of the box")
EDIT: I've done a little bit of a dirty hack in my Perl script:
# now open the map file one, and edit it to remove the full path.. needs to be relative
my $contents = File::Slurp::read_file("/home/user/public_html/$tmp.map");
$contents =~ s|/home/user/public_html||g;
File::Slurp::write_file("/home/user/public_html/$tmp.map",$contents);
That gets rid of the path info correctly. I've prefer if there were an option to use relative urls in the .map file (compared to the full path it currently puts in)
Thanks!
Specify sourcemap location transformations by using the --source_map_location_mapping flag. The flag expects a value formatted as:
--source_map_location_mapping=/filesystem/src/root|relative/source/root
I am trying to set up a SCSS transpiler in PyCharm for Django project.
Basically, what I need is to convert /static/scss/main.scss to /static/css/main.css
Here are the configurations of SCSS File Watcher:
Program: /home/maverick/.rvm/gems/ruby-2.2.3/bin/scss
Arguments: --no-cache --update /home/maverick/Documents/DjangoProjects/timberg/static/css/$FileNameWithoutExtension$.css
Working directory: /home/maverick/Documents/DjangoProjects/timberg/static/scss
Output paths to refresh: /home/maverick/Documents/DjangoProjects/timberg/static/css/$FileNameWithoutExtension$.css
What is happening is that main.css is being generated where it should. But it contains only errors, not the expected css, like this:
/*
Error: Inconsistent indentation: 2 spaces were used for indentation, but the rest of the document was indented using 8 spaces.
on line 39 of /home/maverick/Documents/DjangoProjects/timberg/static/css/main.css
and etc.
What is wrong here? How can I fix it?
The problem was in the Arguments part.
It should be:
$FileName$:/home/maverick/Documents/DjangoProjects/timberg/static/css/$FileNameWithoutExtension$.css
not just:
/home/maverick/Documents/DjangoProjects/timberg/static/css/$FileNameWithoutExtension$.css
My mistake was leaving out $FileName$: at the beginning.
How can I compile a LESS file to output a source map file (.css.map) in addition to a CSS file? Is there a way to do it on both command line (NodeJS's lessc) and on any GUI-based programs?
Update: New shortest answer
The docs have been updated! As new features hit LESS, sometimes the docs lag behind a bit, so if you're looking for bleeding-edge features, you're still probably better off running lessc (see longer answer) and checking what pops out of the help text.
http://lesscss.org/usage/
Short answer
You're looking for any number of the following options from the command line:
--source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)
--source-map-rootpath=X adds this path onto the sourcemap filename and less file paths
--source-map-basepath=X Sets sourcemap base path, defaults to current working directory.
--source-map-less-inline puts the less files into the map instead of referencing them
--source-map-map-inline puts the map (and any less files) into the output css file
--source-map-url=URL the complete url and filename put in the less file
As I write this I'm not aware of any GUI options that generate maps (source maps were only added to LESS in the last few months) -- sorry to not have any better news. I'm sure they'll add support in as they update over the next year.
Longer answer
If you run lessc from the command line without any parameters it will give you all the options. (In my experience, this is more up to date than their documentation, so it'll at least get you pointed in the right direction.) with all the most recent map stuff included.
The easiest combo to use for dev is --source-map-less-inline --source-map-map-inline as that will give you your source maps embedded in your output css.
If you'd like to add a separate map file, you can use --source-map which, from my.less will output my.css and my.css.map
For reference: when I run my copy (v 1.6.1 at the moment) I get
usage: lessc [option option=parameter ...] <source> [destination]
If source is set to `-' (dash or hyphen-minus), input is read from stdin.
options:
-h, --help Print help (this message) and exit.
--include-path=PATHS Set include paths. Separated by `:'. Use `;' on Windows.
-M, --depends Output a makefile import dependency list to stdout
--no-color Disable colorized output.
--no-ie-compat Disable IE compatibility checks.
--no-js Disable JavaScript in less files
-l, --lint Syntax check only (lint).
-s, --silent Suppress output of error messages.
--strict-imports Force evaluation of imports.
--insecure Allow imports from insecure https hosts.
-v, --version Print version number and exit.
-x, --compress Compress output by removing some whitespaces.
--clean-css Compress output using clean-css
--clean-option=opt:val Pass an option to clean css, using CLI arguments from
https://github.com/GoalSmashers/clean-css e.g.
--clean-option=--selectors-merge-mode:ie8
and to switch on advanced use --clean-option=--advanced
--source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)
--source-map-rootpath=X adds this path onto the sourcemap filename and less file paths
--source-map-basepath=X Sets sourcemap base path, defaults to current working directory.
--source-map-less-inline puts the less files into the map instead of referencing them
--source-map-map-inline puts the map (and any less files) into the output css file
--source-map-url=URL the complete url and filename put in the less file
-rp, --rootpath=URL Set rootpath for url rewriting in relative imports and urls.
Works with or without the relative-urls option.
-ru, --relative-urls re-write relative urls to the base less file.
-sm=on|off Turn on or off strict math, where in strict mode, math
--strict-math=on|off requires brackets. This option may default to on and then
be removed in the future.
-su=on|off Allow mixed units, e.g. 1px+1em or 1px*1px which have units
--strict-units=on|off that cannot be represented.
--global-var='VAR=VALUE' Defines a variable that can be referenced by the file.
--modify-var='VAR=VALUE' Modifies a variable already declared in the file.
-------------------------- Deprecated ----------------
-O0, -O1, -O2 Set the parser's optimization level. The lower
the number, the less nodes it will create in the
tree. This could matter for debugging, or if you
want to access the individual nodes in the tree.
--line-numbers=TYPE Outputs filename and line numbers.
TYPE can be either 'comments', which will output
the debug info within comments, 'mediaquery'
that will output the information within a fake
media query which is compatible with the SASS
format, and 'all' which will do both.
--verbose Be verbose.
If the command line doesn't suite you, Grunt is great at this type of thing. You can configure the grunt-contrib-less plugin to generate inline maps with a config like this:
less: {
options: {
sourceMap:true,
outputSourceFiles: true
},
lessFiles: {
expand: true,
flatten:false,
src: ['**/*.less'],
dest: ['dist/'],
ext: '.css',
}
},
https://github.com/gruntjs/grunt-contrib-less
Example to Create Map and CSS file from Less File
Install latest Node JS and go to command prompt and run npm install less, Now less installed successfully
Go to Command Prompt and move to less file folder that we are going to create
For e.g., I am going to change HelloWorld [Less File]
In Command prompt go to C:\Project\CSS or give the correct path in the below command.
Run following Command in Command Prompt
lessc HelloWorld.less HelloWorld.css --source-map=HelloWorld.css.map –verbose
Now CSS and Map file is generated in the respective folder.
For more reference check the link : royalarun.blogspot.com
Is it possible to automatically add a timestamp on the compiled CSS file using SASS?
e.g.
/* CSS Compiled on: {date+time} */
...compiled css goes here...
I've checked the SASS and Compass docs but no mention of such a feature.
I don't know of any built-in SASS or Compass feature for this, but it's not too hard to add a custom Ruby function to do it. (See the section entitled "Adding Custom Functions" in the SASS reference here.)
Your file with the Ruby function would look like this (let's call it "timestamp.rb"):
module Sass::Script::Functions
def timestamp()
return Sass::Script::String.new(Time.now.to_s)
end
end
And you'd reference the new timestamp function in your SASS file like this:
/*!
* CSS Compiled on: #{timestamp()}
*/
You just need to make sure your "timestamp.rb" file is loaded when you compile the SASS, either by requiring it from a Compass config file, or by using the --require parameter with the SASS command line. When all is said and done, you should get output like the following:
/*
* CSS Compiled on: 2012-10-23 08:53:03 -0400
*/
If you are using the command line version of SCSS you can do the following:
Install sass-timestamp
gem install sass-timestamp
Use it within your code like (see documentation for more information)
/* Compiled on #{timestamp()} */
Require it on the command line
scss -r sass-timestamp ...
Output will be
/* Compiled on 2015-02-02 13:01:40 +0800 */
Note: Use #{unix_timestamp()} for a unix timestamp
I don't know if everyone needs it (cause the question is a long time answered ago),
but a simple solution is to write the timestamp/date
to a single sass/scss file as SASS variable,
import them to the location where the timestamp should be
and then let a comment in sass write them out.
Nothing to install, compile or anything else - just using scripts and sass:
1.) Write the timestamp to a separate sass file: (Here a dos-script, but you can also use any other script/language to generate the simple file):
echo $BuildTimeStamp : "%date% %time%"> _timestamp.scss
2.) Import the generated file with the timestamp:
#import '_timestamp.scss';
3.) Write the header out as comment:
/*! Automatic build at: #{$BuildTimeStamp} */
Write the timestamp before you call the original sass command
and it will also work without the need to install, build or do anything else.