module JSMin

JSMin

Ruby implementation of Douglas Crockford’s JavaScript minifier, JSMin.

Author

Ryan Grove (ryan@wonko.com)

Version

1.0.1 (2008-11-10)

Copyright

Copyright © 2008 Ryan Grove. All rights reserved.

Website

github.com/rgrove/jsmin

Example

require 'rubygems'
require 'jsmin'

File.open('example.js', 'r') {|file| puts JSMin.minify(file) }

Constants

CHR_APOS
CHR_ASTERISK
CHR_BACKSLASH
CHR_CR
CHR_FRONTSLASH
CHR_LF
CHR_QUOTE
CHR_SPACE
ORD_LF
ORD_SPACE
ORD_TILDE

Public Class Methods

minify(input) click to toggle source

Reads JavaScript from input (which can be a String or an IO object) and returns a String containing minified JS.

# File lib/jsmin.rb, line 78
def minify(input)
  @js = StringScanner.new(input.is_a?(IO) ? input.read : input.to_s)
  @source = input.is_a?(IO) ? input.inspect : input.to_s[0..100]
  @line = 1

  @a         = "\n"
  @b         = nil
  @lookahead = nil
  @output    = ''

  action_get

  while !@a.nil? do
    case @a
    when CHR_SPACE
      if alphanum?(@b)
        action_output
      else
        action_copy
      end

    when CHR_LF
      if @b == CHR_SPACE
        action_get
      elsif @b =~ /[{\[\(+-]/
        action_output
      else
        if alphanum?(@b)
          action_output
        else
          action_copy
        end
      end

    else
      if @b == CHR_SPACE
        if alphanum?(@a)
          action_output
        else
          action_get
        end
      elsif @b == CHR_LF
        if @a =~ /[}\]\)\\"+-]/
          action_output
        else
          if alphanum?(@a)
            action_output
          else
            action_get
          end
        end
      else
        action_output
      end
    end
  end

  @output
end
raise(err) click to toggle source
Calls superclass method
# File lib/jsmin.rb, line 72
def raise(err)
  super ParseError.new(err, @source, @line)
end

Private Class Methods

action_copy() click to toggle source

Corresponds to action(2) in jsmin.c.

# File lib/jsmin.rb, line 147
def action_copy
  @a = @b

  if @a == CHR_APOS || @a == CHR_QUOTE
    loop do
      @output << @a
      @a = get

      break if @a == @b

      if @a[0] <= ORD_LF
        raise "unterminated string literal: #{@a.inspect}"
      end

      if @a == CHR_BACKSLASH
        @output << @a
        @a = get

        if @a[0] <= ORD_LF
          raise "unterminated string literal: #{@a.inspect}"
        end
      end
    end
  end

  action_get
end
action_get() click to toggle source

Corresponds to action(3) in jsmin.c.

# File lib/jsmin.rb, line 176
def action_get
  @b = nextchar

  if @b == CHR_FRONTSLASH && (@a == CHR_LF || @a =~ /[\(,=:\[!&|?{};]/)
    @output << @a
    @output << @b

    loop do
      @a = get

      if @a == CHR_FRONTSLASH
        break
      elsif @a == CHR_BACKSLASH
        @output << @a
        @a = get
      elsif @a[0] <= ORD_LF
        raise "unterminated regular expression : #{@a.inspect}"
      end

      @output << @a
    end

    @b = nextchar
  end
end
action_output() click to toggle source

Corresponds to action(1) in jsmin.c.

# File lib/jsmin.rb, line 141
def action_output
  @output << @a
  action_copy
end
alphanum?(c) click to toggle source

Returns true if c is a letter, digit, underscore, dollar sign, backslash, or non-ASCII character.

# File lib/jsmin.rb, line 204
def alphanum?(c)
  c.is_a?(String) && !c.empty? && (c[0] > ORD_TILDE || c =~ /[0-9a-z_$\\]/i)
end
get() click to toggle source

Returns the next character from the input. If the character is a control character, it will be translated to a space or linefeed.

# File lib/jsmin.rb, line 210
def get
  if @lookahead
    c = @lookahead
    @lookahead = nil
  else
    c = @js.getch
    if c == CHR_LF || c == CHR_CR
      @line += 1
      return CHR_LF
    end
    return ' ' unless c.nil? || c[0] >= ORD_SPACE
  end
  c
end
nextchar() click to toggle source

Gets the next character, excluding comments.

# File lib/jsmin.rb, line 226
def nextchar
  c = get
  return c unless c == CHR_FRONTSLASH

  case peek
  when CHR_FRONTSLASH
    loop do
      c = get
      return c if c[0] <= ORD_LF
    end

  when CHR_ASTERISK
    get
    loop do
      case get
      when CHR_ASTERISK
        if peek == CHR_FRONTSLASH
          get
          return ' '
        end

      when nil
        raise 'unterminated comment'
      end
    end

  else
    return c
  end
end
peek() click to toggle source

Gets the next character without getting it.

# File lib/jsmin.rb, line 258
def peek
  @lookahead = get
end