2860 lines
76 KiB
TypeScript
Raw Normal View History

import { v4 as uuidv4 } from 'uuid'
// import { Buffer } from 'buffer'
// weird hack for remix client-side logic
try {
window.process = {
env: {}
}
window.Buffer = () => {}
// window.Buffer = Buffer
} catch (err) {
// nothing
}
// const babel = require('@babel/standalone')
// babel.template = require('@babel/template').default
// babel.t = require('@babel/types')
// babel.generator = require('@babel/generator').default
// babel.babylon = require('@babel/parser')
// babel.prettier = require('prettier')
const objList = []
const stringList = []
// ES6?
// import { uuidv4 as uuid } from 'uuid'
// import babel from '@babel/standalone'
// import template from '@babel/template'
// babel.template = template
// import t from '@babel/types'
// babel.t = t
// import generator from '@babel/generator'
// babel.generator = generator
// import babylon from '@babel/parser'
// babel.babylon = babylon
// import prettier from 'prettier'
// babel.prettier = prettier
export const babel = () => {}
export const local = {}
export const t = () => {}
// export const t = babel?.t
export const getBabel = () => {
return babel
}
export const uuid = uuidv4
export const pause = (value, opts) => {
return stringify(value, opts)
}
export const save = (value, opts) => {
return stringify(value, opts)
}
export const toJavascript = (value, opts = {}) => {
let defaultOpts = {
wrapInFunction: true,
declarations: [],
identifier: 'obj',
keys: {
obj: 1
},
dependancies: {},
mappings: [],
seen: [],
db: []
}
Object.assign(defaultOpts, opts)
opts = defaultOpts
toJavascriptAux(value, opts)
// sort declarations by dependancies
for (let declaration of opts.declarations) {
if (declaration.type === 'ExpressionStatement') {
// nothing
} else {
let key = declaration.declarations[0].id.name
let dependancies = opts.dependancies[key]
let dependancyCheck = {}
for (let dependancy of dependancies) {
dependancyCheck[dependancy] = false
}
let sortableDeclarationIndex = 0
let insertionIndex = 0
for (let sortableDeclaration of opts.declarations) {
if (sortableDeclaration.type === 'ExpressionStatement') {
// nothing
} else {
let sortableDeclarationKey =
sortableDeclaration.declarations[0].id.name
if (dependancyCheck[sortableDeclarationKey] === false) {
insertionIndex = sortableDeclarationIndex + 1
dependancyCheck[sortableDeclarationKey] = true
}
}
// increment iterator index
sortableDeclarationIndex++
}
let declarationIndex = opts.declarations.indexOf(declaration)
opts.declarations.splice(declarationIndex, 1)
opts.declarations.splice(insertionIndex, 0, declaration)
}
}
let program
if (opts.wrapInFunction && !opts.moduleExport) {
opts.declarations.push(t.returnStatement(t.identifier('obj')))
let expression = t.expressionStatement(
t.callExpression(
t.functionExpression(null, [], t.blockStatement(opts.declarations)),
[]
)
)
program = t.program([expression])
} else {
if (opts.moduleExport) {
opts.declarations.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(t.identifier('module'), t.identifier('exports')),
t.identifier('obj')
)
)
)
}
program = t.program(opts.declarations)
}
let stringifiedProgram = getBabel().generator(program).code
return getBabel().prettier.format(stringifiedProgram, {
semi: false,
parser: 'babel',
useTabs: true
})
}
export const toJavascriptAux = (value, opts) => {
if (typeof value === 'object') {
if (!opts.seen.includes(value)) {
// update map and seen
opts.seen.push(value)
let properties = createObjectProperties(value, opts)
let declaration = t.variableDeclaration('let', [
t.variableDeclarator(
t.identifier(opts.identifier),
t.objectExpression(properties)
)
])
opts.declarations.unshift(declaration)
for (let key of Object.keys(value)) {
let property = properties.find(v => {
return v.value.name === key
})
if (property) {
let identifier = property.value.name
toJavascriptAux(value[key], { ...opts, identifier })
}
}
}
} else if (typeof value === 'string') {
let declaration = t.variableDeclaration('let', [
t.variableDeclarator(
t.identifier(opts.identifier),
t.stringLiteral(value)
)
])
opts.declarations.unshift(declaration)
}
}
export const createObjectProperties = (value, opts) => {
let properties = []
let dependancies = opts.dependancies[opts.identifier]
if (!dependancies) dependancies = opts.dependancies[opts.identifier] = []
for (let key of Object.keys(value)) {
if (value[key] === value) {
opts.declarations.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.identifier(opts.identifier),
t.identifier(key)
),
t.identifier(opts.identifier)
)
)
)
} else {
if (!opts.db.includes(value[key])) {
opts.db.push(value[key])
if (opts.keys[key] === undefined) {
opts.keys[key] = 1
}
let keyIncrement = opts.keys[key]
if (keyIncrement == 1) {
opts.mappings.push(key)
} else {
opts.mappings.push(key + keyIncrement)
}
opts.keys[key]++
}
let identifier = opts.mappings[opts.db.indexOf(value[key])]
dependancies.push(identifier)
properties.push(
t.objectProperty(
t.identifier(key),
t.identifier(identifier),
false,
key == identifier
)
)
}
}
return properties
}
export const serialize = (value, opts = {}) => {
opts.strictFunctions = false
opts.serializeArrayProps = true
return stringify(value, opts)
}
export const stringify = (value, opts = {}) => {
let schema = {
stringifier: stringifier,
replace (key, value) {
if (opts.firstRun) {
opts.firstRun = !opts.firstRun
return value
}
var after = opts.stringifier(key, value, opts)
const type = typeof after.value
if (type === 'object') {
if (after === null || after.value === null) {
const ret = after.value
return ret
}
} else if (type === 'string') {
const ret =
opts.known.get(after.key) || setKnown(opts.known, opts.input, after)
return ret
}
return after.value
},
strictFunctions: true,
firstRun: undefined,
known: new Map(),
input: [],
output: []
}
Object.keys(schema).forEach(key => {
if (getsmart(opts, epp(key), { undefined: true }, true).undefined == true) {
opts[key] = schema[key]
}
})
opts.virtual = opts.stringifier('', value, opts)
for (
let i = parseInt(setKnown(opts.known, opts.input, opts.virtual));
i < opts.input.length;
i++
) {
opts.firstRun = true
try {
opts.output[i] = JSON.stringify(opts.input[i], opts.replace, opts.space)
} catch (err) {
console.error(err)
}
}
return '[' + opts.output.join(',') + ']'
}
export const replacer = (key, value) => {
const opts = opts || {}
if (opts.firstRun) {
opts.firstRun = !opts.firstRun
return value
}
var after = opts.stringifier(key, value, opts)
// replace with if statements
const type = typeof after.value
if (type === 'object') {
if (after === null) {
const ret = after.value
return ret
}
} else if (type === 'string') {
const ret =
opts.known.get(after.key) || setKnown(opts.known, opts.input, after)
return ret
}
return after.value
}
export const setKnown = (known, input, virtual) => {
var index = String(input.push(virtual.value) - 1)
known.set(virtual.key, index)
return index
}
export const stringifier = (key, val, opts) => {
let ret = { value: val, key: val }
if (
val instanceof Function &&
typeof val.toString === 'function' &&
(!opts.strictFunctions || typeof val.$scopes != 'undefined')
) {
let known = opts.known.get(ret.key)
ret = {
value: known || {
type: 'function',
js: val.toString(),
$scopes: val.$scopes,
$context: val.$context,
...val
},
key: val
}
if (ret.value.js == 'function () { [native code] }') return
if (typeof known == 'undefined') setKnown(opts.known, opts.input, ret)
} else if (ret.value === Infinity && typeof ret.value != 'string') {
let known = opts.known.get(ret.key)
ret = {
value: known || {
type: 'number',
js: 'Infinity',
$scopes: [],
$context: {}
},
key: val
}
if (typeof known == 'undefined') setKnown(opts.known, opts.input, ret)
} else if (typeof ret.value === 'undefined') {
ret = {
value: {
type: 'undefined',
js: 'undefined'
},
key: val
}
} else if (ret.value instanceof Array && opts.serializeArrayProps) {
let known = opts.known.get(ret.key)
ret = {
value: known
? ret.value
: {
type: 'Array',
js: ret.value,
uuid: ret.value.uuid
},
key: val
}
setKnown(opts.known, opts.input, ret)
}
return ret
}
export const primitives = value => {
return value instanceof String ? String(value) : value
}
export const Primitives = (key, value) => {
return typeof value === 'string' ? new String(value) : value
}
export const play = (text, opts) => {
return parse(text, opts)
}
export const load = (text, opts = {}) => {
opts.strictFunctions = false
return parse(text, opts)
}
export const parse = (text, opts = {}) => {
let schema = {
// parser: eval('(function '+parser+')'),
parser: parser(opts),
value: {},
strictFunctions: true,
noFunctions: false,
firstPass: true,
output: new Map()
}
Object.keys(schema).forEach(key => {
if (getsmart(opts, epp(key), { undefined: true }, true).undefined == true) {
opts[key] = schema[key]
}
})
// opts.parser = opts.parser.bind(opts)
opts.input = JSON.parse(text, Primitives)
opts.firstPass = false
opts.input = opts.input.map(primitives)
opts.value = opts.input[0]
let isObject = typeof opts.value === 'object' && opts.value
var tmp = isObject
? revive(opts.input, opts.output, opts.value, opts.parser, opts)
: opts.value
opts.replaceMode = true
let ret = revive(opts.input, new Map(), tmp, opts.parser, opts)
ret = opts.parser('', tmp, opts)
return ret
}
export const parser = opts => {
return function (key, val) {
if (val.js && val.type === 'Array') {
const ret = opts.input[opts.output.get(val)].js
ret.uuid = opts.input[opts.output.get(val)].uuid
return ret
} else if (
val.js &&
val.type === 'function' &&
opts.replaceMode &&
!opts.noFunctions
) {
let ret = opts.input[opts.output.get(val)]
if (typeof ret == val.type) return ret
let uuid = jsUUID()
var fn
var scopedEval
if (val.$scopedEval && typeof val.$scopedEval == 'function') {
scopedEval = val.$scopedEval
} else {
var fns = createScopedEval(uuid)
try {
fn = eval(`(${fns})`)
var input = { val, smarts }
scopedEval = fn(input)
} catch (err) {
console.log('Caught error evaling createScopedEval', err)
}
}
ret = scopedEval({ val })
try {
Object.defineProperty(ret, '$scopes', {
value: val.$scopes,
enumerable: true
})
if (val.uuid) {
ret.uuid = val.uuid
}
} catch (err) {
if (opts.verbose) console.error(err)
}
try {
Object.defineProperty(ret, '$context', {
value: val.$context,
enumerable: true
})
} catch (err) {
if (opts.verbose) console.error(err)
}
try {
Object.defineProperty(ret, '$scopedEval', {
value: scopedEval,
enumerable: true
})
} catch (err) {
if (opts.verbose) console.error(err)
}
opts.input[opts.output.get(val)] = ret
return ret
} else if (opts.replaceMode) {
return val
}
return Primitives(key, val)
}
}
export const revive = (input, parsed, output, parser, opts) => {
return Object.keys(output).reduce((output, key) => {
var value = output[key]
// if the value hasn't been revived yet
if (value instanceof String) {
var tmp = input[value]
if (typeof tmp === 'object' && !parsed.get(tmp)) {
parsed.set(tmp, value)
output[key] = primitives(
parser(key, revive(input, parsed, tmp, parser, opts))
)
} else {
try {
output[key] = primitives(parser(key, tmp))
} catch (err) {
delete output[key]
}
}
} else {
try {
if (opts.replaceMode) {
// output[key] = primitives(parser(key, revive(input, parsed, value, parser, opts)))
value = parser(key, value)
if (typeof value === 'object' && !parsed.get(value)) {
parsed.set(value, value)
output[key] = primitives(
parser(key, revive(input, parsed, value, parser, opts))
)
} else {
try {
output[key] = primitives(value)
} catch (err) {
delete output[key]
}
}
} else {
output[key] = primitives(parser(key, value))
}
} catch (err) {
delete output[key]
}
}
return output
}, output)
}
export const createScopedEval = uuid => {
let ret = /*javascript*/ `
function createScopedEval(${uuid}){
// scopeCode
${uuid}.scopeCode = ${uuid}.scopeCode || ${uuid}.getBabel().template.ast('try{}catch(err){console.log(err)}')
${uuid}.previousScopeCode = ${uuid}.currentScopeCode || ${uuid}.scopeCode
${uuid}.currentScopeCode = ${uuid}.scopeCode.block.body.length ? ${uuid}.getBabel().template.ast('try{}catch(err){console.log(err)}') : ${uuid}.scopeCode
if(${uuid}.previousScopeCode != ${uuid}.currentScopeCode){
${uuid}.previousScopeCode.block.body.push(
${uuid}.currentScopeCode
)
}
${uuid}.closureIndex = ${uuid}.closureIndex || 0
${uuid}.closure = ${uuid}.getsmart.bind(this)(${uuid}, ${
/*javascript*/ `\`val.$scopes.\${${uuid}.closureIndex}\``
}, {})
${uuid}.variableKeys = Object.keys(${uuid}.closure)
${uuid}.variableMap = ${uuid}.getsmart.bind(this)(${uuid}, ${
/*javascript*/ `\`val.$context.$variableMaps.\${${uuid}.closureIndex}\``
}, [])
${uuid}.allowedIdentifiers = ['let','var','const']
${uuid}.variableKeys.forEach((key)=>{
if(
typeof ${uuid}.variableMap[key] == 'string'
&& ${uuid}.allowedIdentifiers.indexOf(${uuid}.variableMap[key]) >= 0
){
try{
${uuid}.currentScopeCode.block.body.push(
${uuid}.getBabel().template.ast(
${
/*javascript*/ `\`
\${${uuid}.variableMap[key]} \${key} = ${uuid}.val.$scopes[\${${uuid}.closureIndex}]['\${key}']
\``
}
)
)
}catch(err){console.log(1,err)}
try{
${uuid}.currentScopeCode.block.body.push(
${uuid}.getBabel().template.ast(
${
/*javascript*/ `\`
Object.defineProperty(
${uuid}.val.$scopes[\${${uuid}.closureIndex}],
\${${uuid}.stringify(key)},
{
get(){
return \${key}
},
set(val){
\${key} = val
},
enumerable: true
}
)
\``
}
)
)
}catch(err){console.log(2,err)}
}
// console.log(${uuid}.scopeCode)
})
// console.log(${uuid}.scopeCode)
${uuid}.closureIndex++
if(${uuid}.closureIndex >= ${uuid}.getsmart.bind(this)(${uuid}, 'val.$scopes.length', -1)){
// console.log(${uuid}.scopeCode)
try{
${uuid}.currentScopeCode.block.body.push(
${uuid}.getBabel().template.ast(
${
/*javascript*/ `\`
return \${${uuid}.scopedEval('${uuid}')}
\``
}
)
)
}catch(err){console.log(3,err)}
try{
${uuid}.wrapper = ${uuid}.getBabel().template.ast(
${
/*javascript*/ `\`
function anonymous(){}
\``
}
)
}catch(err){console.log(4,err)}
// console.log(${uuid}.wrapper)
// console.log(${uuid}.scopeCode)
${uuid}.wrapper.body.body.push(${uuid}.scopeCode)
${uuid}.scopeCode = ${uuid}.wrapper
${uuid}.scopeCode = ${uuid}.getBabel().generator(
${uuid}.scopeCode
).code
// console.log(${uuid}.scopeCode)
${uuid}.scopeCode = eval("("+${uuid}.scopeCode+")")
// console.log(${uuid}.scopeCode.toString())
try {
${uuid}.val.$scopedEval = ${uuid}.scopeCode()
}catch(err){console.log(5,err)}
// console.log(${uuid}.val.$scopedEval)
// return ${uuid}.scopeCode.toString()
return ${uuid}.val.$scopedEval
} else {
return eval(${
/*javascript*/ `\`(\${${uuid}.createScopedEval('${uuid}')})\``
})(${uuid})
}
}
`
return ret
}
export const defineVariable = uuid => {
return /*javascript*/ `
${uuid.variableType} ${uuid.variableKey} = ${uuid}.$scope[${uuid}.variableKey]
Object.defineProperty(
${uuid}.$scope,
${uuid}.variableKey,
{
get(){
return ${uuid.variableKey}
},
set(val){
${uuid.variableKey} = val
},
enumerable: true
}
)
`
}
export const scopedEval = uuid => {
let ret = /*javascript*/ `function scopedEval(${uuid}){
if(typeof ${uuid} == 'string'){
${uuid} = {
val: {
js: ${uuid}
}
}
} else if(typeof ${uuid} == 'function' && typeof ${uuid}.toString == 'function'){
${uuid} = {
val: {
js: ${uuid}.toString()
}
}
}
try {
${uuid}.ret = eval('('+${uuid}.val.js+')')
} catch(err1){
try {
${uuid}.ret = eval('({'+${uuid}.val.js+'})')
${uuid}.keys = Object.keys(${uuid}.ret)
${uuid}.ret = ${uuid}.ret[${uuid}.keys[0]]
} catch(err2){
try {
${uuid}.ret = eval('({b:'+ ${uuid}.val.js +'})').b
} catch(err3){
console.error(err1)
console.error(err2)
console.error(err3)
}
}
}
return ${uuid}.ret
}
`
return ret
}
export const jsUUID = (prefix = 'uuid') => {
return prefix + uuid().replace(/-/g, '')
}
export const context = opts => {
let uuid = gosmart.bind(this)(opts, 'path.context.scope.uuid', jsUUID())
return eval(/*javascript*/ `
(
function(){
${contextObject(uuid)}
return ${uuid}
}
)()
`)
}
export const contextObject = uuid => {
return /*javascript*/ `
let ${uuid} = {
$$uuid: '${uuid}',
$closure: {},
$variableMap: {},
$functionScoper: (func)=>{
Object.defineProperty(
func,
'$scopes',
{
value: (function(arr){
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2
})((typeof ${uuid} != 'undefined') ? ${uuid}.$scopes : []),
enumerable: true
}
)
Object.defineProperty(
func,
'$context',
{
value: ${uuid}
}
)
return func
},
$add: (type, name, value)=>{
${uuid}.$closure[name] = value
${uuid}.$variableMap[name] = type
},
$scopes: (function(arr){
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2
})((typeof $context == 'object') ? $context.$scopes : []),
$variableMaps: (function(arr){
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2
})((typeof $context == 'object') ? $context.$variableMaps : []),
$contexts: {},
$contextsList: [],
$parentContexts: [],
$contextStatus: "var",
$mode: (eval("var ${uuid}1 = null"), (typeof ${
uuid + '1'
} === "undefined")) ? "strict" : "non-strict",
}
${uuid}.$functionScoper = ${uuid}.$functionScoper(${uuid}.$functionScoper)
${uuid}.$scopes.splice(0,0,${uuid}.$closure)
${uuid}.$variableMaps.splice(0,0,${uuid}.$variableMap)
var globalThis = globalThis || global || window || {}
${uuid}.$contextStatus = ${uuid}.$mode == 'strict' ? '' : 'var'
try {
eval(${
/*javascript*/ `\`\${${uuid}.$contextStatus} $context = $context || ${uuid}\``
})
} catch(err){
${uuid}.$contextStatus = ''
}
eval(${
/*javascript*/ `\`\${${uuid}.$contextStatus} $context = $context || ${uuid}\``
})
if(typeof $context == 'object' && $context != ${uuid} && $context.$contexts instanceof Object){
$context.$contexts[${uuid}.$$uuid] = $context.$contexts[${uuid}.$$uuid] || []
${uuid}.$$instance = $context.$contexts[${uuid}.$$uuid].push(${uuid})-1
${uuid}.$parentContexts.push($context)
$context.$contextsList.push(${uuid})
}
if(!globalThis.$contexts){
globalThis.$contexts = {}
globalThis.$contexts[${uuid}.$$uuid] = [${uuid}]
globalThis.$contextsList = [${uuid}]
${uuid}.$$instance = 0
} else if(
globalThis.${uuid}s instanceof Object
&& ${uuid}.$parentContexts.length == 0
&& typeof ${uuid}.$$instance == 'undefined'
){
globalThis.${uuid}s[${uuid}.$$uuid] = globalThis.$contexts[${uuid}.$$uuid] || []
${uuid}.$$instance = globalThis.$contexts[${uuid}.$$uuid].push(${uuid})-1
globalThis.$contextsList.push(${uuid})
}
{
let $context = ${uuid}
}
`
}
export const createContext = opts => {
schema(opts, {
wrapBody: true
})
let node = opts.aster(/*javascript*/ `
${contextObject(opts.uuid)}
`)
node[0].declarations[0].contextDeclaration = true
// so the $functionScoper function doesn't get wrapped or have $context inserted
let property3 = node[0].declarations[0].init.properties[3]
property3.value.scoperWrapped = true
property3.value.body.scopeInitialized = true
let property3ScopesValue =
property3.value.body.body[0].expression.arguments[2].properties[0].value
property3ScopesValue.callee.scoperWrapped = true
property3ScopesValue.callee.body.scopeInitialized = true
let property3ForStatement = property3ScopesValue.callee.body.body[0]
property3ForStatement.body.scopeInitialized = true
property3ForStatement.init.declarations[0].inScope = true
property3ForStatement.init.declarations[1].inScope = true
// so the $add function doesn't get wrapped or have $context inserted
let property4 = node[0].declarations[0].init.properties[4]
property4.value.scoperWrapped = true
property4.value.body.scopeInitialized = true
// so the $scopes self-invoking function doesn't get wrapped or have $context inserted
let property5 = node[0].declarations[0].init.properties[5]
property5.value.callee.scoperWrapped = true
property5.value.callee.body.scopeInitialized = true
let property5ForStatement = property5.value.callee.body.body[0]
property5ForStatement.body.scopeInitialized = true
property5ForStatement.init.declarations[0].inScope = true
property5ForStatement.init.declarations[1].inScope = true
// so the $variableMaps self-invoking function doesn't get wrapped or have $context inserted
let property6 = node[0].declarations[0].init.properties[6]
property6.value.callee.scoperWrapped = true
property6.value.callee.body.scopeInitialized = true
let property6ForStatement = property6.value.callee.body.body[0]
property6ForStatement.body.scopeInitialized = true
property6ForStatement.init.declarations[0].inScope = true
property6ForStatement.init.declarations[1].inScope = true
const node6 = node[6]
// make sure try statement block doesn't get scoped either
node6.block.scopeInitialized = true
// make sure catch statement block doesn't get scoped either
node6.handler.body.scopeInitialized = true
const node8 = node[8]
// make sure if statement block doesn't get scoped either
node8.consequent.scopeInitialized = true
const node9 = node[9]
// make sure if statement block doesn't get scoped either
node9.consequent.scopeInitialized = true
// make sure else if statement block doesn't get scoped either
node9.alternate.consequent.scopeInitialized = true
const node10 = node[10]
node10.scopeInitialized = true
node10.inheritScope = true
node[node.length - 1].lastContextNode = true
if (opts.wrapBody) {
let bodyWrapper = node[node.length - 1]
bodyWrapper.body.push(...opts.path.node.body)
}
addBindingsToContext({ ...opts, node })
// let addContextToScopeNode = scopeVar({
// uuid,
// key: '$context',
// type: 'let',
// aster
// })
// wrapper.body.splice(1,0,addContextToScopeNode)
return node
}
export const createInlineContext = opts => {
let wrapperString = /*javascript*/ `
for(let ${opts.uuid} = function(){
// node goes here
return ${opts.uuid}
}() ; a<1;a++){}
`
let inlineContextNode = opts.aster(wrapperString).init.declarations[0]
let contextBody = createContext({ ...opts, wrapBody: false })
inlineContextNode.init.callee.body.body.splice(0, 0, ...contextBody)
inlineContextNode.contextDeclaration = true
return inlineContextNode
}
export const addBindingsToContext = opts => {
for (let key in opts.path.scope.bindings) {
let binding = opts.path.scope.bindings[key]
if (binding.kind == 'var') {
let newNode = scopeVar({
...opts,
key,
type: binding.kind
})
opts.node.splice(opts.node.length - 1, 0, newNode)
}
}
}
export const scopeVarCode = opts => {
let ret = /*javascript*/ `
Object.defineProperty(
${opts.uuid}.$closure,
${stringify(opts.key)},
{
get(){
return ${opts.key}
},
set(val){
${opts.key} = val
},
enumerable: true
}
) &&
(${opts.uuid}.$variableMap["${opts.key}"] = "${opts.type}")
`
return ret
}
export const scopeVarInlineCode = opts => {
let ret = /*javascript*/ `
let ${jsUUID()} = (
${scopeVarCode(opts)}
)
`
return ret
}
export const scopeVar = (opts = {}) => {
let string
let thirdArg
let node
if (opts.inline) {
string = scopeVarInlineCode(opts)
node = opts.aster(string)
thirdArg = node.declarations[0].init.left.arguments[2]
node.declarations[0].inScope = true
} else {
string = scopeVarCode(opts)
node = opts.aster(string)
thirdArg = node.expression.left.arguments[2]
}
let getter = thirdArg.properties[0]
let setter = thirdArg.properties[1]
getter.body.scopeInitialized = true
setter.body.scopeInitialized = true
getter.body.scoperWrapped = true
setter.body.scoperWrapped = true
getter.scoperWrapped = true
setter.scoperWrapped = true
if (opts.inline) return node.declarations[0]
return node
}
export const functionWrapper = (uuid, path, aster) => {
let wrapper = aster(/*javascript*/ `
${uuid}.$functionScoper()
`)
wrapper.expression.arguments.push(path.node)
return wrapper
}
export const bodyInsert = (index, body, aster, ...things) => {
body.splice(index, 0, ...things)
return things.length
}
export const initBlock = (path, aster) => {
if (!path.node.scopeInitialized) {
path.node.scopeInitialized = true
let uuid = getPathUUID({ path })
let contextNode = createContext({ uuid, aster, path })
path.node.body = contextNode
}
}
export const getNodeUUID = opts => {
if (
opts.node &&
opts.node.type != 'BlockStatement' &&
opts.node.type != 'Program'
)
return getNodeUUID({
...opts,
node: opts.node.body || opts.node.block
})
return gosmart.bind(this)(opts.node, 'uuid', jsUUID())
}
export const getPathUUID = opts => {
if (
opts.path.context.scope.path.node.inheritScope ||
opts.path.scope.path.node.inheritScope
)
return getPathUUID({ ...opts, path: opts.path.parentPath })
return getNodeUUID({
...opts,
node: opts.path.context.scope.path.node
})
}
export const babelPlugin = babel => {
const aster = babel.template.ast
let metaVisitor = {
Program (path) {
initBlock(path, aster)
},
BlockStatement (path) {
initBlock(path, aster)
},
ForInStatement () {
// nothing
},
// ForInStatement (path) {
// path = path
// },
ObjectMethod (path) {
let name = path.node.key.name
let replacement = aster(/*javascript*/ `
let a = {
${name}: function ${name}(){}
}
`)
replacement = replacement.declarations[0].init.properties[0]
replacement.value.body = path.node.body
replacement.value.params = path.node.params
path.replaceWith(replacement)
},
Function (path) {
if (
path.type != 'FunctionDeclaration' &&
!path.node.scoperWrapped &&
!path.node.body.scoperWrapped
) {
path.node.scoperWrapped = true
path.node.body.scoperWrapped = true
let uuid = getPathUUID({ path })
let replacement = functionWrapper(uuid, path, aster)
path.replaceWith(replacement)
}
},
FunctionDeclaration (path) {
if (!path.node.scoped) {
path.node.scoped = true
const parentBlock = path.scope.parent
try {
parentBlock.block.body.forEach(node => {
if (node.lastContextNode) {
let uuid = getPathUUID({ path })
let newNode = aster(/*javascript*/ `
${uuid}.$functionScoper(${path.node.id.name})
`)
node.body.splice(1, 0, newNode)
throw new Error('break foreach')
}
})
} catch (err) {
// nothing
}
}
},
VariableDeclarator (path) {
if (!path.node.inScope) {
path.node.inScope = true
let parentPath = getsmart.bind(this)(path, 'parentPath', undefined)
if (
// this is for inline let and const declarations in normal
// js blocks
(parentPath.node.kind == 'let' || parentPath.node.kind == 'const') &&
// we check the length of declarations because we only have to do inline replacement
// if there's a chance another declaration might use a former one
parentPath.node.declarations.length > 1 &&
!(
parentPath.parentPath.node.type == 'ForInStatement' ||
parentPath.parentPath.node.type == 'ForOfStatement' ||
parentPath.parentPath.node.type == 'ForStatement'
)
) {
let uuid = getPathUUID({ path })
if (uuid) {
let indexInParent = parentPath.node.declarations.indexOf(path.node)
let newDeclaration = scopeVar({
aster,
inline: true,
uuid,
key: parentPath.node.declarations[indexInParent].id.name,
type: parentPath.node.kind
})
parentPath.node.declarations.splice(
indexInParent + 1,
0,
newDeclaration
)
}
} else if (
//
(parentPath.node.kind == 'let' ||
parentPath.node.kind == 'var' ||
parentPath.node.kind == 'const') &&
// only do this for singular declarations
parentPath.node.declarations.length < 2 &&
// and check if variable is declared inside a ForX statement
(parentPath.parentPath.node.type == 'ForInStatement' ||
parentPath.parentPath.node.type == 'ForOfStatement' ||
parentPath.parentPath.node.type == 'ForStatement')
) {
let uuid = getPathUUID({ path })
if (uuid) {
let indexInParent = parentPath.node.declarations.indexOf(path.node)
let newNode = scopeVar({
aster,
uuid,
key: parentPath.node.declarations[indexInParent].id.name,
type: parentPath.node.kind
})
parentPath.parentPath.node.body.body.splice(0, 0, newNode)
}
} else if (
// this is a special case for when ForStatements get their own scope
(parentPath.node.kind == 'let' || parentPath.node.kind == 'const') &&
// we check the length of declarations because we only have to do inline replacement
// if there's a chance another declaration might use a former one
parentPath.node.declarations.length > 1 &&
parentPath.parentPath.node.type == 'ForStatement'
) {
// if the first declaration isn't our context declaration, insert one
let uuid = gosmart.bind(this)(path, 'scope.uuid', jsUUID())
if (!parentPath.node.declarations[0].contextDeclaration) {
let inlineContextDeclaration = createInlineContext({
path,
uuid,
aster
})
parentPath.node.declarations.splice(0, 0, inlineContextDeclaration)
}
if (uuid) {
let indexInParent = parentPath.node.declarations.indexOf(path.node)
let newDeclaration = scopeVar({
aster,
inline: true,
uuid,
key: parentPath.node.declarations[indexInParent].id.name,
type: parentPath.node.kind
})
parentPath.node.declarations.splice(
indexInParent + 1,
0,
newDeclaration
)
}
} else if (
parentPath.node.kind == 'let' ||
parentPath.node.kind == 'const'
) {
let uuid = getPathUUID({ path })
if (uuid) {
let indexInParent = parentPath.node.declarations.indexOf(path.node)
let newNode = scopeVar({
aster,
uuid,
key: parentPath.node.declarations[indexInParent].id.name,
type: parentPath.node.kind
})
parentPath.insertAfter(newNode)
}
} else {
// let uuid = getPathUUID({path})
// if(uuid){
// let indexInParent = parentPath.node.declarations.indexOf(path.node)
// let newNode = scopeVar({
// aster,
// uuid,
// key: parentPath.node.declarations[indexInParent].id.name,
// type: parentPath.node.kind
// })
// parentPath.insertAfter(newNode)
// }
}
}
}
}
let ret = {
visitor: metaVisitor
// visitor: {
// Program(path){
// path.traverse(metaVisitor)
// }
// }
}
return ret
}
export const transform = (src, opts = {}) => {
return getBabel().transform(src, {
plugins: [babelPlugin],
...opts
})
}
/** non-parser stuff */
export const stripUuids = (thing, seen = []) => {
try {
delete thing.uuid
} catch {
// nothing
}
if (typeof thing === 'object') {
const keys = Object.keys(thing)
for (const key of keys) {
const val = thing[key]
if (!seen.includes(val)) {
seen.push(val)
stripUuids(val, seen)
}
}
}
}
export const dupe = (obj, opts = {}) => {
return parse(stringify(obj, opts), opts)
}
export const clone = (obj, opts = {}) => {
return dupe(obj, opts)
}
export const schema = (obj1, obj2, opts = {}) => {
if (!opts.noSchemaClone) {
obj2 = clone(obj2, opts)
}
return merge(obj1, obj2, {
...opts
})
}
export const create = (obj1, obj2, opts) => {
let ret = merge(obj1, obj2, {
clone: true,
...opts
})
return ret
}
export const merge = (value1, value2, opts = {}, seen = new Map()) => {
if (seen.has(value1)) return seen.get(value1)
if (value1 instanceof Array && value2 instanceof Array) {
return value1
}
// base case non-basic value
if (basic(value1) && !opts.overwrite) {
return value1
}
if (opts.overwrite && basic(value2)) {
return value2
}
if (!basic(value1) && basic(value2)) {
return value1
}
if (opts.clone) {
value1 = clone(value1)
value2 = clone(value2)
}
let props = Object.keys(value2)
props.forEach(prop => {
let propertyValue1 = value1[prop]
if (prop in value1 && basic(propertyValue1) && !opts.overwrite) {
return
}
let propertyValue2 = value2[prop]
seen.set(value1, value1)
let newVal = propertyValue2
if (prop in value1) {
newVal = merge.bind(this)(
propertyValue1,
propertyValue2,
{ ...opts, ...{ clone: false } },
seen
)
}
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
value1
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse?.(value1, prop, newVal)
if (
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
value1[prop] = newVal
if (
getsmart.bind(this)(local.vue, 'store', false) &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
})
return value1
}
export const basic = value => {
let valueType = typeof value
let ret =
!(
valueType == 'object' ||
valueType == 'array' ||
valueType == 'function'
) || value === null
return ret
}
export const mod = (args, mod) => {
return mod(args) || args
}
// transform(value, fn, path, ret={}){
// return forEach(value, fn, path, ret)
// },
export const deepForEach = (
value,
fn,
path,
ret = {},
seens = { originals: [], clones: [] },
first = true
) => {
path = path || ''
if (first) {
value = { '': value }
}
// if(!(typeof value == 'string' || typeof value == 'boolean' || typeof value == 'number')){
// seens.originals.push(value)
// }
if (Array.isArray(value)) {
forEachArray(value, fn, path, ret, seens)
} else if (typeof value == 'object') {
forEachObject(value, fn, path, ret, seens)
}
return ret['']
}
export const forEachObject = (obj, fn, path, ret, seens) => {
for (const key in obj) {
const deepPath = path ? `${path}.${key}` : key
let primitive =
typeof obj[key] == 'string' ||
typeof obj[key] == 'boolean' ||
typeof obj[key] == 'number'
if (primitive || seens.originals.indexOf(obj[key]) < 0) {
if (!primitive) {
seens.originals.push(obj[key])
}
// Note that we always use obj[key] because it might be mutated by forEach
fn(obj[key], key, obj, deepPath, ret, seens)
deepForEach(obj[key], fn, deepPath, ret, seens, false)
}
}
}
export const forEachArray = (array, fn, path, ret = {}, seens) => {
array.forEach((value, index, arr) => {
let primitive =
typeof value == 'string' ||
typeof value == 'boolean' ||
typeof value == 'number'
if (primitive || seens.originals.indexOf(value) < 0) {
if (!primitive) {
seens.originals.push(value)
}
const deepPath = `${path}.${index}`
fn(value, index, arr, deepPath, ret, seens)
// Note that we use arr[index] because it might be mutated by forEach
deepForEach(arr[index], fn, deepPath, ret, seens, false)
}
})
}
export const setThing = ({
option,
list = getsmart.bind(this)(objList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype,
push,
strings,
targets
} = {}) => {
let index = thingIndex({
option,
list,
obj,
keys,
keymatchtype,
strings
})
if (obj == 'debug') {
console.log('index')
console.log(index)
console.log('list')
console.log(list)
}
if (index >= 0 && list) {
if (targets && targets.length && typeof targets.length == 'number') {
for (var i = 0; i < targets.length; i++) {
let value = getsmart.bind(this)(option, targets[i], undefined)
if (value) {
setsmart.bind(this)(list[index], targets[i], value)
}
}
} else {
list.splice(index, 1, option)
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
)
) {
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else if (
getsmart.bind(this)(local.vue, 'store', false) &&
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
// list[index] = option
} else if (push && list) {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
list.splice(list.length, 0, option)
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
list.push(option)
}
index = list.length - 1
}
return index
}
export const setThings = ({
options,
list = getsmart.bind(this)(objList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype,
push,
async
} = {}) => {
if (options && options instanceof Array && list) {
for (let option of options) {
if (async) {
new Promise(() => {
setThing({
option,
list,
obj,
keys,
keymatchtype,
push
})
})
} else {
setThing({
option,
list,
obj,
keys,
keymatchtype,
push
})
}
}
}
return list
}
export const optIn = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype,
index
) => {
if (typeof option === 'object') {
obj = true
}
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
return index ? list.indexOf(option) : true
} else if (obj && list && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (!(keys && typeof keys.length == 'number')) return
for (var indKey = 0; indKey < keys.length; indKey++) {
if (keymatchtype == 'broad') {
if (
list[i] &&
getsmart.bind(this)(list[i], keys[indKey], undefined) ==
getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined
) {
return index ? i : true
} else if (
list[i] &&
typeof list[i] == 'string' &&
list[i] == getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(option, keys[indKey], undefined) !== undefined
) {
return index ? i : true
}
} else {
if (
list[i] &&
getsmart.bind(this)(list[i], keys[indKey], undefined) ==
getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined
) {
if (indKey == keys.length - 1) {
return index ? i : true
}
} else if (
list[i] &&
typeof list[i] == 'string' &&
list[i] == getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(option, keys[indKey], undefined) !== undefined
) {
if (indKey == keys.length - 1) {
return index ? i : true
}
}
}
}
}
}
return index ? -1 : false
}
export const thingIn = ({
option,
list = getsmart.bind(this)(objList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype,
retIndex
// vue = local.vue
} = {}) => {
if (typeof option === 'object') {
obj = true
}
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
if (retIndex) {
return list.indexOf(option)
} else {
return true
}
} else if (obj && list && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (!(keys && typeof keys.length == 'number')) return
for (var indKey = 0; indKey < keys.length; indKey++) {
if (keymatchtype == 'broad') {
if (
list[i] &&
getsmart.bind(this)(list[i], keys[indKey], undefined) ==
getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined
) {
if (retIndex) {
return i
} else {
return true
}
} else if (
list[i] &&
typeof list[i] == 'string' &&
list[i] == getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(option, keys[indKey], undefined) !== undefined
) {
if (retIndex) {
return i
} else {
return true
}
}
} else {
if (
list[i] &&
getsmart.bind(this)(list[i], keys[indKey], undefined) ==
getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined
) {
if (indKey == keys.length - 1) {
if (retIndex) {
return i
} else {
return true
}
}
} else if (
list[i] &&
typeof list[i] == 'string' &&
list[i] == getsmart.bind(this)(option, keys[indKey], undefined) &&
getsmart.bind(this)(option, keys[indKey], undefined) !== undefined
) {
if (indKey == keys.length - 1) {
if (retIndex) {
return i
} else {
return true
}
}
}
}
}
}
}
if (retIndex) {
return -1
} else {
return false
}
}
export const optsIn = (
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (!(options instanceof Array)) return true
for (let option of options) {
// if(typeof option === 'object'){
// obj = true
// }
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
// return true
} else if (obj && list) {
for (var i = 0; i < list.length; i++) {
if (!optIn(option, list[i], obj, keys, keymatchtype)) {
return false
}
}
} else {
return false
}
}
return true
}
export const thingsIn = ({
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (!(options instanceof Array)) return true
for (let option of options) {
// if(typeof option === 'object'){
// obj = true
// }
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
// return true
} else if (obj && list && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (!optIn(option, list[i], obj, keys, keymatchtype)) {
return false
}
}
} else {
return false
}
}
return true
}
export const anyOptsIn = (
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (!(options instanceof Array)) return false
for (let option of options) {
// if(typeof option === 'object'){
// obj = true
// }
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
return true
} else if (obj && list && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (optIn(option, list[i], obj, keys, keymatchtype)) {
return true
}
}
}
}
return false
}
export const anyThingsIn = ({
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (!(options instanceof Array)) return false
for (let option of options) {
// if(typeof option === 'object'){
// obj = true
// }
if (!obj && list && list.indexOf && list.indexOf(option) >= 0) {
return true
} else if (obj && list && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (optIn(option, list[i], obj, keys, keymatchtype)) {
return true
}
}
}
}
return false
}
export const optIndex = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (typeof option === 'object') {
obj = true
}
if (obj && list && keys && typeof list.length == 'number') {
for (var i = 0; i < list.length; i++) {
if (optIn(option, list, obj, keys, keymatchtype)) {
return i
}
}
} else if (list) {
return list.indexOf(option)
}
return -1
}
export const thingIndex = ({
option,
list,
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype,
strings
} = {}) => {
if (typeof option === 'object') {
obj = true
}
if (obj && list && keys) {
let index = thingIn({
option,
list,
obj,
keys,
keymatchtype,
strings,
retIndex: true
})
return index
} else if (list) {
return list.indexOf(option)
}
return -1
}
export const pushOpt = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype,
index
) => {
if (
typeof list == 'object' &&
!optIn(option, list, obj, keys, keymatchtype)
) {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
list.splice(list.length, 0, option)
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
list.push(option)
}
}
return index
? optIn(option, list, obj, keys, keymatchtype, index)
: optIn(option, list, obj, keys, keymatchtype, index)
}
export const addOpt = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype,
index
) => {
if (typeof list == 'object') {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
list.splice(list.length, 0, option)
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
list.push(option)
}
}
return index
? optIn(option, list, obj, keys, keymatchtype, index)
: optIn(option, list, obj, keys, keymatchtype, index)
}
export const pushThing = ({
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (
typeof list == 'object' &&
!thingIn({ option, list, obj, keys, keymatchtype })
) {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
list.splice(list.length, 0, option)
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
list.push(option)
}
}
}
export const pushOpts = (
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (!(options instanceof Array)) return
for (let option of options) {
pushOpt(option, list, obj, keys, keymatchtype)
}
}
export const pushThings = ({
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (!(options instanceof Array)) return
for (let option of options) {
pushThing({ option, list, obj, keys, keymatchtype })
}
}
export const popOpt = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (typeof list == 'object' && optIn(option, list, obj, keys, keymatchtype)) {
list.splice(optIndex(option, list, obj, keys, keymatchtype), 1)
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
}
}
export const popThing = ({
option,
list = getsmart.bind(this)(stringList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (
typeof list == 'object' &&
thingIn({
option,
list,
obj,
keys,
keymatchtype
})
) {
list.splice(
thingIndex({
option,
list,
obj,
keys,
keymatchtype
}),
1
)
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) ||
getsmart.bind(this)(local.vue, 'store', false)
) {
if (
!localStorage.getItem('vuexWriteLock') &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
}
}
export const popOpts = (
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (!(options instanceof Array)) return
for (let option of options) {
popOpt(option, list, obj, keys, keymatchtype)
}
}
export const popThings = ({
options,
list = getsmart.bind(this)(stringList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (!(options instanceof Array)) return
for (let option of options) {
popOpt(option, list, obj, keys, keymatchtype)
}
}
export const toggleOpt = (
option,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (optIn(option, list, obj, keys, keymatchtype)) {
popOpt(option, list, obj, keys, keymatchtype)
} else {
pushOpt(option, list, obj, keys, keymatchtype)
}
}
export const toggleThing = ({
option,
list = getsmart.bind(this)(stringList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (optIn(option, list, obj, keys, keymatchtype)) {
popOpt(option, list, obj, keys, keymatchtype)
} else {
pushOpt(option, list, obj, keys, keymatchtype)
}
}
export const toggleOpts = (
options,
list = getsmart.bind(this)(stringList),
obj,
keys = ['uuid', '_id', 'id'],
keymatchtype
) => {
if (!(options instanceof Array)) return
for (let option in options) {
toggleOpt(option, list, obj, keys, keymatchtype)
}
}
export const toggleThings = ({
options,
list = getsmart.bind(this)(stringList),
obj = true,
keys = ['uuid', '_id', 'id'],
keymatchtype
} = {}) => {
if (!(options instanceof Array)) return
for (let option in options) {
if (optIn(option, list, obj, keys, keymatchtype)) {
popOpt(option, list, obj, keys, keymatchtype)
} else {
pushOpt(option, list, obj, keys, keymatchtype)
}
}
}
// no use right now
// export const ratchetOpt = (
// option,
// list,
// obj,
// keys = ['uuid', '_id', 'id'],
// keymatchtype
// ) => {
// // find(obj, property, equals){
// // if(getsmart.bind(this)(obj, 'constructor', undefined) == Array){
// // for(var i=0; i<obj.length; i++){
// // find(obj[i], )
// // }
// // }
// // },
// }
export const getsmart = (
obj,
property,
defaultValue?: any,
context?: any,
schema?: any
) => {
if (!property && obj && typeof obj == 'string') {
property = obj.split('.')
try {
obj = eval(property[0])
} catch (err) {
// console.error(err)
obj = property[0]
}
property = property.slice(1, property.length)
}
if (!property) {
if (context) {
return {
value: defaultValue,
undefined: true
}
} else {
return defaultValue
}
}
// If the property list is in dot notation, convert to array
if (typeof property == 'string') {
property = parsePropertyPath(property)
} else if (getsmart.bind(this)(property, 'constructor', false) !== Array) {
if (context) {
return {
value: defaultValue,
undefined: true,
err: 'properties path @property argument was not passed properly'
}
} else {
return defaultValue
}
}
let deepGetByArray = deepGetByArrayUnbound.bind(this)
return deepGetByArray(obj, property, defaultValue)
// In order to avoid constantly checking the type of the property
// we separate the real logic out into an inner function.
function deepGetByArrayUnbound (obj, propsArray, defaultValue) {
// This case getting to the last property but it not being ultimately defined
// Not just having a value of undefined
if (
propsArray.length > 0 &&
context &&
typeof obj == 'object' &&
obj !== null &&
!(ee(propsArray[0]) in obj)
) {
return {
value: defaultValue,
undefined: true
}
}
// If we have reached an undefined/null property
// then stop executing and return the default value.
// If no default was provided it will be undefined.
if (
typeof obj == 'undefined' ||
obj == null ||
(schema && obj.constructor.name !== schema)
) {
if (context) {
let undef = true
if (propsArray.length === 0) {
undef = false
}
return {
value: defaultValue,
undefined: undef,
schema: schema && obj.constructor.name === schema
}
} else {
return defaultValue
}
} // If the path array has no more elements, we've reached
// the intended property and return its value
if (propsArray.length === 0) {
if (context) {
return {
value: obj,
undefined: false
}
} else {
return obj
}
} // Prepare our found property and path array for recursion
var nextObj = obj[ee(propsArray[0])]
var remainingProps = propsArray.slice(1)
return deepGetByArray(nextObj, remainingProps, defaultValue)
}
}
export const escapePropertyPath = (path = '') => {
let newPath = escapeEscapes(path)
return '["' + newPath + '"]'
}
export const epp = (path = '') => {
return escapePropertyPath(path)
}
export const escapeEscapes = (path = '') => {
let newPath = ''
for (let i in path) {
i = +i
let char = path[i]
if (i > 0 && i < path.length - 1) {
let prevChar = path[i - 1]
let nextChar = path[i + 1]
let openingArrayPath = char === '"' && prevChar === '['
// && (nextChar !== "\\" || i === path.length-1)
let closingArrayPath =
char === '"' && nextChar === ']' && prevChar !== '\\'
// let offset = 0
// if(openingArrayPath) offset = 1
if (openingArrayPath || closingArrayPath) {
newPath += '\\'
// path = path.slice(0,i+offset)+"\\"+path.slice(i+offset,path.length)
}
}
newPath += char
}
return newPath
}
export const ee = (path = '') => {
return escapeEscapes(path)
}
// TODO
// Make parsing use \" or \'
// Currently only uses \"
export const parsePropertyPath = (path = '') => {
let array = []
let readingArrayBasedPath = false
let i = 0
let push = false
let pushed = false
while (i < path.length) {
let arrayPathStart = path[i] == '[' && path[i + 1] == '"'
let escapedStart = !(path[i + 1] !== '\\' || i === 0)
if (readingArrayBasedPath) {
// we found the end of an array delimited path
let arrayPathEnd = path[i] == '"' && path[i + 1] == ']'
let escapedEnd = !(path[i - 1] !== '\\' || i == 0)
if (arrayPathEnd && !escapedEnd) {
i += 1
readingArrayBasedPath = false
if (!pushed) push = true
} else {
// if the path includes an "escaped" array based path begin or end value
// do not push the escape character
if (
(path[i] == '\\' && path[i + 1] == '"' && path[i + 2] == ']') ||
(path[i - 1] == '[' && path[i] == '\\' && path[i + 1] == '"')
) {
// nothing
} else {
array[array.length - 1] += path[i]
}
}
} else if (path[i] == '.') {
if (!pushed) push = true
}
// we found the start of an array delimited path
else if (arrayPathStart && !escapedStart) {
readingArrayBasedPath = true
if (!pushed) push = true
i += 1
} else {
if (i == 0) array.push('')
array[array.length - 1] += path[i]
}
i++
if (push && i < path.length) {
pushed = true
array.push('')
push = false
} else {
pushed = false
}
}
return array
}
export const ppp = (path = '') => {
return this.parsePropertyPath(path)
}
export const parsePropertyArray = pathArray => {
let path = ''
if (pathArray instanceof Array) {
pathArray.forEach(subPath => {
path += epp(subPath)
})
} else if (typeof pathArray === 'string') {
return path
}
return path
}
export const ppa = pathArray => {
return this.parsePropertyArray(pathArray)
}
export const pathToArray = path => {
if (typeof path == 'string') {
return parsePropertyPath(path)
} else {
return path
}
}
export const pathToString = path => {
if (typeof path == 'string') {
let ret = parsePropertyPath(path)
ret = parsePropertyArray(ret)
return ret
} else {
let ret = parsePropertyArray(path)
return ret
}
}
export const setsmart = (obj, property, value, context?: any) => {
if (!property && typeof obj == 'string') {
property = obj.split('.')
try {
obj = eval(property[0])
} catch (err) {
obj = property[0]
}
property = property.slice(1, property.length)
}
// If the property list is in dot notation, convert to array
if (typeof property == 'string') {
property = parsePropertyPath(property)
} else if (getsmart.bind(this)(property, 'constructor', false) !== Array) {
if (context) {
return {
value: value,
undefined: true,
err: 'properties path @property argument was not passed properly'
}
} else {
return value
}
}
// if no obj make obj
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) obj = {}
let deepSetByArray = deepSetByArrayUnbound.bind(this)
if (property) {
return deepSetByArray(obj, property, value)
} else {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
obj
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse(obj, undefined, value)
if (
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
obj = value
if (
getsmart.bind(this)(local.vue, 'store', false) &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
if (context) {
return {
value: obj,
undefined: false,
err: 'there were no properties passed'
}
} else {
return obj
}
}
// In order to avoid constantly checking the type of the property
// we separate the real logic out into an inner function.
function deepSetByArrayUnbound (obj, propsArray, value) {
// If the path array has only 1 more element, we've reached
// the intended property and set its value
if (propsArray.length == 1) {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
obj
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse(obj, ee(propsArray[0]), value)
if (
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
// TODO: make parent object new object so that react can see the change
obj[ee(propsArray[0])] = value
if (
getsmart.bind(this)(local.vue, 'store', false) &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
if (context) {
return {
value: obj[ee(propsArray[0])],
undefined: false
}
} else {
return obj[ee(propsArray[0])]
}
}
// Prepare our path array for recursion
var remainingProps = propsArray.slice(1)
// check if next prop is object
if (typeof obj[ee(propsArray[0])] !== 'object') {
// If we have reached an undefined/null property
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
obj
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse(obj, ee(propsArray[0]), {})
if (
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
obj[ee(propsArray[0])] = {}
if (
getsmart.bind(this)(local.vue, 'store', false) &&
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
}
}
return deepSetByArray(obj[ee(propsArray[0])], remainingProps, value)
}
}
export const deletesmart = (obj, property) => {
if (!property && typeof obj == 'string') {
property = obj.split('.')
try {
obj = eval(property[0])
} catch (err) {
// console.error(err)
obj = property[0]
}
property = property.slice(1, property.length)
}
// If the property list is in dot notation, convert to array
if (typeof property == 'string') {
property = parsePropertyPath(property)
}
let parentPathArray = property.slice(0, property.length - 1)
let path = property[property.length - 1]
let parentObj = getsmart(obj, parentPathArray, {})
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
obj
) {
this.$delete(parentObj, path)
} else {
delete parentObj[path]
}
}
export const pushSmart = (array, value) => {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
array
) {
array.push(value)
if (
typeof getsmart.bind(this)(window, '$store.commit', undefined) ==
'function'
) {
window.$store.commit(local.vue.basePath || 'graph/thing')
}
} else {
array.push(value)
}
}
export const gosmart = (obj, property, value, context, schema) => {
// stands for get or set smart
var get = getsmart.bind(this)(
obj,
property,
value,
true,
schema ? absoluteType.bind(this)(value) : false
)
if (get.undefined || (schema && get.schema === false)) {
get = setsmart.bind(this)(obj, property, get.value, context)
if (context) {
return get
} else {
return get
}
} else {
return get.value
}
}
export const gosmarter = (obj, property, value, context, schema = true) => {
return gosmart.bind(this)(obj, property, value, context, schema)
}
export const absoluteType = value => {
let type
try {
type = value.constructor.name
} catch (e) {
if (typeof value === 'undefined') type = 'undefined'
if (value === null) type = 'null'
}
return type
}
export const vgosmart = (obj, property, value, context) => {
// stands for v-model get or set smart
// return value from property path, either gotten or smartly set
return {
get: () => {
var get = getsmart.bind(this)(obj, property, value, true)
if (get.undefined) {
get = setsmart.bind(this)(obj, property, get.value, context)
}
if (context) {
return get
} else {
return getsmart.bind(this)(get, 'value', get)
}
},
set: val => {
setsmart.bind(this)(obj, property, val)
}
}
}
export const getsmartval = (obj, property, defaultValue) => {
// get the value of a property path based off its type
let target = getsmart.bind(this)(obj, property, defaultValue)
if (target && target.type) {
if (target[target.type]) {
return target[target.type]
} else {
return defaultValue
}
} else if (target) {
return target
}
return defaultValue
}
export const safestring = something => {
return smarts.stringify(something || '')
}
export const safeparse = something => {
return smarts.parse(something || '')
}
export const flatten = (arrays, func = i => i) => {
const flat = []
arrays.forEach(array => {
if (Array.isArray(array)) {
flat.push(...flatten(array))
} else {
flat.push(func(array))
}
})
return flat
}
export const mapsmart = (list, keyProperty = 'title', returnExistant) => {
return new Promise((resolve, reject) => {
if (!keyProperty) {
reject()
} else if (list && typeof list.length == 'number') {
if (list.length == 0) {
if (
(returnExistant &&
getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) ||
!returnExistant
) {
resolve(true)
} else if (returnExistant) {
resolve(false)
} else {
resolve()
}
}
if (!list.mapped || typeof list.mapped === 'boolean') {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
list
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse(list, 'mapped', {})
} else {
list['mapped'] = {}
}
}
for (var i = 0; i < list.length; i++) {
if (typeof list[i] !== 'string') {
if (
getsmart.bind(this)(local.vue, 'reactiveSetter', false) &&
getsmart.bind(this)(
this,
'$set',
getsmart.bind(this)(local.vue, 'Vue.set', false)
) &&
list.mapped
) {
const setToUse = this.$set || local.vue.Vue.set
setToUse(list.mapped, list[i][keyProperty], list[i])
} else {
list['mapped'][list[i][keyProperty]] = list[i]
}
if (i == list.length - 1) {
if (
(returnExistant &&
getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) ||
!returnExistant
) {
resolve(true)
} else if (returnExistant) {
resolve(false)
} else {
resolve()
}
}
}
// else if(populate){
// var funCounter = this.funCounter
// this.funCounter = this.funCounter + 1
// getThing({
// thing: list[i],
// clientId: this._uid,
// funCounter
// })
// this.$options.sockets['giveThing'] = data => {
// if(this._uid == data.clientId && data.funCounter == funCounter){
// (this.$set || local.vue.Vue.set)(list, i.toString(), data.thing)
// (this.$set || local.vue.Vue.set)(list.mapped, list[i][keyProperty], list[i])
// }
// if(i==list.length-1){
// if((returnExistant && getsmart.bind(this)(list, 'mapped.'+returnExistant, false)) || !returnExistant){
// resolve(true)
// } else if(returnExistant) {
// resolve(false)
// } else {
// resolve()
// }
// }
// }
// }
else if (i == list.length - 1) {
if (
(returnExistant &&
getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) ||
!returnExistant
) {
resolve(true)
} else if (returnExistant) {
resolve(false)
} else {
resolve()
}
}
}
// if(list.mapped && !list.mapped['agora-client-mapped']){
// (this.$set || local.vue.Vue.set)(list.mapped, 'agora-client-mapped', true)
// }
}
})
}
export const domval = thing => {
return getsmart.bind(this)(thing, 'properties.description', '')
}
export const getParent = (levels = Infinity) => {
if (typeof levels == 'string') levels = (levels.match(/\.\./g) || []).length
if (levels >= this.pathAsArray.length - 1) {
return this.pathAsArray[0]
}
let level = this.pathAsArray.length - 1 - levels
return this.pathAsArray[level]
}
export const getThing = (props = {}) => {
const { list = getsmart.bind(this)(objList), defaultValue = undefined } =
props
var index = thingIn({
...props,
retIndex: true
})
if (index >= 0) {
return list[index]
} else {
return defaultValue
}
}
export const equal = (obj1, obj2, seen = []) => {
if (obj1 && obj2 && typeof obj1 == 'object' && typeof obj2 == 'object') {
seen.push(obj1, obj2)
//Loop through properties in object 1
for (const p in obj1) {
//Check property exists on both objects
if (
typeof obj1.hasOwnProperty == 'function' &&
typeof obj2.hasOwnProperty == 'function' &&
Object.prototype.hasOwnProperty.call(obj1, p) !==
Object.prototype.hasOwnProperty.call(obj2, p)
)
return false
switch (typeof obj1[p]) {
//Deep compare objects
case 'object':
if (seen.indexOf(obj1[p]) < 0 && !equal(obj1[p], obj2[p], seen))
return false
break
//Compare function code
case 'function':
if (
typeof obj2[p] == 'undefined' ||
obj1[p].toString() != obj2[p].toString()
)
return false
break
//Compare values
default:
if (obj1[p] != obj2[p]) return false
}
}
//Check object 2 for any extra properties
for (const p in obj2) {
if (!(p in obj1)) return false
}
return true
}
}
export const mergeall = (array, options) => {
if (!Array.isArray(array)) {
throw new Error('first argument should be an array')
}
return array.reduce(function (prev, next) {
return merge(prev, next, options)
}, {})
}
// export const smarts with all other exports
export const smarts = {
local,
t,
getBabel,
uuid,
pause,
save,
toJavascript,
toJavascriptAux,
createObjectProperties,
serialize,
stringify,
replacer,
setKnown,
stringifier,
primitives,
Primitives,
play,
load,
parse,
parser,
revive,
createScopedEval,
defineVariable,
scopedEval,
jsUUID,
context,
contextObject,
createContext,
createInlineContext,
addBindingsToContext,
scopeVarCode,
scopeVarInlineCode,
scopeVar,
functionWrapper,
bodyInsert,
initBlock,
getNodeUUID,
getPathUUID,
babelPlugin,
transform,
stripUuids,
dupe,
clone,
schema,
create,
merge,
basic,
mod,
deepForEach,
forEachObject,
forEachArray,
setThing,
setThings,
optIn,
thingIn,
optsIn,
thingsIn,
anyOptsIn,
anyThingsIn,
optIndex,
thingIndex,
pushOpt,
addOpt,
pushThing,
pushOpts,
pushThings,
popOpt,
popThing,
popOpts,
popThings,
toggleOpt,
toggleThing,
toggleOpts,
toggleThings,
// ratchetOpt,
getsmart,
escapePropertyPath,
epp,
escapeEscapes,
ee,
parsePropertyPath,
ppp,
parsePropertyArray,
ppa,
pathToArray,
pathToString,
setsmart,
deletesmart,
pushSmart,
gosmart,
gosmarter,
absoluteType,
vgosmart,
getsmartval,
safestring,
safeparse,
flatten,
mapsmart,
domval,
getParent,
getThing,
equal,
mergeall
}