You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
4.7 KiB
JavaScript
160 lines
4.7 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("assert");
|
|
const types = require("@babel/types");
|
|
const template = require("@babel/template").default;
|
|
|
|
const templateExpression = require("./util/template-expression");
|
|
const NoChange = require("astformer/actions/no-change");
|
|
|
|
// FIXME: Auto-generate argument list based on exposed API surface?
|
|
let tmplModule = template(`
|
|
module.exports = function({ builtins, removeAttrs, $memoize, $handleArgument, $assertUniqueKeys }) {
|
|
return %%contents%%;
|
|
};
|
|
`);
|
|
|
|
let tmplIdentifierReference = templateExpression(`(
|
|
%%name%%()
|
|
)`);
|
|
|
|
let tmplWithWrapper = templateExpression(`
|
|
(() => {
|
|
const %%contextName%% = %%setupCode%%;
|
|
|
|
return %%body%%;
|
|
})()
|
|
`);
|
|
|
|
let tmplImplicitContextNested = templateExpression(`
|
|
Object.assign(Object.create(%%parent%%), %%environment%%)
|
|
`);
|
|
|
|
let tmplImplicitContextTop = templateExpression(`
|
|
Object.assign({}, %%environment%%)
|
|
`);
|
|
|
|
let tmplConditionalExpression = templateExpression(`
|
|
(() => {
|
|
if (%%condition%%) {
|
|
return %%consequence%%;
|
|
} else {
|
|
return %%alternative%%;
|
|
}
|
|
})()
|
|
`);
|
|
|
|
let implicitContextCounter = 0;
|
|
|
|
let trivial = {
|
|
name: "trivial-transforms",
|
|
visitors: {
|
|
// Trivial transforms
|
|
NixParenthesizedExpression: (node) => node.expression,
|
|
Program: (_node, { setContext, defer }) => {
|
|
setContext(null, "identifierType", "reference");
|
|
|
|
return defer((node) => {
|
|
assert(node.body.length === 1);
|
|
return tmplModule({ contents: node.body[0] });
|
|
});
|
|
},
|
|
NixAttributePath: (_node, { setContext }) => {
|
|
// This ensures that attribute paths get excluded from being processed as an identifier, now that the parser no longer distinguishes between attribute identifiers and regular identifiers
|
|
setContext([ "attr" ], "identifierType", "attributePath");
|
|
return NoChange;
|
|
},
|
|
NixIdentifier: (node, { getContext }) => {
|
|
// FIXME: Mangle reserved keywords like `const`
|
|
// let safeName = (node.name === "import")
|
|
// ? "$import"
|
|
// : node.name;
|
|
let safeName = node.name;
|
|
|
|
if (getContext("identifierType") === "define") {
|
|
return types.identifier(safeName);
|
|
} else if (getContext("identifierType") === "attributePath") {
|
|
// Do nothing, this will get removed from the tree in attribute set handling
|
|
return NoChange;
|
|
} else { // reference
|
|
return tmplIdentifierReference({ name: safeName });
|
|
}
|
|
},
|
|
NixUnaryOperation: (_node, { defer }) => {
|
|
return defer((node) => {
|
|
if (node.operator === "-" || node.operator === "!") {
|
|
return types.unaryExpression(node.operator, node.argument, true);
|
|
} else {
|
|
throw new Error(`Unsupported operator: ${node.operator}`);
|
|
}
|
|
});
|
|
},
|
|
NixBinaryOperation: (_node, { defer }) => {
|
|
return defer((node) => {
|
|
// FIXME: Verify that all the 'operator' values match between Nix and JS!
|
|
// FIXME: Need to replace this with utility functions to deal with eg. paths
|
|
if (node.operator === "&&" || node.operator === "||") {
|
|
return types.logicalExpression(node.operator, node.left, node.right);
|
|
} if (node.operator === "//") {
|
|
return types.objectExpression([
|
|
types.spreadElement(node.left),
|
|
types.spreadElement(node.right)
|
|
]);
|
|
} else if (node.operator === "++") {
|
|
return types.arrayExpression([
|
|
types.spreadElement(node.left),
|
|
types.spreadElement(node.right)
|
|
]);
|
|
} else {
|
|
return types.binaryExpression(node.operator, node.left, node.right);
|
|
}
|
|
});
|
|
},
|
|
NixWithExpression: (node, { defer, setContext, getContextOptional }) => {
|
|
// TODO: Can we optimize for the fast case (no nested `with`) by referencing the source attrset directly?
|
|
let parentContext = getContextOptional("implicitContext");
|
|
let hasParent = (parentContext != null);
|
|
|
|
let contextName = `$implicit${implicitContextCounter++}`;
|
|
setContext(null, "implicitContext", contextName);
|
|
|
|
return defer((node) => {
|
|
let setupCode = (hasParent)
|
|
? tmplImplicitContextNested({ environment: node.environment, parent: parentContext })
|
|
: tmplImplicitContextTop({ environment: node.environment });
|
|
|
|
return tmplWithWrapper({
|
|
contextName: contextName,
|
|
setupCode: setupCode,
|
|
body: node.body
|
|
});
|
|
});
|
|
},
|
|
NixConditional: (_node, { defer }) => {
|
|
// condition, consequence, alternative
|
|
return defer((node) => {
|
|
return tmplConditionalExpression({
|
|
condition: node.condition,
|
|
consequence: node.consequence,
|
|
alternative: node.alternative
|
|
});
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = [
|
|
// FIXME: desugar-search-path
|
|
require("./desugar-inherits"),
|
|
require("./desugar-attrsets"),
|
|
require("./desugar-let-attribute-set"),
|
|
require("./desugar-interpolation-expressions"),
|
|
require("./mangle-identifiers"),
|
|
require("./let-in"),
|
|
require("./literals"),
|
|
require("./lists"),
|
|
require("./functions"),
|
|
require("./attribute-sets"),
|
|
trivial,
|
|
];
|