react-native-integrate
Version:
Automate integration of additional code into React Native projects
488 lines (487 loc) • 20.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsObjectParser = void 0;
/* eslint-disable @typescript-eslint/no-unsafe-argument */
const processScript_1 = require("./processScript");
const prompter_1 = require("../prompter");
const variables_1 = require("../variables");
const findClosingTagIndex_1 = require("./findClosingTagIndex");
class JsObjectParser {
tree = [];
constructor(content) {
if (content)
this.parse(content);
}
parse(content) {
this.tree = [];
this._parseRootObject(content, this.tree);
}
_parseRootObject(content, tree) {
let reducedContent = content;
let objectMatch;
const addPartialToTree = () => {
if (objectMatch?.index != null) {
const partial = reducedContent.substring(0, objectMatch.index);
if (partial)
tree.push({
type: 'partial',
content: partial,
});
}
else {
if (reducedContent)
tree.push({
type: 'partial',
content: reducedContent,
});
}
};
// eslint-disable-next-line no-constant-condition
while (true) {
// match an object
objectMatch = reducedContent.match(/([^{\s[,][^{\s[]+)['"\]\s]*=\s*((\w+\(\s*)*)?[{[]/s);
if (objectMatch?.index == null) {
// no more object
addPartialToTree();
return;
}
addPartialToTree();
const objectStart = objectMatch.index + objectMatch[0].length;
const isObject = objectMatch[0][objectMatch[0].length - 1] === '{';
const tag = isObject ? findClosingTagIndex_1.TagDefinitions.CURLY : findClosingTagIndex_1.TagDefinitions.BRACKETS;
const objectEnd = (0, findClosingTagIndex_1.findClosingTagIndex)(reducedContent, objectStart, tag);
const objectContent = reducedContent.substring(objectStart, objectEnd);
const objectExpr = {
type: 'variable',
name: objectMatch[1],
partial: objectMatch[2],
valueType: isObject ? 'object' : 'array',
tree: [],
};
tree.push(objectExpr);
if ('tree' in objectExpr)
this._parse(objectContent, objectExpr);
// reduce content
reducedContent = reducedContent.substring(objectEnd + 1);
}
}
_parse(content, parent) {
let reducedContent = content;
let objectMatch;
const addPartialToTree = () => {
if (objectMatch?.index != null) {
const partial = reducedContent.substring(0, objectMatch.index);
if (partial)
parent.tree.push({
type: 'partial',
content: partial,
});
reducedContent = reducedContent.substring(objectMatch.index);
objectMatch.index = 0;
}
else {
if (reducedContent)
parent.tree.push({
type: 'partial',
content: reducedContent,
});
reducedContent = '';
}
};
// eslint-disable-next-line no-constant-condition
while (true) {
const matchRegex = parent.valueType === 'object'
? /([^{\s[,][^{\s[]+['"\]\s]*)\s*:\s*[{[\w'"`0-9]/s
: /[{[\w'"`0-9]/s;
// match a property or value
objectMatch = reducedContent.match(matchRegex);
if (objectMatch?.index == null) {
// no more object
addPartialToTree();
return;
}
addPartialToTree();
let objectStart = objectMatch.index + objectMatch[0].length;
const isObject = objectMatch[0][objectMatch[0].length - 1] === '{';
const isArray = objectMatch[0][objectMatch[0].length - 1] === '[';
let shouldReduceClosure = true;
let objectEnd;
if (isObject || isArray) {
const tag = isObject ? findClosingTagIndex_1.TagDefinitions.CURLY : findClosingTagIndex_1.TagDefinitions.BRACKETS;
objectEnd = (0, findClosingTagIndex_1.findClosingTagIndex)(reducedContent, objectStart, tag);
}
else {
objectStart--;
const strippedBracketContent = (0, findClosingTagIndex_1.stripNonCode)(reducedContent, findClosingTagIndex_1.TagDefinitions.BRACKETS.comment);
const commaIndex = strippedBracketContent.indexOf(',', objectStart);
if (commaIndex != -1) {
objectEnd = commaIndex;
shouldReduceClosure = false;
}
else
objectEnd = reducedContent.length;
// objectEnd = commaIndex;
// const regExp = /,\s*[{[\w'"`0-9]?/s;
// regExp.lastIndex = objectStart;
// const commaMatch = regExp.exec(reducedContent);
// if (commaMatch?.index != null) {
// objectEnd = commaMatch.index + commaMatch[0].length;
// shouldReduceClosure = false;
// } else objectEnd = reducedContent.length;
}
const objectContent = reducedContent.substring(objectStart, objectEnd);
if (parent.valueType === 'object') {
const propExpr = {
type: 'property',
name: objectMatch[1],
valueType: isObject ? 'object' : isArray ? 'array' : 'primitive',
tree: [],
};
if (propExpr.valueType === 'primitive')
propExpr.tree.push({
type: 'primitive',
content: objectContent,
});
else
this._parse(objectContent, propExpr);
const propertyName = objectMatch[1];
// special property
if (propertyName.replace(/['"[\]]/g, '') === '__$raw') {
// unwrap it to the current tree
// parent.type = 'value';
parent.valueType = 'primitive';
const transformedExpr = propExpr.tree[0];
transformedExpr.type = 'partial';
transformedExpr.content = (0, processScript_1.processScript)(transformedExpr.content, variables_1.variables, true, false);
// parent.tree.push(propExpr);
parent.tree = [...propExpr.tree];
}
else {
parent.tree.push(propExpr);
}
}
else {
const propExpr = {
type: 'value',
valueType: isObject ? 'object' : isArray ? 'array' : 'primitive',
tree: [],
};
parent.tree.push(propExpr);
if (propExpr.valueType === 'primitive')
propExpr.tree.push({
type: 'primitive',
content: objectContent,
});
else
this._parse(objectContent, propExpr);
}
// reduce content
reducedContent = reducedContent.substring(objectEnd + (shouldReduceClosure ? 1 : 0));
}
}
stringify() {
return this._stringifyTree(this.tree);
}
_stringifyTree(tree) {
let content = '';
for (const expr of tree) {
content += this._stringifyExpr(expr);
}
return content;
}
_stringifyExpr(expr) {
switch (expr.type) {
case 'primitive':
case 'partial':
return expr.content;
case 'variable':
return (`${expr.name} = ${expr.partial ?? ''}` +
(expr.valueType === 'object'
? this._stringifyObject(expr.tree)
: this._stringifyArray(expr.tree)));
case 'property':
return (`${expr.name}: ` +
(expr.valueType === 'object'
? this._stringifyObject(expr.tree)
: expr.valueType === 'array'
? this._stringifyArray(expr.tree)
: this._stringifyTree(expr.tree)));
case 'value':
return expr.valueType === 'object'
? this._stringifyObject(expr.tree)
: expr.valueType === 'array'
? this._stringifyArray(expr.tree)
: this._stringifyTree(expr.tree);
}
}
_stringifyArray(tree) {
return `[${tree.map(expr => this._stringifyExpr(expr)).join('')}]`;
}
_stringifyObject(tree) {
return `{${this._stringifyTree(tree)}}`;
}
merge(object, opts = {}) {
this._merge(object, this.tree, opts);
}
_merge(object, tree, opts) {
let insert = opts.insert;
if (Array.isArray(object)) {
const hasObject = object.length && typeof object[0] === 'object';
const forceSearch = hasObject && !!object[0]?.$search;
const forceDelete = hasObject && !!object[0]?.$delete;
const forceBefore = hasObject && !!object[0]?.$before;
const forceAfter = hasObject && !!object[0]?.$after;
const forceReplace = hasObject && !!object[0]?.$replace;
const forceAppend = hasObject && !!object[0]?.$append;
const forcePrepend = hasObject && !!object[0]?.$prepend;
const items = tree.filter(expr => expr.type != 'partial');
let contextStart = 0;
let contextEnd = items.length;
if (forceSearch) {
const index = items.findIndex(expr => this._stringifyExpr(expr)?.includes(object[0].$search));
if (index != -1) {
contextStart = index;
contextEnd = index + 1;
}
else
return undefined;
}
if (forceAfter) {
const index = items.findIndex(expr => this._stringifyExpr(expr)?.includes(object[0].$after));
if (index != -1) {
contextStart = index + 1;
}
}
if (forceBefore) {
const index = items.findIndex(expr => this._stringifyExpr(expr)?.includes(object[0].$before));
if (index != -1) {
contextEnd = index;
}
}
if (forceReplace) {
insert = contextStart;
object[0] = object[0].$replace;
}
else if (forcePrepend) {
insert = contextStart;
object[0] = object[0].$prepend;
}
else if (forceAppend) {
insert = contextEnd;
object[0] = object[0].$append;
}
else if (forceDelete) {
insert = contextStart;
}
const newItemExprs = object
.flatMap((value, index) => {
const newExpr = this._objectToExpr(value);
if (newExpr) {
const property = {
type: 'value',
valueType: newExpr.valueType,
tree: newExpr.tree,
};
return [
...(index > 0 ? [{ type: 'partial', content: ', ' }] : []),
property,
];
}
(0, prompter_1.logWarning)('could not convert value to js tree: ' + (0, prompter_1.summarize)(value));
return undefined;
})
.filter(Boolean);
if (opts.strategy === 'assign') {
tree.splice(0, tree.length, ...newItemExprs);
}
else if (!opts.strategy || opts.strategy === 'merge_concat') {
if (insert != null) {
const itemAtIndex = items[insert];
//find real index in tree
if (!itemAtIndex) {
// just push it
insert = undefined;
}
else
insert = tree.indexOf(itemAtIndex);
}
//merge arrays
if (insert != null) {
if (items.length) {
const previousItem = tree[insert];
if (previousItem.type != 'partial' ||
!previousItem.content?.includes(',')) {
if (forceDelete)
tree.splice(insert, Math.max(0, (contextEnd - contextStart) * 2 - 1) ? 1 : 0);
else
tree.splice(insert, forceReplace ? 1 : 0, {
type: 'partial',
content: ', ',
});
}
}
if (forceDelete)
tree.splice(insert, Math.max(0, (contextEnd - contextStart) * 2 - 1));
else
tree.splice(insert, forceReplace
? Math.max(0, (contextEnd - contextStart) * 2 - 1)
: 0, ...newItemExprs);
}
else {
if (items.length) {
const lastItem = tree[tree.length - 1];
if (lastItem.type != 'partial' || !lastItem.content?.includes(','))
tree.push({ type: 'partial', content: ', ' });
}
tree.push(...newItemExprs);
}
}
}
else if (typeof object === 'object') {
for (const key in object) {
let value = object[key];
const forceAssign = typeof value === 'object' && !!value?.$assignTo;
const forceDelete = typeof value === 'object' && !!value?.$delete;
if (forceAssign) {
value = value.$assignTo;
}
const exprObject = tree.find(expr => {
return ((expr.type === 'variable' || expr.type === 'property') &&
expr.name.replace(/['"[\]]/g, '') === key);
});
const items = tree.filter(expr => expr.type != 'partial');
if (forceDelete) {
if (exprObject) {
//find real index in tree
const realIndex = tree.indexOf(exprObject);
tree.splice(realIndex, 2);
}
continue;
}
const valueType = getValueType(value);
// if (!exprObject) {
// const newExpr = this._objectToExpr({ [key]: value });
// if (newExpr) {
// const property: PropertyExpression = {
// type: 'property',
// name: key,
// valueType,
// tree: [],
// };
// if (items.length) {
// const lastItem = tree[tree.length - 1];
// if (
// lastItem.type != 'partial' ||
// !lastItem.content?.includes(',')
// )
// tree.push({ type: 'partial', content: ', ' });
// }
// tree.push(property);
// property.valueType = newExpr.valueType;
// property.tree.push(...newExpr.tree);
// } else
// logWarning(
// 'could not convert value to js tree: ' + summarize(value)
// );
//
// }
if (!exprObject) {
//object doesnt exist, add it
const newExpr = this._objectToEmptyExpr(value);
if (newExpr) {
const property = {
type: 'property',
name: key,
valueType: newExpr.valueType,
tree: [],
};
if (items.length) {
const lastItem = tree[tree.length - 1];
if (lastItem.type != 'partial' ||
!lastItem.content?.includes(','))
tree.push({ type: 'partial', content: ', ' });
}
tree.push(property);
property.valueType = newExpr.valueType;
if (newExpr.valueType === 'primitive')
property.tree = newExpr.tree;
else
this._merge(value, property.tree, opts);
}
else
(0, prompter_1.logWarning)('could not convert value to js tree: ' + (0, prompter_1.summarize)(value));
continue;
}
else if (exprObject && exprObject.valueType != valueType) {
const newExpr = this._objectToExpr(value);
if (newExpr) {
exprObject.valueType = valueType;
exprObject.tree = newExpr.tree;
continue;
}
else
(0, prompter_1.logWarning)('could not convert value to js tree: ' + (0, prompter_1.summarize)(value));
}
if (!forceAssign &&
(exprObject.type === 'variable' ||
!opts.strategy ||
opts.strategy === 'merge' ||
opts.strategy === 'merge_concat' ||
opts.strategy === 'merge_distinct')) {
const newExpr = this._objectToEmptyExpr(value);
if (newExpr.valueType === 'primitive')
exprObject.tree = newExpr.tree;
else
this._merge(value, exprObject.tree, opts);
}
else if (!forceAssign && opts.strategy === 'append') {
const existingExpr = tree.find(expr => {
return ((expr.type === 'variable' || expr.type === 'property') &&
expr.name.replace(/['"[\]]/g, '') === key);
});
if (!existingExpr) {
const newExpr = this._objectToEmptyExpr(value);
if (newExpr.valueType === 'primitive')
exprObject.tree = newExpr.tree;
else
this._merge(value, exprObject.tree, opts);
}
}
else if (forceAssign || opts.strategy === 'assign') {
const newExpr = this._objectToExpr(value);
exprObject.valueType = newExpr.valueType;
exprObject.tree = newExpr.tree;
}
}
}
}
_objectToExpr(object) {
let json = JSON.stringify(object);
if (/^[{[]/.test(json))
json = json.substring(1);
if (/[}\]]$/.test(json))
json = json.substring(0, json.length - 1);
const parent = {
type: 'value',
valueType: getValueType(object),
tree: [],
};
this._parse(`${json}`, parent);
return parent;
}
_objectToEmptyExpr(object) {
return this._objectToExpr(Array.isArray(object)
? []
: object && typeof object === 'object'
? {}
: object);
}
}
exports.JsObjectParser = JsObjectParser;
function getValueType(object) {
return Array.isArray(object)
? 'array'
: object != null && typeof object === 'object'
? 'object'
: 'primitive';
}