"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.hasZeroOrOneAsteriskCharacter = hasZeroOrOneAsteriskCharacter;
exports.pathIsRelative = pathIsRelative;
exports.tryParsePattern = tryParsePattern;
exports.findBestPatternMatch = findBestPatternMatch;
exports.matchPatternOrExact = matchPatternOrExact;
exports.isString = isString;
exports.matchedText = matchedText;
exports.patternText = patternText;
var _path = _interopRequireDefault(require("path"));
var _debug = require("next/dist/compiled/debug");
function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const log = (0, _debug).debug("next:jsconfig-paths-plugin");
const asterisk = 0x2a;
function hasZeroOrOneAsteriskCharacter(str) {
    let seenAsterisk = false;
    for(let i = 0; i < str.length; i++){
        if (str.charCodeAt(i) === asterisk) {
            if (!seenAsterisk) {
                seenAsterisk = true;
            } else {
                // have already seen asterisk
                return false;
            }
        }
    }
    return true;
}
function pathIsRelative(testPath) {
    return /^\.\.?($|[\\/])/.test(testPath);
}
function tryParsePattern(pattern) {
    // This should be verified outside of here and a proper error thrown.
    const indexOfStar = pattern.indexOf("*");
    return indexOfStar === -1 ? undefined : {
        prefix: pattern.slice(0, indexOfStar),
        suffix: pattern.slice(indexOfStar + 1)
    };
}
function isPatternMatch({ prefix , suffix  }, candidate) {
    return candidate.length >= prefix.length + suffix.length && candidate.startsWith(prefix) && candidate.endsWith(suffix);
}
function findBestPatternMatch(values, getPattern, candidate) {
    let matchedValue;
    // use length of prefix as betterness criteria
    let longestMatchPrefixLength = -1;
    for (const v of values){
        const pattern = getPattern(v);
        if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
            longestMatchPrefixLength = pattern.prefix.length;
            matchedValue = v;
        }
    }
    return matchedValue;
}
function matchPatternOrExact(patternStrings, candidate) {
    const patterns = [];
    for (const patternString of patternStrings){
        if (!hasZeroOrOneAsteriskCharacter(patternString)) continue;
        const pattern = tryParsePattern(patternString);
        if (pattern) {
            patterns.push(pattern);
        } else if (patternString === candidate) {
            // pattern was matched as is - no need to search further
            return patternString;
        }
    }
    return findBestPatternMatch(patterns, (_)=>_, candidate);
}
function isString(text) {
    return typeof text === "string";
}
function matchedText(pattern, candidate) {
    return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
function patternText({ prefix , suffix  }) {
    return `${prefix}*${suffix}`;
}
/**
 * Calls the iterator function for each entry of the array
 * until the first result or error is reached
 */ function forEachBail(array, iterator, callback) {
    if (array.length === 0) return callback();
    let i = 0;
    const next = ()=>{
        let loop = undefined;
        iterator(array[i++], (err, result)=>{
            if (err || result !== undefined || i >= array.length) {
                return callback(err, result);
            }
            if (loop === false) while(next());
            loop = true;
        });
        if (!loop) loop = false;
        return loop;
    };
    while(next());
}
const NODE_MODULES_REGEX = /node_modules/;
class JsConfigPathsPlugin {
    constructor(paths, resolvedBaseUrl){
        this.paths = paths;
        this.resolvedBaseUrl = resolvedBaseUrl;
        this.jsConfigPlugin = true;
        log("tsconfig.json or jsconfig.json paths: %O", paths);
        log("resolved baseUrl: %s", resolvedBaseUrl);
    }
    apply(resolver) {
        const target = resolver.ensureHook("resolve");
        resolver.getHook("described-resolve").tapAsync("JsConfigPathsPlugin", (request, resolveContext, callback)=>{
            const paths = this.paths;
            const pathsKeys = Object.keys(paths);
            // If no aliases are added bail out
            if (pathsKeys.length === 0) {
                log("paths are empty, bailing out");
                return callback();
            }
            const moduleName = request.request;
            // Exclude node_modules from paths support (speeds up resolving)
            if (request.path.match(NODE_MODULES_REGEX)) {
                log("skipping request as it is inside node_modules %s", moduleName);
                return callback();
            }
            if (_path.default.posix.isAbsolute(moduleName) || process.platform === "win32" && _path.default.win32.isAbsolute(moduleName)) {
                log("skipping request as it is an absolute path %s", moduleName);
                return callback();
            }
            if (pathIsRelative(moduleName)) {
                log("skipping request as it is a relative path %s", moduleName);
                return callback();
            }
            // log('starting to resolve request %s', moduleName)
            // If the module name does not match any of the patterns in `paths` we hand off resolving to webpack
            const matchedPattern = matchPatternOrExact(pathsKeys, moduleName);
            if (!matchedPattern) {
                log("moduleName did not match any paths pattern %s", moduleName);
                return callback();
            }
            const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName);
            const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern);
            let triedPaths = [];
            forEachBail(paths[matchedPatternText], (subst, pathCallback)=>{
                const curPath = matchedStar ? subst.replace("*", matchedStar) : subst;
                // Ensure .d.ts is not matched
                if (curPath.endsWith(".d.ts")) {
                    // try next path candidate
                    return pathCallback();
                }
                const candidate = _path.default.join(this.resolvedBaseUrl, curPath);
                const obj = Object.assign({}, request, {
                    request: candidate
                });
                resolver.doResolve(target, obj, `Aliased with tsconfig.json or jsconfig.json ${matchedPatternText} to ${candidate}`, resolveContext, (resolverErr, resolverResult)=>{
                    if (resolverErr || resolverResult === undefined) {
                        triedPaths.push(candidate);
                        // try next path candidate
                        return pathCallback();
                    }
                    return pathCallback(resolverErr, resolverResult);
                });
            }, callback);
        });
    }
}
exports.JsConfigPathsPlugin = JsConfigPathsPlugin;

//# sourceMappingURL=jsconfig-paths-plugin.js.map