"use strict"; const asExpression = require("as-expression"); const assert = require("assert"); const types = require("@babel/types"); const template = require("@babel/template").default; const unpackExpression = require("./_unpack-expression"); // NOTE: These are arrow functions because variable references within functions should always refer to the nearest scope; and since we use `this` to handle variable references within recursive attribute sets, we need to ensure that a function definition *does not* create its own `this` context. let tmplFunctionDefinitionFormalsUniversal = template(` ((%%universal%%) => { const %%formals%% = %%universal%%; return %%body%%; }) `); // FIXME: Test that this template form actually works with our formals structure let tmplFunctionDefinitionFormals = template(` ((%%formals%%) => { return %%body%%; }) `); let tmplFunctionDefinitionUniversal = template(` ((%%universal%%) => { return %%body%%; }) `); let tmplFunctionCall = template(` (%%function%%(%%arg%%)) `); // NOTE: Duplicated from `attribute-sets` for now let tmplLazyWrapper = template(`( $$jsNix$memoize(() => %%expression%%) )`); function functionDefinition({ universal, formals, body }) { let convertedFormals = asExpression(() => { if (formals != null) { return types.objectPattern(formals.map((formal) => { return types.objectProperty( types.identifier(formal), types.identifier(formal) ); })); } else { return undefined; } }); if (universal != null && formals != null) { return tmplFunctionDefinitionFormalsUniversal({ body: body, universal: universal, formals: convertedFormals }); } else if (universal != null) { return tmplFunctionDefinitionUniversal({ body: body, universal: universal }); } else { return tmplFunctionDefinitionFormals({ body: body, formals: convertedFormals }); } } module.exports = { name: "functions", visitors: { NixFunctionDefinition: (_node, { defer, setContext }) => { setContext([ "universal", "formals" ], "identifierType", "define"); return defer((node) => { return unpackExpression( functionDefinition({ universal: asExpression(() => { if (node.universal != null) { assert(node.universal.type === "Identifier"); return node.universal.name; } }), formals: asExpression(() => { if (node.formals != null) { assert(node.formals.type === "NixUnpackedAttributes"); return node.formals.formal.map((formal) => { assert(formal.type === "NixUnpackedAttribute"); assert(formal.name.type === "Identifier"); return formal.name.name; }); } }), body: node.body }) ); }); }, NixFunctionCall: (_node, { defer }) => { return defer((node) => { return unpackExpression( tmplFunctionCall({ function: node.function, arg: unpackExpression(tmplLazyWrapper({ expression: node.argument })) }) ); }); } } };