Skip to content

Instantly share code, notes, and snippets.

@jrharshath
Last active August 5, 2016 18:55
Show Gist options
  • Save jrharshath/73a84079398422de715df4433d763abc to your computer and use it in GitHub Desktop.
Save jrharshath/73a84079398422de715df4433d763abc to your computer and use it in GitHub Desktop.
To be used with node-sass to import, inline and minify css from local sources, as well as from npm modules.
/**
* USAGE: Place this script in a directory named `build-tools` in the root of your project.
* Add node-sass to your project's dev-dependencies.
* Add an npm script "bundle-scss" with the following definition:
*
* node-sass main.scss output.css --importer ./build-tools/node-sass-import-resolver
*
* Supported imports:
*
* @import ('theme.css'); // will resolve css files locally
* @import ('theme'); // will resolve local css/scss files without an extension or leading underscore.
* @import ('bootstrap-sass'); // will resolve css from the main/style/css fields of package.json, or from index.css
* // however if you have a local "bootstrap-sass.scss" file for some reason, that one will win out.
* @import ('bootstrap/assets/styles/bootstrap.css'); // will resolve "assets/styles/bootstrap.css" from the "bootstrap" npm package
*
* Note how local files are prioritized over npm package files. So in case of name/path collisions, local files win.
*/
'use strict';
var path = require('path');
var fs = require('fs');
/* globals console */
module.exports = function (url, file, done)
{
var PROCESS_PATH = process.cwd();
console.log('INFO: resolving "' + url + '" in file "' + file + '"');
var resolved = null;
if (url.startsWith('/')) // try as absolute path first
{
resolved = resolveResource(url);
}
else // try as relative path first
{
resolved = resolveResource(path.join(path.dirname(file), url));
if (!resolved)
{
if (url.indexOf('/') === -1) // if no path elements in url, try as npm package
{
var packageFilePath = path.join('.', 'node_modules', url, 'package.json');
try
{
var pkg = JSON.parse(fs.readFileSync(packageFilePath, 'utf8'));
var stylePath = pkg.style ? pkg.style : (pkg.sass ? pkg.sass : (pkg.css ? pkg.css : 'index'));
resolved = resolveResource(path.join('.', 'node_modules', url, stylePath));
}
catch (err)
{
console.log('ERROR: could not find style entrypoint for package "' + url + '"!', err);
}
}
else if (!url.startsWith('.')) // if path is not explicitly relative, resolve in npm packages
{
resolved = resolveResource(path.join('.', 'node_modules', url));
}
}
}
if (resolved)
{
console.log('SUCCESS: resolving to: "' + resolved + '"');
if (url.endsWith('.css'))
{
resolved = path.relative(PROCESS_PATH, resolved);
console.log('INFO: CSS file path converted to "' + resolved + '" relative to "' + PROCESS_PATH + '"');
}
return {
file: resolved
};
}
else
{
console.log('ERROR: could not resolve import url "' + url + '" in file "' + file + '"');
return null;
}
};
function addUnderscoreToFileName(filepath)
{
var parts = filepath.split('/');
parts[parts.length - 1] = '_' + parts[parts.length - 1];
return parts.join('/');
}
function resolveResource(filepath)
{
if (!filepath)
{
return null;
}
// todo deal with '.css' files differently
if (filepath.endsWith('.css') || filepath.endsWith('.scss'))
{
if (fileExistsAndIsReadable(filepath))
{
return filepath;
}
else
{
var underscored = addUnderscoreToFileName(filepath);
if (fileExistsAndIsReadable(underscored))
{
return underscored;
}
return null;
}
}
var existingPath = null;
['.css', '.scss'].some(function (ext)
{
var newfilepath = filepath + ext;
if (fileExistsAndIsReadable(newfilepath))
{
existingPath = newfilepath;
return true;
}
else
{
var underscored = addUnderscoreToFileName(newfilepath);
if (fileExistsAndIsReadable(underscored))
{
existingPath = underscored;
return true;
}
}
return false;
});
if (!existingPath)
{
return null;
}
return existingPath;
}
function fileExistsAndIsReadable(filepath)
{
try
{
var stat = fs.lstatSync(filepath);
if (!stat.isFile())
{
return false;
}
}
catch (err)
{
return false;
}
try
{
fs.accessSync(filepath, fs.R_OK);
}
catch (err)
{
return false;
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment