From 87a714fff8d10073f813a9afce8aceda6060c2f9 Mon Sep 17 00:00:00 2001 From: kozbial Date: Wed, 28 Jul 2021 14:47:25 -0700 Subject: [PATCH] Add handling for modules that are not leaf nodes --- scripts/goog_module/convert-file.sh | 85 ++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/scripts/goog_module/convert-file.sh b/scripts/goog_module/convert-file.sh index a412a7933..1c5b92fc8 100755 --- a/scripts/goog_module/convert-file.sh +++ b/scripts/goog_module/convert-file.sh @@ -125,6 +125,59 @@ commit-step() { success "created commit with message: \"${message}\"" } +####################################### +# Extracts a list of properties that are accessed on the specified module name. +# Excludes any matches +# Arguments: +# The module name to find properties accessed for. +# The modules required by the specified module as a single string. +# The filepath to extract requires from. +# Optional: The top-level module. +# Outputs: +# Writes list of properties to stdout as items separated by spaces. +####################################### +getPropertiesAccessed() { + local module_name="$1" + local requires="$2" + local filepath="$3" + local top_module_name="$4" + # Get any strings that follow "$module_name.", excluding matches for + # "$module_name.prototype" and remove list item duplicates (sort -u). + local properties_accessed=$(perl -nle 'print $& while m{(?<='"${module_name}"'\.)(?!prototype)\w+}g' "${filepath}" | sort -u) + + # Get a list of any requires that are a child of $module_name. + # Ex: Blockly.utils.dom is a child of Blockly.utils, this would return "dom" + local requires_overlap=$(echo "${requires}" | perl -nle 'print $& while m{(?<='"${module_name}"'\.)\w+}g') + # Detect if there was any overlap. + if [[ -n "${requires_overlap}" ]]; then + while read -r requires_overlap_prop; do + properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${requires_overlap_prop}"'//g') + done <<<"${requires_overlap}" + fi + + # Fix formatting (remove extra whitespace) and delimit the list with spaces. + properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/\s+/ /g' | xargs) + + echo "${properties_accessed}" +} + +####################################### +# Extracts a list of requires defined in the file in the form of a single string +# of items separated by newlines. +# Arguments: +# The filepath to extract requires from. +# Outputs: +# Writes list of requires to stdout as items separated by newlines. +####################################### +getRequires() { + local filepath="$1" + # Extracts all strings that start with goog.require(' or goog.requireType(' + # up until the ending single quote. + # Ex: "goog.require('Blockly.utils')" would extract "Blockly.utils" + local requires=$(perl -nle 'print $& while m{(?:(?<=^goog.require\('\'')|(?<=^goog.requireType\('\''))[^'\'']+}g' "${filepath}") + echo "${requires}" +} + ####################################### # Runs step 2 of the automated conversion. # Arguments: @@ -166,8 +219,18 @@ step2 () { # No top level class. inf 'Updating top-level property declarations...' perl -pi -e 's/^'"${module_name}"'\.([^ ]+) =/const \1 =/g' "${filepath}" + + # Extract specific properties accessed so that properties from requires that + # are children of the module aren't changed. + # Ex: The module Blockly.utils shouldn't update Blockly.utils.dom (since it is + # a require from another module. + local requires=$(getRequires "${filepath}") + local properties_accessed=$(getPropertiesAccessed "${module_name}" "${requires}" "${filepath}") inf "Updating local references to module..." - perl -pi -e 's/'"${module_name}"'\.([^ ]+)/\1/g' "${filepath}" + for property in $(echo "${properties_accessed}"); do + inf "Updating references of ${module_name}.${property} to ${property}..." + perl -pi -e 's/'"${module_name}"'\.'"${property}"'(?!\w)/'"${property}"'/g' "${filepath}" + done npm run build:deps success "Completed automation for step 2. Please manually review and add exports for non-private top-level functions." @@ -187,7 +250,7 @@ step3() { fi inf "Extracted module name \"${module_name}\"" - local requires=$(perl -nle 'print $& while m{(?:(?<=^goog.require\('\'')|(?<=^goog.requireType\('\''))[^'\'']+}g' "${filepath}") + local requires=$(getRequires "${filepath}") # Process each require echo "${requires}" | while read -r require; do @@ -205,23 +268,17 @@ step3() { # Parse property access of module local direct_access_count=$(perl -nle 'print $& while m{'"${require}"'[^\.'\'']}g' "${filepath}" | wc -l) - local properties_accessed=$(perl -nle 'print $& while m{(?<='"${require}"'\.)(?!prototype)\w+}g' "${filepath}" | sort -u) + local properties_accessed=$(getPropertiesAccessed "${require}" "${requires}" "${filepath}") - # Detect requires overlap - # (ex: Blockly.utils require and Blockly.utils.dom also in requires) - local requires_overlap=$(echo "${requires}" | perl -nle 'print $& while m{(?<='"${require}"'\.)\w+}g') - if [[ -n "${requires_overlap}" ]]; then - while read -r requires_overlap_prop; do - properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${requires_overlap_prop}"'//g') - done <<<"${requires_overlap}" - fi - # Detect module name overlap - # (ex: Blockly require and Blockly.ContextMenuItems module being converted) + # Remove $module_name in case it is a child of $require. + # Ex: Blockly.utils.dom would be a child of Blockly, module_overlap would be + # "utils" local module_overlap=$(echo "${module_name}" | perl -nle 'print $& while m{(?<='"${require}"'\.)\w+}g') if [[ -n "${module_overlap}" ]]; then properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${module_overlap}"'//g') + # Trim any extra whitespace created. + properties_accessed=$(echo "${properties_accessed}" | xargs) fi - properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/\s+/ /g' | xargs) if [[ -n "${properties_accessed}" ]]; then local comma_properties=$(echo "${properties_accessed}" | perl -pe 's/\s+/, /g' | perl -pe 's/, $//')