0

[Files app]: Add scripts for JS modules conversion

Command:
ui/file_manager/base/tools/modules.sh [1] [2]
[1] build directory (e.g. out/Release)
[2] path of JS file to convert (relative to chromium/src)

What does the script do:
1. Update BUILD.gn to enable conversion of JS file.
2. Add exports.
3. Run closure compiler.
4. Parse closure compiler output to retrieve unknown types/variables/functions.
6. Parse these dependencies to find relevant exported types/variables/functions.
7. When found, update BUILD.gn and JS file.

Made sure that:
The script doesn't add any irrelevant import/export.
No duplicate lines are added.

Steps 3 to 7 are ran until the closure compiler output
'X error(s), Y warning(s), Z% typed' doesn't change. The closure compiler
is ran at least 3 times (except if no imports are found), sometimes more.

Bug: 1133186
Change-Id: I1528c8187bc1476d6add1bcb14a0d4fd1b64a4fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2550328
Commit-Queue: Jeremie Boulic <jboulic@chromium.org>
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834181}
This commit is contained in:
Jérémie Boulic
2020-12-07 13:40:00 +00:00
committed by Chromium LUCI CQ
parent b8c6670fe3
commit 2ce3bfdf66
2 changed files with 825 additions and 0 deletions
ui/file_manager/base/tools

@ -0,0 +1,774 @@
#!/usr/bin/env python
#
# Copyright (c) 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import re
import sys
import subprocess
def get_file_lines(file_path):
'''Returns list of lines from a file. '\n' at the end of lines are
removed.'''
with open(file_path, 'r') as f:
file_lines = f.readlines()
return [l.rstrip() for l in file_lines]
def save_file_lines(file_path, file_lines):
'''Saves file_lines in file pointed by `file_path`, '\n' at the end of
lines are added back.'''
with open(file_path, 'w') as f:
for line in file_lines:
f.write(line + '\n')
def get_relative_dependency(path, dir_path):
'''Given a file path, returns formatted BUILD.gn dependency.
Parameters:
path: file path to format as relative dependency.
dir_path: directory from which the relative dependency is calculated.
Returns:
Formatted dependency.
The 3 cases illustrated below are handled:
- ":file_type.m" if returned if file_type.js is in dir_path.
- "metadata:metadata_model.m" is returned if metadata/metadata_model.js
is in dir_path.
- "//ui/file_manager/externs:volume_manager.m" is returned if
ui/file_manager/externs is not included in dir_path.
'''
split_path = path.split('/')
file_name = split_path.pop().replace('.js', '.m')
split_dir_path = dir_path.split('/')
while (len(split_path) > 0 and len(split_dir_path) > 0
and split_path[0] == split_dir_path[0]):
del split_path[0]
del split_dir_path[0]
if len(split_dir_path) == 0:
# The dependency is within dir_path.
return '/'.join(split_path) + ':' + file_name
else:
return '//' + re.sub(r"\/[a-zA-Z0-9_]+\.js", ":", path) + file_name
def get_index_substr(file_lines, substr):
'''Finds first occurence of `substr` and returns its index in
`file_lines`.'''
for i, line in enumerate(file_lines):
if substr in line:
return i
return -1
def get_end_of_copyright(file_lines):
'''Get index of last line of copyright (after checking that the section
exists).'''
index = get_index_substr(file_lines, 'Copyright 20')
if index < 0:
return -1
while index < len(file_lines) and file_lines[index].startswith('// '):
index += 1
return index - 1
def get_end_of_file_overview(file_lines):
'''Get index of last line of file_overview (after checking that the section
exists).'''
index = get_index_substr(file_lines, '@fileoverview')
if index < 0:
return -1
while index < len(file_lines) and file_lines[index] != ' */':
index += 1
return index
def get_last_index_of_line_starting_with(file_lines, substr):
'''Get last line starting with the given `substr`.'''
last_index = -1
for i, line in enumerate(file_lines):
if line.startswith(substr):
last_index = i
return last_index
def get_end_of_build_imports(file_lines):
'''In BUILD.gn, gets index of last 'import' line at the beginning of the
file.'''
index = get_last_index_of_line_starting_with(file_lines, 'import("//')
if index < 0:
return get_end_of_copyright(file_lines)
return index
def add_js_library(file_lines, file_name, dir_path):
'''Adds js_library target in BUILD.gn.'''
i = file_lines.index('js_library("%s") {' % (file_name))
# Check that the library does exist and hasn't already been converted.
if i < 0:
print 'Unable to find js_library for {}'.format(file_name)
return False
if 'js_library("%s.m") {' % (file_name) in file_lines:
print 'js_library for {}.m already added'.format(file_name)
return False
# Find the end of the library definition.
while i < len(file_lines) and file_lines[i] != '}':
i += 1
i += 1
if i == len(file_lines):
print 'reached end of file'
return False
new_lines = '''
js_library("%s.m") {
sources = [ "$root_gen_dir/%s/%s.m.js" ]
extra_deps = [ ":modulize" ]
}''' % (file_name, dir_path, file_name)
file_lines[i:i] = new_lines.split('\n')
return True
def add_import_line(file_lines, variable, relative_path, is_unittest):
'''Adds import line (import {...} from '...') in JS file.'''
# Construct import line.
import_line = 'import ' if is_unittest else '// #import '
# Check: existing relative path.
i = get_index_substr(file_lines, relative_path)
if i >= 0 and '}' in file_lines[i]:
if variable + '}' in file_lines[i] or variable + ',' in file_lines[i]:
return
split_line = file_lines[i].split('}')
file_lines[i] = '%s, %s}%s' % (split_line[0], variable, split_line[1])
return
import_line += "{%s} from '%s';" % (variable, relative_path)
if import_line in file_lines:
return
# Add clang-format off/on if necessary.
index = 0
if '// clang-format off' in file_lines:
index = file_lines.index('// clang-format off')
else:
# Go to the end of copyright and fileoverview.
index = get_end_of_file_overview(file_lines)
if index < 0:
index = get_end_of_copyright(file_lines)
if (index < 0):
index = 0
index += 1
if len(import_line) > 80:
index += 1
file_lines.insert(index, '// clang-format off')
# Go to the last existing import line.
last_import_line_index = get_last_index_of_line_starting_with(
file_lines, 'import ')
if last_import_line_index >= 0:
index = last_import_line_index
else:
file_lines.insert(index + 1, '')
file_lines.insert(index + 1, '// clang-format off')
elif 'import ' not in file_lines[index + 1]:
file_lines.insert(index, '')
# Add import line.
file_lines.insert(index + 1, import_line)
def add_namespace_rewrite(build_gn_path):
build_file_lines = get_file_lines(build_gn_path)
# Add import("//ui/webui/resources/js/cr.gni").
cr_gni = 'import("//ui/webui/resources/js/cr.gni")'
modulizer_gni = 'import("//ui/webui/resources/tools/js_modulizer.gni")'
if not cr_gni in build_file_lines:
if not modulizer_gni in build_file_lines:
raise ValueError('"js_modulizer.gni" not found')
index = build_file_lines.index(modulizer_gni)
build_file_lines.insert(index, cr_gni)
# Add namespace_rewrites = cr_namespace_rewrites.
namespace_rewrite = ' namespace_rewrites = cr_namespace_rewrites'
if not namespace_rewrite in build_file_lines:
index = get_index_substr(build_file_lines,
'js_modulizer("modulize") {')
if index < 0:
print 'No modulize rule found'
return
while index < len(build_file_lines) and build_file_lines[index] != '':
index += 1
index -= 1
build_file_lines.insert(index, '')
build_file_lines.insert(index + 1, namespace_rewrite)
save_file_lines(build_gn_path, build_file_lines)
def add_hide_third_party(build_gn_path):
build_file_lines = get_file_lines(build_gn_path)
hide_third_party = ' "hide_warnings_for=third_party/",'
prefix_replacement_line = 'browser_resolver_prefix_replacements='
if not hide_third_party in build_file_lines:
index = get_index_substr(build_file_lines, prefix_replacement_line)
if index < 0:
print 'prefix replacement not found in "js_test_gen_html_modules"'
return
build_file_lines.insert(index + 1, hide_third_party)
save_file_lines(build_gn_path, build_file_lines)
def add_dependency(file_lines, section_first_line, list_name, dependency_line):
'''
Add dependency in BUILD.gn.
Parameters:
file_lines: lines of BUILD.gn file.
section_first_line: opening line of target to update.
list_name: name of the dependency list, deps', 'input_files' etc...
dependency_line: line to add to the dependency list to update.
'''
# Find a line that starts with deps.
if not section_first_line in file_lines:
print 'Unable to find ' + section_first_line
return False
# Return False if dependency already found.
if dependency_line in file_lines:
return False
# Find index of `list_name`. Get index of 'sources = [' in case `list_name`
# is not defined.
rule_index = file_lines.index(section_first_line)
sources_index = -1
insertion_index = -1
single_line_dependency_list = False
for i in range(rule_index, len(file_lines)):
if 'sources = [' in file_lines[i]:
# Jump to the end of the 'sources' list.
while not ']' in file_lines[i]:
i += 1
sources_index = i
if ' {} = '.format(list_name) in file_lines[i]:
# Dependency line found.
if file_lines[i].endswith(']'):
single_line_dependency_list = True
# Jump to the end of the dependency list.
while not ']' in file_lines[i]:
i += 1
insertion_index = i
break
if file_lines[i] == '}':
# End of build rule.
break
if insertion_index == -1:
# Add dependency line, after sources if possible.
index = sources_index + 1 if sources_index > 0 else rule_index + 1
# Define new list over 2 lines: 'list_name = [\n]'
file_lines.insert(index, ' {} = ['.format(list_name))
file_lines.insert(index + 1, ' ]')
insertion_index = index + 1
if single_line_dependency_list:
# If one-line dependency is found, rewrite it over 2 lines, as above.
existing_dependency = file_lines[insertion_index].split(' ')[-2]
file_lines[insertion_index] = ' {} = ['.format(list_name)
file_lines[insertion_index + 1] = ' {},'.format(existing_dependency)
file_lines[insertion_index + 2] = ' ]'
# Check for already imported dependency after reformatting.
if file_lines[insertion_index + 1] == dependency_line:
return False
insertion_index += 2
# Insert dependency.
file_lines.insert(insertion_index, dependency_line)
return True
def update_build_gn_dependencies(dir_path, file_name, build_gn_path):
print 'Updating BUILD.gn dependencies for ' + file_name
# Get file contents.
file_lines = get_file_lines(build_gn_path)
# Edit file with modules-related targets.
import_gni = 'import("//ui/webui/resources/tools/js_modulizer.gni")'
if not import_gni in file_lines:
index = get_end_of_build_imports(file_lines) + 1
file_lines.insert(index, import_gni)
new_lines = '''
js_modulizer("modulize") {
input_files = [
]
}'''
file_lines.extend(new_lines.split('\n'))
if not add_dependency(file_lines, 'group("closure_compile") {', 'deps',
' ":closure_compile_jsmodules",'):
return
# Add closure_compile_jsmodules rule.
index = get_index_substr(file_lines,
'js_type_check("closure_compile_module") {')
if index < 0:
print 'js_type_check("closure_compile_module") not found'
return
new_lines = '''\
js_type_check("closure_compile_jsmodules") {
uses_js_modules = true
deps = [
]
}
'''
file_lines[index:index] = new_lines.split('\n')
if not add_js_library(file_lines, file_name, dir_path):
return
# Add closure dependency.
if not add_dependency(
file_lines, 'js_type_check("closure_compile_jsmodules") {', 'deps',
' ":{}.m",'.format(file_name)):
return
# Add 'modulize' dependency.
if not add_dependency(file_lines, 'js_modulizer("modulize") {',
'input_files', ' "{}.js",'.format(file_name)):
return
# Save file contents.
save_file_lines(build_gn_path, file_lines)
def update_buid_gn_unittest_dependencies(build_gn_path, js_file_path,
file_name):
'''
Rename _unittest.js to _unittest.m.js.
Update test URL in file_manager_jstest.cc.
Update BUILD.gn rules.
It is assumed that if we're converting [file]_unittest.js, [file].js has
been converted already.
Parameters:
build_gn_path: Path of BUILD.gn file used by the file be converted.
js_file_path: Path of the file to be converted
(ui/file_manager/.../..._unittest.m.js)
file_name: Name of the file to be converted (..._unittest)
'''
print 'Updating BUILD.gn dependencies for ' + file_name
main_build_path = 'ui/file_manager/BUILD.gn'
file_manager_jstest_path = ('chrome/browser/chromeos/file_manager/'
'file_manager_jstest.cc')
# Get file contents.
build_file_lines = get_file_lines(build_gn_path)
main_build_file_lines = get_file_lines(main_build_path)
file_manager_jstest_lines = get_file_lines(file_manager_jstest_path)
# Rename _unittest.js to _unittest.m.js.
original_js_file_path = js_file_path.replace('.m.js', '.js')
if not os.path.isfile(js_file_path):
if not os.path.isfile(original_js_file_path):
e = 'Could not find neither file {} or {}'.format(
js_file_path, original_js_file_path)
raise ValueError(e)
os.rename(original_js_file_path, js_file_path)
# Update chrome/browser/chromeos/file_manager/file_manager_jstest.cc
# by updating RunTestURL for the unittest being converted.
# [...]_unittest_gen.html -> [...]_unittest.m_gen.html.
index = get_index_substr(file_manager_jstest_lines, file_name + '.m')
if index < 0:
index = get_index_substr(file_manager_jstest_lines, file_name)
if index < 0:
print 'file_manager_jstest.cc: {} not found'.format(file_name)
file_manager_jstest_lines[index] = file_manager_jstest_lines[
index].replace(file_name, file_name + '.m')
# Add "js_test_gen_html_modules" target.
build_rule = 'js_test_gen_html("js_test_gen_html_modules") {'
if not build_rule in build_file_lines:
if 'js_test_gen_html("js_test_gen_html") {' in build_file_lines:
index = build_file_lines.index(
'js_test_gen_html("js_test_gen_html") {') - 1
new_lines = r'''
js_test_gen_html("js_test_gen_html_modules") {
deps = [
]
js_module = true
closure_flags =
strict_error_checking_closure_args + [
"js_module_root=./gen/ui",
"js_module_root=../../ui",
"browser_resolver_prefix_replacements=\"chrome://test/=./\"",
]
}'''
build_file_lines[index:index] = new_lines.split('\n')
else:
e = '"js_test_gen_html" build target not found on {}'.format(
build_gn_path)
raise ValueError(e)
# Add ":js_test_gen_html_modules_type_check_auto", in closure_compile deps.
index = get_index_substr(build_file_lines,
':js_test_gen_html_modules_type_check_auto')
if index < 0:
index = get_index_substr(build_file_lines,
':js_test_gen_html_type_check_auto')
if index < 0:
e = '"js_test_gen_html_type_check_auto" dependency not found'
raise ValueError(e)
else:
new_line = ' ":js_test_gen_html_modules_type_check_auto",'
build_file_lines.insert(index, new_line)
# In BUILD.gn, update js_unittest target to .m
js_unittest = 'js_unittest("%s") {' % (file_name)
js_unittest_m = 'js_unittest("%s.m") {' % (file_name)
if js_unittest in build_file_lines:
index = build_file_lines.index(js_unittest)
build_file_lines[index] = js_unittest_m
# Remove dependencies.
while index < len(
build_file_lines) and not 'deps =' in build_file_lines[index]:
index += 1
if ']' in build_file_lines[index]:
build_file_lines[index] = ' deps = ['
build_file_lines.insert(index, ' ]')
else:
index += 1
while index < len(
build_file_lines) and not ']' in build_file_lines[index]:
del build_file_lines[index]
else:
if not js_unittest_m in build_file_lines:
print 'Unable to find "{}" target'.format(file_name)
# Move unittest dependency from js_test_gen_html to
# js_test_gen_html_modules.
index = get_index_substr(build_file_lines, '":{}",'.format(file_name))
if index >= 0:
del build_file_lines[index]
else:
single_line_deps = ' deps = [ ":{}" ]'.format(file_name)
if single_line_deps in build_file_lines:
index = build_file_lines.index(single_line_deps)
build_file_lines[index] = ' deps = []'
add_dependency(build_file_lines,
'js_test_gen_html("js_test_gen_html_modules") {', 'deps',
' ":{}.m",'.format(file_name))
# Add dependency in ui/file_manager/BUILD.gn.
dir_path = os.path.dirname(js_file_path)
rel_dir_path = '/'.join(dir_path.split('/')[2:])
dependency_line = ' "{}:js_test_gen_html_modules",'.format(rel_dir_path)
if not dependency_line in main_build_file_lines:
if not add_dependency(main_build_file_lines,
'group("unit_test_data") {', 'deps',
dependency_line):
print 'unable to update ui/file_manager/BUILD.gn'
# Save file contents.
save_file_lines(build_gn_path, build_file_lines)
save_file_lines(main_build_path, main_build_file_lines)
save_file_lines(file_manager_jstest_path, file_manager_jstest_lines)
def parse_compiler_output(compiler_output, build_gn_path, file_name):
'''Parse closure compile output and return list of token that are
undefined/undeclared (no duplicate).'''
print "Parsing closure compiler output"
with open(compiler_output, 'r') as f:
# Retrieve from the closure compile output the types that need to be
# imported.
variables_to_import = []
for line in f:
line = line.rstrip()
# Select from the output the lines that contain 'ERROR - []' and
# 'Unknown type '.
if 'ERROR - [' in line:
if not file_name in line:
if '/third_party/' in line:
add_hide_third_party(build_gn_path)
else:
print 'UNHANDLED ERROR (Other file): ' + line
elif 'Unknown type ' in line:
unknown_type = line.split('Unknown type ')[1]
# Select the main type by removing what follows the dot:
# ThumbnailLoader.LoaderType -> ThumbnailLoader.
unknown_type = unknown_type.split('.')[0]
if not unknown_type in variables_to_import:
variables_to_import.append(unknown_type)
elif 'variable ' in line and ' is undeclared' in line:
if ' cr ' in line: # namespace rewrite.
add_namespace_rewrite(build_gn_path)
continue
unknown_type = line.split('variable ')[1]
unknown_type = unknown_type.split(' is undeclared')[0]
if not unknown_type in variables_to_import:
variables_to_import.append(unknown_type)
else:
print 'UNHANDLED ERROR: ' + line
return variables_to_import
def find_dependencies(dir_path, build_gn_path, js_file_path,
variables_to_import, is_unittest):
'''Finds dependencies and updates BUILD.gn and JS file to import these
dependencies.'''
# Get BUILD.gn and JS file contents.
build_file_lines = get_file_lines(build_gn_path)
js_file_lines = get_file_lines(js_file_path)
file_name = js_file_path.split('/').pop().replace('.js', '')
# Special case: classes that extend "cr.EventTarget".
index = get_index_substr(js_file_lines, ' extends cr.EventTarget')
if index >= 0:
if is_unittest:
js_file_lines[index] = js_file_lines[index].replace(
' extends cr.EventTarget', ' extends EventTarget')
add_dependency(build_file_lines,
'js_unittest("%s") {' % (file_name), 'deps',
' "//ui/webui/resources/js/cr:event_target.m",')
else:
add_dependency(build_file_lines,
'js_library("%s.m") {' % (file_name), 'deps',
' "//ui/webui/resources/js/cr:event_target.m",')
add_import_line(js_file_lines, 'NativeEventTarget as EventTarget',
'chrome://resources/js/cr/event_target.m.js',
is_unittest)
# General case.
for variable in variables_to_import:
# First, handle special cases.
if is_unittest:
# "//chrome/test/data/webui:chai_assert".
out, _ = subprocess.Popen([
'egrep', '-R', 'export function {}\('.format(variable), '-l',
'./chrome/test/data/webui/chai_assert.js'
],
stdout=subprocess.PIPE).communicate()
if out.rstrip() != '':
add_dependency(build_file_lines,
'js_unittest("%s") {' % (file_name), 'deps',
' "//chrome/test/data/webui:chai_assert",')
add_import_line(js_file_lines, variable,
'chrome://test/chai_assert.js', is_unittest)
continue
else:
# "//ui/webui/resources/js".
out, _ = subprocess.Popen([
'egrep', '-R', '\/\* #export \*\/ \w+ {}\W'.format(variable),
'-l', './ui/webui/resources/js'
],
stdout=subprocess.PIPE).communicate()
path = out.rstrip()
if path != '':
path = path.replace('./', '//').replace('.js', '.m')
dependency = ':'.join(path.rsplit(
'/', 1)) # //ui/webui/resources/js:assert.m.
add_dependency(build_file_lines,
'js_library("%s.m") {' % (file_name), 'deps',
' "{}",'.format(dependency))
path = path.replace('//ui/webui/', 'chrome://').replace(
'.m', '.m.js') # chrome://resources/js/assert.m.js.
add_import_line(js_file_lines, variable, path, is_unittest)
continue
# Look for exported variables.
out, _ = subprocess.Popen([
'egrep', '-R', '\/\* #export \*\/ \w+ {}\W'.format(variable), '-l',
'./ui/file_manager/'
],
stdout=subprocess.PIPE).communicate()
path = out.rstrip()
if path == '':
# Look for variables exported as wrapped objects
out, _ = subprocess.Popen([
'egrep', '-R',
'\/\* #export \*\/ \{%s\}' % variable, '-l',
'./ui/file_manager/'
],
stdout=subprocess.PIPE).communicate()
path = out.rstrip()
if path == '':
continue
if '\n' in path:
print '{}: export found in more than 1 file'.format(variable)
print path
continue
# Remove './' at the start.
path = path[2:]
relative_path = os.path.relpath(path, start=dir_path).replace(
'.js', '.m.js')
if not relative_path.startswith('../'):
relative_path = './' + relative_path
add_import_line(js_file_lines, variable, relative_path, is_unittest)
if is_unittest:
add_dependency(
build_file_lines, 'js_unittest("%s") {' % (file_name), 'deps',
' "{}",'.format(get_relative_dependency(path, dir_path)))
else:
add_dependency(
build_file_lines, 'js_library("%s.m") {' % (file_name), 'deps',
' "{}",'.format(get_relative_dependency(path, dir_path)))
# Save BUILD.gn and JS file contents.
save_file_lines(build_gn_path, build_file_lines)
save_file_lines(js_file_path, js_file_lines)
def convert_js_file(js_file_path):
'''Add exports (/* #export */) and ignore 'use strict'.'''
file_lines = get_file_lines(js_file_path)
# Ignore 'use strict'.
index = get_index_substr(file_lines, "'use strict'")
if index >= 0:
file_lines[index] = file_lines[index].replace(
"'use strict'", "/* #ignore */ 'use strict'")
# Add exports.
for i, line in enumerate(file_lines):
# Export class.
if line.startswith('class '):
# Extract class name.
class_name = line.split(' ')[1]
# Make sure this class is not used locally.
if get_index_substr(file_lines, 'new ' + class_name) >= 0:
# Export only if the class name is the name of the file.
snake_case_name = ''.join([
'_' + c.lower() if c.isupper() else c for c in class_name
]).lstrip('_')
if not snake_case_name + '.js' == js_file_path.split('/')[-1]:
continue
file_lines[i] = '/* #export */ ' + line
# Export variable in global namespace.
elif line.startswith('const ') or line.startswith(
'let ') or line.startswith('var '):
# Extract variable name.
variable_name = line.split(' ')[1]
# Check if the variable is further defined/extended in the file. If
# so, export it.
export_variable = False
for l in range(len(file_lines)):
if file_lines[l].startswith(variable_name + '.'):
export_variable = True
break
if not export_variable:
continue
# Check if export already added.
if get_index_substr(file_lines, '/* #export */ {%s}' %
(variable_name)) >= 0:
continue
# Export variable in wrapping object at the end of the file.
new_lines = '''
// eslint-disable-next-line semi,no-extra-semi
/* #export */ {%s};''' % (variable_name)
file_lines.extend(new_lines.split('\n'))
# Check if @suppress already added.
if get_index_substr(file_lines, ' @suppress {uselessCode}') >= 0:
continue
index = get_end_of_file_overview(file_lines)
if index < 0:
index = get_end_of_copyright(file_lines)
if index < 0:
index = 0
index += 1
new_lines = '''
/**
* @fileoverview
* @suppress {uselessCode} Temporary suppress %s.
*/''' % ('because of the line exporting')
file_lines[index:index] = new_lines.split('\n')
else:
while ' */' not in file_lines[index]:
index += 1
new_line = (' * @suppress {uselessCode} Temporary suppress '
'because of the line exporting.')
file_lines.insert(index, new_line)
# Export function.
elif line.startswith('function '):
if not get_index_substr(file_lines, ' {}('.format(function_name)):
# The function has to be used outside the file, so has to be
# exported.
file_lines[i] = '/* #export */ ' + line
# Save file contents.
save_file_lines(js_file_path, file_lines)
def convert_unittest_file(js_file_path):
'''Add exports and remove 'use strict'.'''
file_lines = get_file_lines(js_file_path)
# Remove 'use strict'.
index = get_index_substr(file_lines, "'use strict'")
if index >= 0:
del file_lines[index]
# Add exports.
for i, line in enumerate(file_lines):
if line.startswith('function setUp()') or 'function test' in line:
file_lines[i] = 'export ' + file_lines[i]
# Save file contents.
save_file_lines(js_file_path, file_lines)
def main():
# Get command line arguments.
js_file_path = sys.argv[1]
action = sys.argv[2]
if not js_file_path.endswith('.js'):
e = "Argument 1 {} isn't a JS file (expected .js extension)".format(
js_file_path)
raise ValueError(e)
dir_path = os.path.dirname(js_file_path)
build_gn_path = dir_path + '/BUILD.gn'
file_name = os.path.basename(js_file_path).replace('.js', '')
is_unittest = file_name.endswith('_unittest')
if is_unittest:
js_file_path = js_file_path.replace('.js', '.m.js')
if (action == 'generate'):
if is_unittest:
update_buid_gn_unittest_dependencies(build_gn_path, js_file_path,
file_name)
convert_unittest_file(js_file_path)
else:
update_build_gn_dependencies(dir_path, file_name, build_gn_path)
convert_js_file(js_file_path)
elif (action == 'parse'):
compiler_output = sys.argv[3]
variables_to_import = parse_compiler_output(compiler_output,
build_gn_path, file_name)
if variables_to_import == []:
return
find_dependencies(dir_path, build_gn_path, js_file_path,
variables_to_import, is_unittest)
print "-----modules.py-----"
main()
print "--------------------"

@ -0,0 +1,51 @@
#!/bin/bash
#
# Copyright (c) 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Command usage:
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
# file_manager/foreground/js/list_thumbnail_loader.js
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
# file_manager/common/js/importer_common.js
# ui/file_manager/base/tools/modules.sh out/Release ui/file_manager/
# file_manager/foreground/js/list_thumbnail_loader_unittest.js
# Input: js file to convert.
build_dir=$1;
file_path=$2;
dir=`dirname $file_path`;
compiler_output="$build_dir/gen/ui/file_manager/base/tools/compiler_output.txt";
# Create containing directory of `compiler_output` if doesn't exist.
mkdir -p `dirname $compiler_output`;
# Process files with Python.
ui/file_manager/base/tools/modules.py $file_path 'generate';
# Parse closure compiler output and update files until the message 'X error(s),
# Y warning(s), Z% typed' doesn't change.
prev_output=""
new_output="."
while [[ $prev_output != $new_output ]]; do
prev_output=$new_output;
# Run closure compiler and save output.
ninja -C $build_dir $dir:closure_compile 2>&1 | tee $compiler_output;
# Parse closure compiler output.
ui/file_manager/base/tools/modules.py $file_path 'parse' $compiler_output;
# Get number of errors from modules.txt.
new_output=$(cat $compiler_output | grep 'error(s)' );
done;
# Format files.
git cl format --js;
if [[ $new_output == "" ]]; then
echo "No closure compiler error found"
else
echo "Final state: " $new_output;
fi;