old_contents: 
  import nodepy
  import os
  import re
  import subprocess
  
  import {Writer as NinjaWriter} from './ninja_syntax'
  
  
  def quote(s, for_ninja=False):
    """
    Enhanced implementation of :func:`shlex.quote` as it generates single-quotes
    on Windows which can lead to problems.
    """
  
    if os.name == 'nt' and os.sep == '\\':
      s = s.replace('"', '\\"')
      if re.search('\s', s) or any(c in s for c in '<>'):
        s = '"' + s + '"'
    else:
      s = shlex.quote(s)
    if for_ninja:
      # Fix escaped $ variables on Unix, see issue craftr-build/craftr#30
      s = re.sub(r"'(\$\w+)'", r'\1', s)
    return s
  
  
  def prepare_build(build_directory, graph):
    build_file = os.path.join(build_directory, 'build.ninja')
    print('note: writing "{}"'.format(build_file))
    with open(build_file, 'w') as fp:
      writer = NinjaWriter(fp)
      writer.comment('This file was automatically generated by Craftr')
      writer.comment('It is not recommended to edit this file manually.')
      writer.newline()
  
      # writer.variable('msvc_deps_prefix')  # TODO
      writer.variable('nodepy_exec_args', ' '.join(map(quote, nodepy.runtime.exec_args)))
      writer.newline()
  
      rule_name_counter = {}
      for node in sorted(graph.nodes(), key=lambda x: x.name):
        rule_name = re.sub('[^\d\w\-_\.]+', '_', node.name) + '_' + graph.hash(node)
        number = rule_name_counter.setdefault(rule_name, 0)
        rule_name_counter[rule_name] = number + 1
        if number > 0:
          rule_name += '__' + str(number)
  
        command = ['$nodepy_exec_args', str(require.resolve('craftr/main').filename),
                  '--build-directory', build_directory, '--run-build-node', node.name,
                  '--cwd', os.getcwd()]
        writer.rule(rule_name, command)
        writer.build(node.output_files or [rule_name] , rule_name, node.input_files)
        writer.newline()
  
  
  def build(build_directory, graph):
    command = ['ninja']
    subprocess.run(command, cwd=build_directory)
  
  
  def clean(build_directory, graph):
    command = ['ninja', '-t', 'clean']
    subprocess.run(command, cwd=build_directory)
  
new_contents: 
  import nodepy
  import os
  import re
  import subprocess
  
  import {Writer as NinjaWriter} from './ninja_syntax'
  
  
  def quote(s, for_ninja=False):
    """
    Enhanced implementation of :func:`shlex.quote` as it generates single-quotes
    on Windows which can lead to problems.
    """
  
    if os.name == 'nt' and os.sep == '\\':
      s = s.replace('"', '\\"')
      if re.search('\s', s) or any(c in s for c in '<>'):
        s = '"' + s + '"'
    else:
      s = shlex.quote(s)
    if for_ninja:
      # Fix escaped $ variables on Unix, see issue craftr-build/craftr#30
      s = re.sub(r"'(\$\w+)'", r'\1', s)
    return s
  
  
  def make_rule_name(graph, node):
    return re.sub('[^\d\w\-_\.]+', '_', node.name) + '_' + graph.hash(node)
  
  
  def prepare_build(build_directory, graph):
    build_file = os.path.join(build_directory, 'build.ninja')
    print('note: writing "{}"'.format(build_file))
    with open(build_file, 'w') as fp:
      writer = NinjaWriter(fp)
      writer.comment('This file was automatically generated by Craftr')
      writer.comment('It is not recommended to edit this file manually.')
      writer.newline()
  
      # writer.variable('msvc_deps_prefix')  # TODO
      writer.variable('nodepy_exec_args', ' '.join(map(quote, nodepy.runtime.exec_args)))
      writer.newline()
  
      for node in sorted(graph.nodes(), key=lambda x: x.name):
        phony_name = 'node_' + make_rule_name(graph, node)
        rule_name = 'rule_' + phony_name
  
        command = [
          '$nodepy_exec_args',
          str(require.resolve('craftr/main').filename),
          '--build-directory', build_directory,
          '--run-build-node', node.name,
          '--cwd', os.getcwd()
        ]
        writer.rule(rule_name, command)
  
        deps = [make_rule_name(x) for x in node.deps] + list(node.output_files)
        writer.build(deps or [phony_name] , rule_name, node.input_files)
        if deps:
          writer.build([phony_name], 'phony', node.output_files)
        writer.newline()
  
  
  def build(build_directory, graph):
    command = ['ninja']
    subprocess.run(command, cwd=build_directory)
  
  
  def clean(build_directory, graph):
    command = ['ninja', '-t', 'clean']
    subprocess.run(command, cwd=build_directory)
  
subject: update Ninja rule naming