22
33load ("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl" , "module_mappings_aspect" , "register_node_modules_linker" )
44
5+ _DOC = """Runs the Rollup.js CLI under Bazel.
6+
7+ See https://rollupjs.org/guide/en/#command-line-reference
8+
9+ Typical example:
10+ ```python
11+ load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
12+
13+ rollup_bundle(
14+ name = "bundle",
15+ srcs = ["dependency.js"],
16+ entry_point = "input.js",
17+ config_file = "rollup.config.js",
18+ )
19+ ```
20+
21+ Note that the command-line options set by Bazel override what appears in the rollup config file.
22+ This means that typically a single `rollup.config.js` can contain settings for your whole repo,
23+ and multiple `rollup_bundle` rules can share the configuration.
24+
25+ Thus, setting options that Bazel controls will have no effect, e.g.
26+
27+ ```javascript
28+ module.exports = {
29+ output: { file: 'this_is_ignored.js' },
30+ }
31+ ```
32+
33+ The rollup_bundle rule always produces a directory output, because it isn't known until
34+ rollup runs whether the output has many chunks or is a single file.
35+
36+ To get multiple output formats, wrap the rule with a macro or list comprehension, e.g.
37+
38+ ```python
39+ [
40+ rollup_bundle(
41+ name = "bundle.%s" % format,
42+ entry_point = "foo.js",
43+ format = format,
44+ )
45+ for format in [
46+ "cjs",
47+ "umd",
48+ ]
49+ ]
50+ ```
51+
52+ This will produce one output directory per requested format.
53+ """
54+
555_ROLLUP_ATTRS = {
656 "srcs" : attr .label_list (
7- doc = """JavaScript source files from the workspace.
57+ doc = """Non-entry point JavaScript source files from the workspace.
858
9- The file passed to entry_point is automatically added .
59+ You must not repeat file(s) passed to entry_point/entry_points .
1060""" ,
1161 # Don't try to constrain the filenames, could be json, svg, whatever
1262 allow_files = True ,
@@ -23,13 +73,65 @@ If not set, a default basic Rollup config is used.
2373 default = "@npm_bazel_rollup//:rollup.config.js" ,
2474 ),
2575 "entry_point" : attr .label (
26- doc = """The bundle's entry point(s) (e.g. your main.js or app.js or index.js).
76+ doc = """The bundle's entry point (e.g. your main.js or app.js or index.js).
77+
78+ This is just a shortcut for the `entry_points` attribute with a single output chunk named the same as the entry_point attribute.
79+
80+ For example, these are equivalent:
81+
82+ ```python
83+ rollup_bundle(
84+ name = "bundle",
85+ entry_point = "index.js",
86+ )
87+ ```
88+
89+ ```python
90+ rollup_bundle(
91+ name = "bundle",
92+ entry_points = {
93+ "index.js": "index"
94+ }
95+ )
96+ ```
97+
98+ If the entry_point attribute is instead a label that produces a single .js file,
99+ this will work, but the resulting output will be named after the label,
100+ so these are equivalent:
27101
28- Passed to the [`--input` option](https://github.com/rollup/rollup/blob/master/docs/999-big-list-of-options.md#input) in Rollup.
29- If you provide an array of entry points or an object mapping names to entry points, they will be bundled to separate output chunks.
102+ ```python
103+ # Outputs index.js
104+ produces_js(
105+ name = "producer",
106+ )
107+ rollup_bundle(
108+ name = "bundle",
109+ entry_point = "producer",
110+ )
111+ ```
112+
113+ ```python
114+ rollup_bundle(
115+ name = "bundle",
116+ entry_points = {
117+ "index.js": "producer"
118+ }
119+ )
120+ ```
30121""" ,
31- mandatory = True ,
32- allow_single_file = True ,
122+ allow_single_file = [".js" ],
123+ ),
124+ "entry_points" : attr .label_keyed_string_dict (
125+ doc = """The bundle's entry points (e.g. your main.js or app.js or index.js).
126+
127+ Passed to the [`--input` option](https://github.com/rollup/rollup/blob/master/docs/999-big-list-of-options.md#input) in Rollup.
128+
129+ Keys in this dictionary are labels pointing to .js entry point files.
130+ Values are the name to be given to the corresponding output chunk.
131+
132+ Either this attribute or `entry_point` must be specified, but not both.
133+ """ ,
134+ allow_files = [".js" ],
33135 ),
34136 "format" : attr .string (
35137 doc = """"Specifies the format of the generated bundle. One of the following:
@@ -79,12 +181,58 @@ Passed to the [`--sourcemap` option](https://github.com/rollup/rollup/blob/maste
79181def _chunks_dir_out (output_dir , name ):
80182 return output_dir if output_dir else name
81183
82- def _rollup_outs (sourcemap , name , entry_point , output_dir ):
83- # TODO: is it okay that entry_point.name includes extension?
84- # what if the label was blah.ts?
85- result = {"entry_point_chunk" : "/" .join ([_chunks_dir_out (output_dir , name ), entry_point .name ])}
86- if sourcemap :
87- result ["sourcemap" ] = "%s.map" % result ["entry_point_chunk" ]
184+ def _desugar_entry_point_names (entry_point , entry_points ):
185+ """Users can specify entry_point (sugar) or entry_points (long form).
186+
187+ This function allows our code to treat it like they always used the long form.
188+
189+ It also performs validation:
190+ - exactly one of these attributes should be specified
191+ """
192+ if entry_point and entry_points :
193+ fail ("Cannot specify both entry_point and entry_points" )
194+ if not entry_point and not entry_points :
195+ fail ("One of entry_point or entry_points must be specified" )
196+ if entry_point :
197+ name = entry_point .name
198+ if name .endswith (".js" ):
199+ name = name [:- 3 ]
200+ if name .endswith (".mjs" ):
201+ name = name [:- 4 ]
202+ return [name ]
203+ return entry_points .values ()
204+
205+ def _desugar_entry_points (entry_point , entry_points ):
206+ """Like above, but used by the implementation function, where the types differ.
207+
208+ It also performs validation:
209+ - attr.label_keyed_string_dict doesn't accept allow_single_file
210+ so we have to do validation now to be sure each key is a label resulting in one file
211+
212+ It converts from dict[target: string] to dict[file: string]
213+ """
214+ names = _desugar_entry_point_names (entry_point .label if entry_point else None , entry_points )
215+
216+ if entry_point :
217+ return {entry_point .files .to_list ()[0 ]: names [0 ]}
218+
219+ result = {}
220+ for ep in entry_points .items ():
221+ entry_point = ep [0 ]
222+ name = ep [1 ]
223+ f = entry_point .files .to_list ()
224+ if len (f ) != 1 :
225+ fail ("keys in rollup_bundle#entry_points must provide one file, but %s has %s" % (entry_point .label , len (f )))
226+ result [f [0 ]] = name
227+ return result
228+
229+ def _rollup_outs (sourcemap , name , entry_point , entry_points , output_dir ):
230+ """Supply some labelled outputs in the common case of a single entry point"""
231+ result = {}
232+ for out in _desugar_entry_point_names (entry_point , entry_points ):
233+ result [out ] = "/" .join ([_chunks_dir_out (output_dir , name ), out + ".js" ])
234+ if sourcemap :
235+ result [out + "_map" ] = "%s.map" % result [out ]
88236 return result
89237
90238def _no_ext (f ):
@@ -93,13 +241,18 @@ def _no_ext(f):
93241def _rollup_bundle (ctx ):
94242 "Generate a rollup config file and run rollup"
95243
96- inputs = [ ctx .file .entry_point ] + ctx .files .srcs + ctx .files .deps
97- outputs = [ctx .outputs . entry_point_chunk ]
244+ inputs = ctx .files .entry_point + ctx . files . entry_points + ctx .files .srcs + ctx .files .deps
245+ outputs = [getattr ( ctx .outputs , o ) for o in dir ( ctx . outputs ) ]
98246
99247 # See CLI documentation at https://rollupjs.org/guide/en/#command-line-reference
100248 args = ctx .actions .args ()
101249
102- args .add_all (["--input" , _no_ext (ctx .file .entry_point )])
250+ # List entry point argument first to save some argv space
251+ # Rollup doc says
252+ # When provided as the first options, it is equivalent to not prefix them with --input
253+ for entry_point in _desugar_entry_points (ctx .attr .entry_point , ctx .attr .entry_points ).items ():
254+ args .add_joined ([entry_point [1 ], _no_ext (entry_point [0 ])], join_with = "=" )
255+
103256 args .add_all (["--format" , ctx .attr .format ])
104257
105258 # Assume we always want to generate chunked output, so supply output.dir rather than output.file
@@ -131,7 +284,6 @@ def _rollup_bundle(ctx):
131284
132285 if (ctx .attr .sourcemap ):
133286 args .add ("--sourcemap" )
134- outputs .append (ctx .outputs .sourcemap )
135287
136288 if ctx .attr .globals :
137289 args .add ("--external" )
@@ -140,42 +292,15 @@ def _rollup_bundle(ctx):
140292 args .add_joined (["%s:%s" % g for g in ctx .attr .globals .items ()], join_with = "," )
141293
142294 ctx .actions .run (
143- progress_message = "Bundling JavaScript %s [rollup]" % ctx . outputs . entry_point_chunk .short_path ,
295+ progress_message = "Bundling JavaScript %s [rollup]" % out_dir .short_path ,
144296 executable = ctx .executable .rollup_bin ,
145297 inputs = inputs ,
146298 outputs = outputs ,
147299 arguments = [args ],
148300 )
149301
150302rollup_bundle = rule (
151- doc = """Runs the Rollup.js CLI under Bazel.
152-
153- See https://rollupjs.org/guide/en/#command-line-reference
154-
155- Typical example:
156- ```python
157- load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
158-
159- rollup_bundle(
160- name = "bundle",
161- srcs = ["dependency.js"],
162- entry_point = "input.js",
163- config_file = "rollup.config.js",
164- )
165- ```
166-
167- Note that the command-line options set by Bazel override what appears in the rollup config file.
168- This means that typically a single `rollup.config.js` can contain settings for your whole repo,
169- and multiple `rollup_bundle` rules can share the configuration.
170-
171- Thus, setting options that Bazel controls will have no effect, e.g.
172-
173- ```javascript
174- module.exports = {
175- output: { file: 'this_is_ignored.js' },
176- }
177- ```
178- """ ,
303+ doc = _DOC ,
179304 implementation = _rollup_bundle ,
180305 attrs = _ROLLUP_ATTRS ,
181306 outputs = _rollup_outs ,
0 commit comments