react-imported-component
Version: 
I will import your component, and help to handle it
140 lines (139 loc) • 9 kB
JavaScript
/* tslint:disable no-console */
import { __assign, __awaiter, __generator } from "tslib";
import { existsSync } from 'fs';
import { dirname, extname, resolve } from 'path';
// @ts-ignore
import scanDirectory from 'scan-directory';
import { CLIENT_SIDE_ONLY } from '../configuration/constants';
import { getFileContent, getMatchString, getRelative, normalizePath, pWriteFile } from './shared';
var RESOLVE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.mjs'];
var trimImport = function (str) { return str.replace(/['"]/g, ''); };
var getImportMatch = getMatchString("(['\"]?[\\w-/.@]+['\"]?)\\)", 1);
var getImports = function (str) {
    return getImportMatch(str
        // remove comments
        .replace(/\/\*([^\*]*)\*\//gi, '')
        .replace(/\/\/(.*)/gi, '')
        // remove new lines
        .replace(/\n/gi, '')
        // remove spaces?
        .replace(/[\s]+\)/i, ')'));
};
var getComment = getMatchString(/\/\*.*\*\//, 0);
var getChunkName = getMatchString('webpackChunkName: "([^"]*)"', 1);
var clientSideOnly = function (comment) { return comment.indexOf(CLIENT_SIDE_ONLY) >= 0; };
var clearComment = function (str) { return str.replace('webpackPrefetch: true', '').replace('webpackPreload: true', ''); };
var getImportString = function (pattern, selected) {
    return function (str) {
        return getMatchString(pattern, selected)(str).map(function (statement) {
            return {
                name: trimImport(getImports(statement + ')')[0] || ''),
                comment: clearComment(getComment(statement)[0] || ''),
            };
        });
    };
};
export var getDynamicImports = getImportString("import[\\s]?\\((([^)])+['\"]?)\\)", 1);
export var cleanFileContent = function (content) {
    var mapping = [];
    // wrap
    var wrapped = content.replace(new RegExp("import[\\s]?\\((([^)])+['\"]?)\\)", 'g'), function (match) {
        var placement = mapping.push(match) - 1;
        return "imported_" + placement + "_replacement";
    });
    var cleaned = wrapped.replace(new RegExp('//.*', 'g'), '').replace(new RegExp('\\/\\*[\\s\\S]*?\\*\\/', 'gm'), '');
    var unwrapped = cleaned.replace(new RegExp('imported_([\\d]*)_replacement', 'g'), function (_, b) {
        return mapping[+b];
    });
    return unwrapped;
};
var mapImports = function (file, imports) {
    return imports.map(function (dep) {
        var name = dep.name;
        if (name && name.charAt(0) === '.') {
            return __assign(__assign({}, dep), { file: file, name: resolve(dirname(file), name), doNotTransform: false });
        }
        return __assign(__assign({}, dep), { file: file, doNotTransform: true });
    });
};
var rejectSystemFiles = function (test) { return function (file, stats) {
    if (stats.isDirectory()) {
        return !test(file);
    }
    return false;
}; };
var rejectNodeModulesAndDotFolders = function (file) { return !(file.match(/node_modules/) || file.match(/(\/\.\w+)/)); };
export var remapImports = function (data, root, targetDir, getRelativeName, imports, testImport, chunkName) {
    return data
        .map(function (_a) {
        var file = _a.file, content = _a.content;
        return mapImports(file, getDynamicImports(cleanFileContent(content)));
    })
        .forEach(function (importBlock) {
        return importBlock.forEach(function (_a) {
            var name = _a.name, comment = _a.comment, doNotTransform = _a.doNotTransform, file = _a.file;
            var rootName = doNotTransform ? name : getRelativeName(root, name);
            var fileName = doNotTransform ? name : getRelativeName(targetDir, name);
            var sourceName = getRelativeName(root, file);
            if (testImport(rootName, sourceName)) {
                var isClientSideOnly = clientSideOnly(comment);
                var givenChunkName = getChunkName(comment)[0] || '';
                var def = "[() => import(" + comment + "'" + fileName + "'), '" + ((chunkName && chunkName(rootName, sourceName, { chunkName: givenChunkName })) || givenChunkName) + "', '" + rootName + "', " + isClientSideOnly + "] /* from " + sourceName + " */";
                var slot = getRelativeName(root, name);
                // keep the maximal definition
                imports[slot] = !imports[slot] ? def : imports[slot].length > def.length ? imports[slot] : def;
            }
        });
    });
};
export function scanTop(root, start, target) {
    function scan() {
        return __awaiter(this, void 0, void 0, function () {
            var sourceDir, configurationFile, _a, _b, testFolder, _c, testFile, _d, testImport, chunkName, configuration, files, data, imports, targetDir;
            var _this = this;
            return __generator(this, function (_e) {
                switch (_e.label) {
                    case 0:
                        sourceDir = resolve(root, start);
                        console.log('scanning', sourceDir, 'for imports...');
                        configurationFile = resolve(root, '.imported.js');
                        _a = existsSync(configurationFile) ? require(configurationFile) : {}, _b = _a.testFolder, testFolder = _b === void 0 ? rejectNodeModulesAndDotFolders : _b, _c = _a.testFile, testFile = _c === void 0 ? function () { return true; } : _c, _d = _a.testImport, testImport = _d === void 0 ? function () { return true; } : _d, chunkName = _a.chunkName, configuration = _a.configuration;
                        return [4 /*yield*/, scanDirectory(sourceDir, undefined, rejectSystemFiles(testFolder))];
                    case 1:
                        files = (_e.sent())
                            .filter(function (name) { return normalizePath(name).indexOf(target) === -1; })
                            .filter(function (name) { return RESOLVE_EXTENSIONS.indexOf(extname(name)) >= 0; })
                            .filter(function (name) { return testFile(name); })
                            .sort();
                        return [4 /*yield*/, Promise.all(files.map(function (file) { return __awaiter(_this, void 0, void 0, function () {
                                var content;
                                return __generator(this, function (_a) {
                                    switch (_a.label) {
                                        case 0: return [4 /*yield*/, getFileContent(file)];
                                        case 1:
                                            content = _a.sent();
                                            return [2 /*return*/, {
                                                    file: file,
                                                    content: content,
                                                }];
                                    }
                                });
                            }); }))];
                    case 2:
                        data = _e.sent();
                        imports = {};
                        targetDir = resolve(root, dirname(target));
                        remapImports(data, root, targetDir, getRelative, imports, testImport, chunkName);
                        console.log(Object.keys(imports).length + " imports found, saving to " + target);
                        pWriteFile(target, "\n    /* eslint-disable */\n    /* tslint:disable */\n    \n    // generated by react-imported-component, DO NOT EDIT     \n    import {assignImportedComponents} from 'react-imported-component/macro';\n    " + (configuration &&
                            "import {setConfiguration} from 'react-imported-component/boot';\n// as configured in .imported.js\nsetConfiguration(" + JSON.stringify(configuration, null, 2) + ");\n    ") + "    \n    \n    // all your imports are defined here\n    // all, even the ones you tried to hide in comments (that's the cost of making a very fast parser)\n    // to remove any import from here\n    // 1) use magic comment `import(/* client-side */ './myFile')` - and it will disappear\n    // 2) use file filter to ignore specific locations (refer to the README - https://github.com/theKashey/react-imported-component/#server-side-auto-import)\n    // 3) use .imported.js to control this table generation (refer to the README - https://github.com/theKashey/react-imported-component/#-imported-js)\n    \n    const applicationImports = assignImportedComponents([\n" + Object.keys(imports)
                            .map(function (key) { return "      " + imports[key] + ","; })
                            .sort()
                            .join('\n') + "\n    ]);\n    \n    export default applicationImports;\n    \n    // @ts-ignore\n    if (module.hot) {\n       // these imports would make this module a parent for the imported modules.\n       // but this is just a helper - so ignore(and accept!) all updates\n       \n       // @ts-ignore\n       module.hot.accept(() => null);\n    }    \n    ");
                        return [2 /*return*/];
                }
            });
        });
    }
    return scan();
}