fix: improve code structure to reduce unecessary dependency of apppreview on heavy packages

This commit is contained in:
Ruben Fiszel
2023-08-01 09:15:46 +02:00
parent 3a40b19cdb
commit 3410e66b22
16 changed files with 239 additions and 100 deletions
@@ -7,10 +7,6 @@
import { ClipboardCopy, Download, Expand } from 'lucide-svelte'
import Portal from 'svelte-portal'
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
import ScriptFix from './codeGen/ScriptFix.svelte'
import type { Preview } from '$lib/gen'
import type Editor from './Editor.svelte'
import type DiffEditor from './DiffEditor.svelte'
export let result: any
export let requireHtmlApproval = false
@@ -18,9 +14,6 @@
export let disableExpand = false
export let jobId: string | undefined = undefined
export let workspaceId: string | undefined = undefined
export let editor: Editor | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
export let lang: Preview.language | undefined = undefined
let resultKind:
| 'json'
@@ -230,9 +223,7 @@
.message}{:else}{JSON.stringify(result.error, null, 4)}{/if}</span
>
<pre class="text-sm whitespace-pre-wrap text-primary">{result.error.stack ?? ''}</pre>
{#if lang && editor && diffEditor}
<ScriptFix error={JSON.stringify(result.error)} {lang} {editor} {diffEditor} />
{/if}
<slot />
</div>
{:else if !forceJson && resultKind == 'approval'}<div class="flex flex-col gap-3 mt-8 mx-4">
<Button
@@ -8,10 +8,10 @@
import { Highlight } from 'svelte-highlight'
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
import typescript from 'svelte-highlight/languages/typescript'
import { cleanExpr } from './flows/utils'
import FlowPathViewer from './flows/content/FlowPathViewer.svelte'
import SchemaViewer from './SchemaViewer.svelte'
import { scriptPathToHref } from '$lib/scripts'
import { cleanExpr } from '$lib/utils'
export let flow: {
summary: string
description?: string
@@ -7,7 +7,7 @@
import FieldHeader from './FieldHeader.svelte'
import DynamicInputHelpBox from './flows/content/DynamicInputHelpBox.svelte'
import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte'
import { codeToStaticTemplate, getDefaultExpr, isCodeInjection } from './flows/utils'
import { codeToStaticTemplate, getDefaultExpr } from './flows/utils'
import SimpleEditor from './SimpleEditor.svelte'
import { Button } from './common'
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
@@ -17,7 +17,7 @@
import type ItemPicker from './ItemPicker.svelte'
import { ResourceService, type InputTransform } from '$lib/gen'
import TemplateEditor from './TemplateEditor.svelte'
import { setInputCat as computeInputCat } from '$lib/utils'
import { setInputCat as computeInputCat, isCodeInjection } from '$lib/utils'
import { Code, Plug } from 'lucide-svelte'
import { workspaceStore } from '$lib/stores'
@@ -1,7 +1,7 @@
<script lang="ts">
import type { InputTransform } from '$lib/gen'
import { cleanExpr } from '$lib/utils'
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
import { cleanExpr } from './flows/utils'
export let inputTransforms: Record<string, InputTransform>
$: entries = Object.entries(inputTransforms)
@@ -6,8 +6,6 @@
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import FieldHeader from './FieldHeader.svelte'
import ObjectResourceInput from './ObjectResourceInput.svelte'
import ResourcePicker from './ResourcePicker.svelte'
import type { SchemaProperty } from '$lib/common'
import autosize from 'svelte-autosize'
import Toggle from './Toggle.svelte'
@@ -17,6 +15,8 @@
import { twMerge } from 'tailwind-merge'
import { fade } from 'svelte/transition'
import { X } from 'lucide-svelte'
import LightweightResourcePicker from './LightweightResourcePicker.svelte'
import LightweightObjectResourceInput from './LightweightObjectResourceInput.svelte'
export let css: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined
export let label: string = ''
@@ -274,7 +274,7 @@
</span>
</div>
{:else if inputCat == 'resource-object'}
<ObjectResourceInput {format} bind:value />
<LightweightObjectResourceInput {format} bind:value />
{:else if inputCat == 'object'}
{#if properties && Object.keys(properties).length > 0}
<div class="p-4 pl-8 border rounded w-full">
@@ -320,12 +320,14 @@
multiple={false}
/>
{:else if inputCat == 'resource-string'}
<ResourcePicker
bind:value
resourceType={format.split('-').length > 1
? format.substring('resource-'.length)
: undefined}
/>
<div class="flex flex-row gap-x-1 w-full">
<LightweightResourcePicker
bind:value
resourceType={format.split('-').length > 1
? format.substring('resource-'.length)
: undefined}
/>
</div>
{:else if inputCat == 'string'}
<div class="flex flex-col w-full">
<div class="flex flex-row w-full items-center justify-between">
@@ -0,0 +1,45 @@
<script lang="ts">
import LightweightResourcePicker from './LightweightResourcePicker.svelte'
export let format: string
export let value: any
export let disablePortal = false
function isString(value: any) {
return typeof value === 'string' || value instanceof String
}
let path: string = ''
function resourceToValue() {
if (path) {
value = `$res:${path}`
} else {
value = undefined
}
}
function isResource() {
return isString(value) && value.length >= '$res:'.length
}
function valueToPath() {
if (isResource()) {
path = value.substr('$res:'.length)
}
}
$: value && valueToPath()
</script>
<div class="flex flex-row w-full flex-wrap gap-x-2 gap-y-0.5">
<LightweightResourcePicker
{disablePortal}
on:change={(e) => {
path = e.detail
resourceToValue()
}}
bind:value={path}
resourceType={format.split('-').length > 1 ? format.substring('resource-'.length) : undefined}
/>
</div>
@@ -0,0 +1,93 @@
<script lang="ts">
import { ResourceService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { createEventDispatcher, onMount } from 'svelte'
import Select from './apps/svelte-select/lib/index'
import { SELECT_INPUT_DEFAULT_STYLE } from '../defaults'
import DarkModeObserver from './DarkModeObserver.svelte'
const dispatch = createEventDispatcher()
export let initialValue: string | undefined = undefined
export let value: string | undefined = initialValue
export let resourceType: string | undefined = undefined
export let disablePortal = false
let valueSelect =
initialValue || value
? {
value: value ?? initialValue,
label: value ?? initialValue
}
: undefined
let collection = [valueSelect]
async function loadResources(resourceType: string | undefined) {
const nc = (
await ResourceService.listResource({
workspace: $workspaceStore!,
resourceType
})
).map((x) => ({
value: x.path,
label: x.path
}))
// TODO check if this is needed
if (!nc.find((x) => x.value == value) && (initialValue || value)) {
nc.push({ value: value ?? initialValue!, label: value ?? initialValue! })
}
collection = nc
}
$: {
if ($workspaceStore) {
loadResources(resourceType)
}
}
$: dispatch('change', value)
let darkMode: boolean = false
function onThemeChange() {
if (document.documentElement.classList.contains('dark')) {
darkMode = true
} else {
darkMode = false
}
}
onMount(() => {
onThemeChange()
})
</script>
<DarkModeObserver on:change={onThemeChange} />
<Select
portal={!disablePortal}
value={valueSelect}
on:change={(e) => {
value = e.detail.value
valueSelect = e.detail
}}
on:clear={() => {
value = undefined
valueSelect = undefined
}}
items={collection}
class="text-clip grow min-w-0"
placeholder="{resourceType ?? 'any'} resource"
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
containerStyles={darkMode
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
/>
<style>
:global(.svelte-select-list) {
font-size: small !important;
}
</style>
@@ -19,6 +19,7 @@
import type { PickableProperties } from './flows/previousResults'
import type DiffEditor from './DiffEditor.svelte'
import type Editor from './Editor.svelte'
import ScriptFix from './codeGen/ScriptFix.svelte'
export let mod: FlowModule
export let schema: Schema
@@ -129,14 +130,16 @@
<Pane size={50} minSize={10} class="text-sm text-tertiary">
{#if testJob != undefined && 'result' in testJob && testJob.result != undefined}
<pre class="overflow-x-auto break-words relative h-full px-2">
<DisplayResult
workspaceId={testJob?.workspace_id}
jobId={testJob?.id}
result={testJob.result}
{editor}
{diffEditor}
{lang}
/>
<DisplayResult workspaceId={testJob?.workspace_id} jobId={testJob?.id} result={testJob.result}>
{#if lang && editor && diffEditor && testJob?.result?.error}
<ScriptFix
error={JSON.stringify(testJob.result.error)}
{lang}
{editor}
{diffEditor}
/>
{/if}
</DisplayResult>
</pre>
{:else}
<div class="p-2">
@@ -1,10 +1,9 @@
<script lang="ts">
import { isCodeInjection } from '$lib/components/flows/utils'
import Tooltip from '$lib/components/Tooltip.svelte'
import { Clipboard } from 'lucide-svelte'
import { getContext } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { copyToClipboard } from '../../../../utils'
import { copyToClipboard, isCodeInjection } from '../../../../utils'
import Button from '../../../common/button/Button.svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import { components } from '../../editor/component'
@@ -1,5 +1,4 @@
<script lang="ts">
import { isCodeInjection } from '$lib/components/flows/utils'
import { createEventDispatcher, getContext, onDestroy, tick } from 'svelte'
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
import type { AppViewerContext, ListContext, RichConfiguration } from '../../types'
@@ -7,6 +6,7 @@
import { computeGlobalContext, eval_like } from './eval'
import deepEqualWithOrderedArray from './deepEqualWithOrderedArray'
import { deepEqual } from 'fast-equals'
import { isCodeInjection } from '$lib/utils'
type T = string | number | boolean | Record<string | number, any> | undefined
@@ -2,7 +2,7 @@
import { getContext } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import type { AppViewerContext, RichConfigurations } from '../../types'
import '../../../../../../node_modules/quill/dist/quill.snow.css'
import 'quill/dist/quill.snow.css'
import InputValue from '../helpers/InputValue.svelte'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
+4 -21
View File
@@ -7,10 +7,10 @@ import {
type InputTransform,
type Job
} from '$lib/gen'
import { inferArgs } from '$lib/infer'
import { loadSchema, loadSchemaFlow } from '$lib/scripts'
import { inferArgs, loadSchemaFromPath } from '$lib/infer'
import { loadSchemaFlow } from '$lib/scripts'
import { workspaceStore } from '$lib/stores'
import { emptySchema } from '$lib/utils'
import { cleanExpr, emptySchema } from '$lib/utils'
import { get } from 'svelte/store'
import type { FlowModuleState } from './flowState'
import type { PickableProperties } from './previousResults'
@@ -91,13 +91,6 @@ export function cleanInputs(flow: Flow | any): Flow {
return newFlow
}
export function cleanExpr(expr: string): string {
return expr
.split('\n')
.filter((x) => x != '' && !x.startsWith(`import `))
.join('\n')
}
export async function loadSchemaFromModule(module: FlowModule): Promise<{
input_transforms: Record<string, InputTransform>
schema: Schema
@@ -110,7 +103,7 @@ export async function loadSchemaFromModule(module: FlowModule): Promise<{
schema = emptySchema()
await inferArgs(mod.language!, mod.content ?? '', schema)
} else if (mod.type == 'script' && mod.path && mod.path != '') {
schema = await loadSchema(mod.path!, mod.hash)
schema = await loadSchemaFromPath(mod.path!, mod.hash)
} else if (mod.type == 'flow' && mod.path && mod.path != '') {
schema = await loadSchemaFlow(mod.path!)
} else {
@@ -151,16 +144,6 @@ export async function loadSchemaFromModule(module: FlowModule): Promise<{
}
}
const dynamicTemplateRegex = new RegExp(/\$\{(.*)\}/)
export function isCodeInjection(expr: string | undefined): boolean {
if (!expr) {
return false
}
return dynamicTemplateRegex.test(expr)
}
export function getDefaultExpr(
key: string = 'myfield',
previousModuleId: string | undefined,
@@ -21,6 +21,7 @@
import { Loader2 } from 'lucide-svelte'
import type Editor from '../Editor.svelte'
import type DiffEditor from '../DiffEditor.svelte'
import ScriptFix from '../codeGen/ScriptFix.svelte'
export let lang: Preview.language | undefined
export let previewIsLoading = false
@@ -96,10 +97,16 @@
workspaceId={previewJob?.workspace_id}
jobId={previewJob?.id}
result={previewJob.result}
{editor}
{diffEditor}
{lang}
/>
>
{#if lang && editor && diffEditor && previewJob?.result?.error}
<ScriptFix
error={JSON.stringify(previewJob.result.error)}
{lang}
{editor}
{diffEditor}
/>
{/if}
</DisplayResult>
</div>
{:else}
<div class="text-sm text-tertiary p-2">
+37 -1
View File
@@ -1,4 +1,4 @@
import { ScriptService, type MainArgSignature, FlowService } from '$lib/gen'
import { ScriptService, type MainArgSignature, FlowService, Script } from '$lib/gen'
import { get, writable } from 'svelte/store'
import type { Schema, SchemaProperty, SupportedLanguage } from './common.js'
import { emptySchema, sortObject } from './utils.js'
@@ -13,6 +13,7 @@ import init, {
parse_bigquery
} from 'windmill-parser-wasm'
import wasmUrl from 'windmill-parser-wasm/windmill_parser_wasm_bg.wasm?url'
import { workspaceStore } from './stores.js'
init(wasmUrl)
@@ -184,6 +185,41 @@ function argSigToJsonSchemaType(
}
}
export async function loadSchemaFromPath(path: string, hash?: string): Promise<Schema> {
if (path.startsWith('hub/')) {
const { content, language, schema } = await ScriptService.getHubScriptByPath({ path })
if (language == 'deno') {
const newSchema = emptySchema()
await inferArgs('deno' as SupportedLanguage, content ?? '', newSchema)
return newSchema
} else {
return schema ?? emptySchema()
}
} else if (hash) {
const script = await ScriptService.getScriptByHash({
workspace: get(workspaceStore)!,
hash
})
return inferSchemaIfNecessary(script)
} else {
const script = await ScriptService.getScriptByPath({
workspace: get(workspaceStore)!,
path: path ?? ''
})
return inferSchemaIfNecessary(script)
}
}
async function inferSchemaIfNecessary(script: Script) {
if (script.schema) {
return script.schema as any
} else {
const newSchema = emptySchema()
await inferArgs(script.language, script.content ?? '', newSchema)
return newSchema
}
}
export async function loadSchema(
workspace: string,
path: string,
+2 -39
View File
@@ -1,9 +1,7 @@
import { get } from 'svelte/store'
import type { Schema, SupportedLanguage } from './common'
import { FlowService, Script, ScriptService } from './gen'
import { inferArgs } from './infer'
import { workspaceStore, hubScripts } from './stores'
import { emptySchema } from './utils'
export function scriptLangToEditorLang(lang: Script.language) {
if (lang == 'deno') {
@@ -31,41 +29,6 @@ export function scriptLangToEditorLang(lang: Script.language) {
}
}
export async function loadSchema(path: string, hash?: string): Promise<Schema> {
if (path.startsWith('hub/')) {
const { content, language, schema } = await ScriptService.getHubScriptByPath({ path })
if (language == 'deno') {
const newSchema = emptySchema()
await inferArgs('deno' as SupportedLanguage, content ?? '', newSchema)
return newSchema
} else {
return schema ?? emptySchema()
}
} else if (hash) {
const script = await ScriptService.getScriptByHash({
workspace: get(workspaceStore)!,
hash
})
return inferSchemaIfNecessary(script)
} else {
const script = await ScriptService.getScriptByPath({
workspace: get(workspaceStore)!,
path: path ?? ''
})
return inferSchemaIfNecessary(script)
}
}
async function inferSchemaIfNecessary(script: Script) {
if (script.schema) {
return script.schema as any
} else {
const newSchema = emptySchema()
await inferArgs(script.language, script.content ?? '', newSchema)
return newSchema
}
}
export async function loadSchemaFlow(path: string): Promise<Schema> {
const flow = await FlowService.getFlowByPath({
workspace: get(workspaceStore)!,
@@ -101,7 +64,7 @@ export async function getScriptByPath(path: string): Promise<{
description: '',
tag: undefined,
concurrent_limit: undefined,
concurrency_time_window_s: undefined,
concurrency_time_window_s: undefined
}
} else {
const script = await ScriptService.getScriptByPath({
@@ -115,7 +78,7 @@ export async function getScriptByPath(path: string): Promise<{
description: script.description,
tag: script.tag,
concurrent_limit: script.concurrent_limit,
concurrency_time_window_s: script.concurrency_time_window_s,
concurrency_time_window_s: script.concurrency_time_window_s
}
}
}
+17
View File
@@ -587,3 +587,20 @@ export function toCamel(s: string) {
return $1.toUpperCase().replace('-', '').replace('_', '')
})
}
export function cleanExpr(expr: string): string {
return expr
.split('\n')
.filter((x) => x != '' && !x.startsWith(`import `))
.join('\n')
}
const dynamicTemplateRegex = new RegExp(/\$\{(.*)\}/)
export function isCodeInjection(expr: string | undefined): boolean {
if (!expr) {
return false
}
return dynamicTemplateRegex.test(expr)
}