opal/corelib/file.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# helpers: truthy
# backtick_javascript: true

class ::File < ::IO
  Separator = SEPARATOR = '/'
  ALT_SEPARATOR = nil
  PATH_SEPARATOR = ':'
  # Assuming case insenstive filesystem
  FNM_SYSCASE = 0
  windows_root_rx = %r{^[a-zA-Z]:(?:\\|\/)}

  class << self
    def absolute_path(path, basedir = nil)
      sep = SEPARATOR
      sep_chars = `$sep_chars()`
      new_parts = []

      path = path.respond_to?(:to_path) ? path.to_path : path
      path = ::Opal.coerce_to!(`path`, ::String, :to_str)

      basedir ||= ::Dir.pwd
      path_abs    = `path.substr(0, sep.length) === sep || windows_root_rx.test(path)`
      basedir_abs = `basedir.substr(0, sep.length) === sep || windows_root_rx.test(basedir)`

      if path_abs
        parts       = path.split(/[#{sep_chars}]/)
        leading_sep = `windows_root_rx.test(path) ? '' : #{path.sub(/^([#{sep_chars}]+).*$/, '\1')}`
        abs         = true
      else
        parts       = basedir.split(/[#{sep_chars}]/) + path.split(/[#{sep_chars}]/)
        leading_sep = `windows_root_rx.test(basedir) ? '' : #{basedir.sub(/^([#{sep_chars}]+).*$/, '\1')}`
        abs         = basedir_abs
      end

      %x{
        var part;
        for (var i = 0, ii = parts.length; i < ii; i++) {
          part = parts[i];

          if (
            (part === nil) ||
            (part === ''  && ((new_parts.length === 0) || abs)) ||
            (part === '.' && ((new_parts.length === 0) || abs))
          ) {
            continue;
          }
          if (part === '..') {
            new_parts.pop();
          } else {
            new_parts.push(part);
          }
        }

        if (!abs && parts[0] !== '.') {
          #{new_parts.unshift '.'}
        }
      }

      new_path = new_parts.join(sep)
      new_path = leading_sep + new_path if abs
      new_path
    end

    def expand_path(path, basedir = nil)
      sep = SEPARATOR
      sep_chars = `$sep_chars()`
      if `path[0] === '~' || (basedir && basedir[0] === '~')`
        home = Dir.home
        ::Kernel.raise(::ArgumentError, "couldn't find HOME environment -- expanding `~'") unless home
        leading_sep = `windows_root_rx.test(home) ? '' : #{home.sub(/^([#{sep_chars}]+).*$/, '\1')}`
        ::Kernel.raise(::ArgumentError, 'non-absolute home') unless home.start_with?(leading_sep)

        home            += sep
        home_path_regexp = /^\~(?:#{sep}|$)/
        path             = path.sub(home_path_regexp, home)
        basedir          = basedir.sub(home_path_regexp, home) if basedir
      end
      absolute_path(path, basedir)
    end

    %x{
      // Coerce a given path to a path string using #to_path and #to_str
      function $coerce_to_path(path) {
        if ($truthy(#{`path`.respond_to?(:to_path)})) {
          path = path.$to_path();
        }

        path = #{::Opal.coerce_to!(`path`, ::String, :to_str)};

        return path;
      }

      // Return a RegExp compatible char class
      function $sep_chars() {
        if (#{ALT_SEPARATOR} === nil) {
          return Opal.escape_regexp(#{SEPARATOR});
        } else {
          return Opal.escape_regexp(#{SEPARATOR + ALT_SEPARATOR});
        }
      }
    }

    def dirname(path, level = 1)
      return path if level == 0
      ::Kernel.raise ::ArgumentError, "level can't be negative" if level < 0

      sep_chars = `$sep_chars()`
      path = `$coerce_to_path(path)`
      %x{
        var absolute = path.match(new RegExp(#{"^[#{sep_chars}]"})), out;

        path = path.replace(new RegExp(#{"[#{sep_chars}]+$"}), ''); // remove trailing separators
        path = path.replace(new RegExp(#{"[^#{sep_chars}]+$"}), ''); // remove trailing basename
        path = path.replace(new RegExp(#{"[#{sep_chars}]+$"}), ''); // remove final trailing separators

        if (path === '') {
          out = absolute ? '/' : '.';
        }
        else {
          out = path;
        }

        if (level == 1) {
          return out;
        }
        else {
          return #{dirname(`out`, level - 1)}
        }
      }
    end

    def basename(name, suffix = nil)
      sep_chars = `$sep_chars()`
      name = `$coerce_to_path(name)`
      %x{
        if (name.length == 0) {
          return name;
        }

        if (suffix !== nil) {
          suffix = #{::Opal.coerce_to!(suffix, ::String, :to_str)}
        } else {
          suffix = null;
        }

        name = name.replace(new RegExp(#{"(.)[#{sep_chars}]*$"}), '$1');
        name = name.replace(new RegExp(#{"^(?:.*[#{sep_chars}])?([^#{sep_chars}]+)$"}), '$1');

        if (suffix === ".*") {
          name = name.replace(/\.[^\.]+$/, '');
        } else if(suffix !== null) {
          suffix = Opal.escape_regexp(suffix);
          name = name.replace(new RegExp(#{"#{suffix}$"}), '');
        }

        return name;
      }
    end

    def extname(path)
      `path = $coerce_to_path(path)`
      filename = basename(path)
      return '' if filename.empty?
      last_dot_idx = filename[1..-1].rindex('.')
      # extension name must contains at least one character .(something)
      last_dot_idx.nil? || last_dot_idx + 1 == filename.length - 1 ? '' : filename[(last_dot_idx + 1)..-1]
    end

    def exist?(path)
      `Opal.modules[#{path}] != null`
    end

    def directory?(path)
      files = []
      %x{
        for (var key in Opal.modules) {
          #{files}.push(key)
        }
      }
      path = path.gsub(/(^.#{SEPARATOR}+|#{SEPARATOR}+$)/)
      file = files.find { |f| f =~ /^#{path}/ }
      file
    end

    def join(*paths)
      if paths.empty?
        return ''
      end
      result = ''
      paths = paths.flatten.each_with_index.map do |item, index|
        if index == 0 && item.empty?
          SEPARATOR
        elsif paths.length == index + 1 && item.empty?
          SEPARATOR
        else
          item
        end
      end
      paths = paths.reject(&:empty?)
      paths.each_with_index do |item, index|
        next_item = paths[index + 1]
        if next_item.nil?
          result = "#{result}#{item}"
        else
          if item.end_with?(SEPARATOR) && next_item.start_with?(SEPARATOR)
            item = item.sub(/#{SEPARATOR}+$/, '')
          end
          result = if item.end_with?(SEPARATOR) || next_item.start_with?(SEPARATOR)
                     "#{result}#{item}"
                   else
                     "#{result}#{item}#{SEPARATOR}"
                   end
        end
      end
      result
    end

    def split(path)
      path.split(SEPARATOR)
    end

    alias realpath expand_path
    alias exists? exist?
  end
end