diff --git a/packages/babel-register/package.json b/packages/babel-register/package.json index de6c6897fd65..1bef5676c5fc 100644 --- a/packages/babel-register/package.json +++ b/packages/babel-register/package.json @@ -15,5 +15,8 @@ "lodash": "^4.2.0", "mkdirp": "^0.5.1", "source-map-support": "^0.4.2" + }, + "devDependencies": { + "decache": "^4.1.0" } } diff --git a/packages/babel-register/src/cache.js b/packages/babel-register/src/cache.js index 4b08e63ac753..204ac040196a 100644 --- a/packages/babel-register/src/cache.js +++ b/packages/babel-register/src/cache.js @@ -3,18 +3,20 @@ import fs from "fs"; import { sync as mkdirpSync } from "mkdirp"; import homeOrTmp from "home-or-tmp"; -const FILENAME = process.env.BABEL_CACHE_PATH || path.join(homeOrTmp, ".babel.json"); -let data = {}; +const FILENAME: string = process.env.BABEL_CACHE_PATH || path.join(homeOrTmp, ".babel.json"); +let data: Object = {}; /** * Write stringified cache to disk. */ export function save() { - let serialised = {}; + let serialised: string = "{}"; + try { serialised = JSON.stringify(data, null, " "); } catch (err) { + if (err.message === "Invalid string length") { err.message = "Cache too large so it's been cleared."; console.error(err.stack); @@ -22,6 +24,7 @@ export function save() { throw err; } } + mkdirpSync(path.dirname(FILENAME)); fs.writeFileSync(FILENAME, serialised); } @@ -49,6 +52,6 @@ export function load() { * Retrieve data from cache. */ -export function get() { +export function get(): Object { return data; } diff --git a/packages/babel-register/test/index.js b/packages/babel-register/test/index.js new file mode 100644 index 000000000000..e736f3e48495 --- /dev/null +++ b/packages/babel-register/test/index.js @@ -0,0 +1,84 @@ +import { expect } from "chai"; +import fs from "fs"; +import path from "path"; +import decache from "decache"; + +const testCacheFilename = path.join(__dirname, ".babel"); +const oldBabelDisableCacheValue = process.env.BABEL_DISABLE_CACHE; + +process.env.BABEL_CACHE_PATH = testCacheFilename; +delete process.env.BABEL_DISABLE_CACHE; + +function writeCache(data) { + if (typeof data === "object") { + data = JSON.stringify(data); + } + + fs.writeFileSync(testCacheFilename, data); +} + +function cleanCache() { + + try { + fs.unlinkSync(testCacheFilename); + } catch (e) { + // It is convenient to always try to clear + } +} + +function resetCache() { + process.env.BABEL_CACHE_PATH = null; + process.env.BABEL_DISABLE_CACHE = oldBabelDisableCacheValue; +} + +describe("babel register", () => { + + describe("cache", () => { + let load, get, save; + + beforeEach(() => { + // Since lib/cache is a singleton we need to fully reload it + decache("../lib/cache"); + const cache = require("../lib/cache"); + + load = cache.load; + get = cache.get; + save = cache.save; + }); + + afterEach(cleanCache); + after(resetCache); + + it("should load and get cached data", () => { + writeCache({ foo: "bar" }); + + load(); + + expect(get()).to.be.an("object"); + expect(get()).to.deep.equal({ foo: "bar" }); + }); + + it("should load and get an object with no cached data", () => { + load(); + + expect(get()).to.be.an("object"); + expect(get()).to.deep.equal({}); + }); + + it("should load and get an object with invalid cached data", () => { + writeCache("foobar"); + + load(); + + expect(get()).to.be.an("object"); + expect(get()).to.deep.equal({}); + }); + + it("should create the cache on save", () => { + save(); + + expect(fs.existsSync(testCacheFilename)).to.be.true; + expect(get()).to.deep.equal({}); + }); + }); +});