| #!/usr/bin/env python3 |
| # Copyright 2022 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import shutil |
| import sys |
| import tempfile |
| |
| _HERE_PATH = os.path.dirname(__file__) |
| _SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..', '..')) |
| |
| sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node')) |
| import node |
| import node_modules |
| |
| def create_rollup_config(config_out_file, rollup_root_dir, path_mappings): |
| """Generates a rollup config file to configure and use the path resolver |
| plugin which enforces import path format.""" |
| plugin_path = os.path.join(os.path.abspath(_HERE_PATH), |
| 'rollup_plugin_src_path_resolver.mjs') |
| |
| path_mappings_json = json.dumps(path_mappings, separators=(',', ':')) |
| |
| config_content = f''' |
| import src_path_resolver from '{plugin_path}'; |
| export default ({{ |
| plugins: [ |
| src_path_resolver('{rollup_root_dir}', '{path_mappings_json}') |
| ] |
| }}); |
| ''' |
| with open(config_out_file, 'w') as f: |
| f.write(config_content) |
| |
| def path_mappings_from_tsconfig(tsconfig_path): |
| path_mappings = {} |
| with open(tsconfig_path, 'r', encoding='utf-8') as tsconfig: |
| tsconfig_contents = json.load(tsconfig) |
| out_dir = tsconfig_contents['compilerOptions']['outDir'] |
| base_dir = os.path.join(os.path.dirname(tsconfig_path), out_dir) |
| files = tsconfig_contents['compilerOptions']['paths'] |
| |
| for file in files: |
| if file.startswith('//') and file.endswith('.js'): |
| script_path = file[2:] |
| path_mappings[script_path] = \ |
| os.path.abspath(os.path.join(base_dir, script_path)) |
| |
| if "references" in tsconfig_contents: |
| for reference in tsconfig_contents['references']: |
| reference_path = \ |
| os.path.join(os.path.dirname(tsconfig_path), reference['path']) |
| path_mappings.update(path_mappings_from_tsconfig(reference_path)) |
| |
| return path_mappings |
| |
| def optimize_js(primary_script, |
| js_out_file, |
| dep_tsconfigs, |
| skip_minification, |
| output_name): |
| """Creates a single output JavaScript file from `primary_script` (and any |
| imported scripts) and writes it out to `js_out_file`. Script imports will be |
| resolved by searching for the script path in the tsconfigs files passed to |
| `dep_tsconfigs` to locate the source file. The output script will be minimized |
| unless `skip_minification` is true.""" |
| |
| path_mappings = {} |
| if dep_tsconfigs != None: |
| for tsconfig_path in dep_tsconfigs: |
| path_mappings.update(path_mappings_from_tsconfig(tsconfig_path)) |
| |
| output_script_dir = os.path.dirname(js_out_file) |
| |
| os.makedirs(output_script_dir, exist_ok=True) |
| with tempfile.TemporaryDirectory(dir = output_script_dir) as tmp_out_dir: |
| |
| rollup_config_file = \ |
| os.path.join(tmp_out_dir, 'rollup.config.mjs') |
| create_rollup_config( |
| rollup_config_file, |
| os.path.abspath(_SRC_PATH), |
| path_mappings) |
| rollup_processed_file = \ |
| os.path.join(tmp_out_dir, os.path.basename(js_out_file)) |
| |
| rollup_command = [node_modules.PathToRollup(), |
| primary_script, |
| '--format', |
| 'iife', |
| '--no-strict', |
| '--config', |
| rollup_config_file, |
| '--failAfterWarnings', # Ensures script imports are correctly resolved |
| '--file', |
| rollup_processed_file, |
| '--silent', |
| ] |
| |
| if output_name != None: |
| rollup_command += ['--name', output_name] |
| |
| node.RunNode(rollup_command) |
| |
| with open(rollup_processed_file, 'r', encoding='utf-8') as f: |
| output = f.read() |
| assert "<if expr" not in output, \ |
| 'Unexpected <if expr> found in bundled output. Check that all ' + \ |
| 'input files using such expressions are preprocessed.' |
| |
| if skip_minification: |
| shutil.copy(rollup_processed_file, js_out_file) |
| else: |
| node.RunNode([ |
| node_modules.PathToTerser(), |
| rollup_processed_file, |
| '--mangle', |
| '--output', |
| js_out_file, |
| ]) |
| |
| def main(argv): |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| '--primary-script', |
| required=True, |
| help="Path to the entry point javascript file to compile") |
| parser.add_argument( |
| '--js-out-file', |
| required=True, |
| help="Path to the output file for the compiled JavaScript to be written") |
| parser.add_argument( |
| '--dep-tsconfigs', |
| required=False, |
| nargs='*', |
| default = None, |
| help="Dependent tsconfig files to locate script imports.") |
| parser.add_argument( |
| '--skip-minification', |
| default=False, |
| action='store_true') |
| parser.add_argument( |
| '--output-name', |
| required=False, |
| default = None, |
| help="The name for the exported symbols") |
| args = parser.parse_args(argv) |
| |
| primary_script = os.path.abspath(args.primary_script) |
| js_out_file = os.path.abspath(args.js_out_file) |
| |
| optimize_js(primary_script, js_out_file, |
| args.dep_tsconfigs, args.skip_minification, args.output_name) |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |