UNPKG

@jymfony/routing

Version:

Jymfony Routing component

168 lines (142 loc) 4.78 kB
import { accessSync, constants } from 'fs'; const FileLocator = Jymfony.Component.Config.FileLocator; const FileLocatorFileNotFoundException = Jymfony.Component.Config.Exception.FileLocatorFileNotFoundException; const Loader = Jymfony.Component.Config.Loader.Loader; const FileExistenceResource = Jymfony.Component.Config.Resource.FileExistenceResource; const GlobResource = Jymfony.Component.Config.Resource.GlobResource; const RouteCollection = Jymfony.Component.Routing.RouteCollection; /** * AnnotationDirectoryLoader loads routing information from annotations set * on classes and methods. * * @memberOf Jymfony.Component.Routing.Loader */ export default class NamespaceLoader extends Loader { /** * Constructor. * * @param {Jymfony.Component.Routing.Loader.AnnotationClassLoader} loader * @param {string | null} [env = null] */ __construct(loader, env = null) { super.__construct(env); this._locator = undefined; this._loader = loader; } /** * Loads from annotations from a namespace. * * @param {string|any} namespace A valid namespace * @param {string|null} type The resource type * * @returns {Jymfony.Component.Routing.RouteCollection} A RouteCollection instance * * @throws {InvalidArgumentException} When the directory does not exist or its routes cannot be parsed */ load(namespace, type = null) { if (isString(namespace)) { const parts = namespace.split('.'); namespace = ReflectionClass._recursiveGet(global, parts); } if (undefined !== namespace.__namespace) { namespace = namespace.__namespace; } const collection = new RouteCollection(); this._locator = new FileLocator(namespace.directories); for (const Class of this._findClasses(namespace.name, collection)) { try { collection.addCollection(this._loader.load(Class, type)); } catch { // Do nothing. } } return collection; } /** * @inheritdoc */ supports(resource, type = null) { if ('namespace' === type) { return true; } if (isString(resource) && resource.startsWith('.')) { return false; } if (type || (isString(resource) && ! resource.match(/^(?:\.?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/))) { return false; } if (isString(resource)) { const parts = resource.split('.'); resource = ReflectionClass._recursiveGet(global, parts); } if (! resource) { return false; } return !! resource.__namespace; } /** * @private */ _glob() { let prefix = '.'; try { prefix = this._locator.locate(prefix, this._currentDir, true); } catch (e) { if (! (e instanceof FileLocatorFileNotFoundException)) { throw e; } } return new GlobResource(prefix, '/*', true, false, []); } /** * Finds classes into a namespace. * * @param {string} namespace * @param {Jymfony.Component.Routing.RouteCollection} collection * * @returns {string[]} * * @private */ _findClasses(namespace, collection) { const classes = {}; const extRegexp = /\.js$/; let prefixLen = null; const resource = this._glob(); for (const path of resource) { if (null === prefixLen) { prefixLen = resource.prefix.length; } const m = path.match(extRegexp); if (null === m) { continue; } try { accessSync(path, constants.R_OK); } catch { continue; } const Class = namespace + '.' + __jymfony.ltrim(path.substr(prefixLen, path.length - prefixLen - m[0].length).replace(/[/\\]/g, '.'), '.'); let r = null; try { r = new ReflectionClass(Class); } catch { continue; } // Check to make sure the expected class exists if (! r || r.isInterface || r.isTrait) { continue; } classes[Class] = true; } // Track only for new & removed files if (resource instanceof GlobResource) { collection.addResource(resource); } else { for (const path of resource) { collection.addResource(new FileExistenceResource(path)); } } return Object.keys(classes); } }