import { promises as fs } from "fs";
import { matcher } from "next/dist/compiled/micromatch";
import { extractExportedConstValue, UnsupportedValueError } from "./extract-const-value";
import { parseModule } from "./parse-module";
import * as Log from "../output/log";
import { SERVER_RUNTIME } from "../../lib/constants";
import { checkCustomRoutes } from "../../lib/load-custom-routes";
import { tryToParsePath } from "../../lib/try-to-parse-path";
import { isAPIRoute } from "../../lib/is-api-route";
import { isEdgeRuntime } from "../../lib/is-edge-runtime";
import { RSC_MODULE_TYPES } from "../../shared/lib/constants";
const CLIENT_MODULE_LABEL = `/* __next_internal_client_entry_do_not_use__ */`;
export function getRSCModuleType(source) {
    return source.includes(CLIENT_MODULE_LABEL) ? RSC_MODULE_TYPES.client : RSC_MODULE_TYPES.server;
}
/**
 * Receives a parsed AST from SWC and checks if it belongs to a module that
 * requires a runtime to be specified. Those are:
 *   - Modules with `export function getStaticProps | getServerSideProps`
 *   - Modules with `export { getStaticProps | getServerSideProps } <from ...>`
 *   - Modules with `export const runtime = ...`
 */ function checkExports(swcAST) {
    if (Array.isArray(swcAST == null ? void 0 : swcAST.body)) {
        try {
            let runtime;
            let ssr = false;
            let ssg = false;
            for (const node of swcAST.body){
                var ref11, ref1, ref2, ref3;
                if (node.type === "ExportDeclaration" && ((ref11 = node.declaration) == null ? void 0 : ref11.type) === "VariableDeclaration") {
                    var ref4, ref5;
                    const id = (ref5 = (ref4 = node.declaration) == null ? void 0 : ref4.declarations[0]) == null ? void 0 : ref5.id.value;
                    if (id === "runtime") {
                        var ref6, ref7;
                        runtime = (ref7 = (ref6 = node.declaration) == null ? void 0 : ref6.declarations[0]) == null ? void 0 : ref7.init.value;
                    }
                }
                if (node.type === "ExportDeclaration" && ((ref1 = node.declaration) == null ? void 0 : ref1.type) === "FunctionDeclaration" && [
                    "getStaticProps",
                    "getServerSideProps"
                ].includes((ref2 = node.declaration.identifier) == null ? void 0 : ref2.value)) {
                    ssg = node.declaration.identifier.value === "getStaticProps";
                    ssr = node.declaration.identifier.value === "getServerSideProps";
                }
                if (node.type === "ExportDeclaration" && ((ref3 = node.declaration) == null ? void 0 : ref3.type) === "VariableDeclaration") {
                    var ref8, ref9;
                    const id = (ref9 = (ref8 = node.declaration) == null ? void 0 : ref8.declarations[0]) == null ? void 0 : ref9.id.value;
                    if ([
                        "getStaticProps",
                        "getServerSideProps"
                    ].includes(id)) {
                        ssg = id === "getStaticProps";
                        ssr = id === "getServerSideProps";
                    }
                }
                if (node.type === "ExportNamedDeclaration") {
                    const values = node.specifiers.map((specifier)=>{
                        var ref, ref10;
                        return specifier.type === "ExportSpecifier" && ((ref = specifier.orig) == null ? void 0 : ref.type) === "Identifier" && ((ref10 = specifier.orig) == null ? void 0 : ref10.value);
                    });
                    ssg = values.some((value)=>[
                            "getStaticProps"
                        ].includes(value));
                    ssr = values.some((value)=>[
                            "getServerSideProps"
                        ].includes(value));
                }
            }
            return {
                ssr,
                ssg,
                runtime
            };
        } catch (err) {}
    }
    return {
        ssg: false,
        ssr: false
    };
}
async function tryToReadFile(filePath, shouldThrow) {
    try {
        return await fs.readFile(filePath, {
            encoding: "utf8"
        });
    } catch (error) {
        if (shouldThrow) {
            throw error;
        }
    }
}
function getMiddlewareMatchers(matcherOrMatchers, nextConfig) {
    let matchers = [];
    if (Array.isArray(matcherOrMatchers)) {
        matchers = matcherOrMatchers;
    } else {
        matchers.push(matcherOrMatchers);
    }
    const { i18n  } = nextConfig;
    let routes = matchers.map((m)=>typeof m === "string" ? {
            source: m
        } : m);
    // check before we process the routes and after to ensure
    // they are still valid
    checkCustomRoutes(routes, "middleware");
    routes = routes.map((r)=>{
        let { source  } = r;
        const isRoot = source === "/";
        if ((i18n == null ? void 0 : i18n.locales) && r.locale !== false) {
            source = `/:nextInternalLocale([^/.]{1,})${isRoot ? "" : source}`;
        }
        source = `/:nextData(_next/data/[^/]{1,})?${source}${isRoot ? `(${nextConfig.i18n ? "|\\.json|" : ""}/?index|/?index\\.json)?` : "(.json)?"}`;
        if (nextConfig.basePath) {
            source = `${nextConfig.basePath}${source}`;
        }
        return {
            ...r,
            source
        };
    });
    checkCustomRoutes(routes, "middleware");
    return routes.map((r)=>{
        const { source , ...rest } = r;
        const parsedPage = tryToParsePath(source);
        if (parsedPage.error || !parsedPage.regexStr) {
            throw new Error(`Invalid source: ${source}`);
        }
        return {
            ...rest,
            regexp: parsedPage.regexStr
        };
    });
}
function getMiddlewareConfig(pageFilePath, config, nextConfig) {
    const result = {};
    if (config.matcher) {
        result.matchers = getMiddlewareMatchers(config.matcher, nextConfig);
    }
    if (typeof config.regions === "string" || Array.isArray(config.regions)) {
        result.regions = config.regions;
    } else if (typeof config.regions !== "undefined") {
        Log.warn(`The \`regions\` config was ignored: config must be empty, a string or an array of strings. (${pageFilePath})`);
    }
    if (config.unstable_allowDynamic) {
        result.unstable_allowDynamicGlobs = Array.isArray(config.unstable_allowDynamic) ? config.unstable_allowDynamic : [
            config.unstable_allowDynamic
        ];
        for (const glob of result.unstable_allowDynamicGlobs ?? []){
            try {
                matcher(glob);
            } catch (err) {
                throw new Error(`${pageFilePath} exported 'config.unstable_allowDynamic' contains invalid pattern '${glob}': ${err.message}`);
            }
        }
    }
    return result;
}
let warnedAboutExperimentalEdge = false;
function warnAboutExperimentalEdge() {
    if (warnedAboutExperimentalEdge) {
        return;
    }
    Log.warn(`You are using an experimental edge runtime, the API might change.`);
    warnedAboutExperimentalEdge = true;
}
const warnedUnsupportedValueMap = new Map();
function warnAboutUnsupportedValue(pageFilePath, page, error) {
    if (warnedUnsupportedValueMap.has(pageFilePath)) {
        return;
    }
    Log.warn(`Next.js can't recognize the exported \`config\` field in ` + (page ? `route "${page}"` : `"${pageFilePath}"`) + ":\n" + error.message + (error.path ? ` at "${error.path}"` : "") + ".\n" + "The default config will be used instead.\n" + "Read More - https://nextjs.org/docs/messages/invalid-page-config");
    warnedUnsupportedValueMap.set(pageFilePath, true);
}
/**
 * For a given pageFilePath and nextConfig, if the config supports it, this
 * function will read the file and return the runtime that should be used.
 * It will look into the file content only if the page *requires* a runtime
 * to be specified, that is, when gSSP or gSP is used.
 * Related discussion: https://github.com/vercel/next.js/discussions/34179
 */ export async function getPageStaticInfo(params) {
    var ref;
    const { isDev , pageFilePath , nextConfig , page , pageType  } = params;
    const fileContent = await tryToReadFile(pageFilePath, !isDev) || "";
    if (/runtime|getStaticProps|getServerSideProps|export const config/.test(fileContent)) {
        var ref15;
        const swcAST = await parseModule(pageFilePath, fileContent);
        const { ssg , ssr , runtime  } = checkExports(swcAST);
        const rsc = getRSCModuleType(fileContent);
        // default / failsafe value for config
        let config = {};
        try {
            config = extractExportedConstValue(swcAST, "config");
        } catch (e) {
            if (e instanceof UnsupportedValueError) {
                warnAboutUnsupportedValue(pageFilePath, page, e);
            }
        // `export config` doesn't exist, or other unknown error throw by swc, silence them
        }
        // Currently, we use `export const config = { runtime: '...' }` to specify the page runtime.
        // But in the new app directory, we prefer to use `export const runtime = '...'`
        // and deprecate the old way. To prevent breaking changes for `pages`, we use the exported config
        // as the fallback value.
        let resolvedRuntime = runtime || config.runtime;
        if (typeof resolvedRuntime !== "undefined" && resolvedRuntime !== SERVER_RUNTIME.nodejs && !isEdgeRuntime(resolvedRuntime)) {
            const options = Object.values(SERVER_RUNTIME).join(", ");
            if (typeof resolvedRuntime !== "string") {
                Log.error(`The \`runtime\` config must be a string. Please leave it empty or choose one of: ${options}`);
            } else {
                Log.error(`Provided runtime "${resolvedRuntime}" is not supported. Please leave it empty or choose one of: ${options}`);
            }
            if (!isDev) {
                process.exit(1);
            }
        }
        const requiresServerRuntime = ssr || ssg || pageType === "app";
        resolvedRuntime = isEdgeRuntime(resolvedRuntime) ? resolvedRuntime : requiresServerRuntime ? resolvedRuntime || ((ref15 = nextConfig.experimental) == null ? void 0 : ref15.runtime) : undefined;
        if (resolvedRuntime === SERVER_RUNTIME.experimentalEdge) {
            warnAboutExperimentalEdge();
        }
        if (resolvedRuntime === SERVER_RUNTIME.edge && pageType === "pages" && page && !isAPIRoute(page.replace(/^\/pages\//, "/"))) {
            const message = `Page ${page} provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.`;
            Log.error(message);
            if (!isDev) {
                process.exit(1);
            }
        }
        const middlewareConfig = getMiddlewareConfig(page ?? "middleware/edge API route", config, nextConfig);
        return {
            ssr,
            ssg,
            rsc,
            ...middlewareConfig && {
                middleware: middlewareConfig
            },
            ...resolvedRuntime && {
                runtime: resolvedRuntime
            }
        };
    }
    return {
        ssr: false,
        ssg: false,
        rsc: RSC_MODULE_TYPES.server,
        runtime: (ref = nextConfig.experimental) == null ? void 0 : ref.runtime
    };
}

//# sourceMappingURL=get-page-static-info.js.map