/**
 * This is a TypeScript language service plugin for Next.js app directory,
 * it provides the following features:
 *
 * - Warns about disallowed React APIs in server components.
 * - Warns about disallowed layout and page exports.
 * - Autocompletion for entry configurations.
 * - Hover hint and docs for entry configurations.
 */ "use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "createTSPlugin", {
    enumerable: true,
    get: function() {
        return createTSPlugin;
    }
});
const _utils = require("./utils");
const _constant = require("./constant");
const _config = /*#__PURE__*/ _interop_require_default(require("./rules/config"));
const _server = /*#__PURE__*/ _interop_require_default(require("./rules/server"));
const _entry = /*#__PURE__*/ _interop_require_default(require("./rules/entry"));
const _clientboundary = /*#__PURE__*/ _interop_require_default(require("./rules/client-boundary"));
const _serverboundary = /*#__PURE__*/ _interop_require_default(require("./rules/server-boundary"));
const _metadata = /*#__PURE__*/ _interop_require_default(require("./rules/metadata"));
const _error = /*#__PURE__*/ _interop_require_default(require("./rules/error"));
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const createTSPlugin = ({ typescript: ts })=>{
    function create(info) {
        // Get plugin options
        // config is the plugin options from the user's tsconfig.json
        // e.g. { "plugins": [{ "name": "next", "enabled": true }] }
        // config will be { "name": "next", "enabled": true }
        // The default user config is { "name": "next" }
        const isPluginEnabled = info.config.enabled ?? true;
        if (!isPluginEnabled) {
            return info.languageService;
        }
        (0, _utils.init)({
            ts,
            info
        });
        // Set up decorator object
        const proxy = Object.create(null);
        for (let k of Object.keys(info.languageService)){
            const x = info.languageService[k];
            // @ts-expect-error - JS runtime trickery which is tricky to type tersely
            proxy[k] = (...args)=>x.apply(info.languageService, args);
        }
        // Auto completion
        proxy.getCompletionsAtPosition = (fileName, position, options)=>{
            let prior = info.languageService.getCompletionsAtPosition(fileName, position, options) || {
                isGlobalCompletion: false,
                isMemberCompletion: false,
                isNewIdentifierLocation: false,
                entries: []
            };
            if (!(0, _utils.isAppEntryFile)(fileName)) return prior;
            // If it's a server entry.
            const entryInfo = (0, _utils.getEntryInfo)(fileName);
            if (!entryInfo.client) {
                // Remove specified entries from completion list
                prior.entries = _server.default.filterCompletionsAtPosition(prior.entries);
            }
            // Add auto completions for export configs.
            _config.default.addCompletionsAtPosition(fileName, position, prior);
            const source = (0, _utils.getSource)(fileName);
            if (!source) return prior;
            ts.forEachChild(source, (node)=>{
                // Auto completion for default export function's props.
                if ((0, _utils.isPositionInsideNode)(position, node) && (0, _utils.isDefaultFunctionExport)(node)) {
                    prior.entries.push(..._entry.default.getCompletionsAtPosition(fileName, node, position));
                }
            });
            return prior;
        };
        // Show auto completion details
        proxy.getCompletionEntryDetails = (fileName, position, entryName, formatOptions, source, preferences, data)=>{
            const entryCompletionEntryDetails = _config.default.getCompletionEntryDetails(entryName, data, fileName);
            if (entryCompletionEntryDetails) return entryCompletionEntryDetails;
            return info.languageService.getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences, data);
        };
        // Quick info
        proxy.getQuickInfoAtPosition = (fileName, position, ...args)=>{
            const prior = info.languageService.getQuickInfoAtPosition(fileName, position, ...args);
            if (!(0, _utils.isAppEntryFile)(fileName)) return prior;
            // Remove type suggestions for disallowed APIs in server components.
            const entryInfo = (0, _utils.getEntryInfo)(fileName);
            if (!entryInfo.client) {
                const definitions = info.languageService.getDefinitionAtPosition(fileName, position);
                if (definitions && _server.default.hasDisallowedReactAPIDefinition(definitions)) {
                    return;
                }
            }
            const overridden = _config.default.getQuickInfoAtPosition(fileName, position, prior);
            if (overridden) return overridden;
            return prior;
        };
        // Show errors for disallowed imports
        proxy.getSemanticDiagnostics = (fileName)=>{
            const prior = info.languageService.getSemanticDiagnostics(fileName);
            const source = (0, _utils.getSource)(fileName);
            if (!source) return prior;
            let isClientEntry = false;
            let isServerEntry = false;
            const isAppEntry = (0, _utils.isAppEntryFile)(fileName);
            try {
                const entryInfo = (0, _utils.getEntryInfo)(fileName, true);
                isClientEntry = entryInfo.client;
                isServerEntry = entryInfo.server;
            } catch (e) {
                prior.push({
                    file: source,
                    category: ts.DiagnosticCategory.Error,
                    code: _constant.NEXT_TS_ERRORS.MISPLACED_ENTRY_DIRECTIVE,
                    ...e
                });
                isClientEntry = false;
                isServerEntry = false;
            }
            if ((0, _utils.isInsideApp)(fileName)) {
                const errorDiagnostic = _error.default.getSemanticDiagnostics(source, isClientEntry);
                prior.push(...errorDiagnostic);
            }
            ts.forEachChild(source, (node)=>{
                var _node_modifiers, _node_modifiers1;
                if (ts.isImportDeclaration(node)) {
                    // import ...
                    if (isAppEntry) {
                        if (!isClientEntry || isServerEntry) {
                            // Check if it has valid imports in the server layer
                            const diagnostics = _server.default.getSemanticDiagnosticsForImportDeclaration(source, node);
                            prior.push(...diagnostics);
                        }
                    }
                } else if (ts.isVariableStatement(node) && ((_node_modifiers = node.modifiers) == null ? void 0 : _node_modifiers.some((m)=>m.kind === ts.SyntaxKind.ExportKeyword))) {
                    // export const ...
                    if (isAppEntry) {
                        // Check if it has correct option exports
                        const diagnostics = _config.default.getSemanticDiagnosticsForExportVariableStatement(source, node);
                        const metadataDiagnostics = isClientEntry ? _metadata.default.client.getSemanticDiagnosticsForExportVariableStatement(fileName, node) : _metadata.default.server.getSemanticDiagnosticsForExportVariableStatement(fileName, node);
                        prior.push(...diagnostics, ...metadataDiagnostics);
                    }
                    if (isClientEntry) {
                        prior.push(..._clientboundary.default.getSemanticDiagnosticsForExportVariableStatement(source, node));
                    }
                    if (isServerEntry) {
                        prior.push(..._serverboundary.default.getSemanticDiagnosticsForExportVariableStatement(source, node));
                    }
                } else if ((0, _utils.isDefaultFunctionExport)(node)) {
                    // export default function ...
                    if (isAppEntry) {
                        const diagnostics = _entry.default.getSemanticDiagnostics(fileName, source, node);
                        prior.push(...diagnostics);
                    }
                    if (isClientEntry) {
                        prior.push(..._clientboundary.default.getSemanticDiagnosticsForFunctionExport(source, node));
                    }
                    if (isServerEntry) {
                        prior.push(..._serverboundary.default.getSemanticDiagnosticsForFunctionExport(source, node));
                    }
                } else if (ts.isFunctionDeclaration(node) && ((_node_modifiers1 = node.modifiers) == null ? void 0 : _node_modifiers1.some((m)=>m.kind === ts.SyntaxKind.ExportKeyword))) {
                    // export function ...
                    if (isAppEntry) {
                        const metadataDiagnostics = isClientEntry ? _metadata.default.client.getSemanticDiagnosticsForExportVariableStatement(fileName, node) : _metadata.default.server.getSemanticDiagnosticsForExportVariableStatement(fileName, node);
                        prior.push(...metadataDiagnostics);
                    }
                    if (isClientEntry) {
                        prior.push(..._clientboundary.default.getSemanticDiagnosticsForFunctionExport(source, node));
                    }
                    if (isServerEntry) {
                        prior.push(..._serverboundary.default.getSemanticDiagnosticsForFunctionExport(source, node));
                    }
                } else if (ts.isExportDeclaration(node)) {
                    // export { ... }
                    if (isAppEntry) {
                        const metadataDiagnostics = isClientEntry ? _metadata.default.client.getSemanticDiagnosticsForExportDeclaration(fileName, node) : _metadata.default.server.getSemanticDiagnosticsForExportDeclaration(fileName, node);
                        prior.push(...metadataDiagnostics);
                    }
                    if (isServerEntry) {
                        prior.push(..._serverboundary.default.getSemanticDiagnosticsForExportDeclaration(source, node));
                    }
                }
            });
            return prior;
        };
        return proxy;
    }
    return {
        create
    };
};

//# sourceMappingURL=index.js.map