| """js_library groups together JS sources and arranges them and their transitive and npm dependencies into a provided |
| `JsInfo`. There are no Bazel actions to run. |
| |
| For example, this `BUILD` file groups a pair of `.js/.d.ts` files along with the `package.json`. |
| The latter is needed because it contains a `typings` key that allows downstream |
| users of this library to resolve the `one.d.ts` file. |
| The `main` key is another commonly used field in `package.json` which would require including it in the library. |
| |
| ```starlark |
| load("@aspect_rules_js//js:defs.bzl", "js_library") |
| |
| js_library( |
| name = "one", |
| srcs = [ |
| "one.d.ts", |
| "one.js", |
| "package.json", |
| ], |
| ) |
| ``` |
| |
| | This is similar to [`py_library`](https://docs.bazel.build/versions/main/be/python.html#py_library) which depends on |
| | Python sources and provides a `PyInfo`. |
| """ |
| |
| load("@bazel_lib//lib:copy_to_bin.bzl", "COPY_FILE_TO_BIN_TOOLCHAINS") |
| load(":js_helpers.bzl", "copy_js_file_to_bin_action", "gather_runfiles") |
| load(":js_info.bzl", "JsInfo", "js_info") |
| load(":proto.bzl", "js_proto_aspect") |
| |
| _DOC = """A library of JavaScript sources. Provides JsInfo, the primary provider used in rules_js |
| and derivative rule sets. |
| |
| Declaration files are handled separately from sources since they are generally not needed at |
| runtime and build rules, such as ts_project, are optimal in their build graph if they only depend |
| on types from `deps` since these they don't need the JavaScript source files from deps to |
| typecheck. |
| |
| Linked npm dependences are also handled separately from sources since not all rules require them and it |
| is optimal for these rules to not depend on them in the build graph. |
| |
| NB: `js_library` copies all source files to the output tree before providing them in JsInfo. See |
| https://github.com/aspect-build/rules_js/tree/dbb5af0d2a9a2bb50e4cf4a96dbc582b27567155/docs#javascript |
| for more context on why we do this.""" |
| |
| _LINKED_NPM_DEPS_DOCSTRING = """If this list contains linked npm packages, npm package store targets or other targets that provide |
| `JsInfo`, `NpmPackageStoreInfo` providers are gathered from `JsInfo`. This is done directly from |
| the `npm_package_store_infos` field of these. For linked npm package targets, the underlying |
| `npm_package_store` target(s) that back the links are used. Gathered `NpmPackageStoreInfo` |
| providers are propagated to the direct dependencies of downstream linked targets. |
| |
| NB: Linked npm package targets that are "dev" dependencies do not forward their underlying |
| `npm_package_store` target(s) through `npm_package_store_infos` and will therefore not be |
| propagated to the direct dependencies of downstream linked targets. npm packages |
| that come in from `npm_translate_lock` are considered "dev" dependencies if they are have |
| `dev: true` set in the pnpm lock file. This should be all packages that are only listed as |
| "devDependencies" in all `package.json` files within the pnpm workspace. This behavior is |
| intentional to mimic how `devDependencies` work in published npm packages. |
| """ |
| |
| _ATTRS = { |
| "srcs": attr.label_list( |
| doc = """Source files that are included in this library. |
| |
| This includes all your checked-in code and any generated source files. |
| |
| The transitive npm dependencies, transitive sources & runfiles of targets in the `srcs` attribute are added to the |
| runfiles of this target. They should appear in the '*.runfiles' area of any executable which is output by or has a |
| runtime dependency on this target. |
| |
| Source files that are JSON files, declaration files or directory artifacts will be automatically provided as |
| "types" available to downstream rules for type checking. To explicitly provide source files as "types" |
| available to downstream rules for type checking that do not match these criteria, move those files to the `types` |
| attribute instead. |
| """, |
| allow_files = True, |
| ), |
| "types": attr.label_list( |
| doc = """Same as `srcs` except all files are also provided as "types" available to downstream rules for type checking. |
| |
| For example, a js_library with only `.js` files that are intended to be imported as `.js` files by downstream type checking |
| rules such as `ts_project` would list those files in `types`: |
| |
| ``` |
| js_library( |
| name = "js_lib", |
| types = ["index.js"], |
| ) |
| ``` |
| """, |
| allow_files = True, |
| ), |
| "deps": attr.label_list( |
| doc = """Dependencies of this target. |
| |
| This may include other js_library targets or other targets that provide JsInfo |
| |
| The transitive npm dependencies, transitive sources & runfiles of targets in the `deps` attribute are added to the |
| runfiles of this target. They should appear in the '*.runfiles' area of any executable which is output by or has a |
| runtime dependency on this target. |
| |
| {linked_npm_deps} |
| """.format(linked_npm_deps = _LINKED_NPM_DEPS_DOCSTRING), |
| providers = [JsInfo], |
| aspects = [js_proto_aspect], |
| ), |
| "data": attr.label_list( |
| doc = """Runtime dependencies to include in binaries/tests that depend on this target. |
| |
| The transitive npm dependencies, transitive sources, default outputs and runfiles of targets in the `data` attribute |
| are added to the runfiles of this target. They should appear in the '*.runfiles' area of any executable which has |
| a runtime dependency on this target. |
| |
| {linked_npm_deps} |
| """.format( |
| linked_npm_deps = _LINKED_NPM_DEPS_DOCSTRING, |
| ), |
| allow_files = True, |
| ), |
| "no_copy_to_bin": attr.label_list( |
| allow_files = True, |
| doc = """List of files to not copy to the Bazel output tree when `copy_data_to_bin` is True. |
| |
| This is useful for exceptional cases where a `copy_to_bin` is not possible or not suitable for an input |
| file such as a file in an external repository. In most cases, this option is not needed. |
| See `copy_data_to_bin` docstring for more info. |
| """, |
| ), |
| "copy_data_to_bin": attr.bool( |
| doc = """When True, `data` files are copied to the Bazel output tree before being passed as inputs to runfiles.""", |
| default = True, |
| ), |
| } |
| |
| def _gather_sources_and_types(ctx, targets, files): |
| """Gathers sources and types from a list of targets |
| |
| Args: |
| ctx: the rule context |
| |
| targets: List of targets to gather sources and types from their JsInfo providers. |
| |
| These typically come from the `srcs` and/or `data` attributes of a rule |
| |
| files: List of files to gather as sources and types. |
| |
| These typically come from the `srcs` and/or `data` attributes of a rule |
| |
| Returns: |
| Sources & declaration files depsets in the sequence (sources, types) |
| """ |
| sources = [] |
| types = [] |
| |
| for file in files: |
| if file.is_source: |
| file = copy_js_file_to_bin_action(ctx, file) |
| |
| if file.is_directory: |
| # assume a directory contains types since we can't know that it doesn't |
| types.append(file) |
| sources.append(file) |
| elif ( |
| file.path.endswith(".d.ts") or |
| file.path.endswith(".d.ts.map") or |
| file.path.endswith(".d.mts") or |
| file.path.endswith(".d.mts.map") or |
| file.path.endswith(".d.cts") or |
| file.path.endswith(".d.cts.map") |
| ): |
| types.append(file) |
| elif file.path.endswith(".json"): |
| # Any .json can produce types: https://www.typescriptlang.org/tsconfig/#resolveJsonModule |
| # package.json may be required to resolve types with the "typings" key |
| types.append(file) |
| sources.append(file) |
| else: |
| sources.append(file) |
| |
| # sources as depset |
| sources = depset(sources, transitive = [ |
| target[JsInfo].sources |
| for target in targets |
| if JsInfo in target |
| ]) |
| |
| # types as depset |
| types = depset(types, transitive = [ |
| target[JsInfo].types |
| for target in targets |
| if JsInfo in target |
| ]) |
| |
| return (sources, types) |
| |
| def _js_library_impl(ctx): |
| sources, types = _gather_sources_and_types( |
| ctx = ctx, |
| targets = ctx.attr.srcs, |
| files = ctx.files.srcs, |
| ) |
| |
| additional_sources, additional_types = _gather_sources_and_types( |
| ctx = ctx, |
| targets = ctx.attr.types, |
| files = ctx.files.types, |
| ) |
| |
| # Direct sources and types |
| sources = depset(transitive = [sources, additional_sources]) |
| types = depset(transitive = [types, additional_sources, additional_types]) |
| |
| # Transitive sources and types |
| transitive_sources = [sources] |
| transitive_types = [types] |
| |
| # npm providers |
| npm_sources = [] |
| npm_package_store_infos = [] |
| |
| # Concat the srcs+types+deps once |
| srcs_types_deps = ctx.attr.srcs + ctx.attr.types + ctx.attr.deps |
| |
| # Collect transitive sources, types and npm providers from srcs+types+deps |
| for target in srcs_types_deps: |
| if JsInfo in target: |
| jsinfo = target[JsInfo] |
| transitive_sources.append(jsinfo.transitive_sources) |
| transitive_types.append(jsinfo.transitive_types) |
| npm_sources.append(jsinfo.npm_sources) |
| npm_package_store_infos.append(jsinfo.npm_package_store_infos) |
| |
| # Also add npm_package_store_infos from ctx.attr.data |
| for target in ctx.attr.data: |
| if JsInfo in target: |
| npm_package_store_infos.append(target[JsInfo].npm_package_store_infos) |
| |
| transitive_sources = depset(transitive = transitive_sources) |
| transitive_types = depset(transitive = transitive_types) |
| npm_sources = depset(transitive = npm_sources) |
| npm_package_store_infos = depset(transitive = npm_package_store_infos) |
| |
| runfiles = gather_runfiles( |
| ctx = ctx, |
| data = ctx.attr.data, |
| deps = srcs_types_deps, |
| data_files = ctx.files.data, |
| copy_data_files_to_bin = ctx.attr.copy_data_to_bin, |
| no_copy_to_bin = ctx.files.no_copy_to_bin, |
| ) |
| |
| return [ |
| coverage_common.instrumented_files_info( |
| ctx, |
| dependency_attributes = ["deps"], |
| source_attributes = ["srcs"], |
| ), |
| js_info( |
| target = ctx.label, |
| sources = sources, |
| types = types, |
| transitive_sources = transitive_sources, |
| transitive_types = transitive_types, |
| npm_sources = npm_sources, |
| npm_package_store_infos = npm_package_store_infos, |
| ), |
| DefaultInfo( |
| files = sources, |
| runfiles = runfiles, |
| ), |
| OutputGroupInfo( |
| types = types, |
| runfiles = runfiles.files, |
| ), |
| ] |
| |
| js_library_lib = struct( |
| attrs = _ATTRS, |
| implementation = _js_library_impl, |
| provides = [DefaultInfo, JsInfo, OutputGroupInfo], |
| ) |
| |
| js_library = rule( |
| doc = _DOC, |
| implementation = js_library_lib.implementation, |
| attrs = js_library_lib.attrs, |
| provides = js_library_lib.provides, |
| toolchains = COPY_FILE_TO_BIN_TOOLCHAINS, |
| ) |