Last active
August 5, 2016 18:55
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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