interpolation is complete (current state)

master
Sir Robert Burbridge 2023-11-17 16:44:27 -05:00
parent f12afb753a
commit 0bc3d52fac
4 changed files with 105 additions and 68 deletions

21
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"hast-util-from-html": "^2.0.1",
"hast-util-to-html": "^9.0.0",
"unist-util-parents": "^3.0.0",
"unist-util-visit-parents": "^6.0.1"
},
"devDependencies": {
@ -4899,6 +4900,18 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-parents": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unist-util-parents/-/unist-util-parents-3.0.0.tgz",
"integrity": "sha512-3DVSfp+MkJhcJbGn/W7aOlZYVpsMQQ054cpfbPHZAqYPu/lvu5rCdzjuIt4eEMriLkCWLcnJjax97Awm1Bkhtg==",
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
@ -8768,6 +8781,14 @@
"@types/unist": "^3.0.0"
}
},
"unist-util-parents": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unist-util-parents/-/unist-util-parents-3.0.0.tgz",
"integrity": "sha512-3DVSfp+MkJhcJbGn/W7aOlZYVpsMQQ054cpfbPHZAqYPu/lvu5rCdzjuIt4eEMriLkCWLcnJjax97Awm1Bkhtg==",
"requires": {
"@types/unist": "^3.0.0"
}
},
"unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",

View File

@ -17,6 +17,7 @@
"dependencies": {
"hast-util-from-html": "^2.0.1",
"hast-util-to-html": "^9.0.0",
"unist-util-parents": "^3.0.0",
"unist-util-visit-parents": "^6.0.1"
}
}

View File

@ -1,15 +1,50 @@
import { visitParents } from 'unist-util-visit-parents';
import { createContext, runInContext } from 'vm';
export function interpolate(node, rootData, options = {}) {
if (!node.data) {
node.data = {};
export function interpolateTree(tree, data, mask = {}, options = {}) {
tree.data = tree.data || {};
if (data) {
tree.data.interp = data;
}
node.data.interp = rootData || {};
visitParents(node, interpolateNode);
const opt = Object.assign({
rootNodeOnly: false,
}, options);
return node;
visitParents(tree, (node, ancestors) => {
if (opt.rootNodeOnly && node !== tree) {
return;
}
const data = getMaskedData(node, ancestors);
if (node.properties && typeof node.properties === 'object') {
for (const key in node.properties) {
if (typeof node.properties[key] === 'string') {
node.properties[key] = interpolateString(
node.properties[key],
data
);
} else if (Array.isArray(node.properties[key])) {
node.properties[key] = node.properties[key].map(
(value) => interpolateString(value, data)
);
}
else {
node.properties[key] = '' + node.properties[key]
}
}
}
if (node.type === 'text' && node.value) {
node.value = interpolateString(node.value, data);
}
})
return tree;
}
function getMaskedData(node, ancestors, mask = {}) {
@ -23,32 +58,8 @@ function getMaskedData(node, ancestors, mask = {}) {
return foo;
}
function interpolateNode(node, ancestors) {
const data = getMaskedData(node, ancestors);
if (node.properties && typeof node.properties === 'object') {
for (const key in node.properties) {
if (typeof node.properties[key] === 'string') {
node.properties[key] = interpolateString(
node.properties[key],
data
);
} else if (Array.isArray(node.properties[key])) {
node.properties[key] = node.properties[key].map(
(value) => interpolateString(value, data)
);
}
else {
node.properties[key] = '' + node.properties[key]
}
}
}
if (node.type === 'text' && node.value) {
node.value = interpolateString(node.value, data);
}
return node;
function interpolateNode(node, data, mask) {
return interpolateTree(node, data, mask, { rootNodeOnly: true });
}
@ -58,19 +69,6 @@ function interpolateString(str, data) {
const interpolatedStr = runInContext(`\`${str}\``, context);
return interpolatedStr;
}
/*
function interpolateString(str, data) {
const context = createContext({ ...data, ...global });
try {
const interpolatedStr = runInContext(`\`${str}\``, context);
return interpolatedStr;
} catch (e) {
console.error('Error interpolating string:', e);
return str;
}
}
*/
export const __testExports = {
getMaskedData,
@ -78,4 +76,4 @@ export const __testExports = {
interpolateString,
}
export default interpolate;
export default interpolateTree;

View File

@ -1,11 +1,11 @@
import interpolate, { __testExports } from '../src/interpolate.mjs';
import interpolateTree, { __testExports } from '../src/interpolate.mjs';
import htmlToAst from '../src/html-to-ast.mjs';
import astToHtml from '../src/ast-to-html.mjs';
describe("interpolate()", () => {
describe("interpolateTree()", () => {
it("should import", () => {
expect(interpolate).toBeDefined();
expect(interpolateTree).toBeDefined();
});
it("should handle nodes without properties", () => {
@ -15,7 +15,7 @@ describe("interpolate()", () => {
data: {},
};
const data = {};
expect(() => interpolate(node, data)).not.toThrow();
expect(() => interpolateTree(node, data)).not.toThrow();
});
it("should handle nodes without data", () => {
@ -24,7 +24,7 @@ describe("interpolate()", () => {
value: 'Test',
data: {},
};
expect(() => interpolate(node)).not.toThrow();
expect(() => interpolateTree(node)).not.toThrow();
});
it("should handle nodes with array properties", () => {
@ -35,7 +35,7 @@ describe("interpolate()", () => {
data: {},
};
const data = { value: 'interpolated' };
const interpolated = interpolate(node, data);
const interpolated = interpolateTree(node, data);
expect(interpolated.properties.test[0]).toEqual('interpolated');
});
@ -46,7 +46,7 @@ describe("interpolate()", () => {
},
};
const data = { value: 'interpolated' };
const interpolated = interpolate(node, data);
const interpolated = interpolateTree(node, data);
expect(interpolated.properties.test).toEqual('interpolated');
});
@ -73,20 +73,43 @@ describe("interpolate()", () => {
},
},
};
const ancestors = [];
const interpolatedNode = __testExports.interpolateNode(node, ancestors);
const interpolatedNode = __testExports.interpolateNode(node);
expect(interpolatedNode.properties.test).toEqual('interpolated');
});
it("should interpolate node properties that are not strings or arrays", () => {
it("should abandon all its children with disdain", () => {
const node = {
properties: {
prop: 45, // Corrected property name
test: '${value}',
},
data: {
interp: {
value: 'interpolated',
},
},
children: [{
properties: {
test: '${piggly}',
},
data: {
interp: {
value: 'wiggly',
},
},
}],
};
const interpolatedNode = __testExports.interpolateNode(node);
expect(interpolatedNode.children[0].properties.test).toEqual('${piggly}');
});
it("should handle node properties that are not strings or arrays", () => {
const node = {
properties: {
prop: 45,
},
data: {},
};
const ancestors = [];
const interpolatedNode = __testExports.interpolateNode(node, ancestors);
const interpolatedNode = __testExports.interpolateNode(node);
expect(interpolatedNode.properties.prop).toEqual('45');
});
});
@ -106,12 +129,6 @@ describe("interpolate()", () => {
});
});
});
describe("interpolate()", () => {
it("should import", () => {
expect(interpolate).toBeDefined();
})
it("should interpolate a single node", () => {
const uninterpolated = '<div class="${classname}"></div>';
@ -119,7 +136,7 @@ describe("interpolate()", () => {
const data = { classname: "foo" };
const ast = htmlToAst(uninterpolated);
const interpolated = interpolate(ast, data);
const interpolated = interpolateTree(ast, data);
const rendered = astToHtml(interpolated);
expect(rendered).toEqual(expected);
@ -155,7 +172,7 @@ describe("interpolate()", () => {
},
};
const ast = htmlToAst(uninterpolated);
const interpolated = interpolate(ast, data);
const interpolated = interpolateTree(ast, data);
const rendered = astToHtml(interpolated);
expect(rendered).toEqual(expected);
@ -184,7 +201,7 @@ describe("interpolate()", () => {
square: (num) => Math.pow(num, 2),
};
const ast = htmlToAst(uninterpolated);
const interpolated = interpolate(ast, data);
const interpolated = interpolateTree(ast, data);
const rendered = astToHtml(interpolated);
expect(rendered).toEqual(expected);