|
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | | -"""js_library allows defining a set of javascript sources and assigning a package_name. |
| 15 | +"""js_library can be used to expose and share any library package. |
16 | 16 |
|
17 | | -DO NOT USE - this is not fully designed, and exists only to enable testing within this repo. |
| 17 | +DO NOT USE - this is not fully designed yet and it is a work in progress. |
18 | 18 | """ |
19 | 19 |
|
20 | | -load("//:providers.bzl", "LinkablePackageInfo", "declaration_info", "js_module_info") |
21 | | -load("//third_party/github.com/bazelbuild/bazel-skylib:rules/private/copy_file_private.bzl", "copy_bash", "copy_cmd") |
| 20 | +load( |
| 21 | + "//:providers.bzl", |
| 22 | + "DeclarationInfo", |
| 23 | + "JSModuleInfo", |
| 24 | + "JSNamedModuleInfo", |
| 25 | + "LinkablePackageInfo", |
| 26 | + "NpmPackageInfo", |
| 27 | + "declaration_info", |
| 28 | + "js_module_info", |
| 29 | + "js_named_module_info", |
| 30 | +) |
| 31 | +load( |
| 32 | + "//third_party/github.com/bazelbuild/bazel-skylib:rules/private/copy_file_private.bzl", |
| 33 | + "copy_bash", |
| 34 | + "copy_cmd", |
| 35 | +) |
22 | 36 |
|
23 | 37 | _AMD_NAMES_DOC = """Mapping from require module names to global variables. |
24 | 38 | This allows devmode JS sources to load unnamed UMD bundles from third-party libraries.""" |
@@ -50,90 +64,201 @@ def write_amd_names_shim(actions, amd_names_shim, targets): |
50 | 64 | actions.write(amd_names_shim, amd_names_shim_content) |
51 | 65 |
|
52 | 66 | def _impl(ctx): |
53 | | - files = [] |
| 67 | + input_files = ctx.files.srcs + ctx.files.named_module_srcs |
| 68 | + all_files = [] |
54 | 69 | typings = [] |
55 | 70 | js_files = [] |
| 71 | + named_module_files = [] |
| 72 | + include_npm_package_info = False |
56 | 73 |
|
57 | | - for src in ctx.files.srcs: |
58 | | - if src.is_source and not src.path.startswith("external/"): |
59 | | - dst = ctx.actions.declare_file(src.basename, sibling = src) |
| 74 | + for idx, f in enumerate(input_files): |
| 75 | + file = f |
| 76 | + |
| 77 | + # copy files into bin if needed |
| 78 | + if file.is_source and not file.path.startswith("external/"): |
| 79 | + dst = ctx.actions.declare_file(file.basename, sibling = file) |
60 | 80 | if ctx.attr.is_windows: |
61 | | - copy_cmd(ctx, src, dst) |
62 | | - else: |
63 | | - copy_bash(ctx, src, dst) |
64 | | - if dst.basename.endswith(".d.ts"): |
65 | | - typings.append(dst) |
| 81 | + copy_cmd(ctx, file, dst) |
66 | 82 | else: |
67 | | - files.append(dst) |
68 | | - elif src.basename.endswith(".d.ts"): |
69 | | - typings.append(src) |
70 | | - else: |
71 | | - files.append(src) |
| 83 | + copy_bash(ctx, file, dst) |
| 84 | + |
| 85 | + # re-assign file to the one now copied into the bin folder |
| 86 | + file = dst |
| 87 | + |
| 88 | + # register js files |
| 89 | + if file.basename.endswith(".js") or file.basename.endswith(".js.map") or file.basename.endswith(".json"): |
| 90 | + js_files.append(file) |
| 91 | + |
| 92 | + # register typings |
| 93 | + if ( |
| 94 | + ( |
| 95 | + file.path.endswith(".d.ts") or |
| 96 | + file.path.endswith(".d.ts.map") or |
| 97 | + # package.json may be required to resolve "typings" key |
| 98 | + file.path.endswith("/package.json") |
| 99 | + ) and |
| 100 | + # exclude eg. external/npm/node_modules/protobufjs/node_modules/@types/node/index.d.ts |
| 101 | + # these would be duplicates of the typings provided directly in another dependency. |
| 102 | + # also exclude all /node_modules/typescript/lib/lib.*.d.ts files as these are determined by |
| 103 | + # the tsconfig "lib" attribute |
| 104 | + len(file.path.split("/node_modules/")) < 3 and file.path.find("/node_modules/typescript/lib/lib.") == -1 |
| 105 | + ): |
| 106 | + typings.append(file) |
72 | 107 |
|
73 | | - for p in files: |
74 | | - if p.basename.endswith(".js") or p.basename.endswith(".js.map") or p.basename.endswith(".json"): |
75 | | - js_files.append(p) |
| 108 | + # auto detect if it entirely an npm package |
| 109 | + # |
| 110 | + # NOTE: it probably can be removed once we support node_modules from more than |
| 111 | + # a single workspace |
| 112 | + if file.is_source and file.path.startswith("external/"): |
| 113 | + # We cannot always expose the NpmPackageInfo as the linker |
| 114 | + # only allow us to reference node modules from a single workspace at a time. |
| 115 | + # Here we are automatically decide if we should or not including that provider |
| 116 | + # by running through the sources and check if we have a src coming from an external |
| 117 | + # workspace which indicates we should include the provider. |
| 118 | + include_npm_package_info = True |
76 | 119 |
|
77 | | - files_depset = depset(files) |
| 120 | + # ctx.files.named_module_srcs are merged after ctx.files.srcs |
| 121 | + if idx >= len(ctx.files.srcs): |
| 122 | + named_module_files.append(file) |
| 123 | + |
| 124 | + # every single file on bin should be added here |
| 125 | + all_files.append(file) |
| 126 | + |
| 127 | + files_depset = depset(all_files) |
| 128 | + js_files_depset = depset(js_files) |
| 129 | + named_module_files_depset = depset(named_module_files) |
| 130 | + typings_depset = depset(typings) |
| 131 | + |
| 132 | + files_depsets = [files_depset] |
| 133 | + npm_sources_depsets = [files_depset] |
| 134 | + direct_sources_depsets = [files_depset] |
| 135 | + direct_named_module_sources_depsets = [named_module_files_depset] |
| 136 | + typings_depsets = [typings_depset] |
| 137 | + js_files_depsets = [js_files_depset] |
| 138 | + |
| 139 | + for dep in ctx.attr.deps: |
| 140 | + if NpmPackageInfo in dep: |
| 141 | + npm_sources_depsets.append(dep[NpmPackageInfo].sources) |
| 142 | + else: |
| 143 | + if JSModuleInfo in dep: |
| 144 | + js_files_depsets.append(dep[JSModuleInfo].direct_sources) |
| 145 | + direct_sources_depsets.append(dep[JSModuleInfo].direct_sources) |
| 146 | + if JSNamedModuleInfo in dep: |
| 147 | + direct_named_module_sources_depsets.append(dep[JSNamedModuleInfo].direct_sources) |
| 148 | + direct_sources_depsets.append(dep[JSNamedModuleInfo].direct_sources) |
| 149 | + if DeclarationInfo in dep: |
| 150 | + typings_depsets.append(dep[DeclarationInfo].declarations) |
| 151 | + direct_sources_depsets.append(dep[DeclarationInfo].declarations) |
| 152 | + if DefaultInfo in dep: |
| 153 | + files_depsets.append(dep[DefaultInfo].files) |
78 | 154 |
|
79 | 155 | providers = [ |
80 | 156 | DefaultInfo( |
81 | | - files = files_depset, |
82 | | - runfiles = ctx.runfiles(files = ctx.files.srcs), |
| 157 | + files = depset(transitive = files_depsets), |
| 158 | + runfiles = ctx.runfiles( |
| 159 | + files = all_files, |
| 160 | + transitive_files = depset(transitive = files_depsets), |
| 161 | + ), |
83 | 162 | ), |
84 | 163 | AmdNamesInfo(names = ctx.attr.amd_names), |
85 | | - js_module_info(depset(js_files)), |
| 164 | + js_module_info( |
| 165 | + sources = depset(transitive = js_files_depsets), |
| 166 | + deps = ctx.attr.deps, |
| 167 | + ), |
| 168 | + js_named_module_info( |
| 169 | + sources = depset(transitive = direct_named_module_sources_depsets), |
| 170 | + deps = ctx.attr.deps, |
| 171 | + ), |
86 | 172 | ] |
87 | 173 |
|
88 | 174 | if ctx.attr.package_name: |
89 | 175 | path = "/".join([p for p in [ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package] if p]) |
90 | 176 | providers.append(LinkablePackageInfo( |
91 | 177 | package_name = ctx.attr.package_name, |
92 | 178 | path = path, |
93 | | - files = files_depset, |
| 179 | + files = depset(transitive = direct_sources_depsets), |
| 180 | + )) |
| 181 | + |
| 182 | + if include_npm_package_info: |
| 183 | + workspace_name = ctx.label.workspace_name if ctx.label.workspace_name else ctx.workspace_name |
| 184 | + providers.append(NpmPackageInfo( |
| 185 | + direct_sources = depset(transitive = direct_sources_depsets), |
| 186 | + sources = depset(transitive = npm_sources_depsets), |
| 187 | + workspace = workspace_name, |
94 | 188 | )) |
95 | 189 |
|
96 | 190 | # Don't provide DeclarationInfo if there are no typings to provide. |
97 | 191 | # Improves error messaging downstream if DeclarationInfo is required. |
98 | 192 | if len(typings): |
99 | | - providers.append(declaration_info(depset(typings))) |
| 193 | + providers.append(declaration_info( |
| 194 | + declarations = depset(transitive = typings_depsets), |
| 195 | + deps = ctx.attr.deps, |
| 196 | + )) |
100 | 197 |
|
101 | 198 | return providers |
102 | 199 |
|
103 | 200 | _js_library = rule( |
104 | 201 | implementation = _impl, |
105 | 202 | attrs = { |
106 | | - "amd_names": attr.string_dict(doc = _AMD_NAMES_DOC), |
107 | | - "is_windows": attr.bool(mandatory = True, doc = "Automatically set by macro"), |
| 203 | + "amd_names": attr.string_dict( |
| 204 | + doc = _AMD_NAMES_DOC, |
| 205 | + ), |
| 206 | + "deps": attr.label_list( |
| 207 | + doc = """Transitive dependencies of the package. |
| 208 | + It should include fine grained npm dependencies from the sources |
| 209 | + or other targets we want to include in the library but also propagate their own deps.""", |
| 210 | + ), |
| 211 | + "is_windows": attr.bool( |
| 212 | + doc = "Automatically set by macro", |
| 213 | + mandatory = True, |
| 214 | + ), |
108 | 215 | # module_name for legacy ts_library module_mapping support |
| 216 | + # which is still being used in a couple of tests |
109 | 217 | # TODO: remove once legacy module_mapping is removed |
110 | | - "module_name": attr.string(), |
111 | | - "package_name": attr.string(), |
| 218 | + "module_name": attr.string( |
| 219 | + doc = "Internal use only. It will be removed soon.", |
| 220 | + ), |
| 221 | + "named_module_srcs": attr.label_list( |
| 222 | + doc = """A subset of srcs that are javascript named-UMD or |
| 223 | + named-AMD for use in rules such as ts_devserver. |
| 224 | + They will be copied into the package bin folder if needed.""", |
| 225 | + allow_files = True, |
| 226 | + ), |
| 227 | + "package_name": attr.string( |
| 228 | + doc = """Optional package_name that this package may be imported as.""", |
| 229 | + ), |
112 | 230 | "srcs": attr.label_list( |
| 231 | + doc = """The list of files that comprise the package. |
| 232 | + They will be copied into the package bin folder if needed.""", |
113 | 233 | allow_files = True, |
114 | | - mandatory = True, |
115 | 234 | ), |
116 | 235 | }, |
| 236 | + doc = "Defines a js_library package", |
117 | 237 | ) |
118 | 238 |
|
119 | 239 | def js_library( |
120 | 240 | name, |
121 | | - srcs, |
| 241 | + srcs = [], |
122 | 242 | amd_names = {}, |
123 | 243 | package_name = None, |
| 244 | + deps = [], |
| 245 | + named_module_srcs = [], |
124 | 246 | **kwargs): |
125 | | - """Internal use only. May be published to the public API in a future release.""" |
| 247 | + """Internal use only yet. It will be released into a public API in a future release.""" |
126 | 248 | module_name = kwargs.pop("module_name", None) |
127 | 249 | if module_name: |
128 | 250 | fail("use package_name instead of module_name in target //%s:%s" % (native.package_name(), name)) |
129 | 251 | if kwargs.pop("is_windows", None): |
130 | 252 | fail("is_windows is set by the js_library macro and should not be set explicitely") |
131 | 253 | _js_library( |
132 | 254 | name = name, |
133 | | - srcs = srcs, |
134 | 255 | amd_names = amd_names, |
| 256 | + srcs = srcs, |
| 257 | + named_module_srcs = named_module_srcs, |
| 258 | + deps = deps, |
135 | 259 | package_name = package_name, |
136 | 260 | # module_name for legacy ts_library module_mapping support |
| 261 | + # which is still being used in a couple of tests |
137 | 262 | # TODO: remove once legacy module_mapping is removed |
138 | 263 | module_name = package_name, |
139 | 264 | is_windows = select({ |
|
0 commit comments