all files / src/ index.js

100% Statements 78/78
100% Branches 18/18
100% Functions 5/5
100% Lines 71/71
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     13×   12×     14×   14×         14×             12× 12× 12×       15×     13× 13×   13×     10×       12×   12× 11×         12×         1483×     12×                               32×            
import requireHacker from 'require-hacker'
import { browserify, unbrowserify } from 'ethical-utility-browserifier'
import { requireModule, getAppPrefix, isRelativePackage } from 'ethical-utility-resolve-module-node'
import { absolute, relative } from 'ethical-utility-path'
import resolveCSSModule from './resolve-css.js'
import resolveJSModule from './resolve-js.js'
 
let moduleID = 0
const cache = {}
 
const resolveModule = (request, parent) => {
    if (request.endsWith('.css')) {
        return resolveCSSModule(request, parent)
    }
    return resolveJSModule(request, parent)
}
 
const resolveCache = (cached, modules, exclude) => {
    const { id, key, alias, source, path } = cached
 
    if (typeof require.cache[path + '.*'] === 'object') {
        global
        .__ethical_server_middleware_module_supplier__
        [path] = require.cache[path + '.*'].exports
    }
 
    if (exclude[id]) {
        const stubbedSource = `
            module.exports = (
                global.__ethical_server_middleware_module_supplier__['${path}']
            )
        `
        return { source: stubbedSource, path }
    }
 
    modules.push(cached)
    exclude[id] = 1
    return { source, path }
}
 
const handler = (modules, exclude) => (request, { filename: parent }) => {
 
    if (cache[request]) {
        return resolveCache(cache[request], modules, exclude)
    }
 
    const resolvedModule = resolveModule(request, parent)
    const { key, alias, path, source: getSource } = resolvedModule
 
    if (cache[key]) {
        return resolveCache(cache[key], modules, exclude)
    }
 
    const source = getSource(path)
    const id =  moduleID++
    cache[key] = { id, key, alias, source, path }
 
    return resolveCache(cache[key], modules, exclude)
}
 
const captureModules = (exclude) => {
    const modules = []
    const globalHook = requireHacker.global_hook('*', handler(modules, exclude))
    return () => {
        const cachedModules = modules.map(cached => {
            const { id, key, alias, source, path } = cached
 
            if (typeof require.cache[path + '.*'] === 'object') {
                global
                .__ethical_server_middleware_module_supplier__
                [path] = require.cache[path + '.*'].exports
            }
 
            return { id, key, alias, source }
        })
        globalHook.unmount()
        return cachedModules
    }
}
 
const decacheNode = () => {
    Object.keys(require.cache).forEach(key => { delete require.cache[key] })
    global.__ethical_server_middleware_module_supplier__ = {}
}
 
const moduleCapturerMiddleware = async (ctx, next, config) => {
 
    if (typeof ctx.response.body !== 'undefined') return await next()
 
    const { main, path = 'module' } = config
 
    if (ctx.request.path !== '/' + path) return await next()
 
    const { request: { query: { entry = absolute(main), exclude = '' } } } = ctx
 
    browserify()
    const excluded = {}
    exclude.split(',').forEach(id => excluded[id] = 1)
    const releaseModules = captureModules(excluded)
    try {
        requireModule(entry)
    } catch (e) {
        console.error(e)
    }
    const modules = releaseModules()
 
    unbrowserify()
 
    const [ mainModule = {} ] = modules
 
    if (main === relative(entry)) {
        mainModule.alias = mainModule.key
        mainModule.key = getAppPrefix()
    }
 
    ctx.response.body = JSON.stringify(modules)
    ctx.response.set('Content-Type', 'application/json')
 
    await next()
}
 
const moduleCapturerMiddlewareInit = (config = {}) => {
    decacheNode()
    return async (ctx, next) => (
        await moduleCapturerMiddleware(ctx, next, config)
    )
}
 
export default moduleCapturerMiddlewareInit