diff --git a/examples/app/BUILD.bazel b/examples/app/BUILD.bazel index 7c62a5b282..b91810de00 100644 --- a/examples/app/BUILD.bazel +++ b/examples/app/BUILD.bazel @@ -19,6 +19,7 @@ ts_devserver( rollup_bundle( name = "bundle", + enable_code_splitting = False, entry_point = ":app.ts", deps = [":app"], ) diff --git a/examples/protocol_buffers/BUILD.bazel b/examples/protocol_buffers/BUILD.bazel index 27804f3a03..e5f5f6a690 100644 --- a/examples/protocol_buffers/BUILD.bazel +++ b/examples/protocol_buffers/BUILD.bazel @@ -70,6 +70,7 @@ ts_devserver( # Test for production mode rollup_bundle( name = "bundle", + enable_code_splitting = False, entry_point = ":app.ts", # TODO(alexeagle): we should be able to get this from //:protobufjs_bootstrap_scripts # and automatically plumb it through to Rollup. diff --git a/internal/e2e/rollup/BUILD.bazel b/internal/e2e/rollup/BUILD.bazel index ab0b519700..bfd53bb355 100644 --- a/internal/e2e/rollup/BUILD.bazel +++ b/internal/e2e/rollup/BUILD.bazel @@ -4,6 +4,7 @@ load("//:defs.bzl", "rollup_bundle") rollup_bundle( name = "bundle", srcs = ["bar.js"], + enable_code_splitting = False, entry_point = ":foo.js", globals = {"some_global_var": "runtime_name_of_global_var"}, license_banner = ":license.txt", diff --git a/internal/e2e/rollup_code_splitting/BUILD.bazel b/internal/e2e/rollup_code_splitting/BUILD.bazel index 6b10131349..2ad957c97a 100644 --- a/internal/e2e/rollup_code_splitting/BUILD.bazel +++ b/internal/e2e/rollup_code_splitting/BUILD.bazel @@ -7,6 +7,17 @@ rollup_bundle( ["*.js"], exclude = ["*.spec.js"], ), + enable_code_splitting = True, + entry_point = ":main1.js", + license_banner = ":license.txt", +) + +rollup_bundle( + name = "bundle_multi_entry", + srcs = glob( + ["*.js"], + exclude = ["*.spec.js"], + ), additional_entry_points = ["build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/additional_entry.js"], entry_point = ":main1.js", license_banner = ":license.txt", @@ -16,15 +27,19 @@ jasmine_node_test( name = "test", srcs = [ "additional_entry.spec.js", + "code_splitting.spec.js", "main1.spec.js", ], data = glob([ "goldens/*", ]) + [ ":bundle", + ":bundle_multi_entry", ":bundle.min.js", ":bundle.min.es2015.js", ], + # TODO(alexeagle): update the test logic for directory lookups vs runfiles manifest + tags = ["fix-windows"], deps = [ "//internal/e2e:check_lib", "@npm//unidiff", @@ -49,6 +64,12 @@ filegroup( output_group = "es5_min_debug", ) +filegroup( + name = "bundle_multi_entry-outputgroups-es2015", + srcs = [":bundle_multi_entry"], + output_group = "es2015", +) + jasmine_node_test( name = "test-outputgroups", srcs = ["outputgroups.spec.js"], @@ -56,5 +77,8 @@ jasmine_node_test( ":bundle-outputgroups-es2015", ":bundle-outputgroups-es5_min", ":bundle-outputgroups-es5_min_debug", + ":bundle_multi_entry-outputgroups-es2015", ], + # TODO(alexeagle): update the test logic for directory lookups vs runfiles manifest + tags = ["fix-windows"], ) diff --git a/internal/e2e/rollup_code_splitting/additional_entry.spec.js b/internal/e2e/rollup_code_splitting/additional_entry.spec.js index c1598de5be..6d8b32e54e 100644 --- a/internal/e2e/rollup_code_splitting/additional_entry.spec.js +++ b/internal/e2e/rollup_code_splitting/additional_entry.spec.js @@ -1,55 +1,62 @@ check = require('../check.js'); const fs = require('fs'); -const expected = 'lib1 fn,dep3 fn,lib2 fn,dep2 fn'; -const path = __dirname; +const expected = 'dep4 fn'; +const path = require('path'); + +describe('bundling chunks', () => { + function findChunk() { + let chunks = fs.readdirSync(path.join(__dirname, 'bundle_chunks')) + .filter(name => name.startsWith('chunk-') && name.endsWith('.js')); + if (chunks.length != 1) { + fail('Not 1 chunk ' + chunks); + } + return chunks[0]; + } -describe('bundling additional entry point', () => { it('should work', () => { - check(path, 'bundle.min.js', 'goldens/bundle.min.js_'); - check(path, 'bundle.min.es2015.js', 'goldens/bundle.min.es2015.js_'); + check(__dirname, 'bundle.min.js', 'goldens/bundle.min.js_'); + check(__dirname, 'bundle.min.es2015.js', 'goldens/bundle.min.es2015.js_'); }); // Disabled because native ESModules can't be loaded in current nodejs // see https://github.com/bazelbuild/rules_nodejs/issues/593 xit('bundle_chunks_es6 should work', () => { const additional_entry = require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_es6/additional_entry.js'); - const actual = (new additional_entry()).test(); + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_es6/' + + findChunk()); + const actual = additional_entry.fn(); expect(actual).toEqual(expected); }); it('bundle_chunks should work', () => { - const additional_entry = - require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks/additional_entry.js') - .default; - const actual = (new additional_entry()).test(); + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks/' + findChunk()); + const actual = additional_entry.fn(); expect(actual).toEqual(expected); }); it('bundle_chunks_min should work', () => { - const additional_entry = - require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min/additional_entry.js') - .default; - const actual = (new additional_entry()).test(); + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min/' + + findChunk()); + const actual = additional_entry.fn(); expect(actual).toEqual(expected); }); it('bundle_chunks_min_debug should work', () => { - const additional_entry = - require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/additional_entry.js') - .default; - const actual = (new additional_entry()).test(); + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/' + + findChunk()); + const actual = additional_entry.fn(); expect(actual).toEqual(expected); }); it('should have a license header', () => { const content = fs.readFileSync( require.resolve( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/additional_entry.js'), + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/' + + findChunk()), {encoding: 'utf-8'}); expect(content).toContain('dummy license banner'); }); -}); \ No newline at end of file +}); diff --git a/internal/e2e/rollup_code_splitting/code_splitting.spec.js b/internal/e2e/rollup_code_splitting/code_splitting.spec.js new file mode 100644 index 0000000000..2d540cda2f --- /dev/null +++ b/internal/e2e/rollup_code_splitting/code_splitting.spec.js @@ -0,0 +1,63 @@ +check = require('../check.js'); +const fs = require('fs'); +const expected = 'dep4 fn'; +const path = require('path'); + +describe('code splitting', () => { + function findChunk() { + let chunks = fs.readdirSync(path.join(__dirname, 'bundle_chunks')) + .filter(name => name.startsWith('chunk-') && name.endsWith('.js')); + if (chunks.length != 1) { + fail('Not 1 chunk ' + chunks); + } + return chunks[0]; + } + + it('should work', () => { + check(__dirname, 'bundle.min.js', 'goldens/bundle.min.js_'); + check(__dirname, 'bundle.min.es2015.js', 'goldens/bundle.min.es2015.js_'); + check(__dirname, 'bundle_multi_entry.min.js', 'goldens/bundle_multi_entry.min.js_'); + }); + + // Disabled because native ESModules can't be loaded in current nodejs + // see https://github.com/bazelbuild/rules_nodejs/issues/593 + xit('bundle_chunks_es6 should work', () => { + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_es6/' + + findChunk()); + const actual = additional_entry.fn(); + expect(actual).toEqual(expected); + }); + + it('bundle_chunks should work', () => { + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks/' + findChunk()); + const actual = additional_entry.fn(); + expect(actual).toEqual(expected); + }); + + it('bundle_chunks_min should work', () => { + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min/' + + findChunk()); + const actual = additional_entry.fn(); + expect(actual).toEqual(expected); + }); + + it('bundle_chunks_min_debug should work', () => { + const additional_entry = require( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/' + + findChunk()); + const actual = additional_entry.fn(); + expect(actual).toEqual(expected); + }); + + it('should have a license header', () => { + const content = fs.readFileSync( + require.resolve( + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/' + + findChunk()), + {encoding: 'utf-8'}); + expect(content).toContain('dummy license banner'); + }); +}); diff --git a/internal/e2e/rollup_code_splitting/dep4.js b/internal/e2e/rollup_code_splitting/dep4.js index 3710e3f1c2..1e5d132404 100644 --- a/internal/e2e/rollup_code_splitting/dep4.js +++ b/internal/e2e/rollup_code_splitting/dep4.js @@ -1,3 +1,3 @@ export function fn() { return 'dep4 fn'; -} \ No newline at end of file +} diff --git a/internal/e2e/rollup_code_splitting/dynamic.js b/internal/e2e/rollup_code_splitting/dynamic.js index 66a56282e3..1c5add5de8 100644 --- a/internal/e2e/rollup_code_splitting/dynamic.js +++ b/internal/e2e/rollup_code_splitting/dynamic.js @@ -2,4 +2,4 @@ export function dynamic() { return import('./dep4.js').then(dep4 => { return dep4.fn(); }); -} \ No newline at end of file +} diff --git a/internal/e2e/rollup_code_splitting/goldens/bundle.min.js_ b/internal/e2e/rollup_code_splitting/goldens/bundle.min.js_ index 925ad39265..c2f9456f32 100755 --- a/internal/e2e/rollup_code_splitting/goldens/bundle.min.js_ +++ b/internal/e2e/rollup_code_splitting/goldens/bundle.min.js_ @@ -3,7 +3,7 @@ (function(global) { System.config({ packages: { - '': {map: {"./main1": "bundle_chunks_min/main1", "./additional_entry.js": "bundle_chunks_min/additional_entry.js"}, defaultExtension: 'js'}, + '': {map: {"./main1": "bundle_chunks_min/main1"}, defaultExtension: 'js'}, } }); System.import('main1.js').catch(function(err) { diff --git a/internal/e2e/rollup_code_splitting/goldens/bundle_multi_entry.min.js_ b/internal/e2e/rollup_code_splitting/goldens/bundle_multi_entry.min.js_ new file mode 100755 index 0000000000..c57b7a4305 --- /dev/null +++ b/internal/e2e/rollup_code_splitting/goldens/bundle_multi_entry.min.js_ @@ -0,0 +1,12 @@ +// SystemJS boilerplate/entry point TEMPLATE for code-split rollup_bundle. +// GENERATED BY Bazel +(function(global) { +System.config({ + packages: { + '': {map: {"./main1": "bundle_multi_entry_chunks_min/main1", "./additional_entry.js": "bundle_multi_entry_chunks_min/additional_entry.js"}, defaultExtension: 'js'}, + } +}); +System.import('main1.js').catch(function(err) { + console.error(err); +}); +})(this); diff --git a/internal/e2e/rollup_code_splitting/main1.spec.js b/internal/e2e/rollup_code_splitting/main1.spec.js index 96cbc9f483..28390a510e 100644 --- a/internal/e2e/rollup_code_splitting/main1.spec.js +++ b/internal/e2e/rollup_code_splitting/main1.spec.js @@ -3,38 +3,36 @@ const exportedTest1 = 'dep1 fn,lib2 fn,dep2 fn'; const exportedTest2 = 'dep4 fn'; describe('bundling main entry point', () => { + async function checkAllTests(main1) { + expect(main1.test()).toEqual(exportedTest1); + const actualTest2 = await main1.test2(); + expect(actualTest2).toEqual(exportedTest2); + } + // Disabled since native ESModules can't be loaded in nodejs yet // https://github.com/bazelbuild/rules_nodejs/issues/593 xit('bundle_chunks_es6 should work', async () => { const main1 = require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_es6/main1.js') - expect(main1.test()).toEqual(exportedTest1); - const actualTest2 = await main1.test2(); - expect(actualTest2).toEqual(exportedTest2); + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_es6/main1.js'); + checkAllTests(main1); }); it('bundle_chunks should work', async () => { const main1 = require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks/main1.js') - expect(main1.test()).toEqual(exportedTest1); - const actualTest2 = await main1.test2(); - expect(actualTest2).toEqual(exportedTest2); + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks/main1.js'); + checkAllTests(main1); }); it('bundle_chunks_min should work', async () => { const main1 = require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min/main1.js') - expect(main1.test()).toEqual(exportedTest1); - const actualTest2 = await main1.test2(); - expect(actualTest2).toEqual(exportedTest2); + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min/main1.js'); + checkAllTests(main1); }); it('bundle_chunks_min_debug should work', async () => { const main1 = require( - 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/main1.js') - expect(main1.test()).toEqual(exportedTest1); - const actualTest2 = await main1.test2(); - expect(actualTest2).toEqual(exportedTest2); + 'build_bazel_rules_nodejs/internal/e2e/rollup_code_splitting/bundle_chunks_min_debug/main1.js'); + checkAllTests(main1); }); it('should have a license header', () => { @@ -44,4 +42,4 @@ describe('bundling main entry point', () => { {encoding: 'utf-8'}); expect(content).toContain('dummy license banner'); }); -}); \ No newline at end of file +}); diff --git a/internal/e2e/rollup_code_splitting/outputgroups.spec.js b/internal/e2e/rollup_code_splitting/outputgroups.spec.js index ac30a46053..118ec42a26 100644 --- a/internal/e2e/rollup_code_splitting/outputgroups.spec.js +++ b/internal/e2e/rollup_code_splitting/outputgroups.spec.js @@ -7,29 +7,45 @@ function checkExists(name) { } } +// With enable_code_splitting=True there should be 1 chunk. We +// don't know its name ahead of time so just assert the count. +function checkChunkCount(name) { + expect(fs.readdirSync(path.join(__dirname, name)) + .filter(name => name.startsWith('chunk-') && name.endsWith('.js')) + .length) + .toBe(1); +} + // TODO: the right assertions are to load up the source-map library // and assert that the sourcemap actually maps back to the sources describe('outputgroups', () => { it('should produce a es2015 sourcemap', () => { checkExists('bundle.es2015.js'); - checkExists('bundle_chunks_es2015/additional_entry.js'); - checkExists('bundle_chunks_es2015/additional_entry.js.map'); checkExists('bundle_chunks_es2015/main1.js'); checkExists('bundle_chunks_es2015/main1.js.map'); + checkChunkCount('bundle_chunks_es2015'); }); it('should produce a es5_min sourcemap', () => { checkExists('bundle.min.js'); - checkExists('bundle_chunks_min/additional_entry.js'); - checkExists('bundle_chunks_min/additional_entry.js.map'); checkExists('bundle_chunks_min/main1.js'); checkExists('bundle_chunks_min/main1.js.map'); + checkChunkCount('bundle_chunks_min'); }); it('should produce a es5_min_debug sourcemap', () => { checkExists('bundle.min_debug.js'); - checkExists('bundle_chunks_min_debug/additional_entry.js'); - checkExists('bundle_chunks_min_debug/additional_entry.js.map'); checkExists('bundle_chunks_min_debug/main1.js'); checkExists('bundle_chunks_min_debug/main1.js.map'); + checkChunkCount('bundle_chunks_min_debug'); + }); +}); + +describe('outputgroups multi entry', () => { + it('should produce a es2015 sourcemap', () => { + checkExists('bundle_multi_entry.es2015.js'); + checkExists('bundle_multi_entry_chunks_es2015/additional_entry.js'); + checkExists('bundle_multi_entry_chunks_es2015/additional_entry.js.map'); + checkExists('bundle_multi_entry_chunks_es2015/main1.js'); + checkExists('bundle_multi_entry_chunks_es2015/main1.js.map'); }); }); diff --git a/internal/e2e/rollup_fine_grained_deps/BUILD.bazel b/internal/e2e/rollup_fine_grained_deps/BUILD.bazel index c88a82a272..d0611dadf6 100644 --- a/internal/e2e/rollup_fine_grained_deps/BUILD.bazel +++ b/internal/e2e/rollup_fine_grained_deps/BUILD.bazel @@ -5,6 +5,7 @@ load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test") # and no fine grained deps rollup_bundle( name = "bundle_no_deps", + enable_code_splitting = False, entry_point = ":no-deps.js", ) @@ -12,6 +13,7 @@ rollup_bundle( # and fine grained deps rollup_bundle( name = "bundle", + enable_code_splitting = False, entry_point = ":has-deps.js", deps = [ "@fine_grained_deps_yarn//@gregmagolan/test-a", @@ -23,6 +25,7 @@ rollup_bundle( # and no fine grained deps rollup_bundle( name = "bundle_legacy", + enable_code_splitting = False, entry_point = ":has-deps.js", node_modules = "@fine_grained_deps_yarn//:node_modules", ) @@ -31,6 +34,7 @@ rollup_bundle( # and fine grained deps so long as they come from the same root rollup_bundle( name = "bundle_hybrid", + enable_code_splitting = False, entry_point = ":has-deps.js", node_modules = "@fine_grained_deps_yarn//:node_modules", deps = [ diff --git a/internal/rollup/rollup.config.js b/internal/rollup/rollup.config.js index 720b5f6e14..65ab7988ee 100644 --- a/internal/rollup/rollup.config.js +++ b/internal/rollup/rollup.config.js @@ -163,7 +163,7 @@ function notResolved(importee, importer) { } const inputs = [TMPL_inputs]; -const enableCodeSplitting = inputs.length > 1; +const multipleInputs = inputs.length > 1; const config = { resolveBazel, @@ -189,15 +189,11 @@ const config = { // with the amd plugin. include: /\.ngfactory\.js$/i, }), - commonjs(), - { + commonjs(), { name: 'notResolved', resolveId: notResolved, }, - sourcemaps(), - rollupJson({ - preferConst: true - }) + sourcemaps(), rollupJson({preferConst: true}) ]), output: { banner, @@ -206,17 +202,16 @@ const config = { preserveSymlinks: true, } -if (enableCodeSplitting) { - config.experimentalCodeSplitting = true; - config.experimentalDynamicImport = true; +if (multipleInputs) { config.input = inputs; - if (process.env.ROLLUP_BUNDLE_FIXED_CHUNK_NAMES) { - config.output.chunkFileNames = '[name].js'; - } } else { config.input = inputs[0]; config.output['name'] = 'TMPL_global_name'; } +if (process.env.ROLLUP_BUNDLE_FIXED_CHUNK_NAMES) { + config.output.chunkFileNames = '[name].js'; +} + module.exports = config; diff --git a/internal/rollup/rollup_bundle.bzl b/internal/rollup/rollup_bundle.bzl index b424a13991..9354107ccd 100644 --- a/internal/rollup/rollup_bundle.bzl +++ b/internal/rollup/rollup_bundle.bzl @@ -449,8 +449,10 @@ def _generate_code_split_entry(ctx, bundles_folder, output): def _rollup_bundle(ctx): if len(ctx.attr.entry_point.files.to_list()) != 1: fail("labels in entry_point must contain exactly one file") + if ctx.attr.additional_entry_points and not ctx.attr.enable_code_splitting: + fail("cannot disable code splitting when there are multiple entry points") - if ctx.attr.additional_entry_points: + if ctx.attr.additional_entry_points or ctx.attr.enable_code_splitting: # Generate code split bundles if additional entry points have been specified. # See doc for additional_entry_points for more information. # Note: "_chunks" is needed on the output folders since ctx.label.name + ".es2015" is already @@ -615,6 +617,18 @@ ROLLUP_ATTRS = { It is sufficient to load one of these SystemJS boilerplate/entry point files as a script in your HTML to load your application""", ), + "enable_code_splitting": attr.bool( + doc = """If True rollup will automatically determine entry points from + the source code. The rollup output format will be 'esm' and rollup will + create entry points based on ES6 import statements. See + https://rollupjs.org/guide/en#code-splitting + + Code splitting is always enabled when additional_entry_points is + non-empty. + + All automatic entry points will be named chunk-.js.""", + default = True, + ), "entry_point": attr.label( doc = """The starting point of the application, passed as the `--input` flag to rollup. diff --git a/internal/web_package/test2/index_golden.html_ b/internal/web_package/test2/index_golden.html_ index dc9093f5c9..8f37f927a0 100644 --- a/internal/web_package/test2/index_golden.html_ +++ b/internal/web_package/test2/index_golden.html_ @@ -1,2 +1,2 @@ - \ No newline at end of file + diff --git a/internal/web_package/test2/spec.js b/internal/web_package/test2/spec.js index 2f6b2bbda4..ce7201361f 100644 --- a/internal/web_package/test2/spec.js +++ b/internal/web_package/test2/spec.js @@ -10,6 +10,6 @@ describe('web_package paths', () => { const actual = fs.readFileSync(require.resolve(output), {encoding: 'utf-8'}); const expected = fs.readFileSync(require.resolve(golden), {encoding: 'utf-8'}); // make the input hermetic by replacing the cache-buster timestamp - expect(actual.replace(/\?v=\d+/g, '?v=123')).toBe(expected); + expect(actual.replace(/\?v=\d+/g, '?v=123').trim()).toBe(expected.trim()); }); }); diff --git a/packages/protractor/test/protractor-2/BUILD.bazel b/packages/protractor/test/protractor-2/BUILD.bazel index f8ac7b9a36..f37194dc1f 100644 --- a/packages/protractor/test/protractor-2/BUILD.bazel +++ b/packages/protractor/test/protractor-2/BUILD.bazel @@ -24,6 +24,7 @@ ts_devserver( rollup_bundle( name = "bundle", + enable_code_splitting = False, entry_point = ":app.ts", deps = [":app"], )