UNPKG

react-native-integrate

Version:

Automate integration of additional code into React Native projects

488 lines (487 loc) 20.9 kB
"use strict"; 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'; }