Module dependency loading for source code parsing of seajs1.3.0

Source: Internet
Author: User

Here is the core part of seajs loader. Some IE-compatible parts are not very clear, mainly understanding how each module depends on ordered loading and CMD specifications.

The Code is a bit long and requires patience:

Copy codeThe Code is as follows:
/**
* The core of loader
*/
; (Function (seajs, util, config ){
// Module Cache
Var cachedModules = {}
// Interface to modify the cache
Var cachedModifiers = {}
// Compile the queue
Var compileStack = []
// Module status
Var STATUS = {
'Fetching ': 1, // The module file is FETCHING now. The module is being downloaded.
'Fetched': 2, // The module file has been FETCHED. The module has been downloaded.
'Saved': 3, // The module info has been SAVED. The module information is saved.
'Ready': 4, // All dependencies and self are READY to compile. The dependencies of the module have been downloaded, waiting for compilation
'Compiling': 5, // The module is in COMPILING now. The module is being compiled.
'Computed': 6 // The module is COMPILED and module. exports is available. The module has been compiled.
}


Function Module (uri, status ){
This. uri = uri
This. status = status | 0

// This. id is set when saving
// This. dependencies is set when saving
// This. factory is set when saving
// This. exports is set when compiling
// This. parent is set when compiling
// This. require is set when compiling
}


Module. prototype. _ use = function (ids, callback ){
// Convert to an array for unified operations
Util. isString (ids) & (ids = [ids])
// Use the path resolution mechanism in the module system to parse and return the module path
Var uris = resolve (ids, this. uri)

This. _ load (uris, function (){
// Loads preload files introduced in modules before compiling.
// Call the preload module again before Compilation
// During code execution, you can call seajs. config to configure the preload module at any time.
Preload (function (){
// Compile each module and pass the exports of each module as parameters to the callback function
Var args = util. map (uris, function (uri ){
Return uri? CachedModules [uri]. _ compile (): null
})

If (callback ){
// Null indicates that this pointer in the callback function is window
Callback. apply (null, args)
}
})
})
}

// The main module loads the dependent module (called a sub-module) and executes the callback function.
Module. prototype. _ load = function (uris, callback ){
// Filter the uris Array
// Case 1: The module does not exist in the cache and Its uri is returned.
// Case 2: This module exists in the cache, but its status <STATUS. READY (that is, compilation is not READY yet)
Var unLoadedUris = util. filter (uris, function (uri ){
Return uri &&(! CachedModules [uri] |
CachedModules [uri]. status <STATUS. READY)
})

Var length = unLoadedUris. length
// If the length is 0, it indicates that the dependency is 0 or the download is complete, then the callback compilation operation is executed.
If (length = 0 ){
Callback ()
Return
}

Var remain = length

For (var I = 0; I <length; I ++ ){
// Closure, which provides the context environment for the onFetched Function
(Function (uri ){
// Create a module object
Var module = cachedModules [uri] |
(CachedModules [uri] = new Module (uri, STATUS. FETCHING ))
// If the module has been downloaded, run onFetched; otherwise, run the fetch operation (request module)
Module. status> = STATUS. FETCHED? OnFetched (): fetch (uri, onFetched)

Function onFetched (){
// CachedModules [uri] is changed in un-corresponsponcase
Module = cachedModules [uri]
// If the module status is SAVED, it indicates that the module dependency has been determined, then download the dependency Module
If (module. status> = STATUS. SAVED ){
// Obtain the list of dependency modules from the module information and process the circular dependencies.
Var deps = getPureDependencies (module)
// If a dependency exists, continue to download
If (deps. length ){
Module. prototype. _ load (deps, function (){
Cb (module)
})
}
// Otherwise, run cb directly.
Else {
Cb (module)
}
}
// Maybe failed to fetch successfully, such as 404 or non-module.
// In these cases, just call cb function directly.
// If the download module fails, for example, 404 or the module is not standard (code error), the module may be in the fetching or fetched status.
// Execute the callback function directly at this time. When compiling the module, this module will only return null
Else {
Cb ()
}
}

}) (UnLoadedUris [I])
}

Function cb (module ){
// Change the module status to READY. When remain is 0, it indicates that all the module dependencies have been completed. Then, run callback.
(Module | {}). status <STATUS. READY & (module. status = STATUS. READY)
-- Remain = 0 & callback ()
}
}


Module. prototype. _ compile = function (){
Var module = this
// If the module has been compiled, the module. exports is returned directly.
If (module. status = STATUS. COMPILED ){
Return module. exports
}

// Just return null when:
// 1. the module file is 404.
// 2. the module file is not written with valid module format.
// 3. other error cases.
// Handle some exceptions. In this case, null is returned directly.
If (module. status <STATUS. SAVED &&! HasModifiers (module )){
Return null
}
// Change the module status to COMPILING, indicating that the module is being compiled
Module. status = STATUS. COMPILING

// Used internally by the module. It is a method used to obtain interfaces provided by other modules (called submodules) for synchronous operations.
Function require (id ){
// Path of the parsing module based on id
Var uri = resolve (id, module. uri)
// Obtain the module from the module cache (note that the dependencies of the sub-module as the main module have been downloaded)
Var child = cachedModules [uri]

// Just return null when uri is invalid.
// If child is empty, it can only indicate that the uri is incorrect due to an error in parameter filling. Then null is returned directly.
If (! Child ){
Return null
}

// Avoids circular cballs.
// If the sub-module is in the STATUS of STATUS. COMPILING, the child. exports is returned directly to avoid repeated compilation of modules due to loop dependency.
If (child. status = STATUS. COMPILING ){
Return child. exports
}
// Point to the module that calls the current module during initialization. Based on this attribute, you can obtain the Call Stack when the module is initialized.
Child. parent = module
// Return the compiled child module. exports.
Return child. _ compile ()
}
// Used internally by the module to load the module asynchronously and execute the specified callback after the module is loaded.
Require. async = function (ids, callback ){
Module. _ use (ids, callback)
}
// Use the path parsing mechanism in the module system to parse and return the module path. This function does not load modules and only returns the parsed absolute path.
Require. resolve = function (id ){
Return resolve (id, module. uri)
}
// You can view all modules loaded by the module system.
// In some cases, if you need to reload a module, you can obtain the uri of the module and delete the module information using delete require. cache [uri. In this way, it will be retrieved again next time.
Require. cache = cachedModules

// Require is a method used to obtain interfaces provided by other modules.
Module. require = require
// Exports is an object used to provide module interfaces.
Module. exports = {}
Var factory = module. factory

// When factory is a function, it indicates the construction method of the module. Run this method to obtain the interface provided by the module.
If (util. isFunction (factory )){
CompileStack. push (module)
RunInModuleContext (factory, module)
CompileStack. pop ()
}
// When factory is an object, string, or other non-function type, it indicates that the interface of the module is the equivalent of this object and string.
// For example, define ({"foo": "bar "});
// For example, define ('I am a template. My name is {name }}.');
Else if (factory! = Undefined ){
Module. exports = factory
}

// Change the module status to COMPILED, indicating that the module has been COMPILED
Module. status = STATUS. COMPILED
// Execute the Module Interface modification through seajs. modify ()
ExecModifiers (module)
Return module. exports
}


Module. _ define = function (id, deps, factory ){
Var argsLength = arguments. length
// Match parameters based on the number of input parameters

// Define (factory)
// One parameter:
// Id: undefined
// Deps: undefined (the list of dependent modules will be retrieved based on the regular expression later)
// Factory: function
If (argsLength === 1 ){
Factory = id
Id = undefined
}
// Define (id | deps, factory)
// Two parameters:

Else if (argsLength === 2 ){
// Default value: define (id, factory)
// Id :'...'
// Deps: undefined
// Factory: function
Factory = deps
Deps = undefined

// Define (deps, factory)
// If the first parameter is an array: define (deps, factory)
// Id: undefined
// Deps: [...]
// Factory: function
If (util. isArray (id )){
Deps = id
Id = undefined
}
}

// Parses dependencies.
// If deps is not an array (that is, deps does not specify a value), use a regular expression to parse the dependency
If (! Util. isArray (deps) & util. isFunction (factory )){
Deps = util. parseDependencies (factory. toString ())
}

// Metadata, which will be passed to the corresponding module object
Var meta = {id: id, dependencies: deps, factory: factory}
Var derivedUri

// Try to derive uri in IE6-9 for anonymous modules.
// For the IE6-9, try to get the module uri through interactive script
If (document. attachEvent ){
// Try to get the current script.
// Obtain the current script
Var script = util. getCurrentScript ()
If (script ){
// UnpareseMap the url of the current script, consistent with the key in the module Cache
DerivedUri = util. unParseMap (util. getScriptAbsoluteSrc (script ))
}

If (! DerivedUri ){
Util. log ('failed' to derive URI from interactive script :',
Factory. toString (), 'warn ')

// NOTE: If the id-deriving methods above is failed, then falls back
// To use onload event to get the uri.
}
}

// Gets uri directly for specific module.
// If an id is specified, the path is parsed Based on the id.
// Apparently, if no id is specified:
// For non-ie browsers, the returned undefined (derivedUri is empty)
// For IE, src of CurrentScript is returned
// If the id is specified:
// Returns the url of the path that has been parsed by seajs (resolve ).
Var resolvedUri = id? Resolve (id): derivedUri
// Store module information when the uri exists
If (resolvedUri ){
// For IE:
// If the first module in a package is not the cachedModules [derivedUri]
// Self, it shoshould assign to the correct module when found.
If (resolvedUri === derivedUri ){
Var refModule = cachedModules [derivedUri]
If (refModule & refModule. realUri &&
RefModule. status = STATUS. SAVED ){
CachedModules [derivedUri] = null
}
}
// Storage module information
Var module = save (resolvedUri, meta)

// For IE:
// Assigns the first module in package to cachedModules [derivedUrl]
If (derivedUri ){
// CachedModules [derivedUri] may be undefined in combo case.
If (cachedModules [derivedUri] | |{}). status = STATUS. FETCHING ){
CachedModules [derivedUri] = module
Module. realUri = derivedUri
}
}
Else {
// Store the first module to firstModuleInPackage
FirstModuleInPackage | (firstModuleInPackage = module)
}
}
// If the uri does not exist, store the module information in the onload callback, where there is a closure
Else {
// Saves information for "memoizing" work in the onload event.
// Because the uri is unknown at this time, the metadata is temporarily stored in anonymousModuleMeta, and the module save operation is performed in the onload callback.
AnonymousModuleMeta = meta
}

}

// Obtain the compiling module
Module. _ getCompilingModule = function (){
Return compileStack [compileStack. length-1]
}

// Quickly view and obtain the loaded module Interface from seajs. cache. The returned value is the module. exports array.
// Selector supports strings and Regular Expressions
Module. _ find = function (selector ){
Var matches = []

Util. forEach (util. keys (cachedModules), function (uri ){
If (util. isString (selector) & uri. indexOf (selector)>-1 |
Util. isRegExp (selector) & selector. test (uri )){
Var module = cachedModules [uri]
Module. exports & matches. push (module. exports)
}
})

Return matches
}

// Modify the Module Interface
Module. _ modify = function (id, modifier ){
Var uri = resolve (id)
Var module = cachedModules [uri]
// If the module exists and is in the COMPILED status, modify the interface
If (module & module. status = STATUS. COMPILED ){
RunInModuleContext (modifier, module)
}
// Otherwise, put it in the modified interface Cache
Else {
CachedModifiers [uri] | (cachedModifiers [uri] = [])
CachedModifiers [uri]. push (modifier)
}

Return seajs
}


// For plugin developers
Module. STATUS = STATUS
Module. _ resolve = util. id2Uri
Module. _ fetch = util. fetch
Module. cache = cachedModules


// Helpers
//-------
// List of modules being downloaded
Var fetchingList = {}
// List of downloaded modules
Var fetchedList = {}
// Callback function list
Var callbackList = {}
// Anonymous module metadata
Var anonymousModuleMeta = null
Var firstModuleInPackage = null
// Cyclic dependency Stack
Var circularCheckStack = []

// Path of the batch parsing module
Function resolve (ids, refUri ){
If (util. isString (ids )){
Return Module. _ resolve (ids, refUri)
}

Return util. map (ids, function (id ){
Return resolve (id, refUri)
})
}

Function fetch (uri, callback ){
// When fetch is executed, the uri is first converted according to the map rule.
Var requestUri = util. parseMap (uri)
// Search in fethedList (list of downloaded modules). If yes, return directly and execute the callback function.
// TODO: Why does fetchedList have this modulo?
If (fetchedList [requestUri]) {
// See test/issues/debug-using-map
CachedModules [uri] = cachedModules [requestUri]
Callback ()
Return
}
// Search in fetchingList (the list of downloaded modules). If yes, you only need to add the callback function to the list and then return it directly.
If (fetchingList [requestUri]) {
CallbackList [requestUri]. push (callback)
Return
}
// If this step is completed, the module is requested for the first time,
// Insert the module information in fetchingList, indicating that the module is already in the download list and the callback function list corresponding to the module is initialized.
FetchingList [requestUri] = true
CallbackList [requestUri] = [callback]

// Fetches it
// Obtain this module, that is, initiate a request
Module. _ fetch (
RequestUri,

Function (){
// Insert the module information in fetchedList, indicating that the module has been downloaded.
FetchedList [requestUri] = true

// Updates module status
Var module = cachedModules [uri]
// At this time, the status may be STATUS. SAVED, which has been mentioned in _ define.
If (module. status = STATUS. FETCHING ){
Module. status = STATUS. FETCHED
}

// Saves anonymous module meta data
// Because it is an anonymous module (in this case, uri is obtained through the closure, where the module information is stored)
// Set anonymousModuleMeta to null
If (anonymousModuleMeta ){
Save (uri, anonymousModuleMeta)
AnonymousModuleMeta = null
}

// Assigns the first module in package to cachedModules [uri]
// See: test/issues/un-corresponsible
If (firstModuleInPackage & module. status = STATUS. FETCHED ){
CachedModules [uri] = firstModuleInPackage
FirstModuleInPackage. realUri = uri
}
FirstModuleInPackage = null

// Clears
// Clear the module information in fetchingList because the module has been fetched and saved
If (fetchingList [requestUri]) {
Delete fetchingList [requestUri]
}

// Callcallbacklist
// Call the callback function sequentially and clear the callback function list.
If (callbackList [requestUri]) {
Util. forEach (callbackList [requestUri], function (fn ){
Fn ()
})
Delete callbackList [requestUri]
}

},

Config. charset
)
}

Function save (uri, meta ){
Var module = cachedModules [uri] | (cachedModules [uri] = new Module (uri ))

// Don't override already saved module
// At this time, the status may have two states:
// STATUS. FETCHING, called in define (id specified), stores the module information
// STATUS. FETCHED, called in the onload callback function, stores the module information
If (module. status <STATUS. SAVED ){
// Lets anonymous module id equal to its uri
// The anonymous module (that is, the id is not specified), and its uri is used as the id
Module. id = meta. id | uri
// Parse the dependency (array) into an absolute path and store it in the module information.
Module. dependencies = resolve (
Util. filter (meta. dependencies | [], function (dep ){
Return !! Dep
}), Uri)
// Store factory (the module code to be executed, object or string)
Module. factory = meta. factory

// Updates module status
// Update the module status to SAVED. (Note that it only has the dependency and has not been downloaded yet (that is, it has not been READY ))
Module. status = STATUS. SAVED
}

Return module
}

// Execute the module code according to the module Context
Function runInModuleContext (fn, module ){
// Input two parameters related to the module and the module itself
// Exports is used to expose interfaces.
// Require is used to obtain the dependency module (synchronization) (Compilation)
Var ret = fn (module. require, module. exports, module)
// Supports the Interface format for exposing returned values, such:
// Return {
// Fn1: xx
//, Fn2: xx
//...
//}
If (ret! = Undefined ){
Module. exports = ret
}
}
// Determine whether the module has an interface Modification
Function hasModifiers (module ){
Return !! CachedModifiers [module. realUri | module. uri]
}
// Modify the Module Interface
Function execModifiers (module ){
Var uri = module. realUri | module. uri
Var modifiers = cachedModifiers [uri]
// The internal variable cachedModifiers is used to store the changes defined by the user using the seajs. modify method.
// Check whether the uri has been modified by modify.
If (modifiers ){
// Execute factory for the modified vertex and return the modified module. exports.
Util. forEach (modifiers, function (modifier ){
RunInModuleContext (modifier, module)
})
// Delete the modify method definition modification point to avoid re-execution
Delete cachedModifiers [uri]
}
}

// Obtain the pure dependency to obtain the dependency array with no circular dependency.
Function getPureDependencies (module ){
Var uri = module. uri
// Filter each dependency, remove those that may form a circular dependency, and print a warning log
Return util. filter (module. dependencies, function (dep ){
// First, place the uri of the checked module into the cyclic dependency check stack, which will be used for subsequent checks.
CircularCheckStack = [uri]
// Next, check whether the module uri has circular dependency with the module on which it depends.
Var isCircular = isCircularWaiting (cachedModules [dep])
If (isCircular ){
// If it is a loop, the uri is placed in the cyclic dependency check stack.
CircularCheckStack. push (uri)
// Print the cyclic warning log
PrintCircularLog (circularCheckStack)
}

Return! IsCircular
})
}

Function isCircularWaiting (module ){
// If the dependent module does not exist, false is returned, because the dependency of the dependent module cannot be obtained at this time, so no judgment can be made here.
// Or if the module's status value is equal to saved, false is also returned, because when the module's status is saved, the information of this module already exists,
// Although circular dependency is formed, the main module of require can also be compiled normally and the interface of the main module is returned (as if nodejs will return undefined)
If (! Module | module. status! = STATUS. SAVED ){
Return false
}
// If not, place the uri of the dependent module in the cyclic dependency check stack, which will be used in subsequent checks.
CircularCheckStack. push (module. uri)
// Retrieve the dependent module of the dependent module again
Var deps = module. dependencies

If (deps. length ){
// Check the stack Through cyclic dependency to check whether there is a circular dependency (here is the first layer of dependency module check, which is the case of circular dependency with the main module)
If (isOverlap (deps, circularCheckStack )){
Return true
}
// If the preceding situation does not exist, further check the dependency modules of the dependent modules to check whether they have circular dependencies on the modules of the uri in the stack.
// In this case, the cycle dependency check stack is like a chain formed. The current module is directed to the main module and the main module of the main module in turn... until the top main module, judge whether there is dependency in turn
For (var I = 0; I <deps. length; I ++ ){
If (isCircularWaiting (cachedModules [deps [I]) {
Return true
}
}
}
// If the circular dependency does not exist, pop out the module uri that has been pushed before and return false.
CircularCheckStack. pop ()
Return false
}
// Print the cyclic warning log
Function printCircularLog (stack, type ){
Util. log ('found circular dependencies: ', stack. join (' --> '), type)
}
// Determine whether two arrays have repeated values
Function isOverlap (arrA, arrB ){
Var arrC = arrA. concat (arrB)
Return arrC. length> util. unique (arrC). length
}
// Read from the configuration file whether there are modules that need to be loaded in advance
// If a pre-loaded module exists, set the pre-loaded module to null (ensure that the pre-loaded module does not need to be loaded again next time), load the pre-loaded module, and execute the callback. If no pre-loaded module exists, execute the pre-loaded module in sequence.
Function preload (callback ){
Var preloadmod = config. preload. slice ()
Config. preload = []
Preloadmod. length? GlobalModule. _ use (preloadmod, callback): callback ()
}


// Public API
// APIs exposed externally
//----------
// Global module, which can be regarded as a page module. js and css files in the page are loaded through it.
// The initial status of the module is COMPILED, and the uri is the page uri.
Var globalModule = new Module (util. pageUri, STATUS. COMPILED)

// Page js and css file Loader
Seajs. use = function (ids, callback ){
// Loads preload modules before all other modules.
// Pre-load Module
Preload (function (){
GlobalModule. _ use (ids, callback)
})

// Chain
Return seajs
}


// For normal users
// Called by common users
Seajs. define = Module. _ define
Seajs. cache = Module. cache
Seajs. find = Module. _ find
Seajs. modify = Module. _ modify


// For plugin developers
// Available to developers
Seajs. pluginSDK = {
Module: Module,
Util: util,
Config: config
}

}) (Seajs, seajs. _ util, seajs. _ config)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.