PRE
This is the third day of my participation in Gwen Challenge
This is the source code analysis column
The analysis content is the process of require in Node.js
Understanding this section requires understanding the CommonJS specification in Node
The following is a brief introduction to the key implementation points of modularity, to get a general impression of the output of Module
Analyze the abstract output and concrete process of require again
Untangling this process is important to understand the execution environments of Module.exports and Node.js
Key to modular implementation
CommonJS specification
- Module reference require
- Module definition export
- Module mark
Module Data structure of Module information
Require accepts the identifier and loads the target module
Exports. The module exports export
Module
// 01.js
const m = require('./m')
// m.js
require('./child')
module.exports = {
add: function () {},
foo: 'foo',}console.log(module)
// child.js
exports.name = 'child'
exports.age = '12'
Copy the code
Module {
id: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module/m.js'.path: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module'.exports: { add: [Function: add], foo: 'foo' },
parent: Module {
id: '. '.path: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module'.exports: {},
parent: null.filename: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module/01.js'.loaded: false.children: [ [Circular *1]],paths: [
'/Users/sedationh/workspace/current/class-notes/07Node/00/01module/node_modules'.'/Users/sedationh/workspace/current/class-notes/07Node/00/node_modules'.'/Users/sedationh/workspace/current/class-notes/07Node/node_modules'.'/Users/sedationh/workspace/current/class-notes/node_modules'.'/Users/sedationh/workspace/current/node_modules'.'/Users/sedationh/workspace/node_modules'.'/Users/sedationh/node_modules'.'/Users/node_modules'.'/node_modules']},filename: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module/m.js'.loaded: false.children: [
Module {
id: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module/child.js'.path: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module'.exports: [Object].parent: [Circular *1].filename: '/Users/sedationh/workspace/current/class-notes/07Node/00/01module/child.js'.loaded: true.children: [].paths: [Array]}],paths: [
'/Users/sedationh/workspace/current/class-notes/07Node/00/01module/node_modules'.'/Users/sedationh/workspace/current/class-notes/07Node/00/node_modules'.'/Users/sedationh/workspace/current/class-notes/07Node/node_modules'.'/Users/sedationh/workspace/current/class-notes/node_modules'.'/Users/sedationh/workspace/current/node_modules'.'/Users/sedationh/workspace/node_modules'.'/Users/sedationh/node_modules'.'/Users/node_modules'.'/node_modules']}Copy the code
In the abstract, the data is as follows
interface Module {
id: string
path: string
exports: any
parent: Module | null
filename: string
loaded: boolean
children: Array<Module>
paths: Array<string>}Copy the code
Module. exports and exports
Exports are used for easy export
Realization equivalent
exports = module.exports
Copy the code
Module. Exports is used when require imports
Require. main gets the entry file
Module classification and loading process
- Built-in module
- Source code, do not go through the complete loading process
- File module
- Path analysis
- The file type
- (m)
- m.js m.json m.node
- Json -> “main”: value
- index
- (m)
- Compile implementation
- js
- Fs get plainText
- wrap plainText -> Function -> executable function
- function args get exports module reuqire… equivalent
- json
- JSON.parse
- js
Cache priority
Add index to loaded modules and use caching when reintroduced
Source code require process analysis
vscode lanch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0"."configurations": [{"type": "pwa-node"."request": "launch"."name": "Launch Program"."skipFiles": [
// "<node_internals>/**"]."program": "${workspaceFolder}/07Node/00/01module/02debug.js"."outFiles": ["${workspaceFolder}/**/*.js"]]}}Copy the code
The require function
// Loads a module at the given file path. Returns that module's
// `exports` property.
Module.prototype.require = function(id) {
validateString(id, 'id');
if (id === ' ') {
throw new ERR_INVALID_ARG_VALUE('id', id,
'must be a non-empty string');
}
requireDepth++;
try {
return Module._load(id, this./* isMain */ false);
} finally{ requireDepth--; }};Copy the code
The internal load function
// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call
// `NativeModule.prototype.compileForPublicLoader()` and return the exports.
// 3. Otherwise, create a new module for the file and save it to the cache.
// Then have it load the file contents before returning its exports
// object.
Module._load = function(request, parent, isMain) {
let relResolveCacheIdentifier;
Copy the code
For now, assume adding a new Module
Module._load = function(request, parent, isMain) {...constfilename = Module._resolveFilename(request, parent, isMain); .const module = cachedModule || newModule(filename, parent); . Module._cache[filename] =module; .module.load(filename);
Copy the code
// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {... Module._extensions[extension](this, filename);
Copy the code
Policy mode, using different loaders for different files
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
if (filename.endsWith('.js')) {... content = fs.readFileSync(filename,'utf8'); .module._compile(content, filename);
Copy the code
The file content in Content is the source code for Requrie
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {...const compiledWrapper = wrapSafe(filename, content, this);
Copy the code
Get the wrapped function. Note that the wrapped function takes several arguments
exports require module __dirname
__filename
For this reason, modules can get them directly during execution
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {...const compiledWrapper = wrapSafe(filename, content, this); .const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new Map(a);if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, thisValue, exports.require.module, filename, dirname);
} else {
result = compiledWrapper.call(thisValue, exports.require.module, filename, dirname); }...return result
Copy the code
Module.exports: this exports module.exports
Perform compilerWrapper. Call…
One part of this process is not clear
That’s where the wrapSafe function is
This uses the compileFunction method
const { compileFunction } = internalBinding('contextify');
This is an internal method whose key function is to create a virtual box to execute the code
The requirement here is that the namespace of the code requiring require should be separated from the namespace calling require
CompileFunction analysis
Wrap functions, separate scopes, execute functions
- From textual content to executable functions
Use function for concatenation
"(function (exports, require, module, __filename, __dirname){" + content + "})""
Copy the code
- Separate scope
Use the vm. RunInThisContext ()
vm.runInThisContext()
compilescode
, runs it within the context of the currentglobal
and returns the result. Running code does not have access to local scope, but does have access to the currentglobal
object.If
options
is a string, then it specifies the filename.The following example illustrates using both
vm.runInThisContext()
and the JavaScripteval()
function to run the same code:
const vm = require('vm');
let localVar = 'initial value';
const vmResult = vm.runInThisContext('localVar = "vm"; ');
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`);
// Prints: vmResult: 'vm', localVar: 'initial value'
const evalResult = eval('localVar = "eval"; ');
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`);
// Prints: evalResult: 'eval', localVar: 'eval'
Copy the code
Debug View the execution process
debug -> const vmResult = vm.runInThisContext(‘localVar = “vm”; ‘)
function runInThisContext(code, options) {
if (typeof options === 'string') {
options = { filename: options };
}
return createScript(code, options).runInThisContext(options);
}
Copy the code
Create scirpt and call runInThisContext from the created script
This points to script
Scirpt’s runInThisContext overrides the runInThisContext inherited from ContextifyScript
class Script extends ContextifyScript {...runInThisContext(options) {
const { breakOnSigint, args } = getRunInContextArgs(options);
if (breakOnSigint && process.listenerCount('SIGINT') > 0) {
return sigintHandlersWrap(super.runInThisContext, this, args);
}
return super.runInThisContext(... args); }Copy the code
const {
ContextifyScript,
MicrotaskQueue,
makeContext,
isContext: _isContext,
constants,
compileFunction: _compileFunction,
measureMemory: _measureMemory,
} = internalBinding('contextify');
Copy the code
Execute here to enter the VM environment
return super.runInThisContext(... args);Copy the code
And the context in eval
Strict mode is not used here
If we use strict
The eval VO environment is created for eval execution
END
Welcome to SedationH
Source really is the best teacher | | document ~