• 手写rollup


    手写rollup

    初始化项目安装依赖

    yarn init -y
    yarn add acorn magic-string
    
    • 1
    • 2

    添加src/main.js

    var a = 1
    var b = 2
    
    • 1
    • 2

    添加debugger.js

    const path = require('path')
    
    const rollup = require('./lib/rollup')
    
    let entry = path.resolve(__dirname,'src/main.js')
    rollup(entry,'bundle.js')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    添加lib/rollup.js

    const Bundle = require('./bundle')
    function rollup(entry,outputFileName){
        // c:\Users\Administrator\Desktop\demo\bind-rollup\src\mian.js bundle.js
        const bundle = new Bundle({entry})
        bundle.build(outputFileName)
    }
    module.exports = rollup
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    添加lib/bundle.js

    const fs = require('fs')
    const { default: MagicString } = require('magic-string')
    const Module  = require('./moudle')
    class Bundle{
        constructor(options){
            this.entryPath = options.entry.replace(/\.js$/,'')+ '.js'
        }
        build(outputFileName){
            // 拿到入口文件
            let entryModule = this.fetchModule(this.entryPath)
            this.statements = entryModule.expandAllStatements()
            const { code } =this.generate()
            fs.writeFileSync(outputFileName,code,'utf-8')
        }
        fetchModule(improtee){
            let route = improtee
            if(route){
                // 拿到入口文件的内容
                let code = fs.readFileSync(route,'utf-8')
    
                let module = new Module({
                    code,
                    path:route,
                    bundle:this
                })
                return module
            }
        }
        generate(){
            let magicString = new MagicString.Bundle()
    
            this.statements.forEach(statement=>{
                const source = statement._source.clone()
                magicString.addSource({
                    content:source,
                    separator:'\n'
                })
            })
    
            return {code:magicString.toString()}
    
        }
    }
    
    module.exports = Bundle
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    添加lib/moudle.js

    const { parse } = require("acorn")
    const { default: MagicString } = require("magic-string")
    const analyse = require("./ast/analyse")
    
    class Module {
        constructor({code,path,bundle}){
            this.code = new MagicString(code,{filename:path})
            this.path = path
            this.bundle = bundle
            // 获取ast语法树
    
            this.ast = parse(code,{
                ecmaVersion:7,
                sourceType:'module'
            })
    
            this.analyse()
    
        }
        analyse(){
            analyse(this.ast,this.code,this)
        }
        expandAllStatements(){
            let allStatements = []
            this.ast.body.forEach(statement => {
                let statements = this.expandStatements(statement)
                allStatements.push(...statements)
            });
            return allStatements
        }
        expandStatements(statement){
            let result = []
            if(!statement._included){
                statement._included = true
                result.push(statement)
            }
            return result
        }
    }
    
    module.exports = Module
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    添加lib/ast/analyse.js

    function analyse(ast,magicString,module){
        ast.body.forEach(statement => {
            Object.defineProperties(statement,{
                _source:{
                    value:magicString.snip(statement.start,statement.end)
                }
            })
        });
    }
    
    module.exports = analyse
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    出现下图说明打包成功

    在这里插入图片描述

    rollup的tree-sharking

    修改main.js

    import {name,age} from './msg'
    
    function say(){
        console.log('hello',name)
    }
    
    say()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • msg.js
    export const name = 'xiaoshunshi'
    export const age = 19
    
    • 1
    • 2

    添加作用域函数scope.js

    class Scope{
        constructor(options = {}){
            this.name = options.name
            this.parent = options.parent
            this.names = options.params || []
        }
        add(name){
            this.names.push(name)
        }
        findDefiningScope(name){
            if(this.names.includes(name)){
                return this
            }
            if(this.parent){
                return this.parent.findDefiningScope(name)
            }
            return null
        }
    }
    
    module.exports = Scope
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    添加遍历节点方法walk.js

    function walk(ast,{enter,leave}){
        visit(ast,null,enter,leave)
    }
    
    function visit(node,parent,enter,leave){
        if(enter){
            enter.call(null,node,parent)
        }
    
        let childKeys = Object.keys(node).filter(key => typeof node[key] === 'object')
        childKeys.forEach(childKey=>{
            let value = node[childKey]
            if(Array.isArray(value)){
                value.forEach(val=>visit(val,node,enter,leave))
            }else if(value && value.type){
                visit(value,node,enter,leave)
            }
        })
        if(leave){
        
            leave.call(null,node,parent)
        }
    }
    
    module.exports = walk
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    修改lib/ast/analyse.js

    const Scope = require("./scope");
    const walk = require("./walk");
    
    function analyse(ast, magicString, module) {
        let scope = new Scope()
    
        ast.body.forEach(statement => {
            function addToScope(declaration) {
                var name = declaration.id.name
                scope.add(name)
                if (!scope.parent) {
                    statement._defines[name] = true
                }
            }
            Object.defineProperties(statement, {
                _defines: {
                    value: {}
                },
                _dependsOn: {
                    value: {}
                },
                _included: {
                    value: false,
                    writable: true
                },
                _source: {
                    value: magicString.snip(statement.start, statement.end)
                }
            })
    
            walk(statement, {
                enter(node) {
                    let newScope
    
                    switch (node.type) {
                        case 'FunctionDeclaration':
                            const params = node.params.map(x => x.name)
                            
                            if(node.type === 'FunctionDeclaration'){
                                addToScope(node)
                            }
                            newScope = new Scope({
                                parent: scope,
                                params
                            })
                            break;
                        case 'VariableDeclaration':
                            node.declarations.forEach(addToScope)
                            break;
                        default:
                            break;
                    }
    
                    if(newScope){
                        Object.defineProperty(node,'_scope',{
                            value:newScope
                        })
                        scope = newScope
                    }
    
                },
                leave(node) {
                    if(node._scope){
                        scope = scope.parent
                    }
                }
            })
        })
    
        ast.body.forEach(statement=>{
            walk(statement,{
                enter(node){
                    if(node._scope){
                        scope = node._scope
                    }
                    if(node.type === 'Identifier'){
                        const definingScope = scope.findDefiningScope(node.name)
                        if(!definingScope){
                            statement._dependsOn[node.name] = true
                        }
                    }
                },
                leave(node){
                    if(node._scope){
                        scope = scope.parent
                    }
                }
    
            })
        })
    
    }
    
    module.exports = analyse
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

    修改lib/bundle.js

    const fs = require('fs')
    const { default: MagicString } = require('magic-string')
    const Module  = require('./moudle')
    const path = require('path')
    class Bundle{
        constructor(options){
            this.entryPath = options.entry.replace(/\.js$/,'')+ '.js'
    
    
        }
        build(outputFileName){
            // 拿到入口文件
            let entryModule = this.fetchModule(this.entryPath)
            this.statements = entryModule.expandAllStatements()
    
            const { code } =this.generate()
            fs.writeFileSync(outputFileName,code,'utf-8')
    
        }
        fetchModule(improtee,importer){
            let route = improtee
            if(!importer){
                route = improtee
        
            }else{
                if(path.isAbsolute(improtee)){
                    route = improtee
                }else if(improtee[0] == '.'){
                    route = path.resolve(path.dirname(importer),improtee.replace(/\.js$/,'') + '.js')
                    
                }
            }
            if(route){
                // 拿到入口文件的内容
                let code = fs.readFileSync(route,'utf-8')
    
                let module = new Module({
                    code,
                    path:route,
                    bundle:this
                })
                return module
            }
        }
        generate(){
            let magicString = new MagicString.Bundle()
    
            this.statements.forEach(statement=>{
                const source = statement._source.clone()
                magicString.addSource({
                    content:source,
                    separator:'\n'
                })
            })
    
            return {code:magicString.toString()}
    
        }
    }
    
    module.exports = Bundle
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    修改lib/moudle.js

    const { parse } = require("acorn")
    const { default: MagicString } = require("magic-string")
    const analyse = require("./ast/analyse")
    
    function hasOwnProperty(obj, prop) {
        return Object.prototype.hasOwnProperty.call(obj, prop)
    }
    class Module {
        constructor({code,path,bundle}){
            this.code = new MagicString(code,{filename:path})
            this.path = path
            this.bundle = bundle
            // 获取ast语法树
    
            this.ast = parse(code,{
                ecmaVersion:7,
                sourceType:'module'
            })
    
            this.analyse()
    
        }
        analyse(){
            this.imports = {}
            this.exports = {}
            this.ast.body.forEach(node =>{
                if(node.type === 'ImportDeclaration'){
                    let source = node.source.value
                    let specifiers = node.specifiers
        
                    specifiers.forEach(specifier =>{
                        const name = specifier.imported.name
                        const loaclName = specifier.local.name
                        // console.log(name,loaclName)
    
                        this.imports[loaclName] = {
                            name,
                            loaclName,
                            source
                        }
                    })
                }else if(node.type === 'ExportNamedDeclaration'){
                    let declaration = node.declaration
                    if(declaration.type === 'VariableDeclaration'){
                        let name = declaration.declarations[0].id.name
    
                        this.exports[name] = {
                            node,localName:name,expression:declaration
                        }
                    }
                }
               
            })
            analyse(this.ast,this.code,this)
            this.definitions = {}
            this.ast.body.forEach(statement=>{
                Object.keys(statement._defines).forEach(name=>{
                    this.definitions[name] = statement
                })
            })
        }
        expandAllStatements(){
            let allStatements = []
    
            this.ast.body.forEach(statement => {
    
    
                if(statement.type === 'ImportDeclaration'){
                    return 
                }
                let statements = this.expandStatements(statement)
                allStatements.push(...statements)
            });
            return allStatements
        }
        expandStatements(statement){
            let result = []
            const dependecies = Object.keys(statement._dependsOn)
            dependecies.forEach(name=>{
                let definition = this.define(name)
                result.push(...definition)
            })
            if(!statement._included){
                statement._included = true
                result.push(statement)
            }
            return result
    
            
        }
        define(name){
            if(hasOwnProperty(this.imports,name)){
                const improtData = this.imports[name]
                const module = this.bundle.fetchModule(improtData.source,this.path)
                const exportData = module.exports[improtData.name]
                return module.define(exportData.localName)
            }else{
                let statement = this.definitions[name]
                if(statement && !statement._included){
                    return this.expandStatements(statement)
                }else{
                    return []
                }
            }
        }
    }
    
    module.exports = Module
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  • 相关阅读:
    朴素贝叶斯模型
    Nginx之会话管理解读
    java毕业设计诚越园区垃圾分类信息科普系统Mybatis+系统+数据库+调试部署
    云迁移-springcould-eureka集群搭建
    用python实现入门级NLP
    【读书笔记】【More Effective C++】异常(Exceptions)
    构建高可用的Redis服务(主从复制/哨兵/集群底层原理)
    推断统计|显著性水平|无偏抽样
    c/c++: 如何区分c和c++
    详解module.exports与exports,export与export default,import 与require
  • 原文地址:https://blog.csdn.net/xiaoshunshi/article/details/126700855