239 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
"use strict";
 | 
						|
Object.defineProperty(exports, "__esModule", {
 | 
						|
    value: true
 | 
						|
});
 | 
						|
Object.defineProperty(exports, "DefaultRouteMatcherManager", {
 | 
						|
    enumerable: true,
 | 
						|
    get: function() {
 | 
						|
        return DefaultRouteMatcherManager;
 | 
						|
    }
 | 
						|
});
 | 
						|
const _utils = require("../../shared/lib/router/utils");
 | 
						|
const _localeroutematcher = require("../route-matchers/locale-route-matcher");
 | 
						|
const _ensureleadingslash = require("../../shared/lib/page-path/ensure-leading-slash");
 | 
						|
const _detachedpromise = require("../../lib/detached-promise");
 | 
						|
class DefaultRouteMatcherManager {
 | 
						|
    /**
 | 
						|
   * When this value changes, it indicates that a change has been introduced
 | 
						|
   * that requires recompilation.
 | 
						|
   */ get compilationID() {
 | 
						|
        return this.providers.length;
 | 
						|
    }
 | 
						|
    async waitTillReady() {
 | 
						|
        if (this.waitTillReadyPromise) {
 | 
						|
            await this.waitTillReadyPromise;
 | 
						|
            delete this.waitTillReadyPromise;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    async reload() {
 | 
						|
        const { promise, resolve, reject } = new _detachedpromise.DetachedPromise();
 | 
						|
        this.waitTillReadyPromise = promise;
 | 
						|
        // Grab the compilation ID for this run, we'll verify it at the end to
 | 
						|
        // ensure that if any routes were added before reloading is finished that
 | 
						|
        // we error out.
 | 
						|
        const compilationID = this.compilationID;
 | 
						|
        try {
 | 
						|
            // Collect all the matchers from each provider.
 | 
						|
            const matchers = [];
 | 
						|
            // Get all the providers matchers.
 | 
						|
            const providersMatchers = await Promise.all(this.providers.map((provider)=>provider.matchers()));
 | 
						|
            // Use this to detect duplicate pathnames.
 | 
						|
            const all = new Map();
 | 
						|
            const duplicates = {};
 | 
						|
            for (const providerMatchers of providersMatchers){
 | 
						|
                for (const matcher of providerMatchers){
 | 
						|
                    // Reset duplicated matches when reloading from pages conflicting state.
 | 
						|
                    if (matcher.duplicated) delete matcher.duplicated;
 | 
						|
                    // Test to see if the matcher being added is a duplicate.
 | 
						|
                    const duplicate = all.get(matcher.definition.pathname);
 | 
						|
                    if (duplicate) {
 | 
						|
                        // This looks a little weird, but essentially if the pathname
 | 
						|
                        // already exists in the duplicates map, then we got that array
 | 
						|
                        // reference. Otherwise, we create a new array with the original
 | 
						|
                        // duplicate first. Then we push the new matcher into the duplicate
 | 
						|
                        // array, and reset it to the duplicates object (which may be a
 | 
						|
                        // no-op if the pathname already existed in the duplicates object).
 | 
						|
                        // Then we set the array of duplicates on both the original
 | 
						|
                        // duplicate object and the new one, so we can keep them in sync.
 | 
						|
                        // If a new duplicate is found, and it matches an existing pathname,
 | 
						|
                        // the retrieval of the `other` will actually return the array
 | 
						|
                        // reference used by all other duplicates. This is why ReadonlyArray
 | 
						|
                        // is so important! Array's are always references!
 | 
						|
                        const others = duplicates[matcher.definition.pathname] ?? [
 | 
						|
                            duplicate
 | 
						|
                        ];
 | 
						|
                        others.push(matcher);
 | 
						|
                        duplicates[matcher.definition.pathname] = others;
 | 
						|
                        // Add duplicated details to each route.
 | 
						|
                        duplicate.duplicated = others;
 | 
						|
                        matcher.duplicated = others;
 | 
						|
                    // TODO: see if we should error for duplicates in production?
 | 
						|
                    }
 | 
						|
                    matchers.push(matcher);
 | 
						|
                    // Add the matcher's pathname to the set.
 | 
						|
                    all.set(matcher.definition.pathname, matcher);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Update the duplicate matchers. This is used in the development manager
 | 
						|
            // to warn about duplicates.
 | 
						|
            this.matchers.duplicates = duplicates;
 | 
						|
            // If the cache is the same as what we just parsed, we can exit now. We
 | 
						|
            // can tell by using the `===` which compares object identity, which for
 | 
						|
            // the manifest matchers, will return the same matcher each time.
 | 
						|
            if (this.previousMatchers.length === matchers.length && this.previousMatchers.every((cachedMatcher, index)=>cachedMatcher === matchers[index])) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            this.previousMatchers = matchers;
 | 
						|
            // For matchers that are for static routes, filter them now.
 | 
						|
            this.matchers.static = matchers.filter((matcher)=>!matcher.isDynamic);
 | 
						|
            // For matchers that are for dynamic routes, filter them and sort them now.
 | 
						|
            const dynamic = matchers.filter((matcher)=>matcher.isDynamic);
 | 
						|
            // As `getSortedRoutes` only takes an array of strings, we need to create
 | 
						|
            // a map of the pathnames (used for sorting) and the matchers. When we
 | 
						|
            // have locales, there may be multiple matches for the same pathname. To
 | 
						|
            // handle this, we keep a map of all the indexes (in `reference`) and
 | 
						|
            // merge them in later.
 | 
						|
            const reference = new Map();
 | 
						|
            const pathnames = new Array();
 | 
						|
            for(let index = 0; index < dynamic.length; index++){
 | 
						|
                // Grab the pathname from the definition.
 | 
						|
                const pathname = dynamic[index].definition.pathname;
 | 
						|
                // Grab the index in the dynamic array, push it into the reference.
 | 
						|
                const indexes = reference.get(pathname) ?? [];
 | 
						|
                indexes.push(index);
 | 
						|
                // If this is the first one set it. If it isn't, we don't need to
 | 
						|
                // because pushing above on the array will mutate the array already
 | 
						|
                // stored there because array's are always a reference!
 | 
						|
                if (indexes.length === 1) reference.set(pathname, indexes);
 | 
						|
                else continue;
 | 
						|
                pathnames.push(pathname);
 | 
						|
            }
 | 
						|
            // Sort the array of pathnames.
 | 
						|
            const sorted = (0, _utils.getSortedRoutes)(pathnames);
 | 
						|
            // For each of the sorted pathnames, iterate over them, grabbing the list
 | 
						|
            // of indexes and merging them back into the new `sortedDynamicMatchers`
 | 
						|
            // array. The order of the same matching pathname doesn't matter because
 | 
						|
            // they will have other matching characteristics (like the locale) that
 | 
						|
            // is considered.
 | 
						|
            const sortedDynamicMatchers = [];
 | 
						|
            for (const pathname of sorted){
 | 
						|
                const indexes = reference.get(pathname);
 | 
						|
                if (!Array.isArray(indexes)) {
 | 
						|
                    throw Object.defineProperty(new Error('Invariant: expected to find identity in indexes map'), "__NEXT_ERROR_CODE", {
 | 
						|
                        value: "E271",
 | 
						|
                        enumerable: false,
 | 
						|
                        configurable: true
 | 
						|
                    });
 | 
						|
                }
 | 
						|
                const dynamicMatches = indexes.map((index)=>dynamic[index]);
 | 
						|
                sortedDynamicMatchers.push(...dynamicMatches);
 | 
						|
            }
 | 
						|
            this.matchers.dynamic = sortedDynamicMatchers;
 | 
						|
            // This means that there was a new matcher pushed while we were waiting
 | 
						|
            if (this.compilationID !== compilationID) {
 | 
						|
                throw Object.defineProperty(new Error('Invariant: expected compilation to finish before new matchers were added, possible missing await'), "__NEXT_ERROR_CODE", {
 | 
						|
                    value: "E242",
 | 
						|
                    enumerable: false,
 | 
						|
                    configurable: true
 | 
						|
                });
 | 
						|
            }
 | 
						|
        } catch (err) {
 | 
						|
            reject(err);
 | 
						|
        } finally{
 | 
						|
            // The compilation ID matched, so mark the complication as finished.
 | 
						|
            this.lastCompilationID = compilationID;
 | 
						|
            resolve();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    push(provider) {
 | 
						|
        this.providers.push(provider);
 | 
						|
    }
 | 
						|
    async test(pathname, options) {
 | 
						|
        // See if there's a match for the pathname...
 | 
						|
        const match = await this.match(pathname, options);
 | 
						|
        // This default implementation only needs to check to see if there _was_ a
 | 
						|
        // match. The development matcher actually changes it's behavior by not
 | 
						|
        // recompiling the routes.
 | 
						|
        return match !== null;
 | 
						|
    }
 | 
						|
    async match(pathname, options) {
 | 
						|
        // "Iterate" over the match options. Once we found a single match, exit with
 | 
						|
        // it, otherwise return null below. If no match is found, the inner block
 | 
						|
        // won't be called.
 | 
						|
        for await (const match of this.matchAll(pathname, options)){
 | 
						|
            return match;
 | 
						|
        }
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
   * This is a point for other managers to override to inject other checking
 | 
						|
   * behavior like duplicate route checking on a per-request basis.
 | 
						|
   *
 | 
						|
   * @param pathname the pathname to validate against
 | 
						|
   * @param matcher the matcher to validate/test with
 | 
						|
   * @returns the match if found
 | 
						|
   */ validate(pathname, matcher, options) {
 | 
						|
        var _options_i18n;
 | 
						|
        if (matcher instanceof _localeroutematcher.LocaleRouteMatcher) {
 | 
						|
            return matcher.match(pathname, options);
 | 
						|
        }
 | 
						|
        // If the locale was inferred from the default locale, then it will have
 | 
						|
        // already added a locale to the pathname. We need to remove it before
 | 
						|
        // matching because this matcher is not locale aware.
 | 
						|
        if ((_options_i18n = options.i18n) == null ? void 0 : _options_i18n.inferredFromDefault) {
 | 
						|
            return matcher.match(options.i18n.pathname);
 | 
						|
        }
 | 
						|
        return matcher.match(pathname);
 | 
						|
    }
 | 
						|
    async *matchAll(pathname, options) {
 | 
						|
        // Guard against the matcher manager from being run before it needs to be
 | 
						|
        // recompiled. This was preferred to re-running the compilation here because
 | 
						|
        // it should be re-ran only when it changes. If a match is attempted before
 | 
						|
        // this is done, it indicates that there is a case where a provider is added
 | 
						|
        // before it was recompiled (an error). We also don't want to affect request
 | 
						|
        // times.
 | 
						|
        if (this.lastCompilationID !== this.compilationID) {
 | 
						|
            throw Object.defineProperty(new Error('Invariant: expected routes to have been loaded before match'), "__NEXT_ERROR_CODE", {
 | 
						|
                value: "E235",
 | 
						|
                enumerable: false,
 | 
						|
                configurable: true
 | 
						|
            });
 | 
						|
        }
 | 
						|
        // Ensure that path matching is done with a leading slash.
 | 
						|
        pathname = (0, _ensureleadingslash.ensureLeadingSlash)(pathname);
 | 
						|
        // If this pathname doesn't look like a dynamic route, and this pathname is
 | 
						|
        // listed in the normalized list of routes, then return it. This ensures
 | 
						|
        // that when a route like `/user/[id]` is encountered, it doesn't just match
 | 
						|
        // with the list of normalized routes.
 | 
						|
        if (!(0, _utils.isDynamicRoute)(pathname)) {
 | 
						|
            for (const matcher of this.matchers.static){
 | 
						|
                const match = this.validate(pathname, matcher, options);
 | 
						|
                if (!match) continue;
 | 
						|
                yield match;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If we should skip handling dynamic routes, exit now.
 | 
						|
        if (options == null ? void 0 : options.skipDynamic) return null;
 | 
						|
        // Loop over the dynamic matchers, yielding each match.
 | 
						|
        for (const matcher of this.matchers.dynamic){
 | 
						|
            const match = this.validate(pathname, matcher, options);
 | 
						|
            if (!match) continue;
 | 
						|
            yield match;
 | 
						|
        }
 | 
						|
        // We tried direct matching against the pathname and against all the dynamic
 | 
						|
        // paths, so there was no match.
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
    constructor(){
 | 
						|
        this.providers = [];
 | 
						|
        this.matchers = {
 | 
						|
            static: [],
 | 
						|
            dynamic: [],
 | 
						|
            duplicates: {}
 | 
						|
        };
 | 
						|
        this.lastCompilationID = this.compilationID;
 | 
						|
        this.previousMatchers = [];
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//# sourceMappingURL=default-route-matcher-manager.js.map
 |