Skip to content

Workaround for monkey patching of fs in runTsc #297

@NikhilVerma

Description

@NikhilVerma

I use this patch to allow Bun to run vue-tsc. This creates a copy of TSC instead of monkey patching the FS methods. I feel monkey patching the FS library isn't the right idea anyway.

diff --git a/lib/quickstart/runTsc.js b/lib/quickstart/runTsc.js
index 9de27714a40531e5b1ade7e0686c937a327a8d38..a36c9ab1f2ae373f36f274738df7753c3da4beea 100644
--- a/lib/quickstart/runTsc.js
+++ b/lib/quickstart/runTsc.js
@@ -20,33 +20,55 @@ function runTsc(tscPath, options, _getLanguagePlugins, typescriptObject) {
         extraExtensionsToRemove = options.extraExtensionsToRemove;
     }
     const proxyApiPath = require.resolve('../node/proxyCreateProgram');
-    const readFileSync = fs.readFileSync;
-    fs.readFileSync = (...args) => {
-        if (args[0] === tscPath) {
-            let tsc = readFileSync(...args);
-            try {
-                return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
+
+    // Alternative approach: Write transformed content to temp file in same directory
+    // This preserves TypeScript's library resolution while working in both Node.js and Bun
+    const crypto = require('crypto');
+
+    try {
+        // Read the original tsc file
+        let tscContent = fs.readFileSync(tscPath, 'utf8');
+        let actualTscPath = tscPath;
+
+        // Handle tsc shim (TypeScript 5.7+)
+        try {
+            const transformed = transformTscContent(tscContent, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
+            tscContent = transformed;
+        }
+        catch {
+            // Support the tsc shim used in Typescript v5.7 and up
+            const requireRegex = /module\.exports\s*=\s*require\((?:"|')(?<path>\.\/\w+\.js)(?:"|')\)/;
+            const requirePath = requireRegex.exec(tscContent)?.groups?.path;
+            if (requirePath) {
+                actualTscPath = path.join(path.dirname(tscPath), requirePath);
+                tscContent = fs.readFileSync(actualTscPath, 'utf8');
+                tscContent = transformTscContent(tscContent, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
             }
-            catch {
-                // Support the tsc shim used in Typescript v5.7 and up
-                const requireRegex = /module\.exports\s*=\s*require\((?:"|')(?<path>\.\/\w+\.js)(?:"|')\)/;
-                const requirePath = requireRegex.exec(tsc)?.groups?.path;
-                if (requirePath) {
-                    tsc = readFileSync(path.join(path.dirname(tscPath), requirePath), 'utf8');
-                    return transformTscContent(tsc, proxyApiPath, extraSupportedExtensions, extraExtensionsToRemove, __filename, typescriptObject);
-                }
-                else {
-                    throw new Error('Failed to locate tsc module path from shim');
-                }
+            else {
+                throw new Error('Failed to locate tsc module path from shim');
+            }
+        }
+
+        // Write transformed content to same directory to preserve relative paths for lib files
+        const hash = crypto.createHash('md5').update(actualTscPath + Date.now()).digest('hex').substring(0, 8);
+        const parsedPath = path.parse(actualTscPath);
+        const tempPath = path.join(parsedPath.dir, `${parsedPath.name}.volar-${hash}${parsedPath.ext}`);
+        fs.writeFileSync(tempPath, tscContent, 'utf8');
+
+        try {
+            return require(tempPath);
+        }
+        finally {
+            // Clean up
+            delete require.cache[tempPath];
+            try {
+                fs.unlinkSync(tempPath);
+            } catch {
+                // Ignore cleanup errors
             }
         }
-        return readFileSync(...args);
-    };
-    try {
-        return require(tscPath);
     }
     finally {
-        fs.readFileSync = readFileSync;
         delete require.cache[tscPath];
     }
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions