raptor
Version:
RaptorJS provides an AMD module loader that works in Node, Rhino and the web browser. It also includes various sub-modules to support building optimized web applications.
324 lines (277 loc) • 11.5 kB
JavaScript
/*
* Copyright 2011 eBay Software Foundation
*
* 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.
*/
/**
* @extension Server
*/
define.extend('raptor/resources', function(require) {
"use strict";
var raptor = require('raptor'),
strings = require('raptor/strings'),
logger = require('raptor/logging').logger('raptor/resources'),
DirSearchPathEntry = require('raptor/resources/DirSearchPathEntry'),
Resource = require('raptor/resources/Resource'),
MissingResource = require('raptor/resources/MissingResource'),
FileResource = require('raptor/resources/FileResource'),
SearchPath = require('raptor/resources/SearchPath'),
searchPath = new SearchPath(),
_createDirSearchPathEntry = function(config) {
var entry = new DirSearchPathEntry(config.path);
return entry;
},
_addSearchPathEntry = function(config) {
logger.debug('Adding search path entry (' + config.type + ": " + config.path + ')');
if (config.type == 'dir')
{
return searchPath.addEntry(_createDirSearchPathEntry(config));
}
else
{
throw raptor.createError(new Error('Invalid search path type: ' + config.type));
}
};
return {
createFileResource: function(path) {
var File = require('raptor/files/File');
if (path instanceof File) {
path = path.getAbsolutePath();
}
else if (typeof path !== 'string') {
throw raptor.createError(new Error("Invalid path: " + path));
}
return new FileResource(null, path, path);
},
/**
*
* @param searchPathEntry
*/
addSearchPathEntry: function(searchPathEntry) {
return searchPath.addEntry(searchPathEntry);
},
/**
*
* @param path
*/
addSearchPathDir: function(path) {
return _addSearchPathEntry({
type: 'dir',
path: path
});
},
hasSearchPathDir: function(path) {
return searchPath.hasDir(path);
},
/**
*
* @param path
* @returns
*/
findResource: function(path, searchPathEntry) {
if (path instanceof Resource) {
return path;
}
if (path.constructor !== String)
{
throw raptor.createError(new Error("Invalid path: " + path));
}
var resource = null;
if (searchPathEntry) {
resource = searchPathEntry.findResource(path);
}
else {
searchPath.forEachEntry(function(entry) {
resource = entry.findResource(path);
if (resource != null) {
return false;
}
}, this);
}
return resource || new MissingResource(path);
},
findResourcePreferredSearchPath: function(path, searchPathEntry) {
if (!searchPathEntry) {
return this.findResource(path);
}
if (path instanceof Resource) {
return path;
}
if (path.constructor !== String)
{
throw raptor.createError(new Error("Invalid path: " + path));
}
var resource = null;
resource = searchPathEntry.findResource(path);
if (!resource) {
searchPath.forEachEntry(function(entry) {
if (entry === searchPathEntry) {
// Skip the search path entry we already checked
return;
}
resource = entry.findResource(path);
if (resource != null) {
return false;
}
}, this);
}
return resource || new MissingResource(path);
},
/**
* Finds all resources with the provided path by searching for the
* resource in all search path entries and invoking the provided
* callback for each found resource.
*
* @param path
* @param callback
* @param thisObj
*/
forEach: function(path, callback, thisObj) {
searchPath.forEachEntry(function(entry) {
if (entry.forEachResource) {
entry.forEachResource(path, callback, thisObj);
}
else {
var resource = entry.findResource(path);
if (resource != null) {
callback.call(thisObj, resource);
}
}
}, this);
},
/**
*/
toString: function() {
return '[resources: searchPath=' + JSON.stringify(searchPath.entries) + ']';
},
getSearchPath: function() {
return searchPath;
},
setSearchPath: function(newSearchPath) {
searchPath = newSearchPath;
},
getSearchPathString: function() {
var parts = [];
searchPath.forEachEntry(function(entry) {
parts.push(entry.toString());
});
return parts.length > 0 ? parts.join('\n') : "(empty)";
},
joinPaths: function(p1, p2) {
if (!p2) return p1;
if (!p1) return p2;
var strings = require('raptor/strings');
if (p1.endsWith('/') && p2.startsWith('/')) {
//Example: p1="/begin/", p2="/end"
p2 = p2.substring(1);
}
else if (!p1.endsWith('/') && !p2.startsWith('/')) {
//Example: p1="/begin", p2="end"
return p1 + '/' + p2;
}
//Example: p1="/begin", p2="/end"
return p1 + p2;
},
resolvePath: function(basePath, relativePath) {
if (relativePath.charAt(0) === '/') {
return relativePath;
}
var parts = relativePath.split('/'),
pathParts = basePath.split('/');
for (var i=0, len=parts.length; i<len; i++) {
var part = parts[i];
if (part === '..') {
pathParts.splice(pathParts.length-1, 1); //Remove the last element
}
else if (part !== '.') {
pathParts.push(part);
}
}
return pathParts.join('/');
},
resolveResource: function(baseResource, relPath) {
var resource;
if (strings.startsWith(relPath, '/')) {
if (baseResource) {
resource = this.findResource(relPath, baseResource.getSearchPathEntry() /* Search within the same search path entry */);
}
if (!resource || resource.exists() === false) {
resource = this.findResource(relPath);
}
}
else if (baseResource) {
resource = baseResource.resolve(relPath);
}
return resource || new MissingResource(relPath + (baseResource ? "@" + baseResource : ""));
},
/**
* This method reads a package.json to discover if there are
* any nested directories that should be added to the RaptorJS
* resource search path.
*
* <p>
* This allows modules to contain nested modules that can be
* discovered as top-level modules. For example:
*
* <js>
* {
* "raptor": {
* "search-path": [
* "modules"
* ]
* }
* }
* </js>
*
* @param {raptor/packaging/PackageManifest} packageManifest The loaded package manifest
*/
addSearchPathsFromManifest: function(packageManifest) {
var packageSearchPath = packageManifest.getRaptorProp('search-path');
if (packageSearchPath != null) {
var moduleName = packageManifest.name;
raptor.forEach(packageSearchPath, function(searchPathConfig) {
if (typeof searchPathConfig === 'string') {
searchPathConfig = {
dir: searchPathConfig
};
}
if (searchPathConfig.dir) {
var dirResource = packageManifest.resolveResource(searchPathConfig.dir);
if (dirResource && dirResource.exists()) {
if (dirResource.isFileResource()) {
if (!dirResource.isDirectory()) {
throw raptor.createError(new Error("Only file system directories can be added to the resource search path. Search path is a file: " + dirResource.getURL()));
}
// see if the search path is a module that has it's own search path...
var modulePackage = dirResource.resolve('./package.json');
if (modulePackage.exists()) {
var nestedPackage = require('raptor/packaging').getPackageManifest(modulePackage);
var nestedSearchPath = nestedPackage.getRaptorProp('search-path');
if (nestedSearchPath) {
this.addSearchPathsFromManifest(nestedPackage);
return;
}
}
var searchPathEntry = this.addSearchPathDir(dirResource.getFilePath());
raptor.setModuleSearchPath(moduleName, searchPathEntry);
}
else {
throw raptor.createError(new Error("Only file system directories can be added to the resource search path. Search path: " + dirResource.getURL()));
}
}
}
}, this);
}
}
};
});