"use strict"; // Copyright 2021 Google Inc. Use of this source code is governed by an // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionRegistry = void 0; const util_1 = require("util"); const protobuf_1 = require("@bufbuild/protobuf"); const utils = require("./utils"); const proto = require("./vendor/embedded_sass_pb"); const utils_1 = require("./utils"); const protofier_1 = require("./protofier"); const value_1 = require("./value"); /** * Tracks functions that are defined on the host so that the compiler can * execute them. */ class FunctionRegistry { /** * The globally unique identifier of the current compilation used for tracking * the ownership of CompilerFunction and CompilerMixin objects. */ compileContext = Symbol(); functionsByName = new Map(); functionsById = new Map(); idsByFunction = new Map(); /** The next ID to use for a function. */ id = 0; constructor(functionsBySignature) { for (const [signature, fn] of Object.entries(functionsBySignature ?? {})) { const openParen = signature.indexOf('('); if (openParen === -1) { throw new Error(`options.functions: "${signature}" is missing "("`); } this.functionsByName.set(signature.substring(0, openParen), fn); } } /** Registers `fn` as a function that can be called using the returned ID. */ register(fn) { return utils.putIfAbsent(this.idsByFunction, fn, () => { const id = this.id; this.id += 1; this.functionsById.set(id, fn); return id; }); } /** * Returns the function to which `request` refers and returns its response. */ call(request) { const protofier = new protofier_1.Protofier(this); const fn = this.get(request); return (0, utils_1.catchOr)(() => { return (0, utils_1.thenOr)(fn(request.arguments.map(value => protofier.deprotofy(value))), result => { if (!(result instanceof value_1.Value)) { const name = request.identifier.case === 'name' ? `"${request.identifier.value}"` : 'anonymous function'; throw (`options.functions: ${name} returned non-Value: ` + (0, util_1.inspect)(result)); } return (0, protobuf_1.create)(proto.InboundMessage_FunctionCallResponseSchema, { result: { case: 'success', value: protofier.protofy(result) }, accessedArgumentLists: protofier.accessedArgumentLists, }); }); }, error => (0, protobuf_1.create)(proto.InboundMessage_FunctionCallResponseSchema, { result: { case: 'error', value: `${error}` }, })); } /** Returns the function to which `request` refers. */ get(request) { if (request.identifier.case === 'name') { const fn = this.functionsByName.get(request.identifier.value); if (fn) return fn; throw (0, utils_1.compilerError)('Invalid OutboundMessage_FunctionCallRequest: there is no function ' + `named "${request.identifier.value}"`); } else if (request.identifier.case === 'functionId') { const fn = this.functionsById.get(request.identifier.value); if (fn) return fn; throw (0, utils_1.compilerError)('Invalid OutboundMessage_FunctionCallRequest: there is no function ' + `with ID "${request.identifier.value}"`); } else { throw (0, utils_1.compilerError)('Invalid OutboundMessage_FunctionCallRequest: function identifier is ' + 'unset'); } } } exports.FunctionRegistry = FunctionRegistry; //# sourceMappingURL=function-registry.js.map