copy-outer-components-for-independent.js 10.8 KB
Newer Older
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
const path = require('path');
const Analyze = require('./analyze');
const { wxComponentsStr, outerComponents, weuiComponentStr } = require('./constant');
const { generateAsset } = require('./util');
const {
    collectAllOutSideComponentsMap,
    getIndependentPkgRoots,
    getIndependentEntryPages,
    getGlobalComponentKeyByGlobalComponentPath,
    copyAllWxComponentsFiles,
    collectPkgCopyFiles,
    getNewComponentPathInIndependentPkg,
    getJsonByPageOrComponentPath
} = require('./util');

// 原则:原生组件不允许使用全局组件

// 忽略原生组件(wxComponents)使用全局组件的情况

function recurIndependentJson (independentRoot, independentPages, sourceRepo, handler, cacheSet = new Set()) {
    independentPages.forEach(independentPage => {
        // 避免无限递归
        const recured = cacheSet.has(independentPage);
        if (recured) return;

        cacheSet.add(independentPage);

        // 关键:映射到独立分包下面的组件路径
        const newComponentPath = getNewComponentPathInIndependentPkg(independentRoot, independentPage);
        const {
            content: jsonObj, fromAssetsFlag
        } = getJsonByPageOrComponentPath(newComponentPath, sourceRepo);
        if (!jsonObj) {
            console.error('independent.error.recurIndependentJson', newComponentPath);
            return;
        }

        // 处理 newComponentPath.json 中的包外组件路径

        const usingComponents = jsonObj.usingComponents || {};
        for (let componentKey in usingComponents) {
            const componentPath = usingComponents[componentKey];
            if (componentPath.indexOf(weuiComponentStr) >= 0) {
                continue;
            }
            handler(usingComponents, componentKey);
            recurIndependentJson(independentRoot, [componentPath], sourceRepo, handler, cacheSet);
        }

        if (fromAssetsFlag) {
            sourceRepo.compilationAssets[`${newComponentPath}.json`] = generateAsset(JSON.stringify(jsonObj));
        }
    });
}

// TODO watch 只针对发生变化的文件
class Index extends Analyze {
    init () {
        const emitFileMap = this.emitFileMap;
        const independentRoots = getIndependentPkgRoots();
        const outSideComponentsMap = {}; // 引用的包外组件(vue组件和原生组件)
        independentRoots.forEach(independentRoot => {
            const independentPages = getIndependentEntryPages(independentRoot);
            let cacheSet = new Set();
            let cacheGlobalUsageMap = new Map();
            // 收集包外组件
S
songyu 已提交
67 68
            const collectOuterCompos = independentPage => collectAllOutSideComponentsMap(independentRoot, emitFileMap, independentPage, cacheSet, cacheGlobalUsageMap);
            independentPages.forEach(collectOuterCompos);
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

            // 如果是原生组件,则忽略wxComponents以外的组件
            cacheSet = [...cacheSet].filter(componentPath => {
                if (componentPath.startsWith('/')) {
                    componentPath = componentPath.substring(1);
                }
                const isVueComponent = emitFileMap.get(`${componentPath}.json`);
                const isWxComponent = componentPath.startsWith(`${wxComponentsStr}`);
                // TODO weui组件
                return !!(isVueComponent || isWxComponent);
            });
            // 暂时只收集包外的vue组件和原生组件(wxComponents)
            outSideComponentsMap[independentRoot] = {
                outerComponentSet: new Set(cacheSet),
                globalComponentsMap: cacheGlobalUsageMap
            };
        });

        // 独立分包使用到[全局组件]和[入口页面]作为[文件依赖分析]的入口
        const componentFileCache = {};
        for (let independentRoot in outSideComponentsMap) {
            const info = outSideComponentsMap[independentRoot];
            this.copyAndUpdateJson(independentRoot, info, componentFileCache);
        }
    }

    copyAndUpdateJson (independentRoot, info, componentFileCache) {
        const { outerComponentSet, globalComponentsMap } = info;
        this.getDependFiles({ [independentRoot]: outerComponentSet }, componentFileCache, true);
        // 1. 先复制
        this.copyOuterComponents(independentRoot, outerComponentSet, componentFileCache);
        // 2. 更新组件json中包外组件引用路径
        this.updateIndependentJson(independentRoot, globalComponentsMap);
    }

    updateIndependentJson (independentRoot, globalComponentsMap) {
        // 1. 先添加全局组件依赖
        this.addGlobalComponentReference(independentRoot, globalComponentsMap);
        // 2. 更新显示引用包外组件路径
        this.updateOuterComponentReference(independentRoot);
    }

    // pages/chat-im/vueOuterComponents/components/navigation-bar.json mini-icon引用出错?
    // 先处理全局组件:将全局组件引用添加到json文件中
    // 整体思路:
    // 1. 在init函数中,收集了独立分包用到的所有全局组件(包括包外组件用到的全局组件),
    // 2. 保存全局组件被那些页面或者组件使用
    // 3. 复制包外组件(全局组件只是包外组件的一部分)
    // 4. 由于独立分包不能使用全局组件,所以该方法将全局组件路径添加到独立分包下的组件或页面关联的json文件中,确保可以访问到。
    addGlobalComponentReference (independentRoot, globalComponentsMap) {
        const globalComponentInfoMap = getGlobalComponentKeyByGlobalComponentPath();
        for (let [globalComponentPath, componentSetWhoUsedGlobalCompo] of globalComponentsMap) {
121
            // weui 暂时先不处理
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
            if (globalComponentPath.indexOf(weuiComponentStr) >= 0) {
                continue;
            }

            if (globalComponentPath.indexOf(weuiComponentStr) >= 0) {
                continue;
            }
            const componentKey = globalComponentInfoMap[globalComponentPath];

            if (globalComponentPath.startsWith('/')) {
                globalComponentPath = globalComponentPath.substring(1);
            }

            const globalComponentReplacePath = getNewComponentPathInIndependentPkg(independentRoot, globalComponentPath);

            if (globalComponentReplacePath === globalComponentPath) return; // 理论上不会走

            const compilationAssets = this.compilation.assets;
            // pages均为vue文件
            [...componentSetWhoUsedGlobalCompo].forEach(componentWhoUsedGlobalCompo => {
                // 获取在 independentRoot 目录下的新路径(独立分包内引用的包外组件也有可能用到全局组件,获取该包外组件在独立分包内的新路径)
                componentWhoUsedGlobalCompo = getNewComponentPathInIndependentPkg(independentRoot, componentWhoUsedGlobalCompo);

                // 获取该组件json文件内容
                // 分包内的vue组件对应json存储在emitFileMap中
                // 分包外vue组件由于前面的复制,内容保存在assets中
                const {
                    content: pageObj,
                    fromAssetsFlag // json内容是否来自assets(还可能来自emiFileMap)
                } = getJsonByPageOrComponentPath(componentWhoUsedGlobalCompo, {
                    emitFileMap: this.emitFileMap, compilationAssets
                });

                const usingComponents = pageObj.usingComponents || {};
                // 如果没有同名标签,则使用全局组件(优先使用显示声明的标签-针对同名标签)
                if (!usingComponents[componentKey]) {
                    usingComponents[componentKey] = `/${globalComponentReplacePath}`;
                }

                // 如果json内容来自emiFileMap(可能还没同步到assets上
                // emitFileMap 后面会统一挂到assets上
                if (!fromAssetsFlag) return;

S
songyu 已提交
165
                delete pageObj.usingGlobalComponents
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
                compilationAssets[`${componentWhoUsedGlobalCompo}.json`] = generateAsset(JSON.stringify(pageObj));
            });
        }
    }

    updateOuterComponentReference (independentRoot) {
        const sourceRepo = {
            emitFileMap: this.emitFileMap,
            compilationAssets: this.compilation.assets
        };
        const independentPages = getIndependentEntryPages(independentRoot);
        recurIndependentJson(independentRoot, independentPages, sourceRepo, (usingComponents, componentKey) => {
            const componentPath = usingComponents[componentKey];
            const newComponentPath = getNewComponentPathInIndependentPkg(independentRoot, componentPath);
            if (newComponentPath && newComponentPath !== componentPath) {
                usingComponents[componentKey] = `/${newComponentPath}`;
            }
        });
    }

    copyOuterComponents (independentRoot, outerComponentSet, componentFileCache) {
        let copyFiles = collectPkgCopyFiles(outerComponentSet, componentFileCache);
        const thisCompilationAssets = this.compilation.assets;
        // TODO 组件依赖分许的时候需要记录 绝对路径(js/css/wxml) 进行模块引用的文件,输出后需要更改为相对路径,
        copyAllWxComponentsFiles(independentRoot, copyFiles, (originalFilePath, targetPath, relativePath) => {
            // 原生组件
            if (relativePath.indexOf(wxComponentsStr) >= 0) {
                return this.copyWxComponent(independentRoot, originalFilePath, targetPath);
            }
            // vue组件
            const assetInfo = thisCompilationAssets[relativePath];
            let assetSource = assetInfo && assetInfo.source();

            // json文件此时还没有同步到 assets 上
            if (!assetSource && relativePath.endsWith('.json')) {
                assetSource = JSON.stringify(this.emitFileMap.get(relativePath));
            }

            if (!assetSource) {
                console.error('independent.error', 'invalid assetSource');
            }

            const targetPrefix = `${independentRoot}/${outerComponents}`;
            const targetJsAssetName = `${targetPrefix}/${relativePath}`;

            if (relativePath.endsWith('.js')) {
                const originalAsset = thisCompilationAssets[relativePath];
                const originalSource = originalAsset && originalAsset.source;
                // 见 generate-component
                const __$wrappered = originalSource && originalSource.__$wrappered;
                if (__$wrappered) {
                    return;
                }

                const relativeToDist = path.relative(path.dirname(targetJsAssetName), `${independentRoot}/common/index.js`);
                assetSource = `require('${relativeToDist}');${assetSource}`;
            }

            thisCompilationAssets[targetJsAssetName] = generateAsset(assetSource);
        });
    }
}

module.exports = Index;