diff --git a/packages/babel-register/src/cache.js b/packages/babel-register/src/cache.js index 439c92c308..0a2983b89a 100644 --- a/packages/babel-register/src/cache.js +++ b/packages/babel-register/src/cache.js @@ -14,11 +14,17 @@ const DEFAULT_FILENAME = path.join( const FILENAME: string = process.env.BABEL_CACHE_PATH || DEFAULT_FILENAME; let data: Object = {}; +let cacheDisabled = false; + +function isCacheDisabled() { + return process.env.BABEL_DISABLE_CACHE ?? cacheDisabled; +} /** * Write stringified cache to disk. */ export function save() { + if (isCacheDisabled()) return; let serialised: string = "{}"; try { @@ -32,8 +38,30 @@ export function save() { } } - mkdirpSync(path.dirname(FILENAME)); - fs.writeFileSync(FILENAME, serialised); + try { + mkdirpSync(path.dirname(FILENAME)); + fs.writeFileSync(FILENAME, serialised); + } catch (e) { + switch (e.code) { + case "EACCES": + case "EPERM": + console.warn( + `Babel could not write cache to file: ${FILENAME} +due to a permission issue. Cache is disabled.`, + ); + cacheDisabled = true; + break; + case "EROFS": + console.warn( + `Babel could not write cache to file: ${FILENAME} +because it resides in a readonly filesystem. Cache is disabled.`, + ); + cacheDisabled = true; + break; + default: + throw e; + } + } } /** @@ -41,18 +69,37 @@ export function save() { */ export function load() { - if (process.env.BABEL_DISABLE_CACHE) return; + if (isCacheDisabled()) { + data = {}; + return; + } process.on("exit", save); process.nextTick(save); - if (!fs.existsSync(FILENAME)) return; + let cacheContent; try { - data = JSON.parse(fs.readFileSync(FILENAME)); - } catch (err) { - return; + cacheContent = fs.readFileSync(FILENAME); + } catch (e) { + switch (e.code) { + // check EACCES only as fs.readFileSync will never throw EPERM on Windows + // https://github.com/libuv/libuv/blob/076df64dbbda4320f93375913a728efc40e12d37/src/win/fs.c#L735 + case "EACCES": + console.warn( + `Babel could not read cache file: ${FILENAME} +due to a permission issue. Cache is disabled.`, + ); + cacheDisabled = true; + /* fall through */ + default: + return; + } } + + try { + data = JSON.parse(cacheContent); + } catch {} } /** diff --git a/packages/babel-register/test/cache.js b/packages/babel-register/test/cache.js index c9704cbdd3..f4fb2de2e8 100644 --- a/packages/babel-register/test/cache.js +++ b/packages/babel-register/test/cache.js @@ -7,12 +7,12 @@ const oldBabelDisableCacheValue = process.env.BABEL_DISABLE_CACHE; process.env.BABEL_CACHE_PATH = testCacheFilename; delete process.env.BABEL_DISABLE_CACHE; -function writeCache(data) { +function writeCache(data, mode = 0o666) { if (typeof data === "object") { data = JSON.stringify(data); } - fs.writeFileSync(testCacheFilename, data); + fs.writeFileSync(testCacheFilename, data, { mode }); } function cleanCache() { @@ -73,5 +73,39 @@ describe("@babel/register - caching", () => { expect(fs.existsSync(testCacheFilename)).toBe(true); expect(get()).toEqual({}); }); + + it("should create the cache after load", cb => { + load(); + + process.nextTick(() => { + expect(fs.existsSync(testCacheFilename)).toBe(true); + expect(get()).toEqual({}); + cb(); + }); + }); + + // Only write mode is effective on Windows + if (process.platform !== "win32") { + it("should be disabled when CACHE_PATH is not allowed to read", () => { + writeCache({ foo: "bar" }, 0o266); + + load(); + + expect(get()).toEqual({}); + }); + } + + it("should be disabled when CACHE_PATH is not allowed to write", cb => { + writeCache({ foo: "bar" }, 0o466); + + load(); + + expect(get()).toEqual({ foo: "bar" }); + process.nextTick(() => { + load(); + expect(get()).toEqual({}); + cb(); + }); + }); }); });