Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] Make sass compliation agnostic to node_modules location #31583

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
52d38ef
refactor: build: reimplement `compile_sass` as a shell script
kdmccormick Jan 17, 2023
9128cf0
squash: add sh command to paver; move sh into assets folder
kdmccormick Jan 20, 2023
6a93016
squash: more arguments for compile-sass.sh
kdmccormick Jan 20, 2023
4584535
squash: more flags, generalizing to watchers (wip)
kdmccormick Jan 22, 2023
728d7e0
squash: use find+xargs+sassc; write most of the logic; passes shellcheck
kdmccormick Jan 24, 2023
3b8f11a
squash: remove unused 'common' code paths
kdmccormick Jan 25, 2023
8b59c60
squash: fix: no quotes around include-path
kdmccormick Jan 25, 2023
175157e
squash: rtl support (WIP)
kdmccormick Jan 25, 2023
cf0182c
squash: remove --source-comments so we can use libsass==0.10
kdmccormick Jan 25, 2023
ccaf14a
squash: make script work on multiple themes + systems
kdmccormick Jan 25, 2023
9ac58f9
squash: fix compile_dir logic
kdmccormick Jan 26, 2023
6356865
squash: improve output
kdmccormick Jan 26, 2023
546e963
squash: more output improvements && rtlcss enable
kdmccormick Jan 26, 2023
6e005c9
squash: extract dir compilation into script
kdmccormick Jan 26, 2023
ee2e87f
squash: compile-sass -> compile-scss
kdmccormick Jan 26, 2023
21ffb65
squash: work on output & watching
kdmccormick Jan 26, 2023
e02a4e5
squash: wip: updating pavelib/assets.py
kdmccormick Jan 26, 2023
760f9b3
squash: make pavelib/assets wrapper more concise
kdmccormick Jan 27, 2023
b2ca4e4
squash: remove watch from compile-scss
kdmccormick Jan 27, 2023
bf8281a
squash: clean up scss scripts. I think they works?
kdmccormick Jan 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 19 additions & 141 deletions pavelib/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,6 @@ def compile_sass(options):
'/edx/app/edxapp/edx-platform/themes' and '/edx/app/edxapp/edx-platform/common/test/'.

"""
debug = options.get('debug')
force = options.get('force')
systems = get_parsed_option(options, 'system', ALL_SYSTEMS)
themes = get_parsed_option(options, 'themes', [])
theme_dirs = get_parsed_option(options, 'theme_dirs', [])
Expand All @@ -449,155 +447,35 @@ def compile_sass(options):
if themes and theme_dirs:
themes = get_theme_paths(themes=themes, theme_dirs=theme_dirs)

# Compile sass for OpenEdx theme after comprehensive themes
if None not in themes:
themes.append(None)

timing_info = []
dry_run = tasks.environment.dry_run
compilation_results = {'success': [], 'failure': []}

print("\t\tStarted compiling Sass:")

# compile common sass files
is_successful = _compile_sass('common', None, debug, force, timing_info)
if is_successful:
print("Finished compiling 'common' sass.")
compilation_results['success' if is_successful else 'failure'].append('"common" sass files.')

for system in systems:
for theme in themes:
print("Started compiling '{system}' Sass for '{theme}'.".format(system=system, theme=theme or 'system'))

# Compile sass files
is_successful = _compile_sass(
system=system,
theme=path(theme) if theme else None,
debug=debug,
force=force,
timing_info=timing_info
)

if is_successful:
print("Finished compiling '{system}' Sass for '{theme}'.".format(
system=system, theme=theme or 'system'
))

compilation_results['success' if is_successful else 'failure'].append('{system} sass for {theme}.'.format(
system=system, theme=theme or 'system',
))

print("\t\tFinished compiling Sass:")
if not dry_run:
for sass_dir, css_dir, duration in timing_info:
print(f">> {sass_dir} -> {css_dir} in {duration}s")

if compilation_results['success']:
print("\033[92m\nSuccessful compilations:\n--- " + "\n--- ".join(compilation_results['success']) + "\n\033[00m")
if compilation_results['failure']:
print("\033[91m\nFailed compilations:\n--- " + "\n--- ".join(compilation_results['failure']) + "\n\033[00m")
sh(
"scripts/assets/compile-scss.sh"
+ ("".join(f" --theme {path(theme_dir)}" for theme in themes))
+ (" --skip-lms" if "lms" not in systems else "")
+ (" --skip-cms" if "cms" not in systems else "")
+ (" --dev" if options.get("debug") else "")
+ (" --force" if options.get("force") else "")
+ (" --dry" if tasks.environment.dry_run else "")
)


def _compile_sass(system, theme, debug, force, timing_info):
"""
Compile sass files for the given system and theme.

:param system: system to compile sass for e.g. 'lms', 'cms', 'common'
:param system: system to compile sass for: 'lms' or 'cms'; any other value is a no-op.
:param theme: absolute path of the theme to compile sass for.
:param debug: boolean showing whether to display source comments in resulted css
:param force: boolean showing whether to remove existing css files before generating new files
:param timing_info: list variable to keep track of timing for sass compilation
:param timing_info: no longer supported; no effect.
"""

# Note: import sass only when it is needed and not at the top of the file.
# This allows other paver commands to operate even without libsass being
# installed. In particular, this allows the install_prereqs command to be
# used to install the dependency.
import sass
if system == "common":
sass_dirs = get_common_sass_directories()
else:
sass_dirs = get_sass_directories(system, theme)

dry_run = tasks.environment.dry_run

# determine css out put style and source comments enabling
if debug:
source_comments = True
output_style = 'nested'
else:
source_comments = False
output_style = 'compressed'

for dirs in sass_dirs:
start = datetime.now()
css_dir = dirs['css_destination_dir']
sass_source_dir = dirs['sass_source_dir']
lookup_paths = dirs['lookup_paths']

if not sass_source_dir.isdir():
print("\033[91m Sass dir '{dir}' does not exists, skipping sass compilation for '{theme}' \033[00m".format(
dir=sass_source_dir, theme=theme or system,
))
# theme doesn't override sass directory, so skip it
continue

if force:
if dry_run:
tasks.environment.info("rm -rf {css_dir}/*.css".format(
css_dir=css_dir,
))
else:
sh(f"rm -rf {css_dir}/*.css")

if dry_run:
tasks.environment.info("libsass {sass_dir}".format(
sass_dir=sass_source_dir,
))
else:
sass.compile(
dirname=(sass_source_dir, css_dir),
include_paths=COMMON_LOOKUP_PATHS + lookup_paths,
source_comments=source_comments,
output_style=output_style,
)

# For Sass files without explicit RTL versions, generate
# an RTL version of the CSS using the rtlcss library.
for sass_file in glob.glob(sass_source_dir + '/**/*.scss'):
if should_generate_rtl_css_file(sass_file):
source_css_file = sass_file.replace(sass_source_dir, css_dir).replace('.scss', '.css')
target_css_file = source_css_file.replace('.css', '-rtl.css')
sh("rtlcss {source_file} {target_file}".format(
source_file=source_css_file,
target_file=target_css_file,
))

# Capture the time taken
if not dry_run:
duration = datetime.now() - start
timing_info.append((sass_source_dir, css_dir, duration))
return True


def should_generate_rtl_css_file(sass_file):
"""
Returns true if a Sass file should have an RTL version generated.
"""
# Don't generate RTL CSS for partials
if path(sass_file).name.startswith('_'):
return False

# Don't generate RTL CSS if the file is itself an RTL version
if sass_file.endswith('-rtl.scss'):
return False

# Don't generate RTL CSS if there is an explicit Sass version for RTL
rtl_sass_file = path(sass_file.replace('.scss', '-rtl.scss'))
if rtl_sass_file.exists():
return False

return True
sh(
"scripts/assets/compile-scss.sh"
+ (f" --theme {theme} --skip-default-theme" if theme else "")
+ (" --skip-lms" if system != "lms")
+ (" --skip-cms" if system != "cms")
+ (" --dev" if debug else "")
+ (" --force" if force else "")
)


def process_npm_assets():
Expand Down
133 changes: 133 additions & 0 deletions scripts/assets/compile-scss-dir.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/bin/sh
HELP="Recursively compile SCSS in one directory."

# Enable stricter sh behavior.
set -eu

USAGE="\
USAGE:\n\
$0 [OPTIONS] <SCSS_SRC> <CSS_DEST> [<INCLUDE_PATHS>] [OPTIONS]\n\
\n\
ARGUMENTS:\n\
SCSS_SRC Source directory with SCSS\n\
CSS_DEST Target directory for output CSS\n\
INCLUDE_PATHS Colon-separated list of SCSS import roots\n\
\n\
OPTIONS:\n\
-d, --dev Dev mode: don't compress output CSS\n\
-r, --dry Dry run: don't execute commands (pairs well with -v)\n\
-v, --verbose Print commands as they are executed.\n\
-h, --help Display this.\n\
"

scss_src=""
css_dest=""
include_paths=""
output_options="--output-style=compressed"

# Flags.
# Empty string (-z) => false
# Nonempty string (-n) => true
verbose=""
dry=""

# Parse arguments and options.
while [ $# -gt 0 ]; do
case $1 in
-d|--dev)
output_options="--output-style=nested"
# TODO: When moving from `sass.compile(...)` to `sassc`, we had to stop using
# the " --source-comments" option because it is not available
# in the `sassc` CLI under libsass==0.10. After upgrading to libsass>=0.11,
# we should add back " --source-comments" when in --dev mode.
shift
;;
-v|--verbose)
verbose="T"
shift
;;
-r|--dry)
dry="T"
shift
;;
-h|--help)
echo "$HELP"
echo
echo "$USAGE"
exit 0
;;
-*)
echo "Error: Unrecognized option: $1"
echo
echo "$USAGE"
exit 1
;;
*)
if [ -z "$scss_src" ] ; then
scss_src="$1"
elif [ -z "$css_dest" ] ; then
css_dest="$1"
elif [ -z "$include_paths" ] ; then
include_paths="$1"
else
echo "Error: unexpected argument: $1"
echo "$USAGE"
exit 1
fi
shift
;;
esac
done

if [ -n "$verbose" ] ; then
set -x
fi

if [ -z "$scss_src" ] || [ -z "$css_dest" ] ; then
echo "Error: SCSS source dir and CSS destination dir are required."
echo "$USAGE"
exit 1
fi

# Convert include paths into string of options.
# Include paths are colon-separated, so we can replace the colons with ' --include-path='
# and then prepend one more '--include_path=' to the entire string, if nonempty.
include_path_options="$(echo "$include_paths" | sed -n 's/:/ --include-path=/pg')"
if [ -n "$include_path_options" ] ; then
include_path_options="--include-path=$include_path_options"
fi

# Navigate into `scss_src` and recursively print out relative paths for all SCSS
# files, excluding underscore-prefixed ones, using `sed` to chop off the file extension.
# For each filepath, run `sassc` and, if appropriate, `rtlcss`.
# TODO: Unlike its Python API, libsass-python's CLI does not support compiling entire
# directories, so we must implement that logic ourselves. After we upgrade
# to node-sass or dart-sass, though, this logic might be able to be simplified.
for rel_path in $(cd "$scss_src" && find . \( -name \*.scss -and \! -name _\* \) | sed -n 's/.scss$//p') ; do

# Make sure the destination directory exists.
mkdir -p "$(dirname "$css_dest/$rel_path")"

# Compile one SCSS file into a CSS file.
# Note that scssc's $..._options arguments are not quoted, because they
# may contain multiple arguments, which we want to split apart rather than
# pass as one big argument. Hence the shellcheck disable directive.
# shellcheck disable=2086
[ -n "$dry" ] || \
sassc \
$output_options \
$include_path_options \
"$scss_src/$rel_path.scss" \
"$css_dest/$rel_path.css"

# Generate converted RTL css too, if relevant.
case "$rel_path" in
*-rtl)
# SCSS is already RTL; no need to generate extra RTL file.
;;
*)
# Generate RTL CSS from LTR CSS, appending -rtl to file name.
[ -n "$dry" ] || rtlcss "$css_dest/$rel_path.css" "$css_dest/$rel_path-rtl.css"
;;
esac
done
Loading