Skip to content

Instantly share code, notes, and snippets.

@allex
Last active June 3, 2024 02:36
Show Gist options
  • Save allex/ab874c487178eade932d220cb079470a to your computer and use it in GitHub Desktop.
Save allex/ab874c487178eade932d220cb079470a to your computer and use it in GitHub Desktop.
#!/bin/bash
# vim: set ft=sh fdm=marker ts=2 sw=2 sts=2 tw=85 et:
# extract and convert scss to css3 variables, output type support json,text,css
# by @allex_wang
#
# Install:
# curl -sfkL -o ./convert-scss-var.sh https://gist.githubusercontent.com/allex/ab874c487178eade932d220cb079470a/raw/convert-scss-var.sh && chmod +x ./convert-scss-var.sh
#
# GistID: ab874c487178eade932d220cb079470a
# GistURL: https://gist.github.com/ab874c487178eade932d220cb079470a
set -euEo pipefail
PROG=$(basename "$0")
SH_DIR="$(cd -P -- "$(dirname -- "$(readlink -f "$0")")" && pwd -P)"
WORKDIR="$(umask 0077; mktemp -d)"
DEBUG=
# input (scss variable file)
scss_file=
# output file
output_file=
# output filetype: json, css, txt
output_format=
### Helpers {{{
BOLD="$(tput bold 2>/dev/null || echo '')"
GREY="$(tput setaf 0 2>/dev/null || echo '')"
UNDERLINE="$(tput smul 2>/dev/null || echo '')"
RED="$(tput setaf 1 2>/dev/null || echo '')"
GREEN="$(tput setaf 2 2>/dev/null || echo '')"
YELLOW="$(tput setaf 3 2>/dev/null || echo '')"
BLUE="$(tput setaf 4 2>/dev/null || echo '')"
MAGENTA="$(tput setaf 5 2>/dev/null || echo '')"
CYAN="$(tput setaf 6 2>/dev/null || echo '')"
NO_COLOR="$(tput sgr0 2>/dev/null || echo '')"
info() { printf "${BOLD}${GREY}>${NO_COLOR} %s\n" "$*"; }
warn() { printf "${YELLOW}! %s${NO_COLOR}\n" "$*" >&2; }
error() { printf "${RED}x %s${NO_COLOR}\n" "$*" >&2; }
complete() { printf "${GREEN}✓${NO_COLOR} %s\n" "$*"; }
die() { [ "${1-}" ] && error "fatal: ${1}"; exit "${2-1}"; }
assert() { [ -n "$1" ] || die "${2?assert message required}"; }
has() { type "${1:?command required}" >/dev/null 2>&1; }
### }}}
show_usage() {
cat <<-HELP
Usage: ${PROG} [options] <scss_file> <output_file>
Options:
--output-format <json|txt|css>
Specifies the desired output format. The output format defines how the extracted
and converted SCSS variables are represented in the output file. Supported output
formats are: json, txt, and css.
Note: if this option is not specified, the output format defaults to "raw" CSS.
--debug
Enables debug mode. When debug mode is enabled, additional debugging information will
be displayed, which can be useful for troubleshooting.
-h, --help
Shows this help information.
Arguments:
<scss_file>
The path for the input SCSS file. This contains the SCSS variables that will be
extracted and converted by the script
<output_file>
The path to the output file. This file will contain the extracted and converted
SCSS variables, formatted according to either the --output-format parameter or
the "raw" CSS default.
Example:
${PROG} --output-format=json input.scss output.json
This command extracts and converts SCSS variables from "input.scss", formats
the output as JSON, and writes the results to "output.json"
Note:
The script needs either sass or node-sass to be installed on the system.
HELP
exit 1
}
### ARGS {{{
handle_exit() {
local ec="$?"
# declare -F on_exit, not avaiable in alipine/sh
if type on_exit >/dev/null 2>&1; then on_exit; fi
local tmpDir=${TMP:-${TMPDIR:-/tmp/}}
if [ "${WORKDIR##${tmpDir%%/}/*}" != "${WORKDIR}" ]; then
rm -rf "${WORKDIR--}"
else
warn "For security, The \$WORKDIR:${WORKDIR} not allot at system tmpdir, will not be cleanup"
fi
trap - INT TERM EXIT
exit $ec
}
trap handle_exit 0 1 2 3 6 15
args=()
while [ $# -gt 0 ]; do
OPT=$1
[ "$OPT" = "--" ] && break
[ "$OPT" = "${OPT#-*}" ] && {
args+=("$OPT")
shift
continue
}
if [ "${OPT%=*}" = "$OPT" ]; then
OPTARG="${2-}"
if [ "${OPTARG##-*}" = "${OPTARG}" ]; then
shift
else
OPTARG=
fi
else
OPT=$(echo "$1" | awk -F= '{print $1}')
OPTARG=$(echo "$1" | awk -F= '{print $2}')
fi
case $OPT in
--debug)
DEBUG=1
;;
--output-format)
output_format=$OPTARG
;;
-h | --help)
show_usage
;;
-*)
error "Unknown parameter \"$OPT\""
args+=("$OPT")
[ "$OPTARG" ] && args+=("\"$OPTARG\"")
;;
esac
[ $# -gt 0 ] && shift
done
[ "$DEBUG" == "1" ] && set -x;
[ ${#args[@]} -gt 0 ] && eval set -- "${args[@]}"
### }}}
scss_file="${1:-}"
output_file="${2:-}"
if [ -z "$scss_file" ]; then
show_usage
fi
awkfile="$WORKDIR/a.awk"
cat <<'EOF' > "$awkfile"
BEGIN {
FS=":"
var_prefix="--v-"
}
/^\$/{
arr[substr($1,4)]=$1
}
END {
print "@import '" scss_file "';";
print "$variables: (";
l = length(arr);
asorti(arr, sorted_keys)
for (i in sorted_keys) {
k = sorted_keys[i]
l--;
printf " %s%s: %s", var_prefix, k, arr[k];
print(l != 0 ? "," : "");
}
print ");";
print "\
:root {\n\
@each $key, $value in $variables {\n\
@if type-of($value) != map {\n\
#{$key}: #{$value};\n\
}\n\
}\n\
}";
}
EOF
eval_sass () {
if has node-sass; then
node-sass --output-style expanded
elif has sass; then
sass --stdin --no-source-map --style=expanded
else
die "both sass and node-sass are not found"
fi
}
# output the css variables with different format (txt, json, raw)
output () {
out=${output_file:-/dev/stdout}
case "${output_format}" in
json)
awk '
/^ *--/ { match($0, /^ *([^:]+): *(.*);/, a); arr[a[1]] = a[2]; }
END {
print "{";
l = length(arr)
asorti(arr, sorted_keys)
for (i in sorted_keys) {
k = sorted_keys[i]
printf(" \"%s\": \"%s\"", k, arr[k]);
print(--l != 0 ? "," : "");
}
print "}";
}
' > "$out"
;;
txt)
awk '/^ *--/ { match($0, /^ *([^:]+): *(.*);/, arr); printf("%s: %s;\n", arr[1], arr[2]); }' > "$out"
;;
*)
cat > "$out"
;;
esac
}
awk -v "scss_file=$scss_file" -f "$awkfile" "$scss_file" \
| eval_sass \
| sed -e '/\/\*/,/\*\//d' \
| output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment