217 lines
7.8 KiB
JavaScript
217 lines
7.8 KiB
JavaScript
// https://github.com/dawaltconley/sass-cast/blob/main/index.js
|
|
|
|
import * as sass from "sass-embedded";
|
|
import { isQuoted, unquoteString, parseString, getAttr } from "./utils";
|
|
import { List, OrderedMap } from "immutable";
|
|
|
|
/**
|
|
* Converts any Javascript object to an equivalent Sass value.
|
|
*
|
|
* This method is recursive and will convert the values of any array or object,
|
|
* as well as the array or object itself.
|
|
*
|
|
* @example
|
|
* const { toSass } = require('sass-cast');
|
|
*
|
|
* const string = toSass('a simple string');
|
|
* // quoted SassString => '"a simple string"'
|
|
*
|
|
* const map = toSass({
|
|
* key: 'value',
|
|
* nested: {
|
|
* 'complex//:key': [ null, 4 ],
|
|
* }
|
|
* });
|
|
* // SassMap => '("key": "value", "nested": ("complex//:key": (null, 4)))'
|
|
*
|
|
* @param {*} value - the value to be converted
|
|
* @param {Object} options
|
|
* @param {boolean} [options.parseUnquotedStrings=false] - whether to parse unquoted strings for colors or numbers with units
|
|
* @param {boolean|*[]} [options.resolveFunctions=false] - if true, resolve functions and attempt to cast their return values. if an array, pass as arguments when resolving
|
|
* @param {boolean} [options.quotes=true] - controls whether returned SassStrings are quoted. input strings that contain quotes will always return a quoted SassString even if this flag is false.
|
|
* @return {Value} - a {@link https://sass-lang.com/documentation/js-api/classes/Value Sass value}
|
|
*/
|
|
|
|
export const toSass = (value, options = {}) => {
|
|
let {
|
|
parseUnquotedStrings = false,
|
|
resolveFunctions = false,
|
|
quotes = true,
|
|
} = options;
|
|
if (value instanceof sass.Value) {
|
|
return value;
|
|
} else if (value === null || value === undefined) {
|
|
return sass.sassNull;
|
|
} else if (typeof value === "boolean") {
|
|
return value ? sass.sassTrue : sass.sassFalse;
|
|
} else if (typeof value === "number") {
|
|
return new sass.SassNumber(value);
|
|
} else if (typeof value === "string") {
|
|
const valueIsQuoted = isQuoted(value);
|
|
if (parseUnquotedStrings && !valueIsQuoted) {
|
|
let parsed = parseString(value);
|
|
if (parsed instanceof sass.SassColor || parsed instanceof sass.SassNumber)
|
|
return parsed;
|
|
}
|
|
return new sass.SassString(value, {
|
|
quotes: valueIsQuoted || quotes,
|
|
});
|
|
} else if (typeof value === "object") {
|
|
if (Array.isArray(value)) {
|
|
let sassList = value.map((value) => toSass(value, options));
|
|
return new sass.SassList(sassList);
|
|
} else {
|
|
let sassMap = OrderedMap(value).mapEntries(([key, value]) => [
|
|
new sass.SassString(key, { quotes: true }),
|
|
toSass(value, options),
|
|
]);
|
|
return new sass.SassMap(sassMap);
|
|
}
|
|
} else if (resolveFunctions && typeof value === "function") {
|
|
const args = Array.isArray(resolveFunctions) ? resolveFunctions : [];
|
|
return toSass(value(...args), options);
|
|
}
|
|
return sass.sassNull;
|
|
};
|
|
|
|
const colorProperties = [
|
|
"red",
|
|
"green",
|
|
"blue",
|
|
"hue",
|
|
"lightness",
|
|
"saturation",
|
|
"whiteness",
|
|
"blackness",
|
|
"alpha",
|
|
];
|
|
|
|
/**
|
|
* Converts Sass values to their Javascript equivalents.
|
|
*
|
|
* @example
|
|
* const { fromSass, toSass } = require('sass-cast');
|
|
*
|
|
* const sassString = toSass('a sass string object');
|
|
* const string = fromSass(sassString);
|
|
* // 'a sass string object'
|
|
*
|
|
* @param {Value} object - a {@link https://sass-lang.com/documentation/js-api/classes/Value Sass value}
|
|
* @param {Object} options
|
|
* @param {boolean} [options.preserveUnits=false] - By default, only the values of numbers are returned, not their units. If true, `fromSass` will return numbers as a two-item Array, i.e. [ value, unit ]
|
|
* @param {boolean} [options.rgbColors=false] - By default, colors are returned as strings. If true, `fromSass` will return colors as an object with `r`, `g`, `b`, and `a`, properties.
|
|
* @param {boolean} [options.preserveQuotes=false] - By default, quoted Sass strings return their inner text as a string. If true, `fromSass` will preserve the quotes in the returned string value.
|
|
* @return {*} - a Javascript value corresponding to the Sass input
|
|
*/
|
|
|
|
export const fromSass = (object, options = {}) => {
|
|
let {
|
|
preserveUnits = false,
|
|
rgbColors = false,
|
|
preserveQuotes = false,
|
|
} = options;
|
|
if (object instanceof sass.SassBoolean) {
|
|
return object.value;
|
|
} else if (object instanceof sass.SassNumber) {
|
|
if (preserveUnits) {
|
|
return [
|
|
object.value,
|
|
object.numeratorUnits.toArray(),
|
|
object.denominatorUnits.toArray(),
|
|
];
|
|
} else if (object.numeratorUnits.size || object.denominatorUnits.size) {
|
|
return object.toString();
|
|
}
|
|
return object.value;
|
|
} else if (object instanceof sass.SassColor) {
|
|
if (rgbColors) {
|
|
return colorProperties.reduce((colorObj, p) => {
|
|
colorObj[p] = object[p];
|
|
return colorObj;
|
|
}, {});
|
|
}
|
|
return object.toString();
|
|
} else if (object instanceof sass.SassString) {
|
|
return preserveQuotes ? object.text : unquoteString(object.text);
|
|
} else if (object instanceof sass.SassList || List.isList(object)) {
|
|
let list = [];
|
|
for (
|
|
let i = 0, value = object.get(i);
|
|
value !== undefined;
|
|
i++, value = object.get(i)
|
|
) {
|
|
list.push(fromSass(value, options));
|
|
}
|
|
return list;
|
|
} else if (object instanceof sass.SassMap) {
|
|
return object.contents
|
|
.mapEntries(([k, v]) => [k.text, fromSass(v, options)])
|
|
.toObject();
|
|
} else {
|
|
return object.realNull;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* An object defining Sass utility functions.
|
|
*
|
|
* @example <caption>Pass to sass using the JS API</caption>
|
|
* const { sassFunctions } = require('sass-cast');
|
|
* const sass = require('sass');
|
|
*
|
|
* sass.compile('main.scss', { functions: sassFunctions });
|
|
*/
|
|
export const sassFunctions = {
|
|
/**
|
|
* Sass function for importing data from Javascript or JSON files.
|
|
* Calls the CommonJS `require` function under the hood.
|
|
*
|
|
* #### Examples
|
|
*
|
|
* ```scss
|
|
* // import config info from tailwindcss
|
|
* $tw: require('./tailwind.config.js', $parseUnquotedStrings: true);
|
|
* $tw-colors: map.get($tw, theme, extend, colors);
|
|
* ```
|
|
* @name require
|
|
* @memberof sassFunctions
|
|
* @param {SassString} $module - Path to the file or module. Relative paths are relative to the Node process running Sass compilation.
|
|
* @param {SassList} [$properties=()] - List of properties, if you only want to parse part of the module data.
|
|
* @param {SassBoolean} [$parseUnquotedStrings=false] - Passed as an option to {@link #tosass toSass}.
|
|
* @param {SassBoolean} [$resolveFunctions=false] - Passed as an option to {@link #tosass toSass}.
|
|
* @param {SassBoolean} [$quotes=true] - Passed as an option to {@link #tosass toSass}.
|
|
* @return {Value} - a {@link https://sass-lang.com/documentation/js-api/classes/Value Sass value}
|
|
*/
|
|
"require($module, $properties: (), $parseUnquotedStrings: false, $resolveFunctions: false, $quotes: true)":
|
|
(args) => {
|
|
const moduleName = args[0].assertString("module").text;
|
|
const properties = args[1].realNull && fromSass(args[1].asList);
|
|
const parseUnquotedStrings = args[2].isTruthy;
|
|
const resolveFunctions = args[3].isTruthy;
|
|
const quotes = args[4].isTruthy;
|
|
const options = {
|
|
parseUnquotedStrings,
|
|
resolveFunctions,
|
|
quotes,
|
|
};
|
|
const convert = (data) =>
|
|
toSass(properties ? getAttr(data, properties) : data, options);
|
|
|
|
let mod,
|
|
paths = [moduleName, `${process.cwd()}/${moduleName}`];
|
|
for (let path of paths) {
|
|
try {
|
|
mod = require(path);
|
|
break;
|
|
} catch (e) {
|
|
if (e.code !== "MODULE_NOT_FOUND") throw e;
|
|
continue;
|
|
}
|
|
}
|
|
if (!mod) throw new Error(`Couldn't find module: ${moduleName}`);
|
|
|
|
if (resolveFunctions && typeof mod === "function") mod = mod();
|
|
if (mod instanceof Promise) return mod.then(convert);
|
|
return convert(mod);
|
|
},
|
|
};
|