Commit 5d96fd38 authored by rbpotter's avatar rbpotter Committed by Commit Bot

WebUI: Update optimize_webui() to handle Polymer3

Use rollup along with existing Polymer CSS build and Uglify tools
to bundle Polymer 3 pages in optimized builds.

This will be used by the extensions Web UI in a followup CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1817293

Bug: 965770
Change-Id: Id4538b5628d1d59824300598082d89ea316eb628
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1846752
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709327}
parent 7016a4c2
...@@ -74,6 +74,11 @@ template("optimize_webui") { ...@@ -74,6 +74,11 @@ template("optimize_webui") {
invoker.insert_in_head, invoker.insert_in_head,
] ]
} }
if (defined(invoker.js_module_in_files)) {
inputs += [ "//chrome/browser/resources/tools/rollup_plugin.js" ]
args += [ "--js_module_in_files" ] + invoker.js_module_in_files
}
} }
} }
......
This diff is collapsed.
...@@ -44,11 +44,12 @@ class OptimizeWebUiTest(unittest.TestCase): ...@@ -44,11 +44,12 @@ class OptimizeWebUiTest(unittest.TestCase):
assert self._out_folder assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read() return open(os.path.join(self._out_folder, file_name), 'r').read()
def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file): def _run_optimize(self, depfile, html_in_file, html_out_file, js_out_file,
js_module_in_file):
# TODO(dbeam): make it possible to _run_optimize twice? Is that useful? # TODO(dbeam): make it possible to _run_optimize twice? Is that useful?
assert not self._out_folder assert not self._out_folder
self._out_folder = self._create_tmp_dir() self._out_folder = self._create_tmp_dir()
optimize_webui.main([ args = [
'--depfile', os.path.join(self._out_folder,'depfile.d'), '--depfile', os.path.join(self._out_folder,'depfile.d'),
'--html_in_files', html_in_file, '--html_in_files', html_in_file,
'--html_out_files', html_out_file, '--html_out_files', html_out_file,
...@@ -56,7 +57,10 @@ class OptimizeWebUiTest(unittest.TestCase): ...@@ -56,7 +57,10 @@ class OptimizeWebUiTest(unittest.TestCase):
'--input', self._tmp_src_dir, '--input', self._tmp_src_dir,
'--js_out_files', js_out_file, '--js_out_files', js_out_file,
'--out_folder', self._out_folder, '--out_folder', self._out_folder,
]) ]
if (js_module_in_file):
args += [ '--js_module_in_files', js_module_in_file ];
optimize_webui.main(args)
def _write_files_to_src_dir(self): def _write_files_to_src_dir(self):
self._write_file_to_src_dir('element.html', '<div>got here!</div>') self._write_file_to_src_dir('element.html', '<div>got here!</div>')
...@@ -69,6 +73,45 @@ class OptimizeWebUiTest(unittest.TestCase): ...@@ -69,6 +73,45 @@ class OptimizeWebUiTest(unittest.TestCase):
<link rel="import" href="element.html"> <link rel="import" href="element.html">
<link rel="import" href="element_in_dir/element_in_dir.html"> <link rel="import" href="element_in_dir/element_in_dir.html">
<script src="element.js"></script> <script src="element.js"></script>
''')
def _write_v3_files_to_src_dir(self):
self._write_file_to_src_dir('element.js', "alert('yay');")
self._write_file_to_src_dir('element_in_dir/element_in_dir.js',
"alert('hello from element_in_dir');")
self._write_file_to_src_dir('ui.js', '''
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''')
def _write_v3_files_with_resources_to_src_dir(self):
resources_path = os.path.join(
_HERE_DIR.replace('\\', '/'), 'gen', 'ui', 'webui', 'resources', 'js',
'fake_resource.m.js')
os.makedirs(os.path.dirname(resources_path))
self._tmp_dirs.append('gen')
with open(resources_path, 'w') as tmp_file:
tmp_file.write("alert('hello from shared resource');")
self._write_file_to_src_dir('element.js', '''
import 'chrome://resources/js/action_link.js';
alert('yay');
''')
self._write_file_to_src_dir('element_in_dir/element_in_dir.js', '''
import {foo} from 'chrome://resources/js/fake_resource.m.js';
import '../strings.m.js';
alert('hello from element_in_dir');
''')
self._write_file_to_src_dir('ui.js', '''
import 'chrome://fake-host/strings.m.js';
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''') ''')
def _check_output_html(self, out_html): def _check_output_html(self, out_html):
...@@ -83,27 +126,71 @@ class OptimizeWebUiTest(unittest.TestCase): ...@@ -83,27 +126,71 @@ class OptimizeWebUiTest(unittest.TestCase):
self.assertIn('yay', fast_js) self.assertIn('yay', fast_js)
self.assertIn('hello from element_in_dir', fast_js) self.assertIn('hello from element_in_dir', fast_js)
def _check_output_depfile(self): def _check_output_depfile(self, has_html):
depfile_d = self._read_out_file('depfile.d') depfile_d = self._read_out_file('depfile.d')
self.assertIn('element.html', depfile_d)
self.assertIn('element.js', depfile_d) self.assertIn('element.js', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.html'),
depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'), self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
depfile_d) depfile_d)
if (has_html):
self.assertIn('element.html', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.html'),
depfile_d)
def testSimpleOptimize(self): def testSimpleOptimize(self):
self._write_files_to_src_dir() self._write_files_to_src_dir()
self._run_optimize(depfile='depfile.d', self._run_optimize(depfile='depfile.d',
html_in_file='ui.html', html_in_file='ui.html',
html_out_file='fast.html', html_out_file='fast.html',
js_out_file='fast.js') js_out_file='fast.js',
js_module_in_file='')
fast_html = self._read_out_file('fast.html') fast_html = self._read_out_file('fast.html')
self._check_output_html(fast_html) self._check_output_html(fast_html)
self.assertIn('<script src="fast.js"></script>', fast_html) self.assertIn('<script src="fast.js"></script>', fast_html)
self._check_output_js() self._check_output_js()
self._check_output_depfile() self._check_output_depfile(True)
def testV3SimpleOptimize(self):
self._write_v3_files_to_src_dir()
self._run_optimize(depfile='depfile.d',
html_in_file='ui.html',
html_out_file='fast.html',
js_out_file='fast.js',
js_module_in_file='ui.js')
fast_html = self._read_out_file('fast.html')
self.assertIn('<script type="module" src="fast.js"></script>', fast_html)
self.assertNotIn('<script type="module" src="ui.js"></script>', fast_html)
self._check_output_js()
self._check_output_depfile(False)
def testV3OptimizeWithResources(self):
self._write_v3_files_with_resources_to_src_dir()
self._run_optimize(depfile='depfile.d',
html_in_file='ui.html',
html_out_file='fast.html',
js_out_file='fast.js',
js_module_in_file='ui.js')
fast_html = self._read_out_file('fast.html')
self.assertIn('<script type="module" src="fast.js"></script>', fast_html)
self.assertNotIn('<script type="module" src="ui.js"></script>', fast_html)
fast_js = self._read_out_file('fast.js')
self.assertIn('yay', fast_js)
self.assertIn('hello from element_in_dir', fast_js)
self.assertIn('hello from shared resource', fast_js)
depfile_d = self._read_out_file('depfile.d')
self.assertIn('element.js', depfile_d)
self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
depfile_d)
self.assertIn(
os.path.normpath('../../../../ui/webui/resources/js/action_link.js'),
depfile_d)
self.assertIn(
os.path.normpath('../gen/ui/webui/resources/js/fake_resource.m.js'),
depfile_d)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
// Copyright 2019 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.
/**
* @fileoverview Plugin for rollup to correctly resolve resources.
*/
const path = require('path');
const chromeResourcesUrl = 'chrome://resources/';
const polymerUrl = 'chrome://resources/polymer/v3_0/';
// TODO: Determine whether it is worth maintaining this list vs always checking
// both directories for the existence of a file.
const nonGeneratedFiles = ['cr.m.js', 'action_link.js'];
function normalizeSlashes(filepath) {
return filepath.replace(/\\/gi, '/');
}
function relativePath(from, to) {
return normalizeSlashes(path.relative(from, to));
}
function joinPaths(a, b) {
return normalizeSlashes(path.join(a, b));
}
/**
* Determines the path to |source| from the root directory based on the origin
* of the request for it. For example, if ../a/b.js is requested from
* c/d/e/f.js, the returned path will be c/d/a/b.js.
* @param {string} origin The origin of the request.
* @param {string} source The requested resource
* @return {string} Path to source from the root directory.
*/
function combinePaths(origin, source) {
const originDir = origin ? path.dirname(origin) : '';
return normalizeSlashes(path.normalize(path.join(originDir, source)));
}
export default function plugin(srcPath, genPath, rootPath, host, excludes) {
const resourcesSrcPath = joinPaths(srcPath, 'ui/webui/resources/');
const polymerSrcPath =
joinPaths(srcPath, 'third_party/polymer/v3_0/components-chromium/');
const resourcesGenPath = joinPaths(genPath, 'ui/webui/resources/');
const rootUrl = 'chrome://' + host + '/';
return {
name: 'webui-path-resolver-plugin',
resolveId(source, origin) {
// Normalize origin paths to use forward slashes.
if (origin) {
origin = normalizeSlashes(origin);
}
// Handle polymer resources
let pathFromPolymer = '';
if (source.startsWith(polymerUrl)) {
pathFromPolymer = source.slice(polymerUrl.length);
} else if (!!origin && origin.startsWith(polymerSrcPath)) {
pathFromPolymer =
combinePaths(relativePath(polymerSrcPath, origin), source);
}
if (pathFromPolymer) {
const fullPath = polymerUrl + pathFromPolymer;
if (excludes.includes(fullPath)) {
return {id: fullPath, external: true};
}
return joinPaths(polymerSrcPath, pathFromPolymer);
}
// Get path from ui/webui/resources
let pathFromResources = '';
if (source.startsWith(chromeResourcesUrl)) {
pathFromResources = source.slice(chromeResourcesUrl.length);
} else if (!!origin && origin.startsWith(resourcesSrcPath)) {
pathFromResources =
combinePaths(relativePath(resourcesSrcPath, origin), source);
} else if (!!origin && origin.startsWith(resourcesGenPath)) {
pathFromResources =
combinePaths(relativePath(resourcesGenPath, origin), source);
}
// Add prefix
if (pathFromResources) {
const fullPath = chromeResourcesUrl + pathFromResources;
if (excludes.includes(fullPath)) {
return {id: fullPath, external: true};
}
const filename = path.basename(source);
return joinPaths(
nonGeneratedFiles.includes(filename) ? resourcesSrcPath :
resourcesGenPath,
pathFromResources);
}
// Not a resources or polymer path -> should be in the root directory.
// Check if it should be excluded from the bundle.
const fullSourcePath = combinePaths(origin, source);
if (fullSourcePath.startsWith(rootPath)) {
const pathFromRoot = relativePath(rootPath, fullSourcePath);
if (excludes.includes(pathFromRoot)) {
return {id: rootUrl + pathFromRoot, external: true};
}
}
return null;
},
};
}
...@@ -30,6 +30,10 @@ def PathToPolymerCssBuild(): ...@@ -30,6 +30,10 @@ def PathToPolymerCssBuild():
return _path_in_node_modules('polymer-css-build', 'bin', 'polymer-css-build') return _path_in_node_modules('polymer-css-build', 'bin', 'polymer-css-build')
def PathToRollup():
return _path_in_node_modules('rollup', 'dist', 'bin', 'rollup')
def PathToSvgo(): def PathToSvgo():
return _path_in_node_modules('svgo', 'bin', 'svgo') return _path_in_node_modules('svgo', 'bin', 'svgo')
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
# encountering any dependency to that file. # encountering any dependency to that file.
import argparse import argparse
import io
import os import os
import re import re
import sys import sys
...@@ -478,9 +479,11 @@ def main(argv): ...@@ -478,9 +479,11 @@ def main(argv):
result = _process_v3_ready(js_file, html_file) result = _process_v3_ready(js_file, html_file)
# Reconstruct file. # Reconstruct file.
with open(os.path.join(out_folder, result[1]), 'w') as f: # Specify the newline character so that the exact same file is generated
# across platforms.
with io.open(os.path.join(out_folder, result[1]), 'w', newline='\n') as f:
for l in result[0]: for l in result[0]:
f.write(l) f.write(unicode(l, 'utf-8'))
return return
......
...@@ -23,7 +23,7 @@ class PolymerModulizerTest(unittest.TestCase): ...@@ -23,7 +23,7 @@ class PolymerModulizerTest(unittest.TestCase):
def _read_out_file(self, file_name): def _read_out_file(self, file_name):
assert self._out_folder assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read() return open(os.path.join(self._out_folder, file_name), 'rb').read()
def _run_test(self, html_type, html_file, js_file, def _run_test(self, html_type, html_file, js_file,
js_out_file, js_file_expected): js_out_file, js_file_expected):
...@@ -44,7 +44,7 @@ class PolymerModulizerTest(unittest.TestCase): ...@@ -44,7 +44,7 @@ class PolymerModulizerTest(unittest.TestCase):
actual_js = self._read_out_file(js_out_file) actual_js = self._read_out_file(js_out_file)
expected_js = open(os.path.join( expected_js = open(os.path.join(
_HERE_DIR, 'tests', js_file_expected), 'r').read() _HERE_DIR, 'tests', js_file_expected), 'rb').read()
self.assertEquals(expected_js, actual_js) self.assertEquals(expected_js, actual_js)
# Test case where HTML is extracted from a Polymer2 <dom-module>. # Test case where HTML is extracted from a Polymer2 <dom-module>.
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
# polymer_modulizer.gni. # polymer_modulizer.gni.
import argparse import argparse
import io
import os import os
import re import re
import sys import sys
...@@ -118,9 +119,11 @@ def ProcessFile(filename, out_folder, namespace_rewrites): ...@@ -118,9 +119,11 @@ def ProcessFile(filename, out_folder, namespace_rewrites):
out_filename = os.path.splitext(os.path.basename(filename))[0] + '.m.js' out_filename = os.path.splitext(os.path.basename(filename))[0] + '.m.js'
# Reconstruct file. # Reconstruct file.
with open(os.path.join(out_folder, out_filename), 'w') as f: # Specify the newline character so that the exact same file is generated
# across platforms.
with io.open(os.path.join(out_folder, out_filename), 'w', newline='\n') as f:
for l in lines: for l in lines:
f.write(l) f.write(unicode(l, 'utf-8'))
return return
def main(argv): def main(argv):
......
...@@ -22,7 +22,7 @@ class JsModulizerTest(unittest.TestCase): ...@@ -22,7 +22,7 @@ class JsModulizerTest(unittest.TestCase):
def _read_out_file(self, file_name): def _read_out_file(self, file_name):
assert self._out_folder assert self._out_folder
return open(os.path.join(self._out_folder, file_name), 'r').read() return open(os.path.join(self._out_folder, file_name), 'rb').read()
def _run_test_(self, js_file, js_file_expected, namespace_rewrites=None): def _run_test_(self, js_file, js_file_expected, namespace_rewrites=None):
assert not self._out_folder assert not self._out_folder
...@@ -40,7 +40,7 @@ class JsModulizerTest(unittest.TestCase): ...@@ -40,7 +40,7 @@ class JsModulizerTest(unittest.TestCase):
js_out_file = os.path.basename(js_file).replace('.js', '.m.js') js_out_file = os.path.basename(js_file).replace('.js', '.m.js')
actual_js = self._read_out_file(js_out_file) actual_js = self._read_out_file(js_out_file)
expected_js = open( expected_js = open(
os.path.join(_HERE_DIR, 'tests', js_file_expected), 'r').read() os.path.join(_HERE_DIR, 'tests', js_file_expected), 'rb').read()
self.assertEquals(expected_js, actual_js) self.assertEquals(expected_js, actual_js)
def testSuccess_WithoutCrDefine(self): def testSuccess_WithoutCrDefine(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment