All files / js-import-sort transform.js

96.25% Statements 77/80
90% Branches 27/30
100% Functions 11/11
96.25% Lines 77/80
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 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  1x 1x 1x   1x         20x         20x 9x     20x 14x 14x 14x 10x 10x   4x 4x 4x       20x   20x     5x 5x 5x   5x   1x     4x   4x 27x 27x 27x 20x           27x 24x 10x       14x 4x           10x 10x 3x 1x   3x 1x       10x 9x                 4x 4x 4x 4x   4x 20x 3x   17x     17x 6x     11x       4x 4x 4x 4x   4x   4x     16x 8x   16x 20x   2x           4x 4x 4x 4x   4x 4x 4x 28x 28x     4x 4x 4x 4x      
'use strict';
const builtInModules = require('./lib/builtInModules').default;
const thirdPartyModules = require('./lib/thirdPartyModules').default;
const importSortFunc = require('./lib/importSort').default;
 
module.exports = function(file, api) {
    function createImportStatement(moduleName, variableName, propName) {
        let declaration, variable, idIdentifier, nameIdentifier;
 
        // if no variable name, return `import '<module>'`
        Iif (!variableName) {
            declaration = j.importDeclaration([], j.literal(moduleName));
            return declaration;
        }
 
        const variableIds = variableName.map(function(v) {
            return j.importSpecifier(j.identifier(v.imported), j.identifier(v.local));
        });
 
        if (propName) {
            var namespace = propName.namespace;
            var name = propName.name;
            if (name) {
                var identifier = j.identifier(name);
                variableIds.unshift(j.importDefaultSpecifier(identifier, identifier));
            }
            else Eif (namespace) {
                var identifier = j.identifier(namespace);
                variableIds.unshift(j.importNamespaceSpecifier(identifier, identifier));
            }
        }
 
        declaration = j.importDeclaration(variableIds, j.literal(moduleName));
 
        return declaration;
    }
 
    const j = api.jscodeshift;
    const root = j(file.source);
    const imports = root.find(j.ImportDeclaration);
 
    if (imports.size() === 0) {
        // Nothing to do, leave as is
        return file.source;
    }
 
    const newImports = {};
 
    imports.forEach(i => {
        const node = i.node;
        const source = node.source.value;
        if (!newImports[source]) {
            newImports[source] = {
                'default': null,
                'specifiers': [],
            };
        }
 
        node.specifiers.forEach(specifier => {
            if (specifier.type === 'ImportDefaultSpecifier') {
                newImports[source].default = {
                    name: specifier.local.name
                };
            }
            else if (specifier.type === 'ImportNamespaceSpecifier') {
                newImports[source].default = {
                    namespace: specifier.local.name,
                };
            }
            else {
                // Check the specifier has not all ready been placed in
                let found = false
                newImports[source].specifiers.forEach(spec => {
                    if (spec.local === specifier.local.name) {
                        found = true;
                    }
                    if (spec.imported === specifier.imported.name) {
                        found = true;
                    }
                });
 
                if (!found) {
                    newImports[source].specifiers.push({
                        local: specifier.local.name,
                        imported: specifier.imported.name
                    });
                }
            }
        });
    });
 
    const nodeModules = {};
    const thirdPartyImports = {};
    const firstPartyImports = {};
    const localImports = {};
 
    Object.keys(newImports).forEach((key)=> {
        if (builtInModules.indexOf(key) > -1) {
            nodeModules[key] = newImports[key];
        }
        else Iif (thirdPartyModules.indexOf(key) > -1) {
            thirdPartyImports[key] = newImports[key];
        }
        else if (key.startsWith('.')) {
            localImports[key] = newImports[key]
        }
        else {
            firstPartyImports[key] = newImports[key];
        }
    });
 
    const nodekeys = Object.keys(nodeModules).sort(importSortFunc).reverse();
    const thirdkeys = Object.keys(thirdPartyImports).sort(importSortFunc).reverse();
    const firstkeys = Object.keys(firstPartyImports).sort(importSortFunc).reverse();
    const localkeys = Object.keys(localImports).sort(importSortFunc).reverse();
 
    const blankLine = '//$$BLANK_LINE';
 
    const outputImports = [];
 
    function pushImports(keys) {
        if (keys.length > 0) {
            outputImports.push(blankLine);
        }
        keys.forEach(key => {
            outputImports.push(
                createImportStatement(key,
                    newImports[key].specifiers.sort((a, b)=>(a.imported.localeCompare(b.imported))),
                    newImports[key].default)
            );
        });
    }
 
    pushImports(localkeys);
    pushImports(firstkeys);
    pushImports(thirdkeys);
    pushImports(nodekeys);
 
    const  comments = root.find(j.Program).get('body', 0).node.comments;
    root.find(j.ImportDeclaration).remove();
    outputImports.forEach(x => {
        const body = root.get().value.program.body;
        body.unshift(x);
    });
 
    root.get().node.comments = comments;
    let source = root.toSource({ quote: 'single' });
    source =  source.replace(/\/\/\$\$BLANK_LINE\n\n/g, '//$$$BLANK_LINE\n');
    return source.replace(/\/\/\$\$BLANK_LINE/g, '');
};