From 0dc01928d2184ef4bfa3d3a845ece948717539dd Mon Sep 17 00:00:00 2001 From: Jacob Clarke Date: Sun, 15 May 2022 00:22:37 +1000 Subject: [PATCH] LES GO --- .gitignore | 4 + .prettierrc.json | 5 + .vscode/settings.json | 1 + README.md | 44 ++++++++- example/Project.tosc | Bin 0 -> 1048 bytes example/scripts/_globals.lua | 2 + example/scripts/_root.lua | 1 + example/scripts/example.lua | 1 + example/scripts/tag_example.lua | 1 + package.json | 11 ++- scripts/_globals.lua | 2 - scripts/example.lua | 1 - scripts/tag_example.lua | 1 - src/banner.ts | 22 +++++ src/fileHandlers.ts | 69 ++++++++++++++ src/index.ts | 58 +++++++++++ src/main.ts | 164 ++++++++++++++++++++++++++++++++ src/types.ts | 65 +++++++++++++ watch.ts | 5 - 19 files changed, 443 insertions(+), 14 deletions(-) create mode 100644 .gitignore create mode 100644 .prettierrc.json create mode 100644 example/Project.tosc create mode 100644 example/scripts/_globals.lua create mode 100644 example/scripts/_root.lua create mode 100644 example/scripts/example.lua create mode 100644 example/scripts/tag_example.lua delete mode 100644 scripts/_globals.lua delete mode 100644 scripts/example.lua delete mode 100644 scripts/tag_example.lua create mode 100644 src/banner.ts create mode 100644 src/fileHandlers.ts create mode 100644 src/index.ts create mode 100644 src/main.ts create mode 100644 src/types.ts delete mode 100644 watch.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b9284c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +**_DEBUG.* +**_INJECTED.* +TouchOSC_ScriptInjector +TouchOSC_ScriptInjector* \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..31ba22d --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 693bd62..0d41aa9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,4 +2,5 @@ "deno.enable": true, "xml.server.vmargs": "-Xmx512M", "xml.symbols.maxItemsComputed": 50000, + "xml.format.joinContentLine": true } \ No newline at end of file diff --git a/README.md b/README.md index a1c76f6..5d797f4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,42 @@ -### VSCode plugins: -[redhat.vscode-xml](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml) -[denoland.vscode-deno](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno) +# TouchOSC Script Injector + +A neat little CLI tool that allows you to write TouchOSC LUA scripts externally by injecting them into the TouchOSC project file directly, with some bonus smarts. + +## Project setup + +In the same folder as your `.tosc` project file make a folder called `scripts`, in there you can make as many `.lua` files as you want. + +`_globals.lua` is a special file that is always injected into the top of all other scripts. + +`_root.lua` will get applied to your document's root script. + +All other `.lua` files will map directly to any controls of the same name. By pre-pending a file name with `tag_` the mapping will be done by tag instead of name. + +This name/tag script copying process will find controls nested in however many groups. + +## Using this thing + +Head to [releases](https://github.com/jacobclarke92/TouchOSC-Script-Injector/releases) and download the version best suited for your OS. +Windows, Mac and Linux options available but I've only tested Mac so far. + +Simply run the program and follow the prompts! +You will be prompted for your project file, drag your `.tosc` file into the window and press enter and you'll be away. + +The program will run in its entirety after initially receiving a project file. It will creating a new `_INJECTED.tosc` file next to your original. + +After this point the program will monitor your `scripts` folder and watch for any changes to `.lua` files, patching your project file as necessary. + +--- + +## Dev stuff + +Built with DENO just because I like typescript and the compile functionality seemed pretty convenient for distribution. + +`.tosc` files by default are compressed with zlib, if decompressed end up just being an xml file. + +Check the scripts inside `package.json` for quick access but otherwise this should be all you need to get started: +``` +deno run --allow-read --allow-write src/index.ts +``` +There is a `--debug` argument that adds extra logs and outputs a debug `.json` file, and you can also pass a `.tosc` file path (relative or absolute) as an argument to skip the prompting step. diff --git a/example/Project.tosc b/example/Project.tosc new file mode 100644 index 0000000000000000000000000000000000000000..c08a6c5677da9fc2e1a032be4374f1ea3d5934cd GIT binary patch literal 1048 zcmV+z1n2vBob8-lZ<{(8$G-~aDmxchY#;sY%XJfkc*8Nrueq>%)(fF{w&=*#CfV|(<< zb7S(`&y11hCM+P@`}1N4+jd|BJPr(FI);$ovF`--ST{{SBvUZOelXI+G9@RYw;w-z zxh(!Tz7;Kb5VH^*xznrB@i=bM5d zN7qD(d{IWa@Z4p&ExK<#%Pd;Ao;&w!t7y%4iC&uZh$H_E!*b}LiyBP?;Rp+QBb`7e zy_n%EB3%!=NM)N+$+k&d&jQQSYV<*2x+%;!C5b?yxMO-bXO@eYCQ_`sTuO3mE3Hpr z8FxyEB$%=)#h08Gr!AJfWG2I$rs9veNqKR|7bDJ?82#HZN7)>uon}r-iB#&a46g?5Vu7773crd84)sA&+I##S1<{_yQMSpXXryaWKinK8AT zh6?STf$%}s8$v+yju8za_CSbE_s>uXK^Evbt?&DzWke@SzmE}}pwj~*l!5na;FHp~ zTkdTG9TOYjmV4U zMi3|f92x*s(XIf{GXN|F00jUAfcpSo5UhtY&;kH508jupGysZ8R{-c805b&u1pozr z`v71eU>ac?0L~0qTLEx!`ue|%-Vm_Mbv0ZBNOwfY66xdCE>o~GOQ7l0EU_Du7*klY zM4u{_JcT7O@<|Mjn;xwjF@@`X3f0EqSE^<<0OS!X{roB|lmDmGz z32VEAkF?9J6LCS3NX&alcq#XZ{*-fW9uMe?5FgEXxHIS9hnkqvOuJ?LD$`;T3N4$n zJPtIUXb~qQ(KwCf?OwkK^6{0y-%!LLbLQyfV{2fT%_;=wpuc5J8aVprSRGF5+QS5P zU3@^Nd5|qqqt>N8rTVDWrTyQkkLOyLrWPISebpF(z|tKZwfx$)QK;TJ9>%R3ub!$m zj{W+yS{iU4~cz>&XDR|HT5cpd^w S*01dU+*AFJ5bqbojhvYby7TS; literal 0 HcmV?d00001 diff --git a/example/scripts/_globals.lua b/example/scripts/_globals.lua new file mode 100644 index 0000000..aac7629 --- /dev/null +++ b/example/scripts/_globals.lua @@ -0,0 +1,2 @@ +NAME_COLOR = '00ff00' +TAG_COLOR = '0000ff' diff --git a/example/scripts/_root.lua b/example/scripts/_root.lua new file mode 100644 index 0000000..389de9d --- /dev/null +++ b/example/scripts/_root.lua @@ -0,0 +1 @@ +print('Hello from root') diff --git a/example/scripts/example.lua b/example/scripts/example.lua new file mode 100644 index 0000000..5de6c32 --- /dev/null +++ b/example/scripts/example.lua @@ -0,0 +1 @@ +self.color = Color.fromHexString(NAME_COLOR) diff --git a/example/scripts/tag_example.lua b/example/scripts/tag_example.lua new file mode 100644 index 0000000..028dca5 --- /dev/null +++ b/example/scripts/tag_example.lua @@ -0,0 +1 @@ +self.color = Color.fromHexString(TAG_COLOR) diff --git a/package.json b/package.json index fc0bc2a..313d7dc 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,17 @@ "name": "touchosc-script-injector", "version": "1.0.0", "description": "Injects external LUA files into TouchOSC control script fields", - "main": "watch.js", + "main": "src/index.js", "author": "Jacob Clarke", "license": "MIT", "scripts": { - "start": "deno run watch.ts --allow-read --allow-write" + "start": "deno run --allow-read --allow-write src/index.ts", + "dev": "deno run --allow-read --allow-write src/index.ts --debug", + "compile": "deno compile --allow-read --allow-write --output TouchOSC_ScriptInjector src/index.ts", + "compile-linux": "deno compile --allow-read --allow-write --target x86_64-unknown-linux-gnu --output TouchOSC_ScriptInjector_linux src/index.ts", + "compile-windows": "deno compile --allow-read --allow-write --target x86_64-pc-windows-msvc --output TouchOSC_ScriptInjector_windows src/index.ts", + "compile-mac": "deno compile --allow-read --allow-write --target x86_64-apple-darwin --output TouchOSC_ScriptInjector_mac src/index.ts", + "compile-macM1": "deno compile --allow-read --allow-write --target aarch64-apple-darwin --output TouchOSC_ScriptInjector_macM1 src/index.ts", + "compile-all": "npm run compile-linux && npm run compile-windows && npm run compile-mac && npm run compile-macM1" } } diff --git a/scripts/_globals.lua b/scripts/_globals.lua deleted file mode 100644 index e2c4788..0000000 --- a/scripts/_globals.lua +++ /dev/null @@ -1,2 +0,0 @@ -NAME_COLOR = 0x00ff00 -TAG_COLOR = 0x0000ff diff --git a/scripts/example.lua b/scripts/example.lua deleted file mode 100644 index add51ae..0000000 --- a/scripts/example.lua +++ /dev/null @@ -1 +0,0 @@ -print('name example hi') diff --git a/scripts/tag_example.lua b/scripts/tag_example.lua deleted file mode 100644 index d4cba43..0000000 --- a/scripts/tag_example.lua +++ /dev/null @@ -1 +0,0 @@ -print('tag example hi') diff --git a/src/banner.ts b/src/banner.ts new file mode 100644 index 0000000..c924b6f --- /dev/null +++ b/src/banner.ts @@ -0,0 +1,22 @@ +const banner = ` + + _____ _ _____ _____ _____ + |_ _|___ _ _ ___| |_| | __| | + | | | . | | | _| | | |__ | --| + |_| |___|___|___|_|_|_____|_____|_____| + + _____ _ __ ____ _ __ + / ___/__________(_)___ / /_ / _/___ (_)__ _____/ /_____ _____ + \\__ \\/ ___/ ___/ / __ \\/ __/ / // __ \\ / / _ \\/ ___/ __/ __ \\/ ___/ + ___/ / /__/ / / / /_/ / /_ _/ // / / / / / __/ /__/ /_/ /_/ / / + /____/\\___/_/ /_/ .___/\\__/ /___/_/ /_/_/ /\\___/\\___/\\__/\\____/_/ + /_/ /___/ + + by Jacob Clarke + https://github.com/jacobclarke92/TouchOSC-Script-Injector + +` +export const printBanner = (clearFirst = false) => { + if (clearFirst) console.clear() + console.log(banner) +} diff --git a/src/fileHandlers.ts b/src/fileHandlers.ts new file mode 100644 index 0000000..d1f83a4 --- /dev/null +++ b/src/fileHandlers.ts @@ -0,0 +1,69 @@ +import { ToscDoc, ToscNode, ToscGroupNode, ToscProperty } from './types.ts' +import { stopwatchTick } from './main.ts' +import { parse as parseXml, stringify as encodeXml } from 'https://deno.land/x/xml@2.0.4/mod.ts' +import { XmlEntities } from 'https://deno.land/x/html_entities@v1.0/mod.js' +import { inflate } from 'https://deno.land/x/compress@v0.4.5/zlib/inflate.ts' + +export async function getToscFileContent(filePath: string) { + const content = await Deno.readTextFile(filePath) + if (content.match(/^<\?xml/)) { + console.log(`✅ Project file is XML-based`) + return content + } + + console.log(`Project file is not XML based, attempting to convert...`) + stopwatchTick() + const decodedFile = await decodeToscFile(filePath) + console.log(`✅ Decoded file in ${stopwatchTick()}ms`) + return decodedFile +} + +export async function decodeToscFile(filePath: string) { + try { + const rawContent = await Deno.readFile(filePath) + const inflatedContent = inflate(rawContent) + return new TextDecoder('utf-8').decode(inflatedContent) + } catch (err) { + throw '❌ Failed to decode file' + } +} + +export function parseToscXML(xmlString: string): ToscDoc { + stopwatchTick() + try { + const json = parseXml(xmlString) as unknown as ToscDoc + console.log(`✅ XML successfully parsed (took ${stopwatchTick()} ms)`) + return json + } catch (e) { + throw '❌ Could not parse XML file' + } +} + +export async function writeDebugFiles(fileDir: string, fileName: string, parsedProject: ToscDoc) { + stopwatchTick() + await Deno.writeTextFile(fileDir + fileName + '_DEBUG.json', JSON.stringify(parsedProject, null, 2)) + console.log(`✅ Wrote to JSON file for debugging (took ${stopwatchTick()} ms)`) + + stopwatchTick() + await Deno.writeTextFile( + fileDir + fileName + '_DEBUG.tosc', + encodeXml(parsedProject as any, { replacer: cDataRestorer }) + ) + console.log(`✅ Wrote to XML file for debugging (took ${stopwatchTick()} ms)`) +} + +type StringifierOptions = Exclude[1], undefined> +const cDataRestorer: StringifierOptions['replacer'] = ({ key, value, tag }) => + ['key', 'value'].includes(tag) && key === '#text' && typeof value === 'string' && !!value + ? `` + : value + +export async function writeProjectFile(parsedProject: ToscDoc, fileDir: string, fileName: string) { + console.log('📝 Re-encoding to XML and saving file...') + stopwatchTick() + const xmlString = encodeXml(parsedProject as any, { indentSize: 0, replacer: cDataRestorer }) + const newFileName = fileDir + fileName + '_INJECTED.tosc' + await Deno.writeTextFile(newFileName, xmlString) + console.log(`✅ Project file written (took ${stopwatchTick()} ms)`) + console.log(newFileName) +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..7014b59 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,58 @@ +import { printBanner } from './banner.ts' +import { processToscFile, startWatcher } from './main.ts' +import Ask from 'https://deno.land/x/ask@1.0.6/mod.ts' + +const debugMode = !!(Deno.args || []).find((arg) => arg === '--debug') +const filePathArg = (Deno.args || []).find((str) => str.match(/\.tosc$/)) +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const fileNameAndExtRegex = /[^\\/:"*?<>|]+\.\w+$/ +export const fileNameRegex = /([^\\/:"*?<>|]+)(?:\.\w+$)/ +const runtimePath = new URL('', import.meta.url).pathname +const runtimeDir = runtimePath.replace(fileNameAndExtRegex, '') + +const ask = new Ask() +const askForFilePath = async () => + ( + await ask.input({ name: 'filePath', message: `Drag a .tosc file into this window, then press enter\n` }) + )?.filePath?.trim() + +const ensureFilePath = async () => { + let answer = await askForFilePath() + while (!answer) answer = await askForFilePath() + const isAbsolutePath = answer.match(/^\//) + if (!isAbsolutePath) answer = runtimeDir + answer + return answer +} + +if (!debugMode) printBanner(true) + +let filePath: string = filePathArg ? filePathArg.replace(/^\.\//, '') : await ensureFilePath() +let scriptsDir: string = filePath.replace(fileNameAndExtRegex, '') + 'scripts/' + +async function letsGo() { + const parsedProject = await (async () => { + try { + return await processToscFile(filePath, scriptsDir, debugMode) + } catch (e) { + console.log(e) + return false + } + })() + + if (!parsedProject) { + console.log('❌ An issue occurred, will restart shortly...') + await sleep(2500) + if (!debugMode) printBanner(true) + filePath = await ensureFilePath() + scriptsDir = filePath.replace(fileNameAndExtRegex, '') + 'scripts/' + await letsGo() + return + } + + console.log('\n🎉🎉🎉🎉🎉\n') + + await startWatcher(parsedProject, filePath, scriptsDir) +} + +letsGo() diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..a11f09c --- /dev/null +++ b/src/main.ts @@ -0,0 +1,164 @@ +import { ToscDoc, ToscNode, ToscGroupNode, ToscProperty } from './types.ts' +import { fileNameRegex, fileNameAndExtRegex } from './index.ts' +import { printBanner } from './banner.ts' +import { getToscFileContent, parseToscXML, writeDebugFiles, writeProjectFile } from './fileHandlers.ts' +import { exists } from 'https://deno.land/std@0.139.0/fs/mod.ts' +import { debounce } from 'https://deno.land/x/debounce@v0.0.7/mod.ts' + +const stopwatch = [Date.now()] +export const stopwatchTick = () => { + stopwatch.push(Date.now()) + return stopwatch.length < 2 ? 0 : stopwatch[stopwatch.length - 1] - stopwatch[stopwatch.length - 2] +} + +let scriptInjectionLog: { [key: string]: number } = {} +const printResults = () => { + console.log('📋 Results:') + Object.keys(scriptInjectionLog).forEach((key) => console.log(`• ${key} - ${scriptInjectionLog[key]} injected`)) +} + +export async function processToscFile(filePath: string, scriptsDir: string, debugMode: boolean = false) { + const fileName = filePath.match(fileNameRegex)?.[1] + if (!fileName) throw '❌ Could not determine file name HALP' + const fileDir = filePath.replace(fileNameAndExtRegex, '') + + console.log(`Reading "${filePath}"...`) + const projectContent = await getToscFileContent(filePath) + + const parsedProject = parseToscXML(projectContent) + if (debugMode) writeDebugFiles(fileDir, fileName, parsedProject) + + await applyAllScriptFiles(parsedProject, scriptsDir) + await writeProjectFile(parsedProject, fileDir, fileName) + + return parsedProject +} + +const propertyMatch = (node: ToscNode | ToscGroupNode, key: string, value: string): boolean => + !!node.properties.property.find((property) => property.key === key && property.value === value) + +const injectScriptRecursive = ( + node: NodeType, + script: string, + propertyKey: string, + propertyValue: string +): NodeType => { + let newNode: typeof node = { ...node } + + if ('children' in newNode && newNode.children.node.length > 0) { + newNode.children = { + node: newNode.children.node.map((child) => injectScriptRecursive(child, script, propertyKey, propertyValue)), + } + } + + if (propertyMatch(newNode, propertyKey, propertyValue)) { + const existingScriptProperty = newNode.properties.property.find(({ key }) => key === 'script') + const newProperty: ToscProperty = { + '@type': 's', + key: 'script', + value: script, + } + newNode.properties = { + property: existingScriptProperty + ? newNode.properties.property.map((property) => (property.key === 'script' ? newProperty : property)) + : [...newNode.properties.property, newProperty], + } + const logKey = `${propertyKey}: ${propertyValue}` + scriptInjectionLog[logKey] = (scriptInjectionLog[logKey] || 0) + 1 + } + + return newNode +} + +const debounced_applyScriptFile: { [key: string]: Function } = {} +async function applyScriptFile(parsedProject: ToscDoc, scriptFilePath: string, prependScript?: string) { + let luaScript = await Deno.readTextFile(scriptFilePath) + if (prependScript) luaScript = prependScript + '\n\n' + luaScript + + // extract file name from path + let fileName = scriptFilePath.match(fileNameRegex)?.[1] + if (!fileName) return + + if (fileName === '_root') { + const newProperty: ToscProperty = { + '@type': 's', + key: 'script', + value: luaScript, + } + const rootProperties = parsedProject.lexml.node.properties.property + const rootScriptPropertyExists = rootProperties.find(({ key }) => key === 'script') + parsedProject.lexml.node.properties.property = rootScriptPropertyExists + ? rootProperties.map((property) => (property.key === 'script' ? newProperty : property)) + : [...rootProperties, newProperty] + } else { + const key = fileName.match(/^tag_/) ? 'tag' : 'name' + if (key === 'tag') fileName = fileName.replace(/^tag_/, '') + + const modifiedRootNode = injectScriptRecursive(parsedProject.lexml.node, luaScript, key, fileName) + parsedProject.lexml.node = modifiedRootNode + } +} + +async function applyAllScriptFiles(parsedProject: ToscDoc, scriptsDir: string) { + scriptInjectionLog = {} + console.log('🔎 Scanning for files to inject...') + const globalsScriptExists = await exists(scriptsDir + '_globals.lua') + const globals = globalsScriptExists ? await Deno.readTextFile(scriptsDir + '_globals.lua') : undefined + if (globals) console.log(`✓ Globals script found`) + for await (const dirEntry of Deno.readDir(scriptsDir)) { + if (!dirEntry.isFile) continue + if (!dirEntry.name.match(/\.lua$/)) continue + if (dirEntry.name.match(/^_globals/)) continue + stopwatchTick() + await applyScriptFile(parsedProject, scriptsDir + dirEntry.name, globals) + console.log(`✓ Script file "${dirEntry.name}" applied (took ${stopwatchTick()} ms)`) + } + console.log('✅ Patching done!') + printResults() +} + +export async function startWatcher( + parsedProject: ToscDoc, + filePath: string, + scriptsDir: string, + debugMode: boolean = false +) { + const fileName = filePath.match(fileNameRegex)?.[1] + if (!fileName) throw '❌ Could not determine file name HALP' + const fileDir = filePath.replace(fileNameAndExtRegex, '') + + console.log('👀 File watcher started') + + const globalsScriptExists = await exists(scriptsDir + '_globals.lua') + const globals = globalsScriptExists ? await Deno.readTextFile(scriptsDir + '_globals.lua') : undefined + + const watcher = Deno.watchFs(scriptsDir) + for await (const event of watcher) { + if (event.kind !== 'modify') continue + for (const checkPath of event.paths) { + if (!checkPath.match(/\.lua$/)) { + console.debug('Ignoring non-lua file:', checkPath) + continue + } + if (!debugMode) printBanner(true) + console.log(`👀 Watcher detected change in:\n${checkPath}\n`) + if (checkPath.match(/_globals\.lua$/)) { + console.log('♻️ Globals script changed, re-injecting all scripts...') + await applyAllScriptFiles(parsedProject, scriptsDir) + await writeProjectFile(parsedProject, fileDir, fileName) + } else { + // We do debouncing to avoid the script running twice -- once for save and once for vscode's formatting save + if (!debounced_applyScriptFile[checkPath]) + debounced_applyScriptFile[checkPath] = debounce(async () => { + scriptInjectionLog = {} + await applyScriptFile(parsedProject, checkPath, globals) + + printResults() + await writeProjectFile(parsedProject, fileDir, fileName) + }, 200) + + await debounced_applyScriptFile[checkPath]() + } + } + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..d7b19c9 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,65 @@ +export type Property = { + ['@type']: TypeStr + key: string + value: ValueType +} + +export type BooleanProperty = Property<'b', 1 | 0> +export type StringProperty = Property<'s', string> +export type IntegerProperty = Property<'i', number> +export type FloatProperty = Property<'f', number> +export type ColorProperty = Property<'c', { r: number; g: number; b: number; a: number }> +export type FrameProperty = Property<'f', { x: number; y: number; w: number; h: number }> + +export type ToscProperty = + | BooleanProperty + | StringProperty + | IntegerProperty + | FloatProperty + | ColorProperty + | FrameProperty + +export type ToscValue = { + key: string + locked: 0 | 1 + lockedDefaultCurrent: 0 | 1 + default: ValueType + defaultPull?: number +} + +export interface ToscNode { + ['@ID']: string + ['@type']: + | 'BOX' + | 'BUTTON' + | 'LABEL' + | 'TEXT' + | 'FADER' + | 'XY' + | 'RADIAL' + | 'ENCODER' + | 'RADAR' + | 'RADIO' + | 'GROUP' + | 'PAGER' + | 'GRID' + properties: { property: ToscProperty[] } + values: { + value: ToscValue | ToscValue[] + } +} + +export interface ToscGroupNode extends ToscNode { + children: { node: (ToscNode | ToscGroupNode)[] } +} + +export interface ToscDoc { + xml: { + ['@version']: string + ['@encoding']: string + } + lexml: { + ['@version']: string + node: ToscGroupNode + } +} diff --git a/watch.ts b/watch.ts deleted file mode 100644 index 038493c..0000000 --- a/watch.ts +++ /dev/null @@ -1,5 +0,0 @@ -const watcher = Deno.watchFs("./scripts"); -for await (const event of watcher) { - console.log(">>>> event", event); - // Example event: { kind: "create", paths: [ "/home/alice/deno/foo.txt" ] } -}