How to include SCSS Glob in a Gatsby project? - sass

I am currently working on setting up a boilerplate that uses Gatsby. Everything so far has been very simple and easy to use, but I can't seem to fix one problem, which is getting SCSS glob hooked up with my global SCSS styling.
I currently have localized SCSS styling for each component. However, I also have a styles directory for my global styles(variables, typography...ect). This is also using SCSS and is working great. Now the last thing I want to do is get SCSS glob working so I can do imports like /**/*.scss within my global styles.
Currently, I am using the gatsby-plugin-sass and have included globImporter as an option within my gatsby-config.js file. However, it does not seem to do it for me.
From what I read node-sass-glob-importer should be what I need but no luck so far.
My configuration looks like the following
{
resolve: `gatsby-plugin-sass`,
options: {
importer: globImporter(),
cssLoaderOptions: {
camelCase: false,
},
},
},
I then try to do a global import in my scss like so #import "./**/*.scss"; but I get the following error:
An #import loop has been found:
has anyone set up scss glob on gatsby or see anything wrong with my configurations.
Thanks

If you're still having this issue (or in case anyone else is), here's what worked for me:
options: {
importer: function(url, prev, done) {
// url is the path in import as is, which LibSass encountered.
// prev is the previously resolved path.
// done is an optional callback, either consume it or return value synchronously.
// this.options contains this options hash, this.callback contains the node-style callback
var result = globImporter();
return {file: result.path, contents: result.data};
}
},
It was inspired by the example code on in the node-sass repo.
Make sure to also include var globImporter = require('node-sass-glob-importer') at the top of your file.

Related

Laravel Mix and SASS changing font directory

I'm using Laravel 5.4 and Laravel Mix to output SASS files.
In my font definitions I'm configuring them so that when the CSS is output it will point to files such as public/assets/fnt/font-name/filename.ext but the processor changes the output so that it will instead point to public/fonts/filename.ext. Is there a way to stop it from changing the output paths?
It makes little sense to me that it would do something like this by default.
Edit
I've seen that the defaults they're using in Mix are the culprit:
module.exports.module = {
rules: [
// ...
{
test: /\.(woff2?|ttf|eot|svg|otf)$/,
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]?[hash]',
publicPath: '/'
}
}
]
};
I've tried using null-loader instead of file-loader but instead it causes it to fail because it can't find the files in node_modules which is not where it should be looking in the first place.
Removing the rule in question results in a flood of errors from trying to open and evaluate the font files in question:
error in ./public/assets/fnt/fanfare-jf/fanfare-jf.ttf
Module parse failed: DIRECTORY\public\assets\fnt\fanfare-jf\fanfare-jf.ttf Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
# ./~/css-loader!./~/postcss-loader!./~/resolve-url-loader!./~/sass-loader?sourceMap&precision=8!./resources/assets/sass/app.scss 6:2525-2590
# ./resources/assets/sass/app.scss
# multi ./resources/assets/js/app.js ./resources/assets/sass/app.scss
I can at least add emitFiles: false to options to prevent it from making copies of the file, but the paths are still being altered.
I ended up with the following configuration to at least get it to a working state.
let assetDir = 'assets/build';
mix.config.fileLoaderDirs.fonts = `${assetDir}/${mix.config.fileLoaderDirs.fonts}`;
mix.config.fileLoaderDirs.images = `${assetDir}/${mix.config.fileLoaderDirs.images}`;
mix.sass('resources/sass/app.scss', `public/${assetDir}/css`)
.js('resources/js/app.js', `public/${assetDir}/js`);
Updated:
In newer versions this has been made customizable via mix.options() and can be adjusted as below:
let assetDir = 'assets/build';
mix.options({
fileLoaderDirs: {
images: `${assetDir}/img`,
fonts: `${assetDir}/fonts`
}
});
// adjust build commands accordingly, for example:
mix.js('resources/js/app.js', `public/${assetDir}/js`);
The output you got is the intended behaviour due to your configuration.
You are using this configuration to load the file:
options: {
name: 'fonts/[name].[ext]?[hash]',
publicPath: '/'
}
Which says use the publicPath as public and create a file with the name fonts/[name].[ext]?[hash] and webpack knows about what these symbols '/', '.', '?' in the name do.
It just looks for the fonts directory and if there is no any fonts directory it creates a new one and place the files into that directory.
So, you need to use this configuration for your folder structure:
options: {
name: 'assets/fnt/font-name/[name].[ext]?[hash]',
publicPath: '/'
}
This should work for your configuration.
More on file-loader configuration:
https://github.com/webpack-contrib/file-loader#filename-templates
Edit:
Since Laravel Mix uses Webpack in it's background and Webpack doesn't have any knowledge of the fonts file when there is no any appropriate loader added to the configuration. So, the error:
Module parse failed: DIRECTORY\public\assets\fnt\fanfare-jf\fanfare-jf.ttf Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type.
is occurred.
You need to tell the Webpack to load the fonts to your desired directory and the fonts linked in your SASS file will be linked by the Webpack without any more configurations.

Is it possible to write (via sass commands or compiler directives) code such that SASS will output the final values of all variables to a JSON file?

How can I compile a SASS file and have it output all the final values of every variable in a json file
I asked a similar question before and someone on here thought I was trying to get you guys to do my job. I'm just trying to see if it's possible at all before I go down a path that ultimately is a waste of time. If it is possible, I don't know the magic sauce to get started.
I have CMS themes that are built on bootstrap-sass, and other sass frameworks, and I'd like my CMS to be able to access variables that we use in the SASS files as well. Seems to me that if when I compiled the SAAS file, I got a CSS, MAP, and JSON file, I'd be all set.
I could write this as some kind of mixin, but even then, I'd need to be able to get
A list of all variables
a command to output json to the dist folder.
Any pointers on these items are appreciated.
If you're using Gulp to compile your SASS there is a plugin that accomplishes this.
https://www.npmjs.com/package/gulp-sass-json
After installing it, and given the following Gulpfile.js:
var sassJson = require('gulp-sass-json');
var paths = {
'json_root' : './bundles/theme/scss/variables/*.scss',
'json_dest' : './json'
};
var sassOptions = {
errLogToConsole: true,
outputStyle: 'expanded'
};
gulp.task('sass-json', function () {
return gulp
.src(paths.json_root)
.pipe(sassJson())
.pipe(gulp.dest(paths.json_dest));
});
gulp.task('default', ['sass-json']);
Given the SCSS file variables/colors.scss:
$red: #ed1414;
$blue: #0351e0;
$green: #259208;
Outputs the following json at ./json/colors.json
{
"red": "#ed1414",
"blue": "#0351e0",
"green": "#259208"
}
So in order to have it re-output it every time you compile, you can just create a task that encompasses this task as well as your normal style compile task.

How can I set a sass variable using an environment variable?

I'm using Gulp as my build system.
I need to identify links pointing to external websites with the scss following rule:
// Links to external websites
a[href*='//']:not([href*='example.com']) {
&::after {
content: ' \e895';
font-family: 'Material Icons';
}
}
OR
$baseURL: 'localhost:3000'; // Set this variable based on environment
a[href*='//']:not([href*='#{$baseurl}']) {
...
}
When I'm running a development server the address I'm serving files from is localhost:3000, not example.com. The result is that every single link on the website (on the dev server) has a small icon indicating the link goes to an external website, which is really distracting.
What's the best way to set a scss variable based on an environment setting?
Edit:
This solution works, but it introduces a temporary file, which I'm not wild about. I moved my _variables.scss into the scss root, I process this file and output it into the base subdirectory where it is used to compile the scss. I would then add scss/base/_variables.scss to my .gitignore to avoid committing to version control.
_variables.scss
$baseURL: '/* #echo PATH */';
Gulpfile.js
// Set baseurl as Sass variable -- used to identify external links
gulp.task('sass-vars', function () {
var baseURL = (config.production) ? 'example.com' : 'localhost:3000';
return gulp.src('./scss/_variables.scss')
.pipe($.preprocess({context: {PATH: baseURL}}))
.pipe(gulp.dest('./scss/base'));
});
Yes, it is possible to do that.
To get environment variables there's a package: gulp-env
To remove these links from static files: gulp-preprocess
But it's also important to check these changed files, not to commit them as a development version. Hooks to your VCS is an option.

Laravel elixir - don't generate map files

The question is - how to force Laravel Elixir not to generate map files?
At the moment if I run gulp I will have generated app.css and app.css.map file. I don't know what for is this app.css.map file but I think it's not necessary for me at the moment. Question is - how to force gulp not to generate this file?
At the moment my gulpfile.js looks like this:
var elixir = require('laravel-elixir');
elixir(function(mix) {
mix.sass('app.scss', 'public/css/app.css');
});
This is no longer achievable via elixir.extend() syntax, instead the official documentation now suggests to use this:
elixir.config.sourcemaps = false;
Starting from Elixir 3.0 you can put a JSON object that will override the default configuration in elixir.json:
{
"sourcemaps": false
}
.map files are called source maps. Their purpose is to map the contents of a concatenated, minified file to it's original files to make debugging easier.
You can disable them by changing elixirs config using extend() in your gulpfile
elixir.extend('sourcemaps', false);
Note that source maps are disabled by default when running in production.

Combining Multiple SASS files into one SASS file

Does anyone know if there is a way to combine multiple SASS/SCSS files into one SASS/SCSS file. I do mean "into one SASS/SCSS" and not into a CSS file.
For example, I have 3 scss files:
app.scss
base.scss
layout.scss
The app.scss file contains 2 imports to base.scss and layout.scss.
I would like to beable to generate 1 SCSS file that basically concatenates the files and does not process the sass.
It's fairly difficult to search for as everything that gets return is to do with combining into CSS.
Why would I want to do this? Basically, I'd like to easily reference a set of SCSS files from within a codepen (other online code editor).
Thanks
I analyze all files by the mask, find all imports inside and concatenate into one file. So I don't need one entry point
npm install -D bundle-scss
"script": {
"postbuild": "npm run themes",
"themes": "bundle-scss -m \"./src/**/*.theme.scss\" -d \"./dist/themes.scss\""
}
scss-bundle solves this problem
https://github.com/reactway/scss-bundle
Caution: Does not support newer module based imports. Issue #90
You could modify this for javascript. Kept it in typescript as I am currently solving this issue on my own (angular 6 library), and ran into this question.
According to the docs, angular material uses this implementation.
import * as path from 'path';
import { Bundler } from 'scss-bundle';
import * as fs from 'fs';
(async () => {
// Absolute project directory path.
// Assuming all of your scss files are in `./projects/my-library/src/styles`
const projectDirectory = path.resolve(__dirname, './projects/my-library/src/styles');
const bundler = new Bundler(undefined, projectDirectory);
// Relative file path to project directory path.
// The name of your file here would be `app.scss`
// Kept this here if anyone runs into this answer and wants to do this with the new angular 6 library.
const { found, bundledContent } = await bundler.Bundle('./_all-theme.scss');
if (found && bundledContent) {
// where you want to save the file, and what you would like it to be called.
const location = path.resolve(__dirname, '_theming.scss');
fs.writeFileSync(location, bundledContent);
}
})();

Resources