commit cef6bd975ada366e792a65a69e7a6aab6477ad38 Author: yname Date: Fri Aug 16 15:24:15 2024 -0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..754b38d --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# SourcemapGenerator + +This program watches multiple Rojo project directories for changes and builds a combined sourcemap. \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..7f5780b Binary files /dev/null and b/bun.lockb differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..f471dfe --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/bun": "^1.1.6" + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..344aa34 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,106 @@ +import fs from 'fs' +import { spawn } from 'child_process' + +// These are where I (yname) have my Rojo projects. +const SOURCE_ROOTS = ['Core', 'Secret'] + +type RojoSourcemap = { + name: string + className: string + filePaths?: string[] + children?: RojoSourcemap[] +} + +const sourcemapCache = new Map() +function getSourcemap(root: string): Promise { + const cached = sourcemapCache.get(root) + if (cached) { + return Promise.resolve(cached) + } + return new Promise((resolve, reject) => { + let stdout = '' + let stderr = '' + const rojo = spawn('rojo', ['sourcemap', root]) + rojo.stdout.on('data', (data) => { + stdout += data.toString() + }) + rojo.stderr.on('data', (data) => { + stderr += data.toString() + }) + rojo.on('close', (code) => { + if (code === 0) { + try { + const sourcemap = JSON.parse(stdout) + sourcemapCache.set(root, sourcemap) + resolve(sourcemap) + } catch { + reject(new Error(`Failed to parse sourcemap: ${stdout}`)) + } + } else { + reject(stderr) + } + }) + }) +} + +function prefixSourcemap(sourcemap: RojoSourcemap, prefix: string): RojoSourcemap { + return { + name: sourcemap.name, + className: sourcemap.className, + filePaths: sourcemap.filePaths?.map(path => `${prefix}/${path}`), + children: sourcemap.children?.map(child => prefixSourcemap(child, prefix)), + } +} + +async function getPrefixedSourcemap(root: string): Promise { + const sourcemap = await getSourcemap(root) + const prefixed = prefixSourcemap(sourcemap, root) + return prefixed +} + +function mergeSourcemaps(sourcemaps: RojoSourcemap[]): RojoSourcemap { + const merged: Required = { + name: '', + className: '', + filePaths: [], + children: [] + }; + + for (const sourcemap of sourcemaps) { + merged.name = sourcemap.name; + merged.className = sourcemap.className; + + if (sourcemap.filePaths) { + merged.filePaths = sourcemap.filePaths; + } + + for (const child of sourcemap.children ?? []) { + const existingChild = merged.children.findIndex(oldChild => oldChild.name === child.name); + if (existingChild !== -1) { + merged.children[existingChild] = mergeSourcemaps([merged.children[existingChild], child]); + } else { + merged.children.push(child); + } + } + } + + return merged; +} + +async function rebuild() { + const sourcemaps = await Promise.all([ + getPrefixedSourcemap('Secret'), + getPrefixedSourcemap('Core'), + ]) + const sourcemap = mergeSourcemaps(sourcemaps) + await fs.promises.writeFile('sourcemap.json', JSON.stringify(sourcemap, null, 2)) +} + +for (const root of SOURCE_ROOTS) { + fs.watch(root, {recursive:true}, () => { + sourcemapCache.delete(root) + rebuild() + }) +} + +rebuild() \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1edfb46 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags + "noUnusedLocals": true, + "noUnusedParameters": true, + "noPropertyAccessFromIndexSignature": true + } + } \ No newline at end of file