github.com/ernestokarim/closurer@v0.0.0-20130119214741-f245d086c750/domain/source.go (about)

     1  package domain
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/gob"
     6  	"io"
     7  	"os"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/ernestokarim/closurer/app"
    12  	"github.com/ernestokarim/closurer/cache"
    13  )
    14  
    15  var (
    16  	provideRe  = regexp.MustCompile(`^\s*goog\.provide\(\s*[\'"](.+)[\'"]\s*\)`)
    17  	requiresRe = regexp.MustCompile(`^\s*goog\.require\(\s*[\'"](.+)[\'"]\s*\)`)
    18  )
    19  
    20  func init() {
    21  	gob.Register(&Source{})
    22  }
    23  
    24  // Represents a JS source
    25  type Source struct {
    26  	// List of namespaces this file provides.
    27  	Provides []string
    28  
    29  	// List of required namespaces for this file.
    30  	Requires []string
    31  
    32  	// Whether this is the base.js file of the Closure Library.
    33  	Base bool
    34  
    35  	// Name of the source file.
    36  	Filename string
    37  }
    38  
    39  // Creates a new source. Returns the source, if it has been
    40  // loaded from cache or not, and an error.
    41  func NewSource(dest, filename, base string) (*Source, bool, error) {
    42  	src := cache.ReadData(dest+filename, new(Source)).(*Source)
    43  
    44  	// Return the file from cache if possible
    45  	if modified, err := cache.Modified(dest, filename); err != nil {
    46  		return nil, false, err
    47  	} else if !modified {
    48  		return src, true, nil
    49  	}
    50  
    51  	// Reset the source info
    52  	src.Provides = []string{}
    53  	src.Requires = []string{}
    54  	src.Base = (filename == base)
    55  	src.Filename = filename
    56  
    57  	// Open the file
    58  	f, err := os.Open(filename)
    59  	if err != nil {
    60  		return nil, false, app.Error(err)
    61  	}
    62  	defer f.Close()
    63  
    64  	r := bufio.NewReader(f)
    65  	for {
    66  		// Read it line by line
    67  		line, _, err := r.ReadLine()
    68  		if err != nil {
    69  			if err == io.EOF {
    70  				break
    71  			}
    72  			return nil, false, err
    73  		}
    74  
    75  		// Find the goog.provide() calls
    76  		if strings.Contains(string(line), "goog.provide") {
    77  			matchs := provideRe.FindSubmatch(line)
    78  			if matchs != nil {
    79  				src.Provides = append(src.Provides, string(matchs[1]))
    80  				continue
    81  			}
    82  		}
    83  
    84  		// Find the goog.require() calls
    85  		if strings.Contains(string(line), "goog.require") {
    86  			matchs := requiresRe.FindSubmatch(line)
    87  			if matchs != nil {
    88  				src.Requires = append(src.Requires, string(matchs[1]))
    89  				continue
    90  			}
    91  		}
    92  	}
    93  
    94  	// Validates the base file
    95  	if src.Base {
    96  		if len(src.Provides) > 0 || len(src.Requires) > 0 {
    97  			return nil, false,
    98  				app.Errorf("base files should not provide or require namespaces: %s", filename)
    99  		}
   100  		src.Provides = append(src.Provides, "goog")
   101  	}
   102  
   103  	return src, false, nil
   104  }