New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@latitude-data/sql-compiler

Package Overview
Dependencies
Maintainers
4
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@latitude-data/sql-compiler - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

6

CHANGELOG.md
# @latitude-data/sql-compiler
## 1.1.0
### Minor Changes
- d34d824: Added support for spread syntax
## 1.0.0

@@ -4,0 +10,0 @@

2

dist/compiler/utils.d.ts
import { QueryMetadata } from './types';
export declare function mergeMetadata(...metadata: QueryMetadata[]): QueryMetadata;
export declare function emptyMetadata(): QueryMetadata;
export declare function isIterable(obj: unknown): obj is Iterable<unknown>;
export declare function hasContent(iterable: Iterable<unknown>): Promise<boolean>;
//# sourceMappingURL=utils.d.ts.map

@@ -106,2 +106,10 @@ declare const _default: {

};
invalidSpreadInObject: (property: string) => {
code: string;
message: string;
};
invalidSpreadInArray: (element: string) => {
code: string;
message: string;
};
unsupportedOperator: (operator: string) => {

@@ -108,0 +116,0 @@ code: string;

2

package.json
{
"name": "@latitude-data/sql-compiler",
"version": "1.0.0",
"version": "1.1.0",
"license": "LGPL",

@@ -5,0 +5,0 @@ "description": "Compiler for Latitude's custom sql sintax based on svelte",

@@ -377,15 +377,43 @@ import { compile, emptyMetadata } from '..'

describe('each loops', async () => {
it('prints each content for each element in the array', async () => {
const sql = "{#each ['a', 'b', 'c'] as element} {element} {/each}"
const result = await compile({
query: sql,
it('iterates over any iterable object', async () => {
const sql1 = '{#each [1, 2, 3] as element} {element} {/each}'
const sql2 = '{#each "foo" as element} {element} {/each}'
const result1 = await compile({
query: sql1,
resolveFn,
supportedMethods: {},
})
const result2 = await compile({
query: sql2,
resolveFn,
supportedMethods: {},
})
expect(result).toBe('$[[a]]$[[b]]$[[c]]')
expect(result1).toBe('$[[1]]$[[2]]$[[3]]')
expect(result2).toBe('$[[f]]$[[o]]$[[o]]')
})
it('gives access to the index of the element', async () => {
const sql = "{#each ['a', 'b', 'c'] as element, index} {index} {/each}"
it('computes the else block when the element is not iterable', async () => {
const sql1 = '{#each 5 as element} {element} {:else} FOO {/each}'
const sql2 =
'{#each { a: 1, b: 2, c: 3 } as element} {element} {:else} FOO {/each}'
const result1 = await compile({
query: sql1,
resolveFn,
supportedMethods: {},
})
const result2 = await compile({
query: sql2,
resolveFn,
supportedMethods: {},
})
expect(result1).toBe('FOO')
expect(result2).toBe('FOO')
})
it('computes the else block when the iterable object is empty', async () => {
const sql = '{#each [] as element} {element} {:else} var {/each}'
const result = await compile({

@@ -397,7 +425,7 @@ query: sql,

expect(result).toBe('$[[0]]$[[1]]$[[2]]')
expect(result).toBe('var')
})
it('replaces a variable with the value of the element', async () => {
const sql = "{#each ['a', 'b', 'c'] as element} {element} {/each}"
it('does not do anything when the iterable object is not iterable and there is no else block', async () => {
const sql = '{#each 5 as element} {element} {/each}'
const result = await compile({

@@ -409,7 +437,7 @@ query: sql,

expect(result).toBe('$[[a]]$[[b]]$[[c]]')
expect(result).toBe('')
})
it('prints else content when the array is empty', async () => {
const sql = '{#each [] as element} {element} {:else} var {/each}'
it('gives access to the index of the element', async () => {
const sql = "{#each ['a', 'b', 'c'] as element, index} {index} {/each}"
const result = await compile({

@@ -421,7 +449,7 @@ query: sql,

expect(result).toBe('var')
expect(result).toBe('$[[0]]$[[1]]$[[2]]')
})
it('prints else content when the element is not an array', async () => {
const sql = '{#each 5 as element} {element} {:else} var {/each}'
it('replaces a variable with the value of the element', async () => {
const sql = "{#each ['a', 'b', 'c'] as element} {element} {/each}"
const result = await compile({

@@ -433,3 +461,3 @@ query: sql,

expect(result).toBe('var')
expect(result).toBe('$[[a]]$[[b]]$[[c]]')
})

@@ -436,0 +464,0 @@

@@ -10,3 +10,3 @@ import { BaseNode, type TemplateNode } from '../parser/interfaces'

import { getLogicNodeMetadata, resolveLogicNode } from './logic'
import { emptyMetadata, mergeMetadata } from './utils'
import { emptyMetadata, hasContent, isIterable, mergeMetadata } from './utils'
import { createHash } from 'node:crypto'

@@ -194,3 +194,3 @@

if (baseNode.type === 'EachBlock') {
const iterableElement = await resolveLogicNode({
const iterableElement = (await resolveLogicNode({
node: baseNode.expression,

@@ -202,4 +202,8 @@ scope: localScope,

resolveFn: this.context.resolveFn,
})
if (!Array.isArray(iterableElement) || !iterableElement.length) {
})) as Iterable<unknown>
if (
!isIterable(iterableElement) ||
!(await hasContent(iterableElement))
) {
return await this.resolveBaseNode(baseNode.else, localScope, depth + 1)

@@ -218,4 +222,4 @@ }

const parsedChildren: string[] = []
for (let i = 0; i < iterableElement.length; i++) {
const element = iterableElement[i]
let i = 0
for await (const element of iterableElement) {
if (indexVar) localScope.set(indexVar, i)

@@ -230,2 +234,3 @@ localScope.set(contextVar, element)

)
i++
}

@@ -232,0 +237,0 @@ return parsedChildren.join('') || ''

import { getLogicNodeMetadata, resolveLogicNode } from '..'
import { emptyMetadata, mergeMetadata } from '../../utils'
import errors from '../../../error/errors'
import { emptyMetadata, isIterable, mergeMetadata } from '../../utils'
import type { ReadNodeMetadataProps, ResolveNodeProps } from '../types'

@@ -15,12 +16,30 @@ import type { ArrayExpression } from 'estree'

}: ResolveNodeProps<ArrayExpression>) {
return await Promise.all(
node.elements.map((element) =>
element
? resolveLogicNode({
node: element,
...props,
})
: null,
),
)
const { raiseError } = props
const resolvedArray = []
for (const element of node.elements) {
if (!element) continue
if (element.type !== 'SpreadElement') {
const value = await resolveLogicNode({
node: element,
...props,
})
resolvedArray.push(value)
continue
}
const spreadObject = await resolveLogicNode({
node: element.argument,
...props,
})
if (!isIterable(spreadObject)) {
raiseError(errors.invalidSpreadInArray(typeof spreadObject), element)
}
for await (const value of spreadObject as Iterable<unknown>) {
resolvedArray.push(value)
}
}
return resolvedArray
}

@@ -34,5 +53,7 @@

node.elements.map(async (element) => {
if (element)
return await getLogicNodeMetadata({ node: element, ...props })
return emptyMetadata()
if (!element) return emptyMetadata()
if (element.type === 'SpreadElement') {
return await getLogicNodeMetadata({ node: element.argument, ...props })
}
return await getLogicNodeMetadata({ node: element, ...props })
}),

@@ -39,0 +60,0 @@ )

@@ -5,8 +5,3 @@ import { getLogicNodeMetadata, resolveLogicNode } from '..'

import { ReadNodeMetadataProps, type ResolveNodeProps } from '../types'
import {
Property,
SpreadElement,
type Identifier,
type ObjectExpression,
} from 'estree'
import { type Identifier, type ObjectExpression } from 'estree'

@@ -25,13 +20,29 @@ /**

for (const prop of node.properties) {
if (prop.type !== 'Property') {
throw raiseError(errors.invalidObjectKey, node)
if (prop.type === 'SpreadElement') {
const spreadObject = await resolveLogicNode({
node: prop.argument,
scope,
raiseError,
...props,
})
if (typeof spreadObject !== 'object') {
raiseError(errors.invalidSpreadInObject(typeof spreadObject), prop)
}
Object.entries(spreadObject as object).forEach(([key, value]) => {
resolvedObject[key] = value
})
continue
}
const key = prop.key as Identifier
const value = await resolveLogicNode({
node: prop.value,
scope,
raiseError,
...props,
})
resolvedObject[key.name] = value
if (prop.type === 'Property') {
const key = prop.key as Identifier
const value = await resolveLogicNode({
node: prop.value,
scope,
raiseError,
...props,
})
resolvedObject[key.name] = value
continue
}
throw raiseError(errors.invalidObjectKey, prop)
}

@@ -41,6 +52,2 @@ return resolvedObject

function isProperty(prop: Property | SpreadElement): prop is Property {
return prop.type === 'Property'
}
export async function readMetadata({

@@ -51,14 +58,24 @@ node,

const propertiesMetadata = await Promise.all(
node.properties.filter(isProperty).map((prop) =>
Promise.all([
getLogicNodeMetadata({
node: prop.key,
...props,
}),
getLogicNodeMetadata({
node: prop.value,
...props,
}),
]),
),
node.properties
.map((prop) => {
if (prop.type === 'SpreadElement') {
return getLogicNodeMetadata({
node: prop.argument,
...props,
})
}
if (prop.type === 'Property') {
return Promise.all([
getLogicNodeMetadata({
node: prop.key,
...props,
}),
getLogicNodeMetadata({
node: prop.value,
...props,
}),
])
}
})
.filter((p) => p !== undefined),
)

@@ -65,0 +82,0 @@

@@ -43,1 +43,12 @@ import { QueryMetadata } from './types'

}
export function isIterable(obj: unknown): obj is Iterable<unknown> {
return (obj as Iterable<unknown>)?.[Symbol.iterator] !== undefined
}
export async function hasContent(iterable: Iterable<unknown>) {
for await (const _ of iterable) {
return true
}
return false
}

@@ -115,2 +115,10 @@ function getKlassName(error: unknown): string {

},
invalidSpreadInObject: (property: string) => ({
code: 'invalid-spread-in-object',
message: `Property '${property}' is not valid for spreading`,
}),
invalidSpreadInArray: (element: string) => ({
code: 'invalid-spread-in-array',
message: `Element '${element}' is not iterable`,
}),
unsupportedOperator: (operator: string) => ({

@@ -143,3 +151,3 @@ code: 'unsupported-operator',

code: 'not-a-function',
message: `Object '${objectType}' is callable`,
message: `Object '${objectType}' is not callable`,
}),

@@ -146,0 +154,0 @@ functionCallError: (err: unknown) => {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc