<commit_before># Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules for executing programs in the nodejs runtime.
"""
load(":common/module_mappings.bzl", "module_mappings_runtime_aspect")

def _sources_aspect_impl(target, ctx):
  result = depset()
  if hasattr(ctx.rule.attr, "deps"):
    for dep in ctx.rule.attr.deps:
      if hasattr(dep, "node_sources"):
        result += dep.node_sources
  # Note layering: until we have JS interop providers, this needs to know how to
  # get TypeScript outputs.
  if hasattr(target, "typescript"):
    result += target.typescript.es5_sources
  elif hasattr(target, "files"):
    result += target.files
  return struct(node_sources = result)

sources_aspect = aspect(
    _sources_aspect_impl,
    attr_aspects = ["deps"],
)

def _write_loader_script(ctx):
  # Generates the JavaScript snippet of module roots mappings, with each entry
  # in the form:
  #   {module_name: /^mod_name\b/, module_root: 'path/to/mod_name'}
  module_mappings = []
  for d in ctx.attr.data:
    if hasattr(d, "runfiles_module_mappings"):
      for [mn, mr] in d.runfiles_module_mappings.items():
        escaped = mn.replace("/", r"\/").replace(".", r"\.")
        mapping = r"{module_name: /^%s\b/, module_root: '%s'}" % (escaped, mr)
        module_mappings.append(mapping)
  ctx.template_action(
      template=ctx.file._loader_template,
      output=ctx.outputs.loader,
      substitutions={
          "TEMPLATED_module_roots": "\n  " + ",\n  ".join(module_mappings),
          "TEMPLATED_bootstrap": "\n  " + ",\n  ".join(
              ["\"" + d + "\"" for d in ctx.attr.bootstrap]),
          "TEMPLATED_entry_point": ctx.attr.entry_point,
          "TEMPLATED_label_package": ctx.attr.node_modules.label.package,
          # There are two workspaces in general:
          # A) The user's workspace is the one where the bazel command is run
          # B) The label's workspace contains the target being built/run
          #
          # If A has an npm dependency on B, then we'll look in the node_modules
          # to find B's dependency D. It could be in two different places
          # depending on hoisting [1]:
          # A/node_modules/B/node_modules/D
          # A/node_modules/D
          # That means we must resolve runfiles relative to A.
          #
          # However if A has a bazel dependency on B, then B is not under A's
          # node_modules directory.
          # A
          # B/node_modules/D
          # That means we must resolve runfiles relative to B.
          #
          # Since Bazel does not tell us whether the label's workspace was
          # created with `local_repository(path="node_modules/blah")` we can't
          # distinguish the two cases. Therefore we add both workspaces to the
          # resolution search paths.
          #
          # [1] https://yarnpkg.com/lang/en/docs/workspaces/#toc-limitations-caveats
          "TEMPLATED_user_workspace_name": ctx.workspace_name,
          "TEMPLATED_label_workspace_name": (
              ctx.attr.node_modules.label.workspace_root.split("/")[1]
              if ctx.attr.node_modules.label.workspace_root
              # If the label is in the same workspace as the user, we don't
              # need another search location.
              else ""
          ),
      },
      executable=True,
  )

def expand_location_into_runfiles(ctx, path):
  """If the path has a location expansion, expand it. Otherwise return as-is.
  """
  if path.find('$(location') < 0:
    return path
  return expand_path_into_runfiles(ctx, path)

def expand_path_into_runfiles(ctx, path):
  """Given a file path that might contain a $(location) label expansion,
   provide the path to the file in runfiles.
   See https://docs.bazel.build/versions/master/skylark/lib/ctx.html#expand_location
  """
  targets = ctx.attr.data if hasattr(ctx.attr, "data") else []
  expanded = ctx.expand_location(path, targets)
  if expanded.startswith(ctx.bin_dir.path):
    expanded = expanded[len(ctx.bin_dir.path + "/"):]
  if expanded.startswith(ctx.genfiles_dir.path):
    expanded = expanded[len(ctx.genfiles_dir.path + "/"):]
  return ctx.workspace_name + "/" + expanded

def _nodejs_binary_impl(ctx):
    node = ctx.file._node
    node_modules = ctx.files.node_modules
    sources = depset()
    for d in ctx.attr.data:
      if hasattr(d, "node_sources"):
        sources += d.node_sources

    _write_loader_script(ctx)

    # Avoid writing non-normalized paths (workspace/../other_workspace/path)
    if ctx.outputs.loader.short_path.startswith("../"):
      script_path = ctx.outputs.loader.short_path[len("../"):]
    else:
      script_path = "/".join([
          ctx.workspace_name,
          ctx.outputs.loader.short_path,
      ])
    env_vars = "export BAZEL_TARGET=%s\n" % ctx.label
    for k in ctx.var.keys():
      env_vars += "export %s=\"%s\"\n" % (k, ctx.var[k])

    templated_args = [a for a in ctx.attr.templated_args]

    # We expect all users will benefit from --preserve-symlinks
    # but in case it breaks something, allow users a way to disable that option
    disable_preserve_symlinks = "--node_options=--nopreserve-symlinks"
    if disable_preserve_symlinks in templated_args:
      templated_args.remove(disable_preserve_symlinks)
    else:
      templated_args.append("--node_options=--preserve-symlinks")

    substitutions = {
        "TEMPLATED_node": ctx.workspace_name + "/" + node.path,
        "TEMPLATED_args": " ".join([
            expand_location_into_runfiles(ctx, a)
            for a in templated_args]),
        "TEMPLATED_script_path": script_path,
        "TEMPLATED_env_vars": env_vars,
    }
    # Write the output twice.
    # In order to have the name "nodejs_test", the rule must be declared
    # with test = True, which means we must write an output called "executable".
    # However, in order to wrap with a sh_test for Windows, we must be able to
    # get a single output file with a ".sh" extension.
    ctx.template_action(
        template=ctx.file._launcher_template,
        output=ctx.outputs.executable,
        substitutions=substitutions,
        executable=True,
    )<commit_msg>Revert "Turn on node option --preserve-symlinks"<commit_after># Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules for executing programs in the nodejs runtime.
"""
load(":common/module_mappings.bzl", "module_mappings_runtime_aspect")

def _sources_aspect_impl(target, ctx):
  result = depset()
  if hasattr(ctx.rule.attr, "deps"):
    for dep in ctx.rule.attr.deps:
      if hasattr(dep, "node_sources"):
        result += dep.node_sources
  # Note layering: until we have JS interop providers, this needs to know how to
  # get TypeScript outputs.
  if hasattr(target, "typescript"):
    result += target.typescript.es5_sources
  elif hasattr(target, "files"):
    result += target.files
  return struct(node_sources = result)

sources_aspect = aspect(
    _sources_aspect_impl,
    attr_aspects = ["deps"],
)

def _write_loader_script(ctx):
  # Generates the JavaScript snippet of module roots mappings, with each entry
  # in the form:
  #   {module_name: /^mod_name\b/, module_root: 'path/to/mod_name'}
  module_mappings = []
  for d in ctx.attr.data:
    if hasattr(d, "runfiles_module_mappings"):
      for [mn, mr] in d.runfiles_module_mappings.items():
        escaped = mn.replace("/", r"\/").replace(".", r"\.")
        mapping = r"{module_name: /^%s\b/, module_root: '%s'}" % (escaped, mr)
        module_mappings.append(mapping)
  ctx.template_action(
      template=ctx.file._loader_template,
      output=ctx.outputs.loader,
      substitutions={
          "TEMPLATED_module_roots": "\n  " + ",\n  ".join(module_mappings),
          "TEMPLATED_bootstrap": "\n  " + ",\n  ".join(
              ["\"" + d + "\"" for d in ctx.attr.bootstrap]),
          "TEMPLATED_entry_point": ctx.attr.entry_point,
          "TEMPLATED_label_package": ctx.attr.node_modules.label.package,
          # There are two workspaces in general:
          # A) The user's workspace is the one where the bazel command is run
          # B) The label's workspace contains the target being built/run
          #
          # If A has an npm dependency on B, then we'll look in the node_modules
          # to find B's dependency D. It could be in two different places
          # depending on hoisting [1]:
          # A/node_modules/B/node_modules/D
          # A/node_modules/D
          # That means we must resolve runfiles relative to A.
          #
          # However if A has a bazel dependency on B, then B is not under A's
          # node_modules directory.
          # A
          # B/node_modules/D
          # That means we must resolve runfiles relative to B.
          #
          # Since Bazel does not tell us whether the label's workspace was
          # created with `local_repository(path="node_modules/blah")` we can't
          # distinguish the two cases. Therefore we add both workspaces to the
          # resolution search paths.
          #
          # [1] https://yarnpkg.com/lang/en/docs/workspaces/#toc-limitations-caveats
          "TEMPLATED_user_workspace_name": ctx.workspace_name,
          "TEMPLATED_label_workspace_name": (
              ctx.attr.node_modules.label.workspace_root.split("/")[1]
              if ctx.attr.node_modules.label.workspace_root
              # If the label is in the same workspace as the user, we don't
              # need another search location.
              else ""
          ),
      },
      executable=True,
  )

def expand_location_into_runfiles(ctx, path):
  """If the path has a location expansion, expand it. Otherwise return as-is.
  """
  if path.find('$(location') < 0:
    return path
  return expand_path_into_runfiles(ctx, path)

def expand_path_into_runfiles(ctx, path):
  """Given a file path that might contain a $(location) label expansion,
   provide the path to the file in runfiles.
   See https://docs.bazel.build/versions/master/skylark/lib/ctx.html#expand_location
  """
  targets = ctx.attr.data if hasattr(ctx.attr, "data") else []
  expanded = ctx.expand_location(path, targets)
  if expanded.startswith(ctx.bin_dir.path):
    expanded = expanded[len(ctx.bin_dir.path + "/"):]
  if expanded.startswith(ctx.genfiles_dir.path):
    expanded = expanded[len(ctx.genfiles_dir.path + "/"):]
  return ctx.workspace_name + "/" + expanded

def _nodejs_binary_impl(ctx):
    node = ctx.file._node
    node_modules = ctx.files.node_modules
    sources = depset()
    for d in ctx.attr.data:
      if hasattr(d, "node_sources"):
        sources += d.node_sources

    _write_loader_script(ctx)

    # Avoid writing non-normalized paths (workspace/../other_workspace/path)
    if ctx.outputs.loader.short_path.startswith("../"):
      script_path = ctx.outputs.loader.short_path[len("../"):]
    else:
      script_path = "/".join([
          ctx.workspace_name,
          ctx.outputs.loader.short_path,
      ])
    env_vars = "export BAZEL_TARGET=%s\n" % ctx.label
    for k in ctx.var.keys():
      env_vars += "export %s=\"%s\"\n" % (k, ctx.var[k])

    substitutions = {
        "TEMPLATED_node": ctx.workspace_name + "/" + node.path,
        "TEMPLATED_args": " ".join([
            expand_location_into_runfiles(ctx, a)
            for a in ctx.attr.templated_args]),
        "TEMPLATED_script_path": script_path,
        "TEMPLATED_env_vars": env_vars,
    }
    # Write the output twice.
    # In order to have the name "nodejs_test", the rule must be declared
    # with test = True, which means we must write an output called "executable".
    # However, in order to wrap with a sh_test for Windows, we must be able to
    # get a single output file with a ".sh" extension.
    ctx.template_action(
        template=ctx.file._launcher_template,
        output=ctx.outputs.executable,
        substitutions=substitutions,
        executable=True,
    )
{'fraction_non_alphanumeric': 0.10886290198492075, 'fraction_numerical': 0.002000307739652254, 'mean_word_length': 2.8221111437812407, 'pattern_counts': {'":': 22, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4287161', 'n_tokens_mistral': 4012, 'n_tokens_neox': 3974, 'n_words': 1150}