# weex

# 抽取公共样式

const vueFileReg = /weex-loader/
const cssFileReg = /\.css$/

class WebpackPluginExtractCss {
    constructor(options = {}) {
        this.options = options
        this.cssCommonModules = Object.create(null);
    }

    apply(compiler) { 
        // 编译(compilation)创建之后,执行插件
        compiler.plugin('compilation', (compilation) => {
            compilation.plugin('optimize-tree', (chunks, modules, cb) => {
                const t1 = Date.now();
                console.log('extract css start...');   

                // 抽取公共资源
                this.processAllModules(modules);

                // 开始处理资源
                chunks.forEach(chunk => {
                    this.processChunkModules(chunk.modules);
                })

                const t2 = Date.now()
                console.log('extract css finish in '+(t2-t1)+'ms.')

                cb();
            })
        }) 
    }

    processAllModules(modules) {
        const cssCommonModules = this.cssCommonModules;

        // 抽取公共css模块
        modules.forEach(module => {
            if (cssFileReg.test(module.request)) {
                let resource = this.getResource(module.request);

                if (!cssCommonModules[resource]) {
                    cssCommonModules[resource] = module;
                }
            }
        });

        // 处理重复css模块
        modules.forEach(module => {           
            // 替换css模块
            if (vueFileReg.test(module.request)) {
                module.dependencies.forEach(dep => {
                    if (dep.module && cssFileReg.test(dep.module.request)) {
                        let resource = this.getResource(dep.module.request);
                        dep.module = cssCommonModules[resource];
                    }
                })
            }
        });

        // 删除重复css模块
        this.delRepeatModules(modules);
    }

    processChunkModules(modules) {
        const cssCommonModules = this.cssCommonModules;
        let usedModuleReqs = [];

        // 替换vue文件里的公共css模块依赖
        modules.forEach(module => {
            if (vueFileReg.test(module.request)) {
                module.dependencies.forEach(dep => {
                    if (dep.module && cssFileReg.test(dep.module.request)) {
                        let resource = this.getResource(dep.module.request);
                        dep.module = cssCommonModules[resource];
                        usedModuleReqs.push(cssCommonModules[resource].request);
                    }
                })
            }
        });

        // 删除重复css模块
        this.delRepeatModules(modules)

        // 如果chunk里不包含新的依赖,则需要引进来
        usedModuleReqs = Array.from(new Set(usedModuleReqs));
        this.processNewModules(modules, usedModuleReqs);
    }

    getResource(request) {
        let elements = request.replace(/^-?!+/, "").replace(/!!+/g, "!").split("!");
        return elements.pop();
    }

    processNewModules(modules, usedModuleReqs) {
        const cssCommonModules = this.cssCommonModules;
        const moduleReqs = modules.map(mod => mod.request);

        usedModuleReqs.forEach(req => {
            if (!moduleReqs.includes(req)) {
                let resource = this.getResource(req);
                modules.push(cssCommonModules[resource]);
            }
        });
    }

    delRepeatModules(modules) {
        const cssCommonModules = this.cssCommonModules;
        const repeatCssModules = [];

        modules.forEach(module => {
            Object.keys(cssCommonModules).forEach(resource => {
                if (module.request.indexOf(resource) != -1 && 
                    module.request !== cssCommonModules[resource].request) {
                    repeatCssModules.push(module.request);
                }
            });
        });

        repeatCssModules.forEach(req => {
            let removeIndex = modules.findIndex(module => module.request === req);
            if (removeIndex != -1) {
                modules.splice(removeIndex, 1);
            }
        })
    }
}
module.exports = WebpackPluginExtractCss
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127